Skip to content

Commit 43caac6

Browse files
committed
feat(LayoutPane): add component
1 parent f5ed0c0 commit 43caac6

File tree

10 files changed

+668
-40
lines changed

10 files changed

+668
-40
lines changed

src/components/content/Layout/GridLayout.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { LayoutFlex } from './LayoutFlex';
1515
import { LayoutFooter } from './LayoutFooter';
1616
import { LayoutGrid } from './LayoutGrid';
1717
import { LayoutHeader } from './LayoutHeader';
18+
import { LayoutPane } from './LayoutPane';
1819
import { LayoutPanel } from './LayoutPanel';
1920
import { LayoutPanelHeader } from './LayoutPanelHeader';
2021
import { LayoutToolbar } from './LayoutToolbar';
@@ -48,9 +49,9 @@ function GridLayoutBase(
4849
<GridLayoutElement
4950
ref={ref}
5051
isGrid
51-
columns={columns}
52-
rows={rows}
53-
template={template}
52+
gridColumns={columns}
53+
gridRows={rows}
54+
gridTemplate={template}
5455
{...otherProps}
5556
/>
5657
);
@@ -72,6 +73,7 @@ interface GridLayoutComponent
7273
Block: typeof LayoutBlock;
7374
Flex: typeof LayoutFlex;
7475
Grid: typeof LayoutGrid;
76+
Pane: typeof LayoutPane;
7577
Panel: typeof LayoutPanel;
7678
PanelHeader: typeof LayoutPanelHeader;
7779
}
@@ -84,6 +86,7 @@ const GridLayout = Object.assign(_GridLayout, {
8486
Block: LayoutBlock,
8587
Flex: LayoutFlex,
8688
Grid: LayoutGrid,
89+
Pane: LayoutPane,
8790
Panel: LayoutPanel,
8891
PanelHeader: LayoutPanelHeader,
8992
}) as GridLayoutComponent;

