@recursica/mui-adapter 0.5.0 → 0.6.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/CHANGELOG.md +9 -0
- package/dist/mui-adapter.cjs +74 -1
- package/dist/mui-adapter.cjs.map +1 -1
- package/dist/mui-adapter.css +1 -1
- package/dist/mui-adapter.js +6652 -204
- package/dist/mui-adapter.js.map +1 -1
- package/dist/src/components/Accordion/Accordion.d.ts +63 -2
- package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +9 -2
- package/dist/src/components/Autocomplete/Autocomplete.d.ts +16 -2
- package/dist/src/components/Avatar/Avatar.d.ts +14 -2
- package/dist/src/components/Badge/Badge.d.ts +13 -3
- package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +10 -3
- package/dist/src/components/Button/Button.d.ts +1 -1
- package/dist/src/components/Card/Card.d.ts +40 -3
- package/dist/src/components/Checkbox/Checkbox.d.ts +15 -2
- package/dist/src/components/Checkbox/CheckboxGroup.d.ts +16 -0
- package/dist/src/components/Checkbox/index.d.ts +5 -1
- package/dist/src/components/Chip/Chip.d.ts +18 -2
- package/dist/src/components/Dropdown/Dropdown.d.ts +23 -3
- package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +10 -2
- package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +24 -2
- package/dist/src/components/HoverCard/HoverCard.d.ts +55 -3
- package/dist/src/components/Label/Label.d.ts +13 -2
- package/dist/src/components/Link/Link.d.ts +1 -1
- package/dist/src/components/Menu/Menu.d.ts +99 -3
- package/dist/src/components/Modal/Modal.d.ts +36 -2
- package/dist/src/components/NumberInput/NumberInput.d.ts +14 -2
- package/dist/src/components/Pagination/Pagination.d.ts +44 -2
- package/dist/src/components/Pagination/Pagination.icons.d.ts +8 -0
- package/dist/src/components/Panel/Panel.d.ts +65 -3
- package/dist/src/components/Radio/Radio.d.ts +13 -3
- package/dist/src/components/Radio/RadioGroup.d.ts +13 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +7 -2
- package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
- package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +28 -0
- package/dist/src/components/ReadOnlyField/index.d.ts +1 -0
- package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +31 -3
- package/dist/src/components/Slider/Slider.d.ts +28 -2
- package/dist/src/components/Stepper/Stepper.d.ts +21 -2
- package/dist/src/components/Switch/Switch.d.ts +10 -3
- package/dist/src/components/Switch/SwitchGroup.d.ts +13 -0
- package/dist/src/components/Tabs/Tabs.d.ts +35 -3
- package/dist/src/components/Text/Text.d.ts +1 -1
- package/dist/src/components/TextArea/TextArea.d.ts +18 -2
- package/dist/src/components/TextField/TextField.d.ts +16 -2
- package/dist/src/components/Timeline/Timeline.d.ts +14 -2
- package/dist/src/components/Timeline/TimelineItem.d.ts +29 -0
- package/dist/src/components/Title/Title.d.ts +1 -1
- package/dist/src/components/Toast/Toast.d.ts +25 -2
- package/dist/src/components/Tooltip/Tooltip.d.ts +42 -3
- package/dist/src/components/Typography/Typography.d.ts +1 -1
- package/package.json +9 -2
- package/src/components/Accordion/Accordion.module.css +295 -56
- package/src/components/Accordion/Accordion.stories.tsx +149 -21
- package/src/components/Accordion/Accordion.tsx +264 -5
- package/src/components/AssistiveElement/AssistiveElement.module.css +111 -20
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +76 -19
- package/src/components/AssistiveElement/AssistiveElement.tsx +85 -5
- package/src/components/Autocomplete/Autocomplete.module.css +320 -42
- package/src/components/Autocomplete/Autocomplete.stories.tsx +180 -19
- package/src/components/Autocomplete/Autocomplete.tsx +199 -6
- package/src/components/Avatar/Avatar.module.css +343 -73
- package/src/components/Avatar/Avatar.stories.tsx +94 -22
- package/src/components/Avatar/Avatar.tsx +110 -6
- package/src/components/Badge/Badge.module.css +127 -29
- package/src/components/Badge/Badge.stories.tsx +65 -22
- package/src/components/Badge/Badge.tsx +70 -6
- package/src/components/Breadcrumb/Breadcrumb.module.css +25 -5
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +57 -22
- package/src/components/Breadcrumb/Breadcrumb.tsx +62 -6
- package/src/components/Button/Button.module.css +3 -3
- package/src/components/Card/Card.module.css +118 -38
- package/src/components/Card/Card.stories.tsx +117 -19
- package/src/components/Card/Card.tsx +179 -5
- package/src/components/Checkbox/Checkbox.module.css +232 -39
- package/src/components/Checkbox/Checkbox.stories.tsx +81 -19
- package/src/components/Checkbox/Checkbox.tsx +146 -5
- package/src/components/Checkbox/CheckboxGroup.tsx +117 -0
- package/src/components/Checkbox/index.ts +6 -1
- package/src/components/Chip/Chip.module.css +228 -43
- package/src/components/Chip/Chip.stories.tsx +101 -21
- package/src/components/Chip/Chip.tsx +138 -6
- package/src/components/Dropdown/Dropdown.module.css +291 -41
- package/src/components/Dropdown/Dropdown.stories.tsx +80 -21
- package/src/components/Dropdown/Dropdown.tsx +182 -6
- package/src/components/FormControlLayout/FormControlLayout.module.css +92 -0
- package/src/components/FormControlLayout/FormControlLayout.stories.tsx +74 -19
- package/src/components/FormControlLayout/FormControlLayout.tsx +58 -4
- package/src/components/FormControlWrapper/FormControlWrapper.module.css +96 -0
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +122 -19
- package/src/components/FormControlWrapper/FormControlWrapper.tsx +145 -6
- package/src/components/HoverCard/HoverCard.module.css +90 -22
- package/src/components/HoverCard/HoverCard.stories.tsx +158 -21
- package/src/components/HoverCard/HoverCard.tsx +150 -5
- package/src/components/Label/Label.module.css +136 -35
- package/src/components/Label/Label.stories.tsx +98 -17
- package/src/components/Label/Label.tsx +88 -5
- package/src/components/Menu/Menu.module.css +285 -51
- package/src/components/Menu/Menu.stories.tsx +307 -21
- package/src/components/Menu/Menu.tsx +318 -5
- package/src/components/Modal/Modal.module.css +171 -35
- package/src/components/Modal/Modal.stories.tsx +66 -22
- package/src/components/Modal/Modal.tsx +216 -4
- package/src/components/NumberInput/NumberInput.module.css +276 -42
- package/src/components/NumberInput/NumberInput.stories.tsx +83 -20
- package/src/components/NumberInput/NumberInput.tsx +148 -6
- package/src/components/Pagination/Pagination.icons.tsx +66 -0
- package/src/components/Pagination/Pagination.module.css +246 -20
- package/src/components/Pagination/Pagination.stories.tsx +42 -21
- package/src/components/Pagination/Pagination.tsx +193 -5
- package/src/components/Panel/Panel.module.css +206 -38
- package/src/components/Panel/Panel.stories.tsx +214 -22
- package/src/components/Panel/Panel.tsx +153 -5
- package/src/components/Radio/Radio.module.css +220 -37
- package/src/components/Radio/Radio.stories.tsx +77 -19
- package/src/components/Radio/Radio.tsx +178 -5
- package/src/components/Radio/RadioGroup.tsx +111 -0
- package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyField.module.css +53 -15
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +175 -16
- package/src/components/ReadOnlyField/ReadOnlyField.tsx +109 -5
- package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
- package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
- package/src/components/ReadOnlyField/index.ts +1 -0
- package/src/components/SegmentedControl/SegmentedControl.module.css +203 -47
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +102 -20
- package/src/components/SegmentedControl/SegmentedControl.tsx +110 -6
- package/src/components/Slider/Slider.module.css +542 -86
- package/src/components/Slider/Slider.stories.tsx +157 -19
- package/src/components/Slider/Slider.tsx +282 -5
- package/src/components/Stepper/Stepper.module.css +410 -71
- package/src/components/Stepper/Stepper.stories.tsx +132 -19
- package/src/components/Stepper/Stepper.tsx +145 -6
- package/src/components/Switch/Switch.module.css +238 -39
- package/src/components/Switch/Switch.stories.tsx +98 -19
- package/src/components/Switch/Switch.tsx +153 -6
- package/src/components/Switch/SwitchGroup.tsx +111 -0
- package/src/components/Tabs/Tabs.module.css +422 -79
- package/src/components/Tabs/Tabs.stories.tsx +138 -19
- package/src/components/Tabs/Tabs.tsx +103 -6
- package/src/components/TextArea/TextArea.module.css +155 -32
- package/src/components/TextArea/TextArea.stories.tsx +80 -21
- package/src/components/TextArea/TextArea.tsx +156 -6
- package/src/components/TextField/TextField.module.css +261 -42
- package/src/components/TextField/TextField.stories.tsx +168 -15
- package/src/components/TextField/TextField.tsx +171 -6
- package/src/components/Timeline/Timeline.module.css +365 -76
- package/src/components/Timeline/Timeline.stories.tsx +114 -21
- package/src/components/Timeline/Timeline.tsx +80 -5
- package/src/components/Timeline/TimelineItem.tsx +101 -0
- package/src/components/Toast/Toast.module.css +166 -28
- package/src/components/Toast/Toast.stories.tsx +54 -22
- package/src/components/Toast/Toast.tsx +90 -5
- package/src/components/Tooltip/Tooltip.module.css +89 -24
- package/src/components/Tooltip/Tooltip.stories.tsx +152 -22
- package/src/components/Tooltip/Tooltip.tsx +114 -5
|
@@ -1,35 +1,227 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
1
2
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
-
import { Panel } from "./Panel";
|
|
3
|
-
import {
|
|
3
|
+
import { Panel, type PanelProps } from "./Panel";
|
|
4
|
+
import { Button } from "../Button";
|
|
5
|
+
import { Text } from "../Text/Text";
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
8
|
+
type PanelStoryArgs = Record<string, any>;
|
|
9
|
+
|
|
10
|
+
const meta: Meta = {
|
|
11
|
+
title: "UI-Kit/Panel",
|
|
7
12
|
component: Panel,
|
|
8
13
|
tags: ["autodocs"],
|
|
14
|
+
argTypes: {
|
|
15
|
+
position: {
|
|
16
|
+
control: "select",
|
|
17
|
+
options: ["left", "right", "top", "bottom"],
|
|
18
|
+
description: "Side of the screen the panel slides in from.",
|
|
19
|
+
},
|
|
20
|
+
title: {
|
|
21
|
+
control: "text",
|
|
22
|
+
description: "Panel title displayed in the header.",
|
|
23
|
+
},
|
|
24
|
+
withOverlay: {
|
|
25
|
+
control: "boolean",
|
|
26
|
+
description: "Whether to display a background overlay.",
|
|
27
|
+
},
|
|
28
|
+
withCloseButton: {
|
|
29
|
+
control: "boolean",
|
|
30
|
+
description: "Whether to display the close button in the header.",
|
|
31
|
+
},
|
|
32
|
+
wrapHeaderText: {
|
|
33
|
+
control: "boolean",
|
|
34
|
+
description:
|
|
35
|
+
"If true, forces the header text to a single line and truncates with an ellipsis.",
|
|
36
|
+
},
|
|
37
|
+
// Hide auto-detected HTML attributes
|
|
38
|
+
defaultChecked: { table: { disable: true } },
|
|
39
|
+
defaultValue: { table: { disable: true } },
|
|
40
|
+
suppressContentEditableWarning: { table: { disable: true } },
|
|
41
|
+
suppressHydrationWarning: { table: { disable: true } },
|
|
42
|
+
},
|
|
9
43
|
parameters: {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
44
|
+
layout: "fullscreen",
|
|
45
|
+
docs: {
|
|
46
|
+
description: {
|
|
47
|
+
component: `
|
|
48
|
+
The \`Panel\` component slides in or expands from the edge of the screen to reveal additional content or functionality. Built on Mantine's \`Drawer\`, it enforces Recursica design tokens for styling.
|
|
49
|
+
|
|
50
|
+
### Anatomy
|
|
51
|
+
1. **Header** — Title and close icon, remains fixed on scroll
|
|
52
|
+
2. **Divider** — Separates header/footer from content
|
|
53
|
+
3. **Body (Slot)** — Scrollable content area for custom content
|
|
54
|
+
4. **Footer** — Fixed action buttons (Recursica-specific)
|
|
55
|
+
|
|
56
|
+
### Usage
|
|
57
|
+
\`\`\`tsx
|
|
58
|
+
const [opened, { open, close }] = useDisclosure(false);
|
|
59
|
+
|
|
60
|
+
<Button onClick={open}>Open Panel</Button>
|
|
61
|
+
<Panel opened={opened} onClose={close} title="Settings" position="right">
|
|
62
|
+
Content goes here
|
|
63
|
+
<Panel.Footer>
|
|
64
|
+
<Button variant="outline">Cancel</Button>
|
|
65
|
+
<Button variant="solid">Save</Button>
|
|
66
|
+
</Panel.Footer>
|
|
67
|
+
</Panel>
|
|
68
|
+
\`\`\`
|
|
69
|
+
`,
|
|
70
|
+
},
|
|
26
71
|
},
|
|
27
72
|
},
|
|
28
73
|
};
|
|
29
74
|
|
|
30
75
|
export default meta;
|
|
31
|
-
|
|
76
|
+
|
|
77
|
+
type Story = StoryObj<PanelStoryArgs>;
|
|
32
78
|
|
|
33
79
|
export const Default: Story = {
|
|
34
|
-
|
|
80
|
+
args: {
|
|
81
|
+
position: "right",
|
|
82
|
+
title: "Panel Title",
|
|
83
|
+
withOverlay: true,
|
|
84
|
+
withCloseButton: true,
|
|
85
|
+
wrapHeaderText: false,
|
|
86
|
+
},
|
|
87
|
+
render: ({ wrapHeaderText, ...args }: PanelStoryArgs) => {
|
|
88
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
89
|
+
const [opened, setOpened] = useState(false);
|
|
90
|
+
|
|
91
|
+
return (
|
|
92
|
+
<>
|
|
93
|
+
<Button variant="solid" onClick={() => setOpened(true)}>
|
|
94
|
+
Open Panel
|
|
95
|
+
</Button>
|
|
96
|
+
<Panel
|
|
97
|
+
{...(args as PanelProps)}
|
|
98
|
+
opened={opened}
|
|
99
|
+
onClose={() => setOpened(false)}
|
|
100
|
+
title="Panel Title"
|
|
101
|
+
position="right"
|
|
102
|
+
wrapHeaderText={wrapHeaderText}
|
|
103
|
+
>
|
|
104
|
+
<Text>
|
|
105
|
+
This is the panel body content area. Panels slide in from the edge
|
|
106
|
+
of the screen to reveal supplementary information, navigation
|
|
107
|
+
options, or toolsets.
|
|
108
|
+
</Text>
|
|
109
|
+
<Panel.Footer>
|
|
110
|
+
<Button variant="outline" onClick={() => setOpened(false)}>
|
|
111
|
+
Cancel
|
|
112
|
+
</Button>
|
|
113
|
+
<Button variant="solid">Save</Button>
|
|
114
|
+
</Panel.Footer>
|
|
115
|
+
</Panel>
|
|
116
|
+
</>
|
|
117
|
+
);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export const LeftPosition: Story = {
|
|
122
|
+
args: {
|
|
123
|
+
position: "left",
|
|
124
|
+
title: "Navigation",
|
|
125
|
+
withOverlay: true,
|
|
126
|
+
withCloseButton: true,
|
|
127
|
+
wrapHeaderText: false,
|
|
128
|
+
},
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
130
|
+
render: ({ withLayer, layer, ...args }: PanelStoryArgs) => {
|
|
131
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
132
|
+
const [opened, setOpened] = useState(false);
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<>
|
|
136
|
+
<Button variant="outline" onClick={() => setOpened(true)}>
|
|
137
|
+
Open Left Panel
|
|
138
|
+
</Button>
|
|
139
|
+
<Panel
|
|
140
|
+
{...(args as PanelProps)}
|
|
141
|
+
opened={opened}
|
|
142
|
+
onClose={() => setOpened(false)}
|
|
143
|
+
>
|
|
144
|
+
<Text>
|
|
145
|
+
A panel sliding in from the left, commonly used for navigation menus
|
|
146
|
+
or sidebars.
|
|
147
|
+
</Text>
|
|
148
|
+
</Panel>
|
|
149
|
+
</>
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
export const ScrollableContent: Story = {
|
|
155
|
+
args: {
|
|
156
|
+
position: "right",
|
|
157
|
+
title: "Scrollable Panel",
|
|
158
|
+
withOverlay: true,
|
|
159
|
+
withCloseButton: true,
|
|
160
|
+
wrapHeaderText: false,
|
|
161
|
+
},
|
|
162
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
163
|
+
render: ({ withLayer, layer, ...args }: PanelStoryArgs) => {
|
|
164
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
165
|
+
const [opened, setOpened] = useState(false);
|
|
166
|
+
|
|
167
|
+
return (
|
|
168
|
+
<>
|
|
169
|
+
<Button variant="solid" onClick={() => setOpened(true)}>
|
|
170
|
+
Open Scrollable Panel
|
|
171
|
+
</Button>
|
|
172
|
+
<Panel
|
|
173
|
+
{...(args as PanelProps)}
|
|
174
|
+
opened={opened}
|
|
175
|
+
onClose={() => setOpened(false)}
|
|
176
|
+
>
|
|
177
|
+
{Array.from({ length: 20 }).map((_, i) => (
|
|
178
|
+
<p key={i} style={{ marginBottom: "1rem" }}>
|
|
179
|
+
Paragraph {i + 1}: This is sample content to demonstrate the
|
|
180
|
+
scrollable behavior of the panel when content exceeds the viewport
|
|
181
|
+
height.
|
|
182
|
+
</p>
|
|
183
|
+
))}
|
|
184
|
+
<Panel.Footer>
|
|
185
|
+
<Button variant="outline" onClick={() => setOpened(false)}>
|
|
186
|
+
Close
|
|
187
|
+
</Button>
|
|
188
|
+
<Button variant="solid">Apply</Button>
|
|
189
|
+
</Panel.Footer>
|
|
190
|
+
</Panel>
|
|
191
|
+
</>
|
|
192
|
+
);
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
export const LongTitle: Story = {
|
|
197
|
+
args: {
|
|
198
|
+
position: "right",
|
|
199
|
+
title:
|
|
200
|
+
"This is a ridiculously long panel title designed to test how the header CSS handles text overflow and whether it truncates correctly or breaks the layout",
|
|
201
|
+
withOverlay: true,
|
|
202
|
+
withCloseButton: true,
|
|
203
|
+
wrapHeaderText: true,
|
|
204
|
+
},
|
|
205
|
+
render: ({ ...args }: PanelStoryArgs) => {
|
|
206
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
207
|
+
const [opened, setOpened] = useState(false);
|
|
208
|
+
|
|
209
|
+
return (
|
|
210
|
+
<>
|
|
211
|
+
<Button variant="solid" onClick={() => setOpened(true)}>
|
|
212
|
+
Open Long Title Panel
|
|
213
|
+
</Button>
|
|
214
|
+
<Panel
|
|
215
|
+
{...(args as PanelProps)}
|
|
216
|
+
opened={opened}
|
|
217
|
+
onClose={() => setOpened(false)}
|
|
218
|
+
>
|
|
219
|
+
<Text>
|
|
220
|
+
Check the header to see if the long title is handled gracefully
|
|
221
|
+
without pushing the close button off screen.
|
|
222
|
+
</Text>
|
|
223
|
+
</Panel>
|
|
224
|
+
</>
|
|
225
|
+
);
|
|
226
|
+
},
|
|
35
227
|
};
|
|
@@ -1,8 +1,156 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
1
|
+
import { forwardRef } from "react";
|
|
2
|
+
import {
|
|
3
|
+
Drawer as MuiDrawer,
|
|
4
|
+
type DrawerProps as MuiDrawerProps,
|
|
5
|
+
} from "@mui/material";
|
|
6
|
+
import {
|
|
7
|
+
filterStylingProps,
|
|
8
|
+
type RecursicaOverStyled,
|
|
9
|
+
} from "../../utils/filterStylingProps";
|
|
10
|
+
import styles from "./Panel.module.css";
|
|
3
11
|
|
|
4
|
-
|
|
12
|
+
// ============================================================
|
|
13
|
+
// PANEL (Drawer)
|
|
14
|
+
// ============================================================
|
|
5
15
|
|
|
6
|
-
export
|
|
7
|
-
|
|
16
|
+
export interface RecursicaPanelProps extends MuiDrawerProps {
|
|
17
|
+
/**
|
|
18
|
+
* If true, forces the header text to a single line and truncates with an ellipsis.
|
|
19
|
+
* Note: While the prop is named `wrapHeaderText` for backward compatibility,
|
|
20
|
+
* setting it to `true` actually PREVENTS wrapping (it forces truncation).
|
|
21
|
+
*/
|
|
22
|
+
wrapHeaderText?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Recursica Panel root props. Extends Mui Drawer.
|
|
27
|
+
*/
|
|
28
|
+
export type PanelProps = RecursicaOverStyled<RecursicaPanelProps>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Recursica Panel component wrapping Mui's Drawer.
|
|
32
|
+
*
|
|
33
|
+
* Panels slide in or expand from the edge of the screen to reveal
|
|
34
|
+
* additional content or functionality. They are commonly used to provide
|
|
35
|
+
* supplementary information, navigation options, or toolsets without
|
|
36
|
+
* cluttering the main interface.
|
|
37
|
+
*
|
|
38
|
+
* ```tsx
|
|
39
|
+
* <Panel opened={opened} onClose={close} title="Panel Title" position="right">
|
|
40
|
+
* <Panel.Body>
|
|
41
|
+
* Content goes here
|
|
42
|
+
* </Panel.Body>
|
|
43
|
+
* </Panel>
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* Mui Drawer sub-components available via dot-notation:
|
|
47
|
+
* - `Panel.Header` — Top section with title and close button
|
|
48
|
+
* - `Panel.Title` — Title text within the header
|
|
49
|
+
* - `Panel.CloseButton` — Close button within the header
|
|
50
|
+
* - `Panel.Body` — Scrollable body content area
|
|
51
|
+
* - `Panel.Content` — Outer content container
|
|
52
|
+
* - `Panel.Overlay` — Background overlay
|
|
53
|
+
* - `Panel.Root` — Root element for advanced composition
|
|
54
|
+
* - `Panel.Stack` — Stacked drawer context
|
|
55
|
+
*/
|
|
56
|
+
const PanelBase = function Panel({
|
|
57
|
+
overStyled = false,
|
|
58
|
+
position = "right",
|
|
59
|
+
keepMounted = true,
|
|
60
|
+
wrapHeaderText = false,
|
|
61
|
+
...rest
|
|
62
|
+
}: PanelProps) {
|
|
63
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
64
|
+
|
|
65
|
+
// Bind CSS module classes to Mui's internal classNames API
|
|
66
|
+
const mergedClassNames: Partial<Record<string, string>> = {
|
|
67
|
+
content: styles.content,
|
|
68
|
+
header: styles.header,
|
|
69
|
+
title: wrapHeaderText ? styles.titleTruncate : styles.title,
|
|
70
|
+
body: styles.body,
|
|
71
|
+
inner: styles.inner,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const classNamesProp = (sanitizedProps as Record<string, unknown>).classNames;
|
|
75
|
+
if (
|
|
76
|
+
classNamesProp &&
|
|
77
|
+
typeof classNamesProp === "object" &&
|
|
78
|
+
!Array.isArray(classNamesProp)
|
|
79
|
+
) {
|
|
80
|
+
const o = classNamesProp as Record<string, string>;
|
|
81
|
+
Object.keys(o).forEach((key) => {
|
|
82
|
+
if (mergedClassNames[key]) {
|
|
83
|
+
mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
|
|
84
|
+
} else {
|
|
85
|
+
mergedClassNames[key] = o[key];
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
<MuiDrawer
|
|
92
|
+
position={position} /* Recursica default: right; Mui default: left */
|
|
93
|
+
keepMounted={keepMounted}
|
|
94
|
+
closeOnClickOutside={rest.closeOnClickOutside ?? Boolean(rest.opened)}
|
|
95
|
+
{...(sanitizedProps as unknown as MuiDrawerProps)}
|
|
96
|
+
classes={mergedClassNames}
|
|
97
|
+
/>
|
|
98
|
+
);
|
|
99
|
+
};
|
|
100
|
+
PanelBase.displayName = "Panel";
|
|
101
|
+
|
|
102
|
+
// ============================================================
|
|
103
|
+
// PANEL FOOTER (custom — Mui Drawer has no Footer sub-component)
|
|
104
|
+
// ============================================================
|
|
105
|
+
|
|
106
|
+
export type PanelFooterProps = RecursicaOverStyled<
|
|
107
|
+
React.HTMLAttributes<HTMLDivElement>
|
|
108
|
+
>;
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Panel footer section with action buttons.
|
|
112
|
+
* Separated from the body by a divider. Remains fixed at the bottom.
|
|
113
|
+
* This is a Recursica-specific sub-component; Mui Drawer does not
|
|
114
|
+
* natively provide a footer.
|
|
115
|
+
*/
|
|
116
|
+
export const PanelFooter = forwardRef<HTMLDivElement, PanelFooterProps>(
|
|
117
|
+
function PanelFooter({ overStyled = false, ...rest }, ref) {
|
|
118
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
119
|
+
const classNameProp = (sanitizedProps as Record<string, unknown>)
|
|
120
|
+
.className as string | undefined;
|
|
121
|
+
|
|
122
|
+
const finalClassName = classNameProp
|
|
123
|
+
? `${styles.footer} ${classNameProp}`
|
|
124
|
+
: styles.footer;
|
|
125
|
+
|
|
126
|
+
return <div ref={ref} className={finalClassName} {...sanitizedProps} />;
|
|
127
|
+
},
|
|
128
|
+
);
|
|
129
|
+
PanelFooter.displayName = "PanelFooter";
|
|
130
|
+
|
|
131
|
+
// ============================================================
|
|
132
|
+
// DOT NOTATION EXPORT
|
|
133
|
+
// ============================================================
|
|
134
|
+
|
|
135
|
+
type PanelComponent = typeof PanelBase & {
|
|
136
|
+
Root: typeof MuiDrawer.Root;
|
|
137
|
+
Overlay: typeof MuiDrawer.Overlay;
|
|
138
|
+
Content: typeof MuiDrawer.Content;
|
|
139
|
+
Header: typeof MuiDrawer.Header;
|
|
140
|
+
Title: typeof MuiDrawer.Title;
|
|
141
|
+
CloseButton: typeof MuiDrawer.CloseButton;
|
|
142
|
+
Body: typeof MuiDrawer.Body;
|
|
143
|
+
Stack: typeof MuiDrawer.Stack;
|
|
144
|
+
Footer: typeof PanelFooter;
|
|
8
145
|
};
|
|
146
|
+
|
|
147
|
+
export const Panel = PanelBase as PanelComponent;
|
|
148
|
+
Panel.Root = MuiDrawer.Root;
|
|
149
|
+
Panel.Overlay = MuiDrawer.Overlay;
|
|
150
|
+
Panel.Content = MuiDrawer.Content;
|
|
151
|
+
Panel.Header = MuiDrawer.Header;
|
|
152
|
+
Panel.Title = MuiDrawer.Title;
|
|
153
|
+
Panel.CloseButton = MuiDrawer.CloseButton;
|
|
154
|
+
Panel.Body = MuiDrawer.Body;
|
|
155
|
+
Panel.Stack = MuiDrawer.Stack;
|
|
156
|
+
Panel.Footer = PanelFooter;
|
|
@@ -1,37 +1,220 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
/*
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
/*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
/*
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Radio.module.css
|
|
3
|
+
*
|
|
4
|
+
* HARDCODED VALUES:
|
|
5
|
+
* - margin / padding: 0 — Structural resets; consider token if spacing needed.
|
|
6
|
+
* - cursor: pointer / not-allowed — Baseline interactive indications.
|
|
7
|
+
* - appearance / box-sizing / border-style — necessary normalization for inputs.
|
|
8
|
+
* - transition: all 0.2s ease — Animation curve; consider a transition token.
|
|
9
|
+
* - min-width: 0 — Forces text wrap boundary in flex children.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/* --- GROUP STYLES --- */
|
|
13
|
+
.groupRoot {
|
|
14
|
+
padding: var(
|
|
15
|
+
--recursica_ui-kit_components_radio-button-group_properties_padding,
|
|
16
|
+
0
|
|
17
|
+
);
|
|
18
|
+
margin: 0; /* HARDCODE: structural reset; consider token if layout needs change */
|
|
19
|
+
display: flex;
|
|
20
|
+
flex-direction: column;
|
|
21
|
+
gap: var(
|
|
22
|
+
--recursica_ui-kit_components_radio-button-group_properties_item-gap,
|
|
23
|
+
16px
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* SideBySide Variant */
|
|
28
|
+
.groupRoot[data-layout="side-by-side"] {
|
|
29
|
+
flex-direction: row;
|
|
30
|
+
flex-wrap: wrap;
|
|
31
|
+
gap: var(
|
|
32
|
+
--recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_gutter
|
|
33
|
+
);
|
|
34
|
+
padding-top: var(
|
|
35
|
+
--recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_vertical-padding
|
|
36
|
+
);
|
|
37
|
+
padding-bottom: var(
|
|
38
|
+
--recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_vertical-padding
|
|
39
|
+
);
|
|
40
|
+
margin-top: var(
|
|
41
|
+
--recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_top-bottom-margin
|
|
42
|
+
);
|
|
43
|
+
margin-bottom: var(
|
|
44
|
+
--recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_top-bottom-margin
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/* Stacked Variant */
|
|
49
|
+
.groupRoot[data-layout="stacked"] {
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
margin-top: var(
|
|
52
|
+
--recursica_ui-kit_components_radio-button-group_variants_layouts_stacked_properties_label-field-gap,
|
|
53
|
+
var(
|
|
54
|
+
--recursica_ui-kit_components_radio-button-group_variants_layouts_stacked_properties_top-bottom-margin
|
|
55
|
+
)
|
|
56
|
+
);
|
|
57
|
+
margin-bottom: var(
|
|
58
|
+
--recursica_ui-kit_components_radio-button-group_variants_layouts_stacked_properties_top-bottom-margin
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/* --- PRIMITIVE RADIO STYLES --- */
|
|
63
|
+
|
|
64
|
+
.root {
|
|
65
|
+
max-width: var(
|
|
66
|
+
--recursica_ui-kit_components_radio-button-item_properties_max-width
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.body {
|
|
71
|
+
display: flex;
|
|
72
|
+
align-items: flex-start;
|
|
73
|
+
gap: var(
|
|
74
|
+
--recursica_ui-kit_components_radio-button-item_properties_label-gap
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.root .inner {
|
|
79
|
+
display: flex;
|
|
80
|
+
align-items: center;
|
|
81
|
+
justify-content: center;
|
|
82
|
+
margin: 0; /* HARDCODE: structural reset */
|
|
83
|
+
width: var(--recursica_ui-kit_components_radio-button_properties_size);
|
|
84
|
+
height: var(--recursica_ui-kit_components_radio-button_properties_size);
|
|
85
|
+
/* Apply font-size to this element so that an 'em' based line-height token evaluates to the correct exact pixel value. */
|
|
86
|
+
font-size: var(
|
|
87
|
+
--recursica_ui-kit_components_radio-button-item_properties_text_font-size
|
|
88
|
+
);
|
|
89
|
+
/* Mathematically syncs the physical input radio bounding block visually with the center of the text's first line.
|
|
90
|
+
NOTE: Since the Figma line-height token resolves to an 'em' length (e.g. 1.05em), we CANNOT multiply it by font-size in CSS.
|
|
91
|
+
Instead, it naturally evaluates to the correct height because we applied the font-size above. */
|
|
92
|
+
margin-top: calc(
|
|
93
|
+
(
|
|
94
|
+
var(
|
|
95
|
+
--recursica_ui-kit_components_radio-button-item_properties_text_line-height
|
|
96
|
+
) -
|
|
97
|
+
var(--recursica_ui-kit_components_radio-button_properties_size)
|
|
98
|
+
) /
|
|
99
|
+
2
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* Base explicit reset masking underlying framework defaults */
|
|
104
|
+
.root .radio {
|
|
105
|
+
cursor: pointer; /* HARDCODE: core interaction behavior */
|
|
106
|
+
appearance: none; /* HARDCODE: reset native widget */
|
|
107
|
+
box-sizing: border-box; /* HARDCODE: ensures size includes borders */
|
|
108
|
+
width: var(--recursica_ui-kit_components_radio-button_properties_size);
|
|
109
|
+
height: var(--recursica_ui-kit_components_radio-button_properties_size);
|
|
110
|
+
border-radius: var(
|
|
111
|
+
--recursica_ui-kit_components_radio-button_properties_border-radius
|
|
112
|
+
);
|
|
113
|
+
border-width: var(
|
|
114
|
+
--recursica_ui-kit_components_radio-button_properties_border-size
|
|
115
|
+
);
|
|
116
|
+
border-style: solid; /* HARDCODE: normalize primitive border-style */
|
|
117
|
+
|
|
118
|
+
/* Standard unchecked state */
|
|
119
|
+
background-color: var(
|
|
120
|
+
--recursica_ui-kit_components_radio-button_properties_colors_background-unselected
|
|
121
|
+
);
|
|
122
|
+
border-color: var(
|
|
123
|
+
--recursica_ui-kit_components_radio-button_properties_colors_border-unselected
|
|
124
|
+
);
|
|
125
|
+
transition: all 0.2s ease; /* HARDCODE: consider global transition token */
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/* Checked State */
|
|
129
|
+
.root .radio:checked {
|
|
130
|
+
background-color: var(
|
|
131
|
+
--recursica_ui-kit_components_radio-button_properties_colors_background-selected
|
|
132
|
+
);
|
|
133
|
+
border-color: var(
|
|
134
|
+
--recursica_ui-kit_components_radio-button_properties_colors_border-selected
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/* Disabled State */
|
|
139
|
+
.root .radio:disabled {
|
|
140
|
+
cursor: not-allowed; /* HARDCODE: disabled state default */
|
|
141
|
+
opacity: var(
|
|
142
|
+
--recursica_ui-kit_components_radio-button_properties_disabled-opacity
|
|
143
|
+
);
|
|
144
|
+
background-color: var(
|
|
145
|
+
--recursica_ui-kit_components_radio-button_properties_colors_disabled-background
|
|
146
|
+
);
|
|
147
|
+
border-color: var(
|
|
148
|
+
--recursica_ui-kit_components_radio-button_properties_colors_disabled-border
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.root .icon {
|
|
153
|
+
position: absolute;
|
|
154
|
+
inset: 0;
|
|
155
|
+
margin: auto;
|
|
156
|
+
pointer-events: none;
|
|
157
|
+
width: var(--recursica_ui-kit_components_radio-button_properties_icon-size);
|
|
158
|
+
height: var(--recursica_ui-kit_components_radio-button_properties_icon-size);
|
|
159
|
+
color: var(
|
|
160
|
+
--recursica_ui-kit_components_radio-button_properties_colors_icon-color
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
.root .radio:disabled + .iconWrapper .icon,
|
|
164
|
+
.root .radio:disabled + .icon {
|
|
165
|
+
color: var(
|
|
166
|
+
--recursica_ui-kit_components_radio-button_properties_colors_disabled-icon
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.root .labelWrapper {
|
|
171
|
+
padding: 0; /* HARDCODE: structural layout reset */
|
|
172
|
+
margin: 0; /* HARDCODE: structural layout reset */
|
|
173
|
+
flex: 1;
|
|
174
|
+
min-width: 0; /* HARDCODE: forces text wrap boundary in flex child */
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.root .label {
|
|
178
|
+
cursor: pointer; /* HARDCODE: interactive affordance */
|
|
179
|
+
padding: 0; /* HARDCODE: reset layout */
|
|
180
|
+
margin: 0; /* HARDCODE: reset layout */
|
|
181
|
+
word-wrap: break-word;
|
|
182
|
+
white-space: normal;
|
|
183
|
+
color: var(
|
|
184
|
+
--recursica_ui-kit_components_radio-button-item_properties_colors_text
|
|
185
|
+
);
|
|
186
|
+
font-family: var(
|
|
187
|
+
--recursica_ui-kit_components_radio-button-item_properties_text_font-family
|
|
188
|
+
);
|
|
189
|
+
font-size: var(
|
|
190
|
+
--recursica_ui-kit_components_radio-button-item_properties_text_font-size
|
|
191
|
+
);
|
|
192
|
+
font-weight: var(
|
|
193
|
+
--recursica_ui-kit_components_radio-button-item_properties_text_font-weight
|
|
194
|
+
);
|
|
195
|
+
line-height: var(
|
|
196
|
+
--recursica_ui-kit_components_radio-button-item_properties_text_line-height
|
|
197
|
+
);
|
|
198
|
+
font-style: var(
|
|
199
|
+
--recursica_ui-kit_components_radio-button-item_properties_text_font-style
|
|
200
|
+
);
|
|
201
|
+
letter-spacing: var(
|
|
202
|
+
--recursica_ui-kit_components_radio-button-item_properties_text_letter-spacing
|
|
203
|
+
);
|
|
204
|
+
text-decoration: var(
|
|
205
|
+
--recursica_ui-kit_components_radio-button-item_properties_text_text-decoration
|
|
206
|
+
);
|
|
207
|
+
text-transform: var(
|
|
208
|
+
--recursica_ui-kit_components_radio-button-item_properties_text_text-transform
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
.root:has(input:disabled) .label {
|
|
212
|
+
cursor: not-allowed; /* HARDCODE: disabled interaction */
|
|
213
|
+
color: var(
|
|
214
|
+
--recursica_ui-kit_components_radio-button-item_properties_colors_disabled-text
|
|
215
|
+
);
|
|
216
|
+
opacity: var(
|
|
217
|
+
--recursica_ui-kit_components_radio-button-item_properties_disabled-opacity,
|
|
218
|
+
var(--recursica_ui-kit_components_radio-button_properties_disabled-opacity)
|
|
219
|
+
);
|
|
220
|
+
}
|