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 cf724ec

Browse files
committed
feat: Add split-view component
1 parent 0c4c33e commit cf724ec

File tree

13 files changed

+923
-8
lines changed

13 files changed

+923
-8
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: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,47 @@
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_ARTIFACT_SIZE = 360;
16+
1117
const AIDrawer = () => {
12-
return (
13-
<Box padding="m">
18+
const [hasArtifact, setHasArtifact] = useState(false);
19+
const [artifactLoaded, setArtifactLoaded] = useState(false);
20+
const [chatSize, setChatSize] = useState(CHAT_SIZE);
21+
const [_maxPanelSize, ref] = useContainerQuery(entry => entry.contentBoxWidth - MIN_ARTIFACT_SIZE);
22+
const maxPanelSize = _maxPanelSize ?? Number.MAX_SAFE_INTEGER;
23+
const constrainedChatSize = Math.min(chatSize, maxPanelSize);
24+
25+
const chatContent = (
26+
<>
1427
<Box variant="h2" padding={{ bottom: 'm' }}>
1528
Chat demo
1629
</Box>
1730
<Button
1831
onClick={() => {
32+
setHasArtifact(true);
1933
updateDrawer({ type: 'expandDrawer', payload: { id: 'ai-panel' } });
34+
updateDrawer({
35+
type: 'updateDrawerConfig',
36+
payload: {
37+
id: 'ai-panel',
38+
defaultSize: DEFAULT_SIZE + CHAT_SIZE,
39+
minSize: DEFAULT_SIZE + CHAT_SIZE,
40+
} as any,
41+
});
2042
}}
2143
>
22-
expand programmatically
44+
Open artifact
2345
</Button>
2446
<Button
2547
onClick={() => {
@@ -39,15 +61,53 @@ const AIDrawer = () => {
3961
{new Array(100).fill(null).map((_, index) => (
4062
<div key={index}>Tela content</div>
4163
))}
64+
</>
65+
);
66+
const artifactContent = (
67+
<Box padding="m">
68+
<Box variant="h2" padding={{ bottom: 'm' }}>
69+
Artifact
70+
</Box>
71+
<Button
72+
onClick={() => {
73+
setHasArtifact(false);
74+
updateDrawer({ type: 'exitExpandedMode' });
75+
updateDrawer({
76+
type: 'updateDrawerConfig',
77+
payload: { id: 'ai-panel', defaultSize: DEFAULT_SIZE, minSize: DEFAULT_SIZE } as any,
78+
});
79+
}}
80+
>
81+
Close artifact panel
82+
</Button>
83+
<Button onClick={() => setArtifactLoaded(true)}>Load artifact details</Button>
84+
{artifactLoaded && new Array(100).fill(null).map((_, index) => <div key={index}>Tela content</div>)}
4285
</Box>
4386
);
87+
return (
88+
<div ref={ref} style={{ width: '100%', overflow: 'hidden' }}>
89+
{hasArtifact ? (
90+
<SplitView
91+
panelContent={chatContent}
92+
panelSize={constrainedChatSize}
93+
maxPanelSize={maxPanelSize}
94+
onResize={({ detail }) => setChatSize(detail.panelSize)}
95+
resizable={true}
96+
>
97+
<div className={styles['ai-artifact-panel']}>{artifactContent}</div>
98+
</SplitView>
99+
) : (
100+
<Box padding="m">{chatContent}</Box>
101+
)}
102+
</div>
103+
);
44104
};
45105

46106
registerLeftDrawer({
47107
id: 'ai-panel',
48108
resizable: true,
49109
isExpandable: true,
50-
defaultSize: 420,
110+
defaultSize: DEFAULT_SIZE,
51111
preserveInactiveContent: true,
52112

53113
ariaLabels: {
Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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, SpaceBetween } from '~components';
8+
import AppLayout from '~components/app-layout';
9+
import Button from '~components/button';
10+
import SplitView from '~components/split-view';
11+
12+
import AppContext, { AppContextType } from '../app/app-context';
13+
import ScreenshotArea from '../utils/screenshot-area';
14+
15+
interface SplitViewContentProps {
16+
longPanelContent: boolean;
17+
longMainContent: boolean;
18+
minPanelSize: number;
19+
maxPanelSize: number;
20+
minContentSize: number;
21+
}
22+
type PageContext = React.Context<AppContextType<Partial<SplitViewContentProps>>>;
23+
24+
const SplitViewContent = ({
25+
longPanelContent,
26+
longMainContent,
27+
minContentSize,
28+
minPanelSize,
29+
maxPanelSize,
30+
}: SplitViewContentProps) => {
31+
const [size, setSize] = useState(Math.max(200, minPanelSize));
32+
33+
const [_actualMaxPanelSize, ref] = useContainerQuery(
34+
entry => Math.min(entry.contentBoxWidth - minContentSize, maxPanelSize),
35+
[minContentSize, maxPanelSize]
36+
);
37+
const actualMaxPanelSize = _actualMaxPanelSize ?? maxPanelSize;
38+
const actualSize = Math.min(size, actualMaxPanelSize);
39+
40+
const collapsed = actualMaxPanelSize < minPanelSize;
41+
42+
return (
43+
<div ref={ref} style={{ height: '100%', overflow: 'hidden', backgroundColor: 'lightgrey' }}>
44+
{collapsed ? (
45+
'Collapsed view'
46+
) : (
47+
<SplitView
48+
panelSize={actualSize}
49+
minPanelSize={minPanelSize}
50+
maxPanelSize={actualMaxPanelSize}
51+
resizable={true}
52+
onResize={({ detail }) => setSize(detail.panelSize)}
53+
panelContent={
54+
<>
55+
<Header>Panel content</Header>
56+
{new Array(longPanelContent ? 20 : 1)
57+
.fill('Lorem ipsum dolor sit amet, consectetur adipiscing elit.')
58+
.map((t, i) => (
59+
<div key={i}>{t}</div>
60+
))}
61+
<Button>Button</Button>
62+
</>
63+
}
64+
>
65+
<Header>Main content</Header>
66+
{new Array(longMainContent ? 200 : 1)
67+
.fill('Lorem ipsum dolor sit amet, consectetur adipiscing elit.')
68+
.map((t, i) => (
69+
<div key={i}>{t}</div>
70+
))}
71+
<Button>button</Button>
72+
</SplitView>
73+
)}
74+
</div>
75+
);
76+
};
77+
78+
export default function SplitViewPage() {
79+
const {
80+
urlParams: {
81+
longPanelContent = false,
82+
longMainContent = false,
83+
minPanelSize = 200,
84+
maxPanelSize = 600,
85+
minContentSize = 600,
86+
},
87+
setUrlParams,
88+
} = useContext(AppContext as PageContext);
89+
90+
return (
91+
<>
92+
<ScreenshotArea disableAnimations={true} gutters={false}>
93+
<AppLayout
94+
navigation={
95+
<Drawer header="Settings">
96+
<SpaceBetween direction="vertical" size="m">
97+
<Checkbox
98+
checked={longMainContent}
99+
onChange={({ detail }) => setUrlParams({ longMainContent: detail.checked })}
100+
>
101+
Long main content
102+
</Checkbox>
103+
<Checkbox
104+
checked={longPanelContent}
105+
onChange={({ detail }) => setUrlParams({ longPanelContent: detail.checked })}
106+
>
107+
Long panel content
108+
</Checkbox>
109+
<FormField label="Minimum panel size">
110+
<Input
111+
type="number"
112+
value={minPanelSize.toString()}
113+
onChange={({ detail }) => setUrlParams({ minPanelSize: detail.value ? parseInt(detail.value) : 0 })}
114+
/>
115+
</FormField>
116+
<FormField label="Maximum panel size">
117+
<Input
118+
type="number"
119+
value={maxPanelSize.toString()}
120+
onChange={({ detail }) =>
121+
setUrlParams({ maxPanelSize: detail.value ? parseInt(detail.value) : Number.MAX_SAFE_INTEGER })
122+
}
123+
/>
124+
</FormField>
125+
<FormField label="Minimum content size">
126+
<Input
127+
type="number"
128+
value={minContentSize.toString()}
129+
onChange={({ detail }) =>
130+
setUrlParams({ minContentSize: detail.value ? parseInt(detail.value) : 0 })
131+
}
132+
/>
133+
</FormField>
134+
</SpaceBetween>
135+
</Drawer>
136+
}
137+
drawers={[
138+
{
139+
id: 'panel',
140+
content: (
141+
<SplitViewContent
142+
longMainContent={longMainContent}
143+
longPanelContent={longPanelContent}
144+
minPanelSize={minPanelSize}
145+
maxPanelSize={maxPanelSize}
146+
minContentSize={minContentSize}
147+
/>
148+
),
149+
resizable: true,
150+
defaultSize: 1000,
151+
ariaLabels: { drawerName: 'Panel' },
152+
trigger: { iconName: 'contact' },
153+
},
154+
]}
155+
/>
156+
</ScreenshotArea>
157+
</>
158+
);
159+
}

0 commit comments

Comments
 (0)