@czi-sds/mcp 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/data/component-docs/Accordion.md +291 -0
- package/data/component-docs/Autocomplete.md +525 -0
- package/data/component-docs/Banner.md +231 -0
- package/data/component-docs/Button.md +224 -0
- package/data/component-docs/ButtonDropdown.md +256 -0
- package/data/component-docs/ButtonGroup.md +320 -0
- package/data/component-docs/ButtonToggle.md +397 -0
- package/data/component-docs/Callout.md +268 -0
- package/data/component-docs/CellBasic.md +122 -0
- package/data/component-docs/CellComponent.md +63 -0
- package/data/component-docs/CellHeader.md +128 -0
- package/data/component-docs/ComplexFilter.md +294 -0
- package/data/component-docs/ContentCard.md +406 -0
- package/data/component-docs/Dialog.md +651 -0
- package/data/component-docs/Dropdown.md +155 -0
- package/data/component-docs/DropdownMenu.md +949 -0
- package/data/component-docs/HeatmapChart.md +782 -0
- package/data/component-docs/Hero.md +363 -0
- package/data/component-docs/Icon.md +283 -0
- package/data/component-docs/InputCheckbox.md +205 -0
- package/data/component-docs/InputDropdown.md +482 -0
- package/data/component-docs/InputRadio.md +173 -0
- package/data/component-docs/InputSearch.md +81 -0
- package/data/component-docs/InputSlider.md +189 -0
- package/data/component-docs/InputText.md +110 -0
- package/data/component-docs/InputToggle.md +141 -0
- package/data/component-docs/IntentMessage.md +293 -0
- package/data/component-docs/Legend.md +442 -0
- package/data/component-docs/Link.md +23 -0
- package/data/component-docs/List.md +300 -0
- package/data/component-docs/LoadingIndicator.md +198 -0
- package/data/component-docs/Menu.md +298 -0
- package/data/component-docs/MenuItem.md +100 -0
- package/data/component-docs/NavigationFooter.md +165 -0
- package/data/component-docs/NavigationHeader.md +500 -0
- package/data/component-docs/NavigationJumpTo.md +414 -0
- package/data/component-docs/Notification.md +288 -0
- package/data/component-docs/Pagination.md +67 -0
- package/data/component-docs/Panel.md +636 -0
- package/data/component-docs/Popover.md +448 -0
- package/data/component-docs/PreComposedTable.md +2947 -0
- package/data/component-docs/SegmentedControl.md +396 -0
- package/data/component-docs/StackedBarChart.md +475 -0
- package/data/component-docs/Table.md +2000 -0
- package/data/component-docs/TableHeader.md +53 -0
- package/data/component-docs/TableRow.md +74 -0
- package/data/component-docs/Tabs.md +455 -0
- package/data/component-docs/Tag.md +647 -0
- package/data/component-docs/TagFilter.md +131 -0
- package/data/component-docs/Tooltip.md +561 -0
- package/data/component-docs/TooltipCondensed.md +203 -0
- package/data/component-docs/TooltipTable.md +145 -0
- package/data/component-docs/index.json +262 -0
- package/data/component-list.json +2 -5
- package/data/component-props/Accordion.json +31 -11
- package/data/component-props/Alert.json +3 -3
- package/data/component-props/Autocomplete.json +150 -15
- package/data/component-props/Banner.json +31 -17
- package/data/component-props/Button.json +45 -20
- package/data/component-props/ButtonDropdown.json +36 -17
- package/data/component-props/ButtonDropdownLegacy.json +11 -11
- package/data/component-props/ButtonGroup.json +32 -11
- package/data/component-props/ButtonIcon.json +10 -10
- package/data/component-props/ButtonLegacy.json +19 -19
- package/data/component-props/ButtonToggle.json +32 -19
- package/data/component-props/ButtonToggleLegacy.json +11 -11
- package/data/component-props/Callout.json +58 -17
- package/data/component-props/CellBasic.json +94 -41
- package/data/component-props/CellComponent.json +23 -7
- package/data/component-props/CellHeader.json +62 -19
- package/data/component-props/ComplexFilter.json +52 -21
- package/data/component-props/ContentCard.json +70 -47
- package/data/component-props/Dialog.json +47 -14
- package/data/component-props/Dropdown.json +68 -31
- package/data/component-props/DropdownMenu.json +128 -23
- package/data/component-props/HeatmapChart.json +88 -12
- package/data/component-props/Hero.json +80 -39
- package/data/component-props/Icon.json +20 -9
- package/data/component-props/InputCheckbox.json +36 -14
- package/data/component-props/InputDropdown.json +75 -27
- package/data/component-props/InputRadio.json +31 -13
- package/data/component-props/InputSearch.json +41 -18
- package/data/component-props/InputSlider.json +46 -19
- package/data/component-props/InputText.json +26 -19
- package/data/component-props/InputToggle.json +26 -9
- package/data/component-props/IntentMessage.json +38 -9
- package/data/component-props/Link.json +13 -7
- package/data/component-props/LoadingIndicator.json +11 -3
- package/data/component-props/MenuItem.json +38 -19
- package/data/component-props/MenuSelect.json +8 -8
- package/data/component-props/NavigationFooter.json +49 -19
- package/data/component-props/NavigationHeader.json +150 -37
- package/data/component-props/NavigationJumpTo.json +33 -7
- package/data/component-props/Notification.json +39 -21
- package/data/component-props/Pagination.json +39 -11
- package/data/component-props/Panel.json +62 -9
- package/data/component-props/Popover.json +80 -7
- package/data/component-props/PreComposedTable.json +99 -27
- package/data/component-props/SegmentedControl.json +42 -3
- package/data/component-props/StackedBarChart.json +116 -42
- package/data/component-props/TableRow.json +38 -19
- package/data/component-props/Tabs.json +66 -9
- package/data/component-props/Tag.json +34 -17
- package/data/component-props/TagFilter.json +10 -3
- package/data/component-props/Tooltip.json +49 -18
- package/data/component-props/TooltipCondensed.json +18 -9
- package/data/component-props/TooltipTable.json +17 -7
- package/data/resources/{zeroheight-rules.md → documentation-rules.md} +43 -30
- package/data/resources/figma-rules.md +2 -2
- package/dist/stdio.js +112 -208
- package/package.json +9 -5
- package/data/component-props/ButtonDropdownV2.json +0 -34
- package/data/component-props/ButtonNew.json +0 -47
- package/data/component-props/ButtonToggleV2.json +0 -44
- package/data/component-props/ButtonV2.json +0 -43
- package/data/zeroheight-cache.json +0 -10259
- package/data/zeroheight-exports/Accordion.md +0 -774
- package/data/zeroheight-exports/Banner.md +0 -691
- package/data/zeroheight-exports/Borders.md +0 -117
- package/data/zeroheight-exports/Buttons.md +0 -1854
- package/data/zeroheight-exports/Callout.md +0 -931
- package/data/zeroheight-exports/Colors.md +0 -1755
- package/data/zeroheight-exports/Content-Card.md +0 -309
- package/data/zeroheight-exports/Contributing-to-SDS.md +0 -98
- package/data/zeroheight-exports/Control-Inputs.md +0 -2818
- package/data/zeroheight-exports/Corners.md +0 -104
- package/data/zeroheight-exports/Cover-page.md +0 -3
- package/data/zeroheight-exports/Dialog.md +0 -1367
- package/data/zeroheight-exports/Downloads.md +0 -236
- package/data/zeroheight-exports/Drop-Shadows.md +0 -79
- package/data/zeroheight-exports/Dropdown-Input.md +0 -1776
- package/data/zeroheight-exports/Dropdown-Menus.md +0 -913
- package/data/zeroheight-exports/Element-Status-Tracker.md +0 -267
- package/data/zeroheight-exports/Field-Inputs.md +0 -1405
- package/data/zeroheight-exports/Filters.md +0 -1111
- package/data/zeroheight-exports/Getting-Started.md +0 -1157
- package/data/zeroheight-exports/Hero.md +0 -414
- package/data/zeroheight-exports/Icons.md +0 -2067
- package/data/zeroheight-exports/Intent.md +0 -86
- package/data/zeroheight-exports/Introduction-to-Bases.md +0 -35
- package/data/zeroheight-exports/Introduction-to-Cells.md +0 -35
- package/data/zeroheight-exports/Introduction-to-DNA.md +0 -35
- package/data/zeroheight-exports/Introduction-to-Genes.md +0 -35
- package/data/zeroheight-exports/Introduction.md +0 -115
- package/data/zeroheight-exports/Link.md +0 -432
- package/data/zeroheight-exports/Lists.md +0 -986
- package/data/zeroheight-exports/Loading-Indicator.md +0 -41
- package/data/zeroheight-exports/Navigation.md +0 -1190
- package/data/zeroheight-exports/Notification.md +0 -1038
- package/data/zeroheight-exports/Panel.md +0 -1030
- package/data/zeroheight-exports/README.md +0 -122
- package/data/zeroheight-exports/Responsive-Design.md +0 -330
- package/data/zeroheight-exports/Search-Input.md +0 -91
- package/data/zeroheight-exports/Search.md +0 -822
- package/data/zeroheight-exports/Segmented-Control.md +0 -609
- package/data/zeroheight-exports/Spacing.md +0 -297
- package/data/zeroheight-exports/Table.md +0 -5345
- package/data/zeroheight-exports/Tabs.md +0 -1324
- package/data/zeroheight-exports/Tags.md +0 -1545
- package/data/zeroheight-exports/Text-Link.md +0 -268
- package/data/zeroheight-exports/Theming.md +0 -362
- package/data/zeroheight-exports/Tooltips.md +0 -2015
- package/data/zeroheight-exports/Typography.md +0 -800
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
# Panel
|
|
2
|
+
|
|
3
|
+
## Source Code
|
|
4
|
+
|
|
5
|
+
The component's source code in the SDS codebase can be found here.
|
|
6
|
+
|
|
7
|
+
## Import
|
|
8
|
+
|
|
9
|
+
**React TypeScript**
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
import { Panel } from "@czi-sds/components";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Code examples
|
|
16
|
+
|
|
17
|
+
> **Note:** A panel fixes itself to an edge of the viewport, which on this page means an edge of the docs rather than of the example. Each example below therefore holds its panel inside its own frame, with a wrapper that fixed positioning resolves against and, for the overlay panels, a container for the portal and another for the transition, so the panel slides the width of the frame rather than of the window. None of that belongs in an application.
|
|
18
|
+
|
|
19
|
+
### Basic panel
|
|
20
|
+
|
|
21
|
+
A persistent panel on the left, with the page's content shifted by the same width so the two do not overlap.
|
|
22
|
+
|
|
23
|
+
**Example: BasicPanel**
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
// sdsType="basic" is a persistent drawer: it sits in the page rather than over
|
|
27
|
+
// it, and nothing closes it but your own control. It does not move the page's
|
|
28
|
+
// content, so shift that yourself by the same width you gave the panel.
|
|
29
|
+
//
|
|
30
|
+
// Panel fixes itself to the edge of the viewport, which on this page is the edge
|
|
31
|
+
// of the docs rather than of the example. Stage carries a transform, which makes
|
|
32
|
+
// it the box that fixed positioning resolves against, so the panel stays in the
|
|
33
|
+
// frame, flush against its edge as it would be against the window's. A real page
|
|
34
|
+
// wants the viewport and needs none of this, and the padding below stands in for
|
|
35
|
+
// whatever spacing the page's own content already has.
|
|
36
|
+
|
|
37
|
+
import {
|
|
38
|
+
Button,
|
|
39
|
+
Icon,
|
|
40
|
+
Panel,
|
|
41
|
+
fontBodyS,
|
|
42
|
+
getSemanticColors,
|
|
43
|
+
type CommonThemeProps,
|
|
44
|
+
} from "@czi-sds/components";
|
|
45
|
+
import styled from "@emotion/styled";
|
|
46
|
+
import { useState } from "react";
|
|
47
|
+
|
|
48
|
+
const PANEL_WIDTH = 280;
|
|
49
|
+
|
|
50
|
+
const Stage = styled.div`
|
|
51
|
+
height: 320px;
|
|
52
|
+
overflow: clip;
|
|
53
|
+
transform: translateZ(0);
|
|
54
|
+
`;
|
|
55
|
+
|
|
56
|
+
const Content = styled.div<CommonThemeProps & { shifted: boolean }>`
|
|
57
|
+
${fontBodyS}
|
|
58
|
+
|
|
59
|
+
${(props) => {
|
|
60
|
+
const semanticColors = getSemanticColors(props);
|
|
61
|
+
|
|
62
|
+
return `
|
|
63
|
+
color: ${semanticColors?.base?.textPrimary};
|
|
64
|
+
margin-left: ${props.shifted ? `${PANEL_WIDTH}px` : "0"};
|
|
65
|
+
padding: 50px;
|
|
66
|
+
transition: margin-left 225ms ease-out;
|
|
67
|
+
`;
|
|
68
|
+
}}
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
function App() {
|
|
72
|
+
const [open, setOpen] = useState(true);
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div className="app">
|
|
76
|
+
<Stage>
|
|
77
|
+
<Panel open={open} sdsType="basic" width={PANEL_WIDTH}>
|
|
78
|
+
Filters, dataset pickers, and anything else that changes what the page
|
|
79
|
+
shows.
|
|
80
|
+
</Panel>
|
|
81
|
+
|
|
82
|
+
<Content shifted={open}>
|
|
83
|
+
<Button
|
|
84
|
+
onClick={() => setOpen((prev) => !prev)}
|
|
85
|
+
sdsStyle="solid"
|
|
86
|
+
sdsType="primary"
|
|
87
|
+
startIcon={<Icon sdsIcon="Filter" sdsSize="s" />}
|
|
88
|
+
>
|
|
89
|
+
{open ? "Hide filters" : "Show filters"}
|
|
90
|
+
</Button>
|
|
91
|
+
|
|
92
|
+
<p>
|
|
93
|
+
The page's own content moves aside for a basic panel, so the two
|
|
94
|
+
never overlap.
|
|
95
|
+
</p>
|
|
96
|
+
</Content>
|
|
97
|
+
</Stage>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export default App;
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Overlay panel
|
|
106
|
+
|
|
107
|
+
An overlay panel on the right with a header, the default close button, and `isBackdropClickEnabled` so a click outside closes it through `onClose`.
|
|
108
|
+
|
|
109
|
+
**Example: OverlayPanel**
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
// sdsType="overlay" is a temporary drawer: it floats over the page and always
|
|
113
|
+
// draws a close button, so give it closeButtonOnClick. isBackdropClickEnabled
|
|
114
|
+
// adds a click-outside target, which reports through onClose rather than through
|
|
115
|
+
// closeButtonOnClick, so both have to set the state.
|
|
116
|
+
//
|
|
117
|
+
// Panel fixes itself to the edge of the viewport, which on this page is the edge
|
|
118
|
+
// of the docs rather than of the example. ModalProps.container renders it inside
|
|
119
|
+
// Stage instead, which is why the panel waits for that node to exist, and
|
|
120
|
+
// Stage's transform makes it the box that fixed positioning resolves against.
|
|
121
|
+
// The transition needs the same node, or it measures its slide against the
|
|
122
|
+
// window and spends most of the animation outside the frame. Stage clips with
|
|
123
|
+
// overflow: clip rather than hidden, because a hidden box is one the browser can
|
|
124
|
+
// scroll: it would scroll the frame to reveal the panel as the panel takes focus
|
|
125
|
+
// on its way in, dragging the example's own content sideways. A real page wants
|
|
126
|
+
// the viewport and needs none of this; the padding on Stage just stands in for
|
|
127
|
+
// the spacing a page's own content would have.
|
|
128
|
+
|
|
129
|
+
import {
|
|
130
|
+
Button,
|
|
131
|
+
Icon,
|
|
132
|
+
Panel,
|
|
133
|
+
fontBodyS,
|
|
134
|
+
fontHeaderM,
|
|
135
|
+
getSemanticColors,
|
|
136
|
+
type CommonThemeProps,
|
|
137
|
+
} from "@czi-sds/components";
|
|
138
|
+
import styled from "@emotion/styled";
|
|
139
|
+
import { useState } from "react";
|
|
140
|
+
|
|
141
|
+
const Stage = styled.div<CommonThemeProps>`
|
|
142
|
+
${fontBodyS}
|
|
143
|
+
|
|
144
|
+
${(props) => {
|
|
145
|
+
const semanticColors = getSemanticColors(props);
|
|
146
|
+
|
|
147
|
+
return `
|
|
148
|
+
color: ${semanticColors?.base?.textPrimary};
|
|
149
|
+
height: 340px;
|
|
150
|
+
overflow: clip;
|
|
151
|
+
padding: 50px;
|
|
152
|
+
transform: translateZ(0);
|
|
153
|
+
`;
|
|
154
|
+
}}
|
|
155
|
+
`;
|
|
156
|
+
|
|
157
|
+
const Header = styled.h3<CommonThemeProps>`
|
|
158
|
+
${fontHeaderM}
|
|
159
|
+
margin: 0;
|
|
160
|
+
`;
|
|
161
|
+
|
|
162
|
+
function App() {
|
|
163
|
+
const [open, setOpen] = useState(false);
|
|
164
|
+
const [stage, setStage] = useState<HTMLDivElement | null>(null);
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<div className="app">
|
|
168
|
+
<Stage ref={setStage}>
|
|
169
|
+
<Button
|
|
170
|
+
onClick={() => setOpen(true)}
|
|
171
|
+
sdsStyle="solid"
|
|
172
|
+
sdsType="primary"
|
|
173
|
+
startIcon={<Icon sdsIcon="InfoCircle" sdsSize="s" />}
|
|
174
|
+
>
|
|
175
|
+
Show details
|
|
176
|
+
</Button>
|
|
177
|
+
|
|
178
|
+
<p>
|
|
179
|
+
An overlay panel reads a piece of the page rather than changing it, so
|
|
180
|
+
the content underneath stays where it is.
|
|
181
|
+
</p>
|
|
182
|
+
|
|
183
|
+
{stage && (
|
|
184
|
+
<Panel
|
|
185
|
+
closeButtonOnClick={() => setOpen(false)}
|
|
186
|
+
HeaderComponent={<Header>Sample details</Header>}
|
|
187
|
+
isBackdropClickEnabled
|
|
188
|
+
ModalProps={{ container: stage }}
|
|
189
|
+
onClose={() => setOpen(false)}
|
|
190
|
+
open={open}
|
|
191
|
+
position="right"
|
|
192
|
+
sdsType="overlay"
|
|
193
|
+
slotProps={{ transition: { container: stage } }}
|
|
194
|
+
>
|
|
195
|
+
Collection date, tissue, and the rest of the record for whichever
|
|
196
|
+
row the page has selected.
|
|
197
|
+
</Panel>
|
|
198
|
+
)}
|
|
199
|
+
</Stage>
|
|
200
|
+
</div>
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export default App;
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Overlay panel along the bottom
|
|
208
|
+
|
|
209
|
+
The bottom position spans the full width, and the `width` prop becomes the panel's height. This one has no backdrop, so clicks outside it land on the page underneath.
|
|
210
|
+
|
|
211
|
+
**Example: OverlayPanelBottom**
|
|
212
|
+
|
|
213
|
+
```tsx
|
|
214
|
+
// position="bottom" belongs to the overlay type only, and it turns the width
|
|
215
|
+
// prop into a height: the panel spans the full width and stands as tall as the
|
|
216
|
+
// value you pass. The paper also has a 320px floor in both directions, so a
|
|
217
|
+
// smaller number changes nothing.
|
|
218
|
+
//
|
|
219
|
+
// Panel fixes itself to the edge of the viewport, which on this page is the edge
|
|
220
|
+
// of the docs rather than of the example. ModalProps.container renders it inside
|
|
221
|
+
// Stage instead, which is why the panel waits for that node to exist, and
|
|
222
|
+
// Stage's transform makes it the box that fixed positioning resolves against.
|
|
223
|
+
// The transition needs the same node, or it measures its slide against the
|
|
224
|
+
// window and spends most of the animation outside the frame, and Stage clips with
|
|
225
|
+
// overflow: clip rather than hidden so the browser cannot scroll the frame to
|
|
226
|
+
// reveal the panel while it is still on its way in. A real page wants the
|
|
227
|
+
// viewport and needs none of this; the padding on Stage just stands in for the
|
|
228
|
+
// spacing a page's own content would have.
|
|
229
|
+
|
|
230
|
+
import {
|
|
231
|
+
Button,
|
|
232
|
+
Icon,
|
|
233
|
+
Panel,
|
|
234
|
+
fontBodyS,
|
|
235
|
+
fontHeaderM,
|
|
236
|
+
getSemanticColors,
|
|
237
|
+
type CommonThemeProps,
|
|
238
|
+
} from "@czi-sds/components";
|
|
239
|
+
import styled from "@emotion/styled";
|
|
240
|
+
import { useState } from "react";
|
|
241
|
+
|
|
242
|
+
const Stage = styled.div<CommonThemeProps>`
|
|
243
|
+
${fontBodyS}
|
|
244
|
+
|
|
245
|
+
${(props) => {
|
|
246
|
+
const semanticColors = getSemanticColors(props);
|
|
247
|
+
|
|
248
|
+
return `
|
|
249
|
+
color: ${semanticColors?.base?.textPrimary};
|
|
250
|
+
height: 520px;
|
|
251
|
+
overflow: clip;
|
|
252
|
+
padding: 50px;
|
|
253
|
+
transform: translateZ(0);
|
|
254
|
+
`;
|
|
255
|
+
}}
|
|
256
|
+
`;
|
|
257
|
+
|
|
258
|
+
const Header = styled.h3<CommonThemeProps>`
|
|
259
|
+
${fontHeaderM}
|
|
260
|
+
margin: 0;
|
|
261
|
+
`;
|
|
262
|
+
|
|
263
|
+
const Table = styled.div<CommonThemeProps>`
|
|
264
|
+
${(props) => {
|
|
265
|
+
const semanticColors = getSemanticColors(props);
|
|
266
|
+
|
|
267
|
+
return `
|
|
268
|
+
border: 1px dashed ${semanticColors?.base?.divider};
|
|
269
|
+
border-radius: 4px;
|
|
270
|
+
color: ${semanticColors?.base?.textSecondary};
|
|
271
|
+
padding: 24px;
|
|
272
|
+
`;
|
|
273
|
+
}}
|
|
274
|
+
`;
|
|
275
|
+
|
|
276
|
+
function App() {
|
|
277
|
+
const [open, setOpen] = useState(false);
|
|
278
|
+
const [stage, setStage] = useState<HTMLDivElement | null>(null);
|
|
279
|
+
|
|
280
|
+
return (
|
|
281
|
+
<div className="app">
|
|
282
|
+
<Stage ref={setStage}>
|
|
283
|
+
<Button
|
|
284
|
+
onClick={() => setOpen(true)}
|
|
285
|
+
sdsStyle="solid"
|
|
286
|
+
sdsType="primary"
|
|
287
|
+
startIcon={<Icon sdsIcon="Table" sdsSize="s" />}
|
|
288
|
+
>
|
|
289
|
+
Show table
|
|
290
|
+
</Button>
|
|
291
|
+
|
|
292
|
+
<p>
|
|
293
|
+
A bottom panel suits content that is wider than it is tall, such as a
|
|
294
|
+
table of the rows behind a chart.
|
|
295
|
+
</p>
|
|
296
|
+
|
|
297
|
+
{stage && (
|
|
298
|
+
<Panel
|
|
299
|
+
closeButtonOnClick={() => setOpen(false)}
|
|
300
|
+
HeaderComponent={<Header>Underlying rows</Header>}
|
|
301
|
+
ModalProps={{ container: stage }}
|
|
302
|
+
onClose={() => setOpen(false)}
|
|
303
|
+
open={open}
|
|
304
|
+
position="bottom"
|
|
305
|
+
sdsType="overlay"
|
|
306
|
+
slotProps={{ transition: { container: stage } }}
|
|
307
|
+
width={360}
|
|
308
|
+
>
|
|
309
|
+
<Table>[Table of rows, 360px of height to work with]</Table>
|
|
310
|
+
</Panel>
|
|
311
|
+
)}
|
|
312
|
+
</Stage>
|
|
313
|
+
</div>
|
|
314
|
+
);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
export default App;
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Overlay panel with a custom close control
|
|
321
|
+
|
|
322
|
+
`CloseButtonComponent` replaces the X with anything you like, and the header holds more than a title.
|
|
323
|
+
|
|
324
|
+
**Example: OverlayPanelCustomClose**
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
// CloseButtonComponent replaces the default X. The click handler stays on
|
|
328
|
+
// closeButtonOnClick, which the panel attaches to the wrapper around whatever
|
|
329
|
+
// you pass, so the replacement needs no onClick of its own. The wrapper also
|
|
330
|
+
// forces any icon inside a button to 24px, so sdsSize on it has no effect.
|
|
331
|
+
//
|
|
332
|
+
// Stage, ModalProps.container and the transition's container are here only to
|
|
333
|
+
// keep the panel and its slide inside the example frame, as in the examples
|
|
334
|
+
// above.
|
|
335
|
+
|
|
336
|
+
import {
|
|
337
|
+
Button,
|
|
338
|
+
Icon,
|
|
339
|
+
Panel,
|
|
340
|
+
fontBodyS,
|
|
341
|
+
fontHeaderM,
|
|
342
|
+
getSemanticColors,
|
|
343
|
+
type CommonThemeProps,
|
|
344
|
+
} from "@czi-sds/components";
|
|
345
|
+
import styled from "@emotion/styled";
|
|
346
|
+
import { useState } from "react";
|
|
347
|
+
|
|
348
|
+
const Stage = styled.div<CommonThemeProps>`
|
|
349
|
+
${fontBodyS}
|
|
350
|
+
|
|
351
|
+
${(props) => {
|
|
352
|
+
const semanticColors = getSemanticColors(props);
|
|
353
|
+
|
|
354
|
+
return `
|
|
355
|
+
color: ${semanticColors?.base?.textPrimary};
|
|
356
|
+
height: 340px;
|
|
357
|
+
overflow: clip;
|
|
358
|
+
padding: 50px;
|
|
359
|
+
transform: translateZ(0);
|
|
360
|
+
`;
|
|
361
|
+
}}
|
|
362
|
+
`;
|
|
363
|
+
|
|
364
|
+
const Header = styled.div<CommonThemeProps>`
|
|
365
|
+
${(props) => {
|
|
366
|
+
const semanticColors = getSemanticColors(props);
|
|
367
|
+
|
|
368
|
+
return `
|
|
369
|
+
h3 {
|
|
370
|
+
${fontHeaderM(props)}
|
|
371
|
+
margin: 0;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
p {
|
|
375
|
+
${fontBodyS(props)}
|
|
376
|
+
color: ${semanticColors?.base?.textSecondary};
|
|
377
|
+
margin: 4px 0 0 0;
|
|
378
|
+
}
|
|
379
|
+
`;
|
|
380
|
+
}}
|
|
381
|
+
`;
|
|
382
|
+
|
|
383
|
+
function App() {
|
|
384
|
+
const [open, setOpen] = useState(false);
|
|
385
|
+
const [stage, setStage] = useState<HTMLDivElement | null>(null);
|
|
386
|
+
|
|
387
|
+
return (
|
|
388
|
+
<div className="app">
|
|
389
|
+
<Stage ref={setStage}>
|
|
390
|
+
<Button
|
|
391
|
+
onClick={() => setOpen(true)}
|
|
392
|
+
sdsStyle="solid"
|
|
393
|
+
sdsType="primary"
|
|
394
|
+
startIcon={<Icon sdsIcon="Gear" sdsSize="s" />}
|
|
395
|
+
>
|
|
396
|
+
Show settings
|
|
397
|
+
</Button>
|
|
398
|
+
|
|
399
|
+
<p>The header holds as much as you want to put in it.</p>
|
|
400
|
+
|
|
401
|
+
{stage && (
|
|
402
|
+
<Panel
|
|
403
|
+
closeButtonOnClick={() => setOpen(false)}
|
|
404
|
+
CloseButtonComponent={
|
|
405
|
+
<Button sdsStyle="minimal" sdsType="primary">
|
|
406
|
+
Done
|
|
407
|
+
</Button>
|
|
408
|
+
}
|
|
409
|
+
HeaderComponent={
|
|
410
|
+
<Header>
|
|
411
|
+
<h3>Display settings</h3>
|
|
412
|
+
<p>Applies to this session only</p>
|
|
413
|
+
</Header>
|
|
414
|
+
}
|
|
415
|
+
ModalProps={{ container: stage }}
|
|
416
|
+
onClose={() => setOpen(false)}
|
|
417
|
+
open={open}
|
|
418
|
+
position="right"
|
|
419
|
+
sdsType="overlay"
|
|
420
|
+
slotProps={{ transition: { container: stage } }}
|
|
421
|
+
width={360}
|
|
422
|
+
>
|
|
423
|
+
Colour scale, point size, and the other controls for the plot.
|
|
424
|
+
</Panel>
|
|
425
|
+
)}
|
|
426
|
+
</Stage>
|
|
427
|
+
</div>
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export default App;
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Changing the animation speed
|
|
435
|
+
|
|
436
|
+
`transitionDuration` times the slide. Each button below reopens the same panel at a different setting, from the 225ms default to none at all, and the example holds the setting while the panel closes so the way out matches the way in.
|
|
437
|
+
|
|
438
|
+
**Example: OverlayPanelTransition**
|
|
439
|
+
|
|
440
|
+
```tsx
|
|
441
|
+
// transitionDuration sets how long the panel takes to travel in and out, in
|
|
442
|
+
// milliseconds. A single number times both directions; { enter, exit } times them
|
|
443
|
+
// separately, and 0 removes the animation. The Panel defaults to
|
|
444
|
+
// { enter: 225, exit: 195 }, which is MUI's drawer timing rather than the SDS
|
|
445
|
+
// theme's much shorter one.
|
|
446
|
+
//
|
|
447
|
+
// The duration stays put while the panel closes so the exit runs at the speed it
|
|
448
|
+
// opened with. Swapping it back on close would exit at the new speed instead.
|
|
449
|
+
//
|
|
450
|
+
// Stage, ModalProps.container and the transition's container are here only to
|
|
451
|
+
// keep the panel and its slide inside the example frame, as in the examples
|
|
452
|
+
// above.
|
|
453
|
+
|
|
454
|
+
import {
|
|
455
|
+
Button,
|
|
456
|
+
Panel,
|
|
457
|
+
fontBodyS,
|
|
458
|
+
fontBodyXs,
|
|
459
|
+
fontHeaderM,
|
|
460
|
+
getSemanticColors,
|
|
461
|
+
getSpaces,
|
|
462
|
+
type CommonThemeProps,
|
|
463
|
+
type PanelProps,
|
|
464
|
+
} from "@czi-sds/components";
|
|
465
|
+
import styled from "@emotion/styled";
|
|
466
|
+
import { useState } from "react";
|
|
467
|
+
|
|
468
|
+
const SPEEDS: {
|
|
469
|
+
duration: PanelProps["transitionDuration"];
|
|
470
|
+
label: string;
|
|
471
|
+
}[] = [
|
|
472
|
+
{ duration: { enter: 225, exit: 195 }, label: "Default" },
|
|
473
|
+
{ duration: { enter: 700, exit: 500 }, label: "Slow" },
|
|
474
|
+
{ duration: 120, label: "Quick, both ways" },
|
|
475
|
+
{ duration: 0, label: "None" },
|
|
476
|
+
];
|
|
477
|
+
|
|
478
|
+
const Stage = styled.div<CommonThemeProps>`
|
|
479
|
+
${fontBodyS}
|
|
480
|
+
|
|
481
|
+
${(props) => {
|
|
482
|
+
const semanticColors = getSemanticColors(props);
|
|
483
|
+
|
|
484
|
+
return `
|
|
485
|
+
color: ${semanticColors?.base?.textPrimary};
|
|
486
|
+
height: 340px;
|
|
487
|
+
overflow: clip;
|
|
488
|
+
padding: 50px;
|
|
489
|
+
transform: translateZ(0);
|
|
490
|
+
`;
|
|
491
|
+
}}
|
|
492
|
+
`;
|
|
493
|
+
|
|
494
|
+
const Controls = styled.div<CommonThemeProps>`
|
|
495
|
+
${(props) => {
|
|
496
|
+
const spaces = getSpaces(props);
|
|
497
|
+
|
|
498
|
+
return `
|
|
499
|
+
display: flex;
|
|
500
|
+
flex-wrap: wrap;
|
|
501
|
+
gap: ${spaces?.s}px;
|
|
502
|
+
`;
|
|
503
|
+
}}
|
|
504
|
+
`;
|
|
505
|
+
|
|
506
|
+
const Readout = styled.p<CommonThemeProps>`
|
|
507
|
+
${fontBodyXs}
|
|
508
|
+
|
|
509
|
+
${(props) => {
|
|
510
|
+
const semanticColors = getSemanticColors(props);
|
|
511
|
+
|
|
512
|
+
return `
|
|
513
|
+
color: ${semanticColors?.base?.textSecondary};
|
|
514
|
+
`;
|
|
515
|
+
}}
|
|
516
|
+
`;
|
|
517
|
+
|
|
518
|
+
const Header = styled.h3<CommonThemeProps>`
|
|
519
|
+
${fontHeaderM}
|
|
520
|
+
margin: 0;
|
|
521
|
+
`;
|
|
522
|
+
|
|
523
|
+
function App() {
|
|
524
|
+
const [open, setOpen] = useState(false);
|
|
525
|
+
const [speed, setSpeed] = useState(SPEEDS[0]);
|
|
526
|
+
const [stage, setStage] = useState<HTMLDivElement | null>(null);
|
|
527
|
+
|
|
528
|
+
return (
|
|
529
|
+
<div className="app">
|
|
530
|
+
<Stage ref={setStage}>
|
|
531
|
+
<Controls>
|
|
532
|
+
{SPEEDS.map((option) => (
|
|
533
|
+
<Button
|
|
534
|
+
key={option.label}
|
|
535
|
+
onClick={() => {
|
|
536
|
+
setSpeed(option);
|
|
537
|
+
setOpen(true);
|
|
538
|
+
}}
|
|
539
|
+
sdsStyle="outline"
|
|
540
|
+
sdsType="secondary"
|
|
541
|
+
>
|
|
542
|
+
{option.label}
|
|
543
|
+
</Button>
|
|
544
|
+
))}
|
|
545
|
+
</Controls>
|
|
546
|
+
|
|
547
|
+
<Readout>
|
|
548
|
+
Opening with transitionDuration of {JSON.stringify(speed.duration)}
|
|
549
|
+
</Readout>
|
|
550
|
+
|
|
551
|
+
{stage && (
|
|
552
|
+
<Panel
|
|
553
|
+
closeButtonOnClick={() => setOpen(false)}
|
|
554
|
+
HeaderComponent={<Header>Sample details</Header>}
|
|
555
|
+
isBackdropClickEnabled
|
|
556
|
+
ModalProps={{ container: stage }}
|
|
557
|
+
onClose={() => setOpen(false)}
|
|
558
|
+
open={open}
|
|
559
|
+
position="right"
|
|
560
|
+
sdsType="overlay"
|
|
561
|
+
slotProps={{ transition: { container: stage } }}
|
|
562
|
+
transitionDuration={speed.duration}
|
|
563
|
+
>
|
|
564
|
+
Close this and pick another speed to compare them.
|
|
565
|
+
</Panel>
|
|
566
|
+
)}
|
|
567
|
+
</Stage>
|
|
568
|
+
</div>
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
export default App;
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
## SDS vs MUI
|
|
576
|
+
|
|
577
|
+
The SDS Panel wraps the MUI Drawer, with these differences:
|
|
578
|
+
|
|
579
|
+
- **sdsType instead of variant:** MUI picks the kind of drawer with `variant`; SDS picks it with `sdsType`, which takes two of the three MUI variants.
|
|
580
|
+
|
|
581
|
+
- `"basic"` is MUI's persistent drawer.
|
|
582
|
+
|
|
583
|
+
- `"overlay"` is MUI's temporary drawer.
|
|
584
|
+
|
|
585
|
+
- **position instead of anchor:** MUI's `anchor` takes four edges. SDS narrows it to `position`: `"left"` or `"right"` for a basic panel, plus `"bottom"` for an overlay. A basic panel asked for `"bottom"` is anchored left instead, and TypeScript rejects the combination up front.
|
|
586
|
+
|
|
587
|
+
- `width`: MUI leaves the drawer's size to your own styles. SDS takes a `width` and defaults it to 240px for a basic panel and 320px for an overlay. Along the bottom edge it sets the height instead, since the panel spans the screen. Either way the panel will not go below its type's default in either direction, so smaller values have no effect.
|
|
588
|
+
|
|
589
|
+
- **A header row on the overlay:** an overlay panel always draws a close button, and `HeaderComponent` fills the space beside it. Both live in a bar that sticks to the top of the panel while its content scrolls under a short gradient. A basic panel has no such bar and no close control of its own.
|
|
590
|
+
|
|
591
|
+
- **A backdrop you opt into:** MUI's temporary drawer always lays a visible backdrop over the page. SDS hides it, so the page behind an overlay panel stays legible and clickable. `isBackdropClickEnabled` brings back an invisible one, which gives you a click-outside target without dimming anything.
|
|
592
|
+
|
|
593
|
+
- **Motion the SDS theme does not shorten:** every MUI transition reads its length from the theme, and SDS sets those lengths short (20ms in, 10ms out) for the small elements that make up most of the system. A panel is far too large to cross the screen in a frame, so it keeps MUI's own drawer timings of 225ms in and 195ms out. `transitionDuration` overrides either direction.
|
|
594
|
+
|
|
595
|
+
- **Focus and scrolling stay with the page:** SDS forces `disableEnforceFocus` and `disableScrollLock` on the underlying Modal, so an open panel neither traps the keyboard nor freezes the page's scrollbar. Anything else you pass through `ModalProps` is kept. The paper also gets a default `aria-label` of `"Panel"`, which `slotProps.paper` can replace.
|
|
596
|
+
|
|
597
|
+
## Behavior notes
|
|
598
|
+
|
|
599
|
+
- Nothing about a basic panel is automatic: it does not close itself, and it does not move the page's content. Put a control on the page to toggle `open`, and shift your content by the same width you gave the panel so the two do not overlap.
|
|
600
|
+
|
|
601
|
+
- An overlay panel closes through two separate props. `closeButtonOnClick` fires from the header's close button; `onClose` fires on Escape, and on a click outside the panel when `isBackdropClickEnabled` is set. Set both to the same handler, or the panel will only close one way.
|
|
602
|
+
|
|
603
|
+
- The close button is drawn whether or not you handle it, so an overlay panel without `closeButtonOnClick` offers a control that does nothing.
|
|
604
|
+
|
|
605
|
+
- `closeButtonOnClick` is attached to the wrapper around the close button rather than to the button itself, so a `CloseButtonComponent` needs no click handler of its own. That wrapper also sizes any icon inside a button to 24px, which overrides `sdsSize` on the icon.
|
|
606
|
+
|
|
607
|
+
- Two basic panels can be open at once, one on each side, because each is part of the page's layout. Overlay panels are not, so a second one opens over the first rather than beside it; the guidance above asks for one at a time.
|
|
608
|
+
|
|
609
|
+
- An overlay panel takes focus when it opens but does not keep it, so Escape only reaches the panel while focus is still inside. Once the reader clicks or tabs back into the page, the close button and the backdrop are the only ways out.
|
|
610
|
+
|
|
611
|
+
- Because an overlay panel deliberately does not trap focus, MUI's `aria-hidden-focus` rule reports a violation against it. SDS suppresses that rule for the Panel's stories while waiting on a fix upstream.
|
|
612
|
+
|
|
613
|
+
- The panel slides from the edge it is anchored to over 225ms, and back out over 195ms. `transitionDuration` changes both, or either one on its own with `{ enter, exit }`. These are MUI's own drawer timings rather than the SDS theme's, which are set short for smaller elements and leave a panel looking like it jumps into place. Note that SDS leaves MUI's `motion.reducedMotion` setting at `"never"`, so the animation plays regardless of the reader's reduced-motion preference; an application that wants to honour it can set that theme option to `"system"`.
|
|
614
|
+
|
|
615
|
+
## MUI Documentation
|
|
616
|
+
|
|
617
|
+
Documentation for the underlying MUI component can be found [here](https://mui.com/material-ui/react-drawer/).
|
|
618
|
+
|
|
619
|
+
## Props
|
|
620
|
+
|
|
621
|
+
Any custom SDS props and MUI props required for implementation are found on the table below. See the MUI documentation for additional optional props.
|
|
622
|
+
|
|
623
|
+
| Name | Type | Default | Description |
|
|
624
|
+
| ------------------------ | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
625
|
+
| `sdsType` | `"basic" \| "overlay"` | - (required) | `"basic"` sits in the page and stays until you close it; `"overlay"` floats over the page with a header and close button. It also decides which of the props below apply, so TypeScript needs it spelled out even though the component falls back to `"basic"`. |
|
|
626
|
+
| `open` | `boolean` | `false` | Whether the panel is showing. The panel never changes this itself, so hold it in your own state. |
|
|
627
|
+
| `position` | `"left"` \| `"right"` \| `"bottom"` (overlay only) | `"left"` | Which edge the panel is attached to. A basic panel takes `"left"` or `"right"`; an overlay adds `"bottom"`. |
|
|
628
|
+
| `width` | `number \| string` | `240` (basic) `320` (overlay) | How wide the panel is, or how tall it is when positioned along the bottom. Numbers are read as pixels. Values below the default are ignored, since the panel keeps that as its minimum in both directions. |
|
|
629
|
+
| `HeaderComponent` | `React.ReactNode` | - | Overlay only. Fills the header bar beside the close button. Leave it out and the bar holds the close button alone. |
|
|
630
|
+
| `closeButtonOnClick` | `React.MouseEventHandler<HTMLDivElement>` | - | Overlay only. Runs when the header's close control is clicked. Without it, the control does nothing. |
|
|
631
|
+
| `CloseButtonComponent` | `React.ReactNode` | `<Button` `sdsStyle="minimal"` `sdsType="secondary"` `size="large"` `backgroundOnHover={false}` `aria-label="Panel Toggle">` `<Icon sdsIcon="XMark" sdsSize="l" />` `</Button>` | Overlay only. Replaces the close button. Clicks are handled by the wrapper around it, so it needs no handler of its own. |
|
|
632
|
+
| `isBackdropClickEnabled` | `boolean` | `false` | Overlay only in practice. Adds an invisible backdrop so a click outside the panel reaches `onClose`. Left off, clicks pass through to the page. |
|
|
633
|
+
| `onClose` | `(event, reason) => void` | - | MUI's Drawer callback, fired on a backdrop click and on Escape while focus is inside the panel. It does not fire for the header's close button, which reports through `closeButtonOnClick`. |
|
|
634
|
+
| `transitionDuration` | `number` \| `{ appear?: number,` `enter?: number,` `exit?: number }` | `{ enter: 225, exit: 195 }` | How long the panel takes to slide in and out, in milliseconds. The Panel keeps MUI's drawer timings instead of the much shorter ones in the SDS theme, which a surface this large arrives too quickly to read as motion. Pass `0` for no animation. |
|
|
635
|
+
| `ModalProps` | `ModalProps` | - | Passed to the Modal behind an overlay panel, except for `disableEnforceFocus` and `disableScrollLock`, which SDS pins to `true`. |
|
|
636
|
+
| `slotProps` | `DrawerProps["slotProps"]` | `{ backdrop: { invisible: true },` `paper: { aria-label: "Panel" } }` | Merged with the SDS defaults slot by slot, so a paper `aria-label` or `aria-labelledby` of your own replaces the default name while the rest of the defaults survive. |
|