@recursica/adapter-mui-v7 1.3.1 → 1.3.3
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/CHANGELOG.md +19 -0
- package/README.md +1 -1
- package/dist/adapter-mui-v7.cjs +16 -16
- package/dist/adapter-mui-v7.cjs.map +1 -1
- package/dist/adapter-mui-v7.css +1 -1
- package/dist/adapter-mui-v7.js +902 -880
- package/dist/adapter-mui-v7.js.map +1 -1
- package/dist/index.d.ts +8 -5
- package/docs/PHILOSOPHY.md +4 -0
- package/package.json +4 -8
- package/src/components/Heading/HEADING_IMPLEMENTATION_NOTES.md +10 -0
- package/src/components/Panel/PANEL_IMPLEMENTATION_NOTES.md +8 -0
- package/src/components/Panel/Panel.tsx +51 -9
- package/src/components/Slider/Slider.module.css +11 -0
- package/src/components/Text/TEXT_IMPLEMENTATION_NOTES.md +12 -0
- package/src/components/Text/Text.headings.dom.test.tsx +54 -0
- package/src/components/Text/Text.tsx +16 -0
- package/.storybook/commonArgTypes.ts +0 -148
- package/recursica_brand.json +0 -2610
- package/recursica_tokens.json +0 -769
- package/recursica_ui-kit.json +0 -9049
- package/recursica_variables_scoped.css +0 -6178
- package/src/Introduction.stories.tsx +0 -178
- package/src/components/Accordion/Accordion.stories.tsx +0 -198
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +0 -68
- package/src/components/AutoComplete/AutoComplete.stories.tsx +0 -336
- package/src/components/Avatar/Avatar.stories.tsx +0 -92
- package/src/components/Badge/Badge.stories.tsx +0 -78
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +0 -114
- package/src/components/Button/Button.stories.tsx +0 -196
- package/src/components/Card/Card.stories.tsx +0 -132
- package/src/components/Checkbox/Checkbox.stories.tsx +0 -97
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +0 -139
- package/src/components/Chip/Chip.stories.tsx +0 -154
- package/src/components/Container/Container.stories.tsx +0 -118
- package/src/components/DatePicker/DatePicker.stories.tsx +0 -163
- package/src/components/Dropdown/Dropdown.stories.tsx +0 -252
- package/src/components/FileInput/FileInput.stories.tsx +0 -313
- package/src/components/FileUpload/FileUpload.stories.tsx +0 -362
- package/src/components/Flex/Flex.stories.tsx +0 -128
- package/src/components/FormControlLayout/FormControlLayout.stories.tsx +0 -118
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +0 -129
- package/src/components/Group/Group.stories.tsx +0 -120
- package/src/components/Heading/Heading.stories.tsx +0 -112
- package/src/components/HoverCard/HoverCard.stories.tsx +0 -172
- package/src/components/Label/Label.stories.tsx +0 -131
- package/src/components/LayoutGrid/LayoutGrid.stories.tsx +0 -61
- package/src/components/Link/Link.stories.tsx +0 -58
- package/src/components/Loader/Loader.stories.tsx +0 -123
- package/src/components/Menu/Menu.stories.tsx +0 -402
- package/src/components/Modal/Modal.stories.tsx +0 -105
- package/src/components/NumberInput/NumberInput.stories.tsx +0 -96
- package/src/components/Pagination/Pagination.stories.tsx +0 -48
- package/src/components/Panel/Panel.stories.tsx +0 -227
- package/src/components/Popover/Popover.stories.tsx +0 -133
- package/src/components/Radio/Radio.stories.tsx +0 -93
- package/src/components/Radio/RadioGroup.stories.tsx +0 -117
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +0 -163
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +0 -106
- package/src/components/Slider/Slider.stories.tsx +0 -282
- package/src/components/Stack/Stack.stories.tsx +0 -108
- package/src/components/Stepper/Stepper.stories.tsx +0 -157
- package/src/components/Switch/Switch.stories.tsx +0 -114
- package/src/components/Switch/SwitchGroup.stories.tsx +0 -132
- package/src/components/Table/Table.stories.tsx +0 -154
- package/src/components/Tabs/Tabs.stories.tsx +0 -174
- package/src/components/Text/Text.stories.tsx +0 -123
- package/src/components/TextArea/TextArea.stories.tsx +0 -94
- package/src/components/TextField/TextField.stories.tsx +0 -162
- package/src/components/TimePicker/TimePicker.stories.tsx +0 -155
- package/src/components/Timeline/Timeline.stories.tsx +0 -128
- package/src/components/Toast/Toast.stories.tsx +0 -67
- package/src/components/Tooltip/Tooltip.stories.tsx +0 -165
- package/src/components/TransferList/TransferList.stories.tsx +0 -121
- package/src/components/Tree/Tree.stories.tsx +0 -104
|
@@ -1,402 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Menu story axes:
|
|
3
|
-
* - trigger: click (default) vs hover behavior
|
|
4
|
-
* - position: dropdown alignment relative to target
|
|
5
|
-
* - withArrow: arrow indicator on the dropdown
|
|
6
|
-
* - content variations: icons, dividers, labels, disabled items, submenus
|
|
7
|
-
* - layer: tested via the global Layer decorator (withLayer/layer args)
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
import React, { useState, useRef, useEffect } from "react";
|
|
11
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
12
|
-
import { Menu, MenuItem, MenuDivider } from "./Menu";
|
|
13
|
-
import { Button } from "../Button";
|
|
14
|
-
import { ListSubheader } from "@mui/material";
|
|
15
|
-
import styles from "./Menu.module.css";
|
|
16
|
-
|
|
17
|
-
const SettingsIcon = (props: React.ComponentProps<"svg">) => (
|
|
18
|
-
<svg
|
|
19
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
20
|
-
width="14"
|
|
21
|
-
height="14"
|
|
22
|
-
viewBox="0 0 24 24"
|
|
23
|
-
fill="none"
|
|
24
|
-
stroke="currentColor"
|
|
25
|
-
strokeWidth="2"
|
|
26
|
-
strokeLinecap="round"
|
|
27
|
-
strokeLinejoin="round"
|
|
28
|
-
{...props}
|
|
29
|
-
>
|
|
30
|
-
<circle cx="12" cy="12" r="3" />
|
|
31
|
-
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z" />
|
|
32
|
-
</svg>
|
|
33
|
-
);
|
|
34
|
-
|
|
35
|
-
const SearchIcon = (props: React.ComponentProps<"svg">) => (
|
|
36
|
-
<svg
|
|
37
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
38
|
-
width="14"
|
|
39
|
-
height="14"
|
|
40
|
-
viewBox="0 0 24 24"
|
|
41
|
-
fill="none"
|
|
42
|
-
stroke="currentColor"
|
|
43
|
-
strokeWidth="2"
|
|
44
|
-
strokeLinecap="round"
|
|
45
|
-
strokeLinejoin="round"
|
|
46
|
-
{...props}
|
|
47
|
-
>
|
|
48
|
-
<circle cx="11" cy="11" r="8" />
|
|
49
|
-
<line x1="21" y1="21" x2="16.65" y2="16.65" />
|
|
50
|
-
</svg>
|
|
51
|
-
);
|
|
52
|
-
|
|
53
|
-
const MessageIcon = (props: React.ComponentProps<"svg">) => (
|
|
54
|
-
<svg
|
|
55
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
56
|
-
width="14"
|
|
57
|
-
height="14"
|
|
58
|
-
viewBox="0 0 24 24"
|
|
59
|
-
fill="none"
|
|
60
|
-
stroke="currentColor"
|
|
61
|
-
strokeWidth="2"
|
|
62
|
-
strokeLinecap="round"
|
|
63
|
-
strokeLinejoin="round"
|
|
64
|
-
{...props}
|
|
65
|
-
>
|
|
66
|
-
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
|
67
|
-
</svg>
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
const ImageIcon = (props: React.ComponentProps<"svg">) => (
|
|
71
|
-
<svg
|
|
72
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
73
|
-
width="14"
|
|
74
|
-
height="14"
|
|
75
|
-
viewBox="0 0 24 24"
|
|
76
|
-
fill="none"
|
|
77
|
-
stroke="currentColor"
|
|
78
|
-
strokeWidth="2"
|
|
79
|
-
strokeLinecap="round"
|
|
80
|
-
strokeLinejoin="round"
|
|
81
|
-
{...props}
|
|
82
|
-
>
|
|
83
|
-
<rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
|
|
84
|
-
<circle cx="8.5" cy="8.5" r="1.5" />
|
|
85
|
-
<polyline points="21 15 16 10 5 21" />
|
|
86
|
-
</svg>
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
const TrashIcon = (props: React.ComponentProps<"svg">) => (
|
|
90
|
-
<svg
|
|
91
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
92
|
-
width="14"
|
|
93
|
-
height="14"
|
|
94
|
-
viewBox="0 0 24 24"
|
|
95
|
-
fill="none"
|
|
96
|
-
stroke="currentColor"
|
|
97
|
-
strokeWidth="2"
|
|
98
|
-
strokeLinecap="round"
|
|
99
|
-
strokeLinejoin="round"
|
|
100
|
-
{...props}
|
|
101
|
-
>
|
|
102
|
-
<polyline points="3 6 5 6 21 6" />
|
|
103
|
-
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
|
104
|
-
</svg>
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
const ArrowsIcon = (props: React.ComponentProps<"svg">) => (
|
|
108
|
-
<svg
|
|
109
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
110
|
-
width="14"
|
|
111
|
-
height="14"
|
|
112
|
-
viewBox="0 0 24 24"
|
|
113
|
-
fill="none"
|
|
114
|
-
stroke="currentColor"
|
|
115
|
-
strokeWidth="2"
|
|
116
|
-
strokeLinecap="round"
|
|
117
|
-
strokeLinejoin="round"
|
|
118
|
-
{...props}
|
|
119
|
-
>
|
|
120
|
-
<polyline points="17 1 21 5 17 9" />
|
|
121
|
-
<path d="M3 11V9a4 4 0 0 1 4-4h14" />
|
|
122
|
-
<polyline points="7 23 3 19 7 15" />
|
|
123
|
-
<path d="M21 13v2a4 4 0 0 1-4 4H3" />
|
|
124
|
-
</svg>
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
const ChevronRightIcon = (props: React.ComponentProps<"svg">) => (
|
|
128
|
-
<svg
|
|
129
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
130
|
-
width="14"
|
|
131
|
-
height="14"
|
|
132
|
-
viewBox="0 0 24 24"
|
|
133
|
-
fill="none"
|
|
134
|
-
stroke="currentColor"
|
|
135
|
-
strokeWidth="2"
|
|
136
|
-
strokeLinecap="round"
|
|
137
|
-
strokeLinejoin="round"
|
|
138
|
-
{...props}
|
|
139
|
-
>
|
|
140
|
-
<polyline points="9 18 15 12 9 6" />
|
|
141
|
-
</svg>
|
|
142
|
-
);
|
|
143
|
-
|
|
144
|
-
// adapter-mui-v7's Menu is monolithic (see IMPLEMENTATION_NOTES.md) — there is no
|
|
145
|
-
// Menu.Sub composable, so a submenu is just a MenuItem that anchors its own
|
|
146
|
-
// nested Menu, mirroring how the top-level trigger manages its own anchorEl.
|
|
147
|
-
interface SubMenuTriggerProps {
|
|
148
|
-
label: string;
|
|
149
|
-
children: React.ReactNode;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const SubMenuTrigger = ({ label, children }: SubMenuTriggerProps) => {
|
|
153
|
-
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
|
154
|
-
|
|
155
|
-
return (
|
|
156
|
-
<>
|
|
157
|
-
<MenuItem onClick={(event) => setAnchorEl(event.currentTarget)}>
|
|
158
|
-
{label}
|
|
159
|
-
<ChevronRightIcon
|
|
160
|
-
className={styles.chevron}
|
|
161
|
-
style={{ marginLeft: "auto", paddingLeft: 8 }}
|
|
162
|
-
/>
|
|
163
|
-
</MenuItem>
|
|
164
|
-
<Menu
|
|
165
|
-
anchorEl={anchorEl}
|
|
166
|
-
open={Boolean(anchorEl)}
|
|
167
|
-
onClose={() => setAnchorEl(null)}
|
|
168
|
-
anchorOrigin={{ vertical: "top", horizontal: "right" }}
|
|
169
|
-
transformOrigin={{ vertical: "top", horizontal: "left" }}
|
|
170
|
-
>
|
|
171
|
-
{children}
|
|
172
|
-
</Menu>
|
|
173
|
-
</>
|
|
174
|
-
);
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
type MenuStoryArgs = Record<string, unknown>;
|
|
178
|
-
|
|
179
|
-
const meta: Meta = {
|
|
180
|
-
title: "UI-Kit/Menu",
|
|
181
|
-
component: Menu,
|
|
182
|
-
tags: ["autodocs"],
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
export default meta;
|
|
186
|
-
|
|
187
|
-
type Story = StoryObj<MenuStoryArgs>;
|
|
188
|
-
|
|
189
|
-
interface InteractiveMenuProps {
|
|
190
|
-
children?: React.ReactNode;
|
|
191
|
-
opened?: boolean;
|
|
192
|
-
[key: string]: unknown;
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const InteractiveMenu = ({ children, ...args }: InteractiveMenuProps) => {
|
|
196
|
-
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
|
197
|
-
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
198
|
-
|
|
199
|
-
const open = Boolean(anchorEl) || Boolean(args.opened);
|
|
200
|
-
|
|
201
|
-
useEffect(() => {
|
|
202
|
-
if (args.opened && buttonRef.current) {
|
|
203
|
-
setAnchorEl(buttonRef.current);
|
|
204
|
-
}
|
|
205
|
-
}, [args.opened]);
|
|
206
|
-
|
|
207
|
-
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
|
|
208
|
-
setAnchorEl(event.currentTarget);
|
|
209
|
-
};
|
|
210
|
-
const handleClose = () => {
|
|
211
|
-
setAnchorEl(null);
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
return (
|
|
215
|
-
<div>
|
|
216
|
-
<Button ref={buttonRef} variant="solid" onClick={handleClick}>
|
|
217
|
-
Toggle Menu
|
|
218
|
-
</Button>
|
|
219
|
-
<Menu anchorEl={anchorEl} open={open} onClose={handleClose} {...args}>
|
|
220
|
-
{children}
|
|
221
|
-
</Menu>
|
|
222
|
-
</div>
|
|
223
|
-
);
|
|
224
|
-
};
|
|
225
|
-
|
|
226
|
-
export const Default: Story = {
|
|
227
|
-
render: (args) => (
|
|
228
|
-
<InteractiveMenu {...args}>
|
|
229
|
-
<ListSubheader>Application</ListSubheader>
|
|
230
|
-
<MenuItem>
|
|
231
|
-
<SettingsIcon style={{ marginRight: 8 }} /> Settings
|
|
232
|
-
</MenuItem>
|
|
233
|
-
<MenuItem>
|
|
234
|
-
<MessageIcon style={{ marginRight: 8 }} /> Messages
|
|
235
|
-
</MenuItem>
|
|
236
|
-
<MenuItem>
|
|
237
|
-
<ImageIcon style={{ marginRight: 8 }} /> Gallery
|
|
238
|
-
</MenuItem>
|
|
239
|
-
<MenuItem>
|
|
240
|
-
<SearchIcon style={{ marginRight: 8 }} /> Search
|
|
241
|
-
</MenuItem>
|
|
242
|
-
<MenuDivider />
|
|
243
|
-
<ListSubheader>Danger zone</ListSubheader>
|
|
244
|
-
<MenuItem>
|
|
245
|
-
<ArrowsIcon style={{ marginRight: 8 }} /> Transfer my data
|
|
246
|
-
</MenuItem>
|
|
247
|
-
<MenuItem>
|
|
248
|
-
<TrashIcon style={{ marginRight: 8 }} /> Delete my account
|
|
249
|
-
</MenuItem>
|
|
250
|
-
</InteractiveMenu>
|
|
251
|
-
),
|
|
252
|
-
args: {
|
|
253
|
-
opened: true,
|
|
254
|
-
},
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
export const WithDisabledItems: Story = {
|
|
258
|
-
render: (args) => (
|
|
259
|
-
<InteractiveMenu {...args}>
|
|
260
|
-
<MenuItem>
|
|
261
|
-
<SettingsIcon style={{ marginRight: 8 }} /> Settings
|
|
262
|
-
</MenuItem>
|
|
263
|
-
<MenuItem disabled>
|
|
264
|
-
<SearchIcon style={{ marginRight: 8 }} /> Search (disabled)
|
|
265
|
-
</MenuItem>
|
|
266
|
-
<MenuItem>
|
|
267
|
-
<MessageIcon style={{ marginRight: 8 }} /> Messages
|
|
268
|
-
</MenuItem>
|
|
269
|
-
<MenuItem disabled>
|
|
270
|
-
<TrashIcon style={{ marginRight: 8 }} /> Delete (disabled)
|
|
271
|
-
</MenuItem>
|
|
272
|
-
</InteractiveMenu>
|
|
273
|
-
),
|
|
274
|
-
args: {
|
|
275
|
-
opened: true,
|
|
276
|
-
},
|
|
277
|
-
};
|
|
278
|
-
|
|
279
|
-
export const WithSubmenus: Story = {
|
|
280
|
-
render: (args) => (
|
|
281
|
-
<InteractiveMenu {...args}>
|
|
282
|
-
<MenuItem>Dashboard</MenuItem>
|
|
283
|
-
<SubMenuTrigger label="Products">
|
|
284
|
-
<MenuItem>All products</MenuItem>
|
|
285
|
-
<MenuItem>Categories</MenuItem>
|
|
286
|
-
<MenuItem>Tags</MenuItem>
|
|
287
|
-
</SubMenuTrigger>
|
|
288
|
-
<SubMenuTrigger label="Orders">
|
|
289
|
-
<MenuItem>Open</MenuItem>
|
|
290
|
-
<MenuItem>Completed</MenuItem>
|
|
291
|
-
<MenuItem>Cancelled</MenuItem>
|
|
292
|
-
</SubMenuTrigger>
|
|
293
|
-
</InteractiveMenu>
|
|
294
|
-
),
|
|
295
|
-
args: {
|
|
296
|
-
opened: true,
|
|
297
|
-
},
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
export const WithMaxHeight: Story = {
|
|
301
|
-
render: (args) => (
|
|
302
|
-
<InteractiveMenu {...args}>
|
|
303
|
-
<MenuItem>
|
|
304
|
-
<SettingsIcon style={{ marginRight: 8 }} /> Settings
|
|
305
|
-
</MenuItem>
|
|
306
|
-
<MenuItem>
|
|
307
|
-
<MessageIcon style={{ marginRight: 8 }} /> Messages
|
|
308
|
-
</MenuItem>
|
|
309
|
-
<MenuItem>
|
|
310
|
-
<ImageIcon style={{ marginRight: 8 }} /> Gallery
|
|
311
|
-
</MenuItem>
|
|
312
|
-
<MenuItem>
|
|
313
|
-
<SearchIcon style={{ marginRight: 8 }} /> Search
|
|
314
|
-
</MenuItem>
|
|
315
|
-
<MenuItem>
|
|
316
|
-
<ArrowsIcon style={{ marginRight: 8 }} /> Transfer my data
|
|
317
|
-
</MenuItem>
|
|
318
|
-
<MenuItem>
|
|
319
|
-
<TrashIcon style={{ marginRight: 8 }} /> Delete my account
|
|
320
|
-
</MenuItem>
|
|
321
|
-
</InteractiveMenu>
|
|
322
|
-
),
|
|
323
|
-
args: {
|
|
324
|
-
opened: true,
|
|
325
|
-
maxHeight: 160,
|
|
326
|
-
},
|
|
327
|
-
parameters: {
|
|
328
|
-
docs: {
|
|
329
|
-
description: {
|
|
330
|
-
story:
|
|
331
|
-
"`maxHeight` overrides the token-driven dropdown max-height with an explicit pixel value, scrolling the item list once it's exceeded.",
|
|
332
|
-
},
|
|
333
|
-
},
|
|
334
|
-
},
|
|
335
|
-
};
|
|
336
|
-
|
|
337
|
-
// adapter-mui-v7's Menu has no native hover-trigger support (unlike Mantine's `trigger` prop),
|
|
338
|
-
// so this story implements open-on-hover itself: hovering the target opens the menu, and a
|
|
339
|
-
// short close delay (mirroring Mantine's `closeDelay`) keeps it open while the pointer moves
|
|
340
|
-
// from the target into the dropdown, since MUI's Menu renders in a portal outside the
|
|
341
|
-
// wrapper's DOM subtree.
|
|
342
|
-
const HoverMenu = ({ children, ...args }: InteractiveMenuProps) => {
|
|
343
|
-
const [open, setOpen] = useState(false);
|
|
344
|
-
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
345
|
-
const closeTimer = useRef<ReturnType<typeof setTimeout> | undefined>(
|
|
346
|
-
undefined,
|
|
347
|
-
);
|
|
348
|
-
|
|
349
|
-
const cancelClose = () => {
|
|
350
|
-
if (closeTimer.current) clearTimeout(closeTimer.current);
|
|
351
|
-
};
|
|
352
|
-
const scheduleClose = () => {
|
|
353
|
-
closeTimer.current = setTimeout(() => setOpen(false), 300);
|
|
354
|
-
};
|
|
355
|
-
|
|
356
|
-
return (
|
|
357
|
-
<div>
|
|
358
|
-
<Button
|
|
359
|
-
ref={buttonRef}
|
|
360
|
-
variant="solid"
|
|
361
|
-
onClick={() => setOpen((prev) => !prev)}
|
|
362
|
-
onMouseEnter={() => {
|
|
363
|
-
cancelClose();
|
|
364
|
-
setOpen(true);
|
|
365
|
-
}}
|
|
366
|
-
onMouseLeave={scheduleClose}
|
|
367
|
-
>
|
|
368
|
-
Hover or Click
|
|
369
|
-
</Button>
|
|
370
|
-
<Menu
|
|
371
|
-
{...args}
|
|
372
|
-
anchorEl={buttonRef.current}
|
|
373
|
-
open={open}
|
|
374
|
-
onClose={() => setOpen(false)}
|
|
375
|
-
slotProps={{
|
|
376
|
-
paper: {
|
|
377
|
-
onMouseEnter: cancelClose,
|
|
378
|
-
onMouseLeave: scheduleClose,
|
|
379
|
-
},
|
|
380
|
-
}}
|
|
381
|
-
>
|
|
382
|
-
{children}
|
|
383
|
-
</Menu>
|
|
384
|
-
</div>
|
|
385
|
-
);
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
export const HoverTrigger: Story = {
|
|
389
|
-
render: (args) => (
|
|
390
|
-
<HoverMenu {...args}>
|
|
391
|
-
<MenuItem>
|
|
392
|
-
<SettingsIcon style={{ marginRight: 8 }} /> Settings
|
|
393
|
-
</MenuItem>
|
|
394
|
-
<MenuItem>
|
|
395
|
-
<MessageIcon style={{ marginRight: 8 }} /> Messages
|
|
396
|
-
</MenuItem>
|
|
397
|
-
<MenuItem>
|
|
398
|
-
<ImageIcon style={{ marginRight: 8 }} /> Gallery
|
|
399
|
-
</MenuItem>
|
|
400
|
-
</HoverMenu>
|
|
401
|
-
),
|
|
402
|
-
};
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { useState } from "react";
|
|
2
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
-
import { Modal, type ModalProps } from "./Modal";
|
|
4
|
-
import { Button } from "../Button";
|
|
5
|
-
|
|
6
|
-
const meta: Meta<typeof Modal> = {
|
|
7
|
-
title: "UI-Kit/Modal",
|
|
8
|
-
component: Modal,
|
|
9
|
-
tags: ["autodocs"],
|
|
10
|
-
argTypes: {
|
|
11
|
-
opened: { table: { disable: true } },
|
|
12
|
-
onClose: { table: { disable: true } },
|
|
13
|
-
defaultChecked: { table: { disable: true } },
|
|
14
|
-
},
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
export default meta;
|
|
18
|
-
|
|
19
|
-
type Story = StoryObj<typeof Modal>;
|
|
20
|
-
|
|
21
|
-
const DefaultWrapper = (args: ModalProps) => {
|
|
22
|
-
// Starts opened so the modal is visible without pressing a button first.
|
|
23
|
-
const [opened, setOpened] = useState(true);
|
|
24
|
-
return (
|
|
25
|
-
<>
|
|
26
|
-
<Modal {...args} opened={opened} onClose={() => setOpened(false)}>
|
|
27
|
-
Please log in to continue accessing this feature.
|
|
28
|
-
<Modal.Footer>
|
|
29
|
-
<Button variant="outline" onClick={() => setOpened(false)}>
|
|
30
|
-
Cancel
|
|
31
|
-
</Button>
|
|
32
|
-
<Button onClick={() => setOpened(false)}>Confirm</Button>
|
|
33
|
-
</Modal.Footer>
|
|
34
|
-
</Modal>
|
|
35
|
-
<Button onClick={() => setOpened(true)}>Open Modal</Button>
|
|
36
|
-
</>
|
|
37
|
-
);
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
export const Default: Story = {
|
|
41
|
-
args: {
|
|
42
|
-
title: "Authentication Required",
|
|
43
|
-
},
|
|
44
|
-
render: (args) => <DefaultWrapper {...args} />,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const LongTitleWrapper = (args: ModalProps) => {
|
|
48
|
-
const [opened, setOpened] = useState(true);
|
|
49
|
-
return (
|
|
50
|
-
<>
|
|
51
|
-
<Modal {...args} opened={opened} onClose={() => setOpened(false)}>
|
|
52
|
-
The title above is longer than the header can display, so it truncates
|
|
53
|
-
with an ellipsis instead of wrapping onto a second line.
|
|
54
|
-
<Modal.Footer>
|
|
55
|
-
<Button onClick={() => setOpened(false)}>Got it</Button>
|
|
56
|
-
</Modal.Footer>
|
|
57
|
-
</Modal>
|
|
58
|
-
<Button onClick={() => setOpened(true)}>Open Modal</Button>
|
|
59
|
-
</>
|
|
60
|
-
);
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export const LongTitle: Story = {
|
|
64
|
-
args: {
|
|
65
|
-
title:
|
|
66
|
-
"This Modal Title Is Deliberately Long Enough To Exceed The Available Header Width",
|
|
67
|
-
},
|
|
68
|
-
render: (args) => <LongTitleWrapper {...args} />,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const ScrollingWrapper = (args: ModalProps) => {
|
|
72
|
-
// Starts opened so the modal is visible without pressing a button first.
|
|
73
|
-
const [opened, setOpened] = useState(true);
|
|
74
|
-
return (
|
|
75
|
-
<>
|
|
76
|
-
<Modal {...args} opened={opened} onClose={() => setOpened(false)}>
|
|
77
|
-
<p>
|
|
78
|
-
This modal demonstrates the dynamically injected scroll dividers. When
|
|
79
|
-
this body content overflows, the borders between the Header and Footer
|
|
80
|
-
automatically appear to define the scrolling boundary.
|
|
81
|
-
</p>
|
|
82
|
-
{Array(20)
|
|
83
|
-
.fill(0)
|
|
84
|
-
.map((_, i) => (
|
|
85
|
-
<p key={i}>Scrolling content block {i + 1}...</p>
|
|
86
|
-
))}
|
|
87
|
-
{/* The new Footer abstraction */}
|
|
88
|
-
<Modal.Footer>
|
|
89
|
-
<Button variant="outline" onClick={() => setOpened(false)}>
|
|
90
|
-
Decline
|
|
91
|
-
</Button>
|
|
92
|
-
<Button onClick={() => setOpened(false)}>Accept Terms</Button>
|
|
93
|
-
</Modal.Footer>
|
|
94
|
-
</Modal>
|
|
95
|
-
<Button onClick={() => setOpened(true)}>Open Scrolling Modal</Button>
|
|
96
|
-
</>
|
|
97
|
-
);
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
export const ScrollingContent: Story = {
|
|
101
|
-
args: {
|
|
102
|
-
title: "Terms and Conditions",
|
|
103
|
-
},
|
|
104
|
-
render: (args) => <ScrollingWrapper {...args} />,
|
|
105
|
-
};
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { NumberInput } from "./NumberInput";
|
|
3
|
-
|
|
4
|
-
const meta: Meta<typeof NumberInput> = {
|
|
5
|
-
title: "UI-Kit/NumberInput",
|
|
6
|
-
component: NumberInput,
|
|
7
|
-
tags: ["autodocs"],
|
|
8
|
-
argTypes: {
|
|
9
|
-
label: { control: "text" },
|
|
10
|
-
assistiveText: { control: "text" },
|
|
11
|
-
disabled: { control: "boolean" },
|
|
12
|
-
error: { control: "boolean" },
|
|
13
|
-
readOnly: { control: "boolean" },
|
|
14
|
-
required: { control: "boolean" },
|
|
15
|
-
hideControls: { control: "boolean" },
|
|
16
|
-
formLayout: {
|
|
17
|
-
control: "select",
|
|
18
|
-
options: ["stacked", "side-by-side"],
|
|
19
|
-
},
|
|
20
|
-
labelSize: {
|
|
21
|
-
control: "select",
|
|
22
|
-
options: ["small", "default", "large"],
|
|
23
|
-
},
|
|
24
|
-
assistiveWithIcon: { table: { disable: true } },
|
|
25
|
-
labelOptionalText: { table: { disable: true } },
|
|
26
|
-
labelWithEditIcon: { table: { disable: true } },
|
|
27
|
-
onLabelEditClick: { table: { disable: true } },
|
|
28
|
-
emptyValueComponent: { table: { disable: true } },
|
|
29
|
-
},
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
export default meta;
|
|
33
|
-
|
|
34
|
-
type Story = StoryObj<typeof NumberInput>;
|
|
35
|
-
|
|
36
|
-
export const Default: Story = {
|
|
37
|
-
args: {
|
|
38
|
-
label: "Amount",
|
|
39
|
-
placeholder: "Enter an amount",
|
|
40
|
-
assistiveText: "Must be greater than 0",
|
|
41
|
-
defaultValue: 10,
|
|
42
|
-
min: 0,
|
|
43
|
-
max: 100,
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
export const SideBySideLayout: Story = {
|
|
48
|
-
args: {
|
|
49
|
-
...Default.args,
|
|
50
|
-
formLayout: "side-by-side",
|
|
51
|
-
},
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
export const States: Story = {
|
|
55
|
-
render: () => (
|
|
56
|
-
<div
|
|
57
|
-
style={{
|
|
58
|
-
display: "flex",
|
|
59
|
-
flexDirection: "column",
|
|
60
|
-
gap: "1rem",
|
|
61
|
-
maxWidth: 400,
|
|
62
|
-
}}
|
|
63
|
-
>
|
|
64
|
-
<NumberInput label="Default" placeholder="Enter a number" />
|
|
65
|
-
<NumberInput label="Disabled" placeholder="Disabled input" disabled />
|
|
66
|
-
<NumberInput label="Error" placeholder="Error state" error />
|
|
67
|
-
<NumberInput label="Read Only" value={42} readOnly />
|
|
68
|
-
<NumberInput label="Required" required />
|
|
69
|
-
</div>
|
|
70
|
-
),
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
export const WithLeftIcon: Story = {
|
|
74
|
-
args: {
|
|
75
|
-
label: "Price",
|
|
76
|
-
placeholder: "0.00",
|
|
77
|
-
leftSection: <span>$</span>,
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
export const WithRightIcon: Story = {
|
|
82
|
-
args: {
|
|
83
|
-
label: "Percentage",
|
|
84
|
-
placeholder: "0",
|
|
85
|
-
rightSection: <span>%</span>,
|
|
86
|
-
hideControls: true, // Typically hiding controls if rightSection is occupied
|
|
87
|
-
},
|
|
88
|
-
};
|
|
89
|
-
|
|
90
|
-
export const HiddenControls: Story = {
|
|
91
|
-
args: {
|
|
92
|
-
label: "Zip Code",
|
|
93
|
-
placeholder: "Enter zip code",
|
|
94
|
-
hideControls: true,
|
|
95
|
-
},
|
|
96
|
-
};
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { Pagination } from "./Pagination";
|
|
3
|
-
|
|
4
|
-
const meta: Meta<typeof Pagination> = {
|
|
5
|
-
title: "UI-Kit/Pagination",
|
|
6
|
-
component: Pagination,
|
|
7
|
-
tags: ["autodocs"],
|
|
8
|
-
argTypes: {
|
|
9
|
-
total: { control: "number" },
|
|
10
|
-
withEdges: { control: "boolean" },
|
|
11
|
-
withControls: { control: "boolean" },
|
|
12
|
-
disabled: { control: "boolean" },
|
|
13
|
-
withLabels: {
|
|
14
|
-
control: "boolean",
|
|
15
|
-
description: "Toggle text labels alongside icons",
|
|
16
|
-
},
|
|
17
|
-
defaultChecked: { table: { disable: true } },
|
|
18
|
-
},
|
|
19
|
-
args: {
|
|
20
|
-
total: 10,
|
|
21
|
-
withEdges: false,
|
|
22
|
-
withControls: true,
|
|
23
|
-
withLabels: false,
|
|
24
|
-
},
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
|
|
26
|
-
render: ({ withLayer, layer, ...args }: any) => {
|
|
27
|
-
return <Pagination {...args} />;
|
|
28
|
-
},
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
export default meta;
|
|
32
|
-
|
|
33
|
-
type Story = StoryObj<typeof Pagination>;
|
|
34
|
-
|
|
35
|
-
export const Default: Story = {};
|
|
36
|
-
|
|
37
|
-
export const WithEdges: Story = {
|
|
38
|
-
args: {
|
|
39
|
-
withEdges: true,
|
|
40
|
-
},
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
export const WithTextLabels: Story = {
|
|
44
|
-
args: {
|
|
45
|
-
withEdges: true,
|
|
46
|
-
withLabels: true,
|
|
47
|
-
},
|
|
48
|
-
};
|