src/components/content/Layout/Layout.stories.tsx

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ export const GridWithScrolling: Story = {
600600
render: () => (
601601
<Layout height="300px">
602602
<Layout.Header title="Scrollable Grid" />
603-
<Layout.Grid gap="1x">
603+
<Layout.Grid gap="1x" flow="column" padding="1x">
604604
{Array.from({ length: 16 }, (_, i) => (
605605
<Card key={i} width="100px" height="80px" whiteSpace="nowrap">
606606
Card {i + 1}
@@ -661,3 +661,98 @@ export const CollapsedWithWarning: Story = {
661661
</Block>
662662
),
663663
};
664+
665+
/**
666+
* Layout.Pane allows creating resizable inline sections within a layout.
667+
* Unlike Layout.Panel (which is absolutely positioned), Pane participates
668+
* in the normal flex/grid flow and can be resized via drag handles.
669+
*/
670+
export const ResizablePanes: Story = {
671+
render: function ResizablePanesRender() {
672+
const [leftSize, setLeftSize] = useState(250);
673+
const [middleSize, setMiddleSize] = useState(400);
674+
675+
return (
676+
<Layout height="100dvh">
677+
<Layout.Header
678+
title="Resizable Panes"
679+
subtitle="Drag the handles between panes to resize"
680+
/>
681+
<Layout.Flex>
682+
<Layout.Pane
683+
isResizable
684+
resizeEdge="right"
685+
size={leftSize}
686+
minSize={150}
687+
maxSize={400}
688+
fill={{ '': '#white', 'drag | focused': '#light' }}
689+
onSizeChange={setLeftSize}
690+
>
691+
<Title level={5}>Left Pane</Title>
692+
<Text>Width: {leftSize}px</Text>
693+
<Text preset="t3" color="#dark-02">
694+
Drag the right edge to resize. Double-click to reset.
695+
</Text>
696+
</Layout.Pane>
697+
698+
<Layout.Pane
699+
isResizable
700+
resizeEdge="right"
701+
size={middleSize}
702+
minSize={200}
703+
fill={{ '': '#white', 'drag | focused': '#light' }}
704+
onSizeChange={setMiddleSize}
705+
>
706+
<Title level={5}>Middle Pane</Title>
707+
<Text>Width: {middleSize}px</Text>
708+
<Text preset="t3" color="#dark-02">
709+
This pane has no maximum size constraint.
710+
</Text>
711+
</Layout.Pane>
712+
713+
<Layout.Content fill="#light" width="min 150px">
714+
<Title level={5}>Flexible Content</Title>
715+
<Text>This area fills the remaining space.</Text>
716+
</Layout.Content>
717+
</Layout.Flex>
718+
</Layout>
719+
);
720+
},
721+
};
722+
723+
/**
724+
* Panes can also be resized vertically using the bottom edge.
725+
*/
726+
export const VerticalResizablePanes: Story = {
727+
render: function VerticalResizablePanesRender() {
728+
const [topSize, setTopSize] = useState(200);
729+
730+
return (
731+
<Layout height="100dvh">
732+
<Layout.Header title="Vertical Panes" />
733+
<Layout.Flex flow="column" flexShrink={1}>
734+
<Layout.Pane
735+
isResizable
736+
resizeEdge="bottom"
737+
size={topSize}
738+
minSize={100}
739+
maxSize={400}
740+
fill="#light"
741+
onSizeChange={setTopSize}
742+
>
743+
<Title level={5}>Top Pane</Title>
744+
<Text>Height: {topSize}px</Text>
745+
<Text preset="t3" color="#dark-02">
746+
Drag the bottom edge to resize.
747+
</Text>
748+
</Layout.Pane>
749+
750+
<Layout.Content fill="#white" flexShrink={0}>
751+
<Title level={5}>Bottom Content</Title>
752+
<Text>This area fills the remaining vertical space.</Text>
753+
</Layout.Content>
754+
</Layout.Flex>
755+
</Layout>
756+
);
757+
},
758+
};

src/components/content/Layout/Layout.tsx

Lines changed: 37 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,13 @@ import {
1313

1414
import {
1515
BaseProps,
16-
BLOCK_STYLES,
1716
BlockStyleProps,
18-
COLOR_STYLES,
1917
ColorStyleProps,
2018
extractStyles,
2119
filterBaseProps,
22-
FLOW_STYLES,
2320
FlowStyleProps,
21+
INNER_STYLES,
22+
mergeStyles,
2423
OUTER_STYLES,
2524
OuterStyleProps,
2625
Styles,
@@ -31,12 +30,6 @@ import { Alert } from '../Alert';
3130

3231
import { LayoutProvider, useLayoutContext } from './LayoutContext';
3332

34-
const INNER_STYLE_KEYS = [
35-
...BLOCK_STYLES,
36-
...COLOR_STYLES,
37-
...FLOW_STYLES,
38-
] as const;
39-
4033
const LayoutElement = tasty({
4134
as: 'div',
4235
qa: 'Layout',
@@ -144,36 +137,47 @@ function LayoutInner(
144137
return { panels: panelElements, content: contentElements };
145138
}, [children]);
146139

147-
// Extract inner content styles using extractStyles
148-
const contentStyles = extractStyles(otherProps, INNER_STYLE_KEYS);
140+
// Extract outer wrapper styles and inner content styles
141+
const outerStyles = extractStyles(otherProps, OUTER_STYLES);
142+
const innerStyles = extractStyles(otherProps, INNER_STYLES);
149143

150-
// Extract outer wrapper styles
151-
const wrapperStyles = extractStyles(otherProps, OUTER_STYLES);
144+
// Merge styles using the same pattern as LayoutPane
145+
const finalStyles = useMemo(() => {
146+
// Handle grid mode and grid properties
147+
const gridStyles: Styles = {};
152148

153-
// Handle grid mode
154-
if (isGrid) {
155-
contentStyles.display = 'grid';
156-
}
149+
if (isGrid) {
150+
gridStyles.display = 'grid';
151+
}
157152

158-
if (columns) {
159-
contentStyles.gridColumns = columns;
160-
}
153+
if (columns) {
154+
gridStyles.gridColumns = columns;
155+
}
161156

162-
if (rows) {
163-
contentStyles.gridRows = rows;
164-
}
157+
if (rows) {
158+
gridStyles.gridRows = rows;
159+
}
165160

166-
if (template) {
167-
contentStyles.gridTemplate = template;
168-
}
161+
if (template) {
162+
gridStyles.gridTemplate = template;
163+
}
169164

170-
// Merge styles
171-
const finalStyles: Styles = {
172-
...wrapperStyles,
173-
...(contentPadding != null && { '$content-padding': contentPadding }),
174-
...styles,
175-
Inner: { ...contentStyles, ...(styles?.Inner as Styles) },
176-
};
165+
return mergeStyles(
166+
outerStyles,
167+
contentPadding != null ? { '$content-padding': contentPadding } : null,
168+
styles,
169+
{ Inner: mergeStyles(innerStyles, gridStyles, styles?.Inner as Styles) },
170+
);
171+
}, [
172+
outerStyles,
173+
innerStyles,
174+
contentPadding,
175+
styles,
176+
isGrid,
177+
columns,
178+
rows,
179+
template,
180+
]);
177181

178182
// Calculate inset values from panel sizes
179183
const panelSizes = layoutContext?.panelSizes ?? {

src/components/content/Layout/LayoutContent.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const ContentElement = tasty({
3333
boxSizing: 'content-box',
3434

3535
Inner: {
36+
$: '>',
3637
display: 'flex',
3738
flow: 'column',
3839
padding: '($content-padding, 1x)',

src/components/content/Layout/LayoutFlex.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const FlexElement = tasty(LayoutContent, {
1212

1313
Inner: {
1414
display: 'flex',
15+
flow: 'row',
16+
padding: 0,
1517
},
1618
},
1719
});

src/components/content/Layout/LayoutGrid.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ const GridElement = tasty(LayoutContent, {
1212

1313
Inner: {
1414
display: 'grid',
15+
flow: 'row',
16+
padding: 0,
1517
},
1618
},
1719
});

0 commit comments

Comments
 (0)