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 f5caa96

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

File tree

15 files changed

+1165
-8
lines changed

15 files changed

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

0 commit comments

Comments
 (0)