@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.
Files changed (159) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/mui-adapter.cjs +74 -1
  3. package/dist/mui-adapter.cjs.map +1 -1
  4. package/dist/mui-adapter.css +1 -1
  5. package/dist/mui-adapter.js +6652 -204
  6. package/dist/mui-adapter.js.map +1 -1
  7. package/dist/src/components/Accordion/Accordion.d.ts +63 -2
  8. package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +9 -2
  9. package/dist/src/components/Autocomplete/Autocomplete.d.ts +16 -2
  10. package/dist/src/components/Avatar/Avatar.d.ts +14 -2
  11. package/dist/src/components/Badge/Badge.d.ts +13 -3
  12. package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +10 -3
  13. package/dist/src/components/Button/Button.d.ts +1 -1
  14. package/dist/src/components/Card/Card.d.ts +40 -3
  15. package/dist/src/components/Checkbox/Checkbox.d.ts +15 -2
  16. package/dist/src/components/Checkbox/CheckboxGroup.d.ts +16 -0
  17. package/dist/src/components/Checkbox/index.d.ts +5 -1
  18. package/dist/src/components/Chip/Chip.d.ts +18 -2
  19. package/dist/src/components/Dropdown/Dropdown.d.ts +23 -3
  20. package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +10 -2
  21. package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +24 -2
  22. package/dist/src/components/HoverCard/HoverCard.d.ts +55 -3
  23. package/dist/src/components/Label/Label.d.ts +13 -2
  24. package/dist/src/components/Link/Link.d.ts +1 -1
  25. package/dist/src/components/Menu/Menu.d.ts +99 -3
  26. package/dist/src/components/Modal/Modal.d.ts +36 -2
  27. package/dist/src/components/NumberInput/NumberInput.d.ts +14 -2
  28. package/dist/src/components/Pagination/Pagination.d.ts +44 -2
  29. package/dist/src/components/Pagination/Pagination.icons.d.ts +8 -0
  30. package/dist/src/components/Panel/Panel.d.ts +65 -3
  31. package/dist/src/components/Radio/Radio.d.ts +13 -3
  32. package/dist/src/components/Radio/RadioGroup.d.ts +13 -0
  33. package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
  34. package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +7 -2
  35. package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
  36. package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
  37. package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +28 -0
  38. package/dist/src/components/ReadOnlyField/index.d.ts +1 -0
  39. package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +31 -3
  40. package/dist/src/components/Slider/Slider.d.ts +28 -2
  41. package/dist/src/components/Stepper/Stepper.d.ts +21 -2
  42. package/dist/src/components/Switch/Switch.d.ts +10 -3
  43. package/dist/src/components/Switch/SwitchGroup.d.ts +13 -0
  44. package/dist/src/components/Tabs/Tabs.d.ts +35 -3
  45. package/dist/src/components/Text/Text.d.ts +1 -1
  46. package/dist/src/components/TextArea/TextArea.d.ts +18 -2
  47. package/dist/src/components/TextField/TextField.d.ts +16 -2
  48. package/dist/src/components/Timeline/Timeline.d.ts +14 -2
  49. package/dist/src/components/Timeline/TimelineItem.d.ts +29 -0
  50. package/dist/src/components/Title/Title.d.ts +1 -1
  51. package/dist/src/components/Toast/Toast.d.ts +25 -2
  52. package/dist/src/components/Tooltip/Tooltip.d.ts +42 -3
  53. package/dist/src/components/Typography/Typography.d.ts +1 -1
  54. package/package.json +9 -2
  55. package/src/components/Accordion/Accordion.module.css +295 -56
  56. package/src/components/Accordion/Accordion.stories.tsx +149 -21
  57. package/src/components/Accordion/Accordion.tsx +264 -5
  58. package/src/components/AssistiveElement/AssistiveElement.module.css +111 -20
  59. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +76 -19
  60. package/src/components/AssistiveElement/AssistiveElement.tsx +85 -5
  61. package/src/components/Autocomplete/Autocomplete.module.css +320 -42
  62. package/src/components/Autocomplete/Autocomplete.stories.tsx +180 -19
  63. package/src/components/Autocomplete/Autocomplete.tsx +199 -6
  64. package/src/components/Avatar/Avatar.module.css +343 -73
  65. package/src/components/Avatar/Avatar.stories.tsx +94 -22
  66. package/src/components/Avatar/Avatar.tsx +110 -6
  67. package/src/components/Badge/Badge.module.css +127 -29
  68. package/src/components/Badge/Badge.stories.tsx +65 -22
  69. package/src/components/Badge/Badge.tsx +70 -6
  70. package/src/components/Breadcrumb/Breadcrumb.module.css +25 -5
  71. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +57 -22
  72. package/src/components/Breadcrumb/Breadcrumb.tsx +62 -6
  73. package/src/components/Button/Button.module.css +3 -3
  74. package/src/components/Card/Card.module.css +118 -38
  75. package/src/components/Card/Card.stories.tsx +117 -19
  76. package/src/components/Card/Card.tsx +179 -5
  77. package/src/components/Checkbox/Checkbox.module.css +232 -39
  78. package/src/components/Checkbox/Checkbox.stories.tsx +81 -19
  79. package/src/components/Checkbox/Checkbox.tsx +146 -5
  80. package/src/components/Checkbox/CheckboxGroup.tsx +117 -0
  81. package/src/components/Checkbox/index.ts +6 -1
  82. package/src/components/Chip/Chip.module.css +228 -43
  83. package/src/components/Chip/Chip.stories.tsx +101 -21
  84. package/src/components/Chip/Chip.tsx +138 -6
  85. package/src/components/Dropdown/Dropdown.module.css +291 -41
  86. package/src/components/Dropdown/Dropdown.stories.tsx +80 -21
  87. package/src/components/Dropdown/Dropdown.tsx +182 -6
  88. package/src/components/FormControlLayout/FormControlLayout.module.css +92 -0
  89. package/src/components/FormControlLayout/FormControlLayout.stories.tsx +74 -19
  90. package/src/components/FormControlLayout/FormControlLayout.tsx +58 -4
  91. package/src/components/FormControlWrapper/FormControlWrapper.module.css +96 -0
  92. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +122 -19
  93. package/src/components/FormControlWrapper/FormControlWrapper.tsx +145 -6
  94. package/src/components/HoverCard/HoverCard.module.css +90 -22
  95. package/src/components/HoverCard/HoverCard.stories.tsx +158 -21
  96. package/src/components/HoverCard/HoverCard.tsx +150 -5
  97. package/src/components/Label/Label.module.css +136 -35
  98. package/src/components/Label/Label.stories.tsx +98 -17
  99. package/src/components/Label/Label.tsx +88 -5
  100. package/src/components/Menu/Menu.module.css +285 -51
  101. package/src/components/Menu/Menu.stories.tsx +307 -21
  102. package/src/components/Menu/Menu.tsx +318 -5
  103. package/src/components/Modal/Modal.module.css +171 -35
  104. package/src/components/Modal/Modal.stories.tsx +66 -22
  105. package/src/components/Modal/Modal.tsx +216 -4
  106. package/src/components/NumberInput/NumberInput.module.css +276 -42
  107. package/src/components/NumberInput/NumberInput.stories.tsx +83 -20
  108. package/src/components/NumberInput/NumberInput.tsx +148 -6
  109. package/src/components/Pagination/Pagination.icons.tsx +66 -0
  110. package/src/components/Pagination/Pagination.module.css +246 -20
  111. package/src/components/Pagination/Pagination.stories.tsx +42 -21
  112. package/src/components/Pagination/Pagination.tsx +193 -5
  113. package/src/components/Panel/Panel.module.css +206 -38
  114. package/src/components/Panel/Panel.stories.tsx +214 -22
  115. package/src/components/Panel/Panel.tsx +153 -5
  116. package/src/components/Radio/Radio.module.css +220 -37
  117. package/src/components/Radio/Radio.stories.tsx +77 -19
  118. package/src/components/Radio/Radio.tsx +178 -5
  119. package/src/components/Radio/RadioGroup.tsx +111 -0
  120. package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
  121. package/src/components/ReadOnlyField/ReadOnlyField.module.css +53 -15
  122. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +175 -16
  123. package/src/components/ReadOnlyField/ReadOnlyField.tsx +109 -5
  124. package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
  125. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
  126. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
  127. package/src/components/ReadOnlyField/index.ts +1 -0
  128. package/src/components/SegmentedControl/SegmentedControl.module.css +203 -47
  129. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +102 -20
  130. package/src/components/SegmentedControl/SegmentedControl.tsx +110 -6
  131. package/src/components/Slider/Slider.module.css +542 -86
  132. package/src/components/Slider/Slider.stories.tsx +157 -19
  133. package/src/components/Slider/Slider.tsx +282 -5
  134. package/src/components/Stepper/Stepper.module.css +410 -71
  135. package/src/components/Stepper/Stepper.stories.tsx +132 -19
  136. package/src/components/Stepper/Stepper.tsx +145 -6
  137. package/src/components/Switch/Switch.module.css +238 -39
  138. package/src/components/Switch/Switch.stories.tsx +98 -19
  139. package/src/components/Switch/Switch.tsx +153 -6
  140. package/src/components/Switch/SwitchGroup.tsx +111 -0
  141. package/src/components/Tabs/Tabs.module.css +422 -79
  142. package/src/components/Tabs/Tabs.stories.tsx +138 -19
  143. package/src/components/Tabs/Tabs.tsx +103 -6
  144. package/src/components/TextArea/TextArea.module.css +155 -32
  145. package/src/components/TextArea/TextArea.stories.tsx +80 -21
  146. package/src/components/TextArea/TextArea.tsx +156 -6
  147. package/src/components/TextField/TextField.module.css +261 -42
  148. package/src/components/TextField/TextField.stories.tsx +168 -15
  149. package/src/components/TextField/TextField.tsx +171 -6
  150. package/src/components/Timeline/Timeline.module.css +365 -76
  151. package/src/components/Timeline/Timeline.stories.tsx +114 -21
  152. package/src/components/Timeline/Timeline.tsx +80 -5
  153. package/src/components/Timeline/TimelineItem.tsx +101 -0
  154. package/src/components/Toast/Toast.module.css +166 -28
  155. package/src/components/Toast/Toast.stories.tsx +54 -22
  156. package/src/components/Toast/Toast.tsx +90 -5
  157. package/src/components/Tooltip/Tooltip.module.css +89 -24
  158. package/src/components/Tooltip/Tooltip.stories.tsx +152 -22
  159. 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 { ComingSoon } from "@recursica/storybook-template";
3
+ import { Panel, type PanelProps } from "./Panel";
4
+ import { Button } from "../Button";
5
+ import { Text } from "../Text/Text";
4
6
 
5
- const meta: Meta<typeof Panel> = {
6
- title: "UI-Kit/🚧 Panel",
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
- controls: {
11
- include: [
12
- "layer",
13
- "withLayer",
14
- "children",
15
- "component",
16
- "variant",
17
- "size",
18
- "icon",
19
- "disabled",
20
- "href",
21
- "onClick",
22
- "onChange",
23
- "value",
24
- "checked",
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
- type Story = StoryObj<typeof Panel>;
76
+
77
+ type Story = StoryObj<PanelStoryArgs>;
32
78
 
33
79
  export const Default: Story = {
34
- render: () => <ComingSoon componentName="Panel" />,
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 React from "react";
2
- import "./Panel.module.css";
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
- export type PanelProps = React.HTMLAttributes<HTMLDivElement>;
12
+ // ============================================================
13
+ // PANEL (Drawer)
14
+ // ============================================================
5
15
 
6
- export const Panel: React.FC<PanelProps> = (props) => {
7
- return <div {...props}>Panel</div>;
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
- /* EXEMPTIONS:
2
- This component is a placeholder stub. Once fully implemented, its specific
3
- tokens will be fully wired up to direct styles. */
4
-
5
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-group_properties_item-gap */
6
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-group_properties_padding */
7
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_gutter */
8
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_top-bottom-margin */
9
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-group_variants_layouts_side-by-side_properties_vertical-padding */
10
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-group_variants_layouts_stacked_properties_label-field-gap */
11
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-group_variants_layouts_stacked_properties_top-bottom-margin */
12
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_colors_disabled-text */
13
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_colors_text */
14
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_disabled-opacity */
15
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_label-gap */
16
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_max-width */
17
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_text_font-family */
18
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_text_font-size */
19
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_text_font-style */
20
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_text_font-weight */
21
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_text_letter-spacing */
22
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_text_line-height */
23
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_text_text-decoration */
24
- /* recursica-ignore: --recursica_ui-kit_components_radio-button-item_properties_text_text-transform */
25
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_border-radius */
26
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_border-size */
27
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_colors_background-selected */
28
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_colors_background-unselected */
29
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_colors_border-selected */
30
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_colors_border-unselected */
31
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_colors_disabled-background */
32
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_colors_disabled-border */
33
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_colors_disabled-icon */
34
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_colors_icon-color */
35
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_disabled-opacity */
36
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_icon-size */
37
- /* recursica-ignore: --recursica_ui-kit_components_radio-button_properties_size */
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
+ }