WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 0729bc2

Browse files
committed
feat: Add split-view component
1 parent 83500f3 commit 0729bc2

File tree

18 files changed

+1386
-10
lines changed

18 files changed

+1386
-10
lines changed

build-tools/utils/pluralize.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ const pluralizationMap = {
6969
SpaceBetween: 'SpaceBetweens',
7070
Spinner: 'Spinners',
7171
SplitPanel: 'SplitPanels',
72+
SplitView: 'SplitViews',
7273
StatusIndicator: 'StatusIndicators',
7374
Steps: 'Steps',
7475
Table: 'Tables',

pages/app-layout/utils/external-global-left-panel-widget.tsx

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,49 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
3-
import React from 'react';
3+
import React, { useState } from 'react';
44

5-
import { Box, Button } from '~components';
5+
import { useContainerQuery } from '@cloudscape-design/component-toolkit';
6+
7+
import { Box, Button, SplitView } from '~components';
68
import { registerLeftDrawer, updateDrawer } from '~components/internal/plugins/widget';
79
import { mount, unmount } from '~mount';
810

911
import styles from '../styles.scss';
1012

13+
const DEFAULT_SIZE = 360;
14+
const CHAT_SIZE = 280;
15+
const MIN_CHAT_SIZE = 150;
16+
const MIN_ARTIFACT_SIZE = 360;
17+
1118
const AIDrawer = () => {
12-
return (
13-
<Box padding="m">
19+
const [hasArtifact, setHasArtifact] = useState(false);
20+
const [artifactLoaded, setArtifactLoaded] = useState(false);
21+
const [chatSize, setChatSize] = useState(CHAT_SIZE);
22+
const [_maxPanelSize, ref] = useContainerQuery(entry => entry.contentBoxWidth - MIN_ARTIFACT_SIZE);
23+
const maxPanelSize = _maxPanelSize ?? Number.MAX_SAFE_INTEGER;
24+
const constrainedChatSize = Math.min(chatSize, maxPanelSize);
25+
const collapsed = constrainedChatSize < MIN_CHAT_SIZE;
26+
27+
const chatContent = (
28+
<>
1429
<Box variant="h2" padding={{ bottom: 'm' }}>
1530
Chat demo
1631
</Box>
1732
<Button
1833
onClick={() => {
34+
setHasArtifact(true);
1935
updateDrawer({ type: 'expandDrawer', payload: { id: 'ai-panel' } });
36+
updateDrawer({
37+
type: 'updateDrawerConfig',
38+
payload: {
39+
id: 'ai-panel',
40+
defaultSize: DEFAULT_SIZE + CHAT_SIZE,
41+
minSize: DEFAULT_SIZE + CHAT_SIZE,
42+
} as any,
43+
});
2044
}}
2145
>
22-
expand programmatically
46+
Open artifact
2347
</Button>
2448
<Button
2549
onClick={() => {
@@ -39,15 +63,50 @@ const AIDrawer = () => {
3963
{new Array(100).fill(null).map((_, index) => (
4064
<div key={index}>Tela content</div>
4165
))}
66+
</>
67+
);
68+
const artifactContent = (
69+
<Box padding="m">
70+
<Box variant="h2" padding={{ bottom: 'm' }}>
71+
Artifact
72+
</Box>
73+
<Button
74+
onClick={() => {
75+
setHasArtifact(false);
76+
updateDrawer({ type: 'exitExpandedMode' });
77+
updateDrawer({
78+
type: 'updateDrawerConfig',
79+
payload: { id: 'ai-panel', defaultSize: DEFAULT_SIZE, minSize: DEFAULT_SIZE } as any,
80+
});
81+
}}
82+
>
83+
Close artifact panel
84+
</Button>
85+
<Button onClick={() => setArtifactLoaded(true)}>Load artifact details</Button>
86+
{artifactLoaded && new Array(100).fill(null).map((_, index) => <div key={index}>Tela content</div>)}
4287
</Box>
4388
);
89+
return (
90+
<div ref={ref} style={{ width: '100%', overflow: 'hidden' }}>
91+
<SplitView
92+
resizable={true}
93+
panelSize={constrainedChatSize}
94+
maxPanelSize={maxPanelSize}
95+
minPanelSize={MIN_CHAT_SIZE}
96+
onPanelResize={({ detail }) => setChatSize(detail.panelSize)}
97+
panelContent={chatContent}
98+
mainContent={<div className={styles['ai-artifact-panel']}>{artifactContent}</div>}
99+
display={hasArtifact ? (collapsed ? 'main-only' : 'all') : 'panel-only'}
100+
/>
101+
</div>
102+
);
44103
};
45104

46105
registerLeftDrawer({
47106
id: 'ai-panel',
48107
resizable: true,
49108
isExpandable: true,
50-
defaultSize: 420,
109+
defaultSize: DEFAULT_SIZE,
51110
preserveInactiveContent: true,
52111

53112
ariaLabels: {
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
import React, { useContext, useState } from 'react';
4+
5+
import { useContainerQuery } from '@cloudscape-design/component-toolkit';
6+
7+
import { Checkbox, Drawer, FormField, Header, Input, SegmentedControl, SpaceBetween } from '~components';
8+
import AppLayout from '~components/app-layout';
9+
import Button from '~components/button';
10+
import SplitView, { SplitViewProps } from '~components/split-view';
11+
12+
import AppContext, { AppContextType } from '../app/app-context';
13+
import labels from '../app-layout/utils/labels';
14+
import ScreenshotArea from '../utils/screenshot-area';
15+
16+
interface SplitViewContentProps {
17+
longPanelContent: boolean;
18+
longMainContent: boolean;
19+
minPanelSize: number;
20+
maxPanelSize: number;
21+
minContentSize: number;
22+
display: SplitViewProps.Display;
23+
}
24+
type PageContext = React.Context<AppContextType<Partial<SplitViewContentProps>>>;
25+
26+
const SplitViewContent = ({
27+
longPanelContent,
28+
longMainContent,
29+
minContentSize,
30+
minPanelSize,
31+
maxPanelSize,
32+
display,
33+
}: SplitViewContentProps) => {
34+
const [size, setSize] = useState(Math.max(200, minPanelSize));
35+
36+
const [_actualMaxPanelSize, ref] = useContainerQuery(
37+
entry => Math.min(entry.contentBoxWidth - minContentSize, maxPanelSize),
38+
[minContentSize, maxPanelSize]
39+
);
40+
const actualMaxPanelSize = _actualMaxPanelSize ?? maxPanelSize;
41+
const actualSize = Math.min(size, actualMaxPanelSize);
42+
43+
const collapsed = actualMaxPanelSize < minPanelSize;
44+
45+
return (
46+
<div ref={ref} style={{ height: '100%', overflow: 'hidden', backgroundColor: 'lightgrey' }}>
47+
{collapsed ? (
48+
'Collapsed view'
49+
) : (
50+
<SplitView
51+
panelSize={actualSize}
52+
minPanelSize={minPanelSize}
53+
maxPanelSize={actualMaxPanelSize}
54+
resizable={true}
55+
onPanelResize={({ detail }) => setSize(detail.panelSize)}
56+
display={display}
57+
panelContent={
58+
<>
59+
<Header>Panel content</Header>
60+
{new Array(longPanelContent ? 20 : 1)
61+
.fill('Lorem ipsum dolor sit amet, consectetur adipiscing elit.')
62+
.map((t, i) => (
63+
<div key={i}>{t}</div>
64+
))}
65+
<Button>Button</Button>
66+
</>
67+
}
68+
mainContent={
69+
<>
70+
<Header>Main content</Header>
71+
{new Array(longMainContent ? 200 : 1)
72+
.fill('Lorem ipsum dolor sit amet, consectetur adipiscing elit.')
73+
.map((t, i) => (
74+
<div key={i}>{t}</div>
75+
))}
76+
<Button>button</Button>
77+
</>
78+
}
79+
/>
80+
)}
81+
</div>
82+
);
83+
};
84+
85+
export default function SplitViewPage() {
86+
const {
87+
urlParams: {
88+
longPanelContent = false,
89+
longMainContent = false,
90+
minPanelSize = 200,
91+
maxPanelSize = 600,
92+
minContentSize = 600,
93+
display = 'all',
94+
},
95+
setUrlParams,
96+
} = useContext(AppContext as PageContext);
97+
98+
return (
99+
<>
100+
<ScreenshotArea disableAnimations={true} gutters={false}>
101+
<AppLayout
102+
ariaLabels={labels}
103+
navigation={
104+
<Drawer header="Settings">
105+
<SpaceBetween direction="vertical" size="m">
106+
<Checkbox
107+
checked={longMainContent}
108+
onChange={({ detail }) => setUrlParams({ longMainContent: detail.checked })}
109+
>
110+
Long main content
111+
</Checkbox>
112+
<Checkbox
113+
checked={longPanelContent}
114+
onChange={({ detail }) => setUrlParams({ longPanelContent: detail.checked })}
115+
>
116+
Long panel content
117+
</Checkbox>
118+
<FormField label="Minimum panel size">
119+
<Input
120+
type="number"
121+
value={minPanelSize.toString()}
122+
onChange={({ detail }) => setUrlParams({ minPanelSize: detail.value ? parseInt(detail.value) : 0 })}
123+
/>
124+
</FormField>
125+
<FormField label="Maximum panel size">
126+
<Input
127+
type="number"
128+
value={maxPanelSize.toString()}
129+
onChange={({ detail }) =>
130+
setUrlParams({ maxPanelSize: detail.value ? parseInt(detail.value) : Number.MAX_SAFE_INTEGER })
131+
}
132+
/>
133+
</FormField>
134+
<FormField label="Minimum content size">
135+
<Input
136+
type="number"
137+
value={minContentSize.toString()}
138+
onChange={({ detail }) =>
139+
setUrlParams({ minContentSize: detail.value ? parseInt(detail.value) : 0 })
140+
}
141+
/>
142+
</FormField>
143+
<FormField label="Display">
144+
<SegmentedControl
145+
options={[
146+
{ id: 'all', text: 'all' },
147+
{ id: 'panel-only', text: 'panel-only' },
148+
{ id: 'main-only', text: 'main-only' },
149+
]}
150+
selectedId={display}
151+
onChange={({ detail }) => setUrlParams({ display: detail.selectedId as any })}
152+
/>
153+
</FormField>
154+
</SpaceBetween>
155+
</Drawer>
156+
}
157+
content={<Header variant="h1">Split view in drawer demo</Header>}
158+
drawers={[
159+
{
160+
id: 'panel',
161+
content: (
162+
<SplitViewContent
163+
longMainContent={longMainContent}
164+
longPanelContent={longPanelContent}
165+
minPanelSize={minPanelSize}
166+
maxPanelSize={maxPanelSize}
167+
minContentSize={minContentSize}
168+
display={display}
169+
/>
170+
),
171+
resizable: true,
172+
defaultSize: 1000,
173+
ariaLabels: {
174+
drawerName: 'Panel',
175+
triggerButton: 'Open panel',
176+
closeButton: 'Close panel',
177+
resizeHandle: 'Resize drawer',
178+
},
179+
trigger: { iconName: 'contact' },
180+
},
181+
]}
182+
/>
183+
</ScreenshotArea>
184+
</>
185+
);
186+
}

0 commit comments

Comments
 (0)