@recursica/mui-adapter 0.9.1 → 0.11.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 (92) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/mui-adapter.cjs +30 -20
  3. package/dist/mui-adapter.cjs.map +1 -1
  4. package/dist/mui-adapter.css +1 -1
  5. package/dist/mui-adapter.js +2410 -2085
  6. package/dist/mui-adapter.js.map +1 -1
  7. package/dist/src/components/Accordion/Accordion.d.ts +43 -10
  8. package/dist/src/components/Autocomplete/Autocomplete.d.ts +13 -2
  9. package/dist/src/components/Avatar/Avatar.d.ts +16 -1
  10. package/dist/src/components/Badge/Badge.d.ts +16 -1
  11. package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +13 -2
  12. package/dist/src/components/Button/Button.d.ts +13 -2
  13. package/dist/src/components/Card/Card.d.ts +45 -2
  14. package/dist/src/components/Checkbox/CheckboxGroup.d.ts +14 -3
  15. package/dist/src/components/Chip/Chip.d.ts +13 -2
  16. package/dist/src/components/Container/Container.d.ts +3 -3
  17. package/dist/src/components/Dropdown/Dropdown.d.ts +14 -3
  18. package/dist/src/components/Flex/Flex.d.ts +3 -4
  19. package/dist/src/components/Group/Group.d.ts +3 -4
  20. package/dist/src/components/Link/Link.d.ts +14 -4
  21. package/dist/src/components/Menu/Menu.d.ts +37 -8
  22. package/dist/src/components/Modal/Modal.d.ts +51 -11
  23. package/dist/src/components/NumberInput/NumberInput.d.ts +13 -2
  24. package/dist/src/components/Pagination/Pagination.d.ts +13 -2
  25. package/dist/src/components/Panel/Panel.d.ts +6 -6
  26. package/dist/src/components/Radio/index.d.ts +1 -0
  27. package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +15 -4
  28. package/dist/src/components/Slider/Slider.d.ts +13 -2
  29. package/dist/src/components/Stack/Stack.d.ts +3 -4
  30. package/dist/src/components/Stepper/Stepper.d.ts +71 -16
  31. package/dist/src/components/Switch/Switch.d.ts +1 -1
  32. package/dist/src/components/Switch/SwitchGroup.d.ts +14 -3
  33. package/dist/src/components/Switch/index.d.ts +1 -0
  34. package/dist/src/components/Table/Table.d.ts +34 -3
  35. package/dist/src/components/Tabs/Tabs.d.ts +45 -8
  36. package/dist/src/components/Text/Text.d.ts +18 -3
  37. package/dist/src/components/TextArea/TextArea.d.ts +14 -3
  38. package/dist/src/components/TextField/TextField.d.ts +13 -2
  39. package/dist/src/components/Timeline/TimelineItem.d.ts +13 -2
  40. package/dist/src/components/Timeline/index.d.ts +1 -0
  41. package/dist/src/components/Title/Title.d.ts +18 -2
  42. package/dist/src/components/Toast/Toast.d.ts +13 -2
  43. package/dist/src/components/Typography/Typography.d.ts +14 -3
  44. package/dist/src/components/Typography/index.d.ts +1 -0
  45. package/dist/src/components/index.d.ts +1 -0
  46. package/dist/src/index.d.ts +85 -1
  47. package/dist/src/utils/filterStylingProps.d.ts +1 -1
  48. package/package.json +1 -1
  49. package/src/OverStyling.tsx +63 -2
  50. package/src/components/Accordion/Accordion.tsx +1 -0
  51. package/src/components/Autocomplete/Autocomplete.tsx +2 -0
  52. package/src/components/Avatar/Avatar.tsx +3 -3
  53. package/src/components/Badge/Badge.tsx +1 -1
  54. package/src/components/Card/CARD_IMPLEMENTATION_NOTES.md +15 -0
  55. package/src/components/Card/Card.module.css +1 -0
  56. package/src/components/Card/Card.tsx +30 -2
  57. package/src/components/Checkbox/Checkbox.tsx +6 -1
  58. package/src/components/Checkbox/CheckboxGroup.tsx +6 -2
  59. package/src/components/Container/Container.tsx +5 -3
  60. package/src/components/Dropdown/Dropdown.tsx +10 -11
  61. package/src/components/Flex/Flex.tsx +5 -2
  62. package/src/components/Group/Group.tsx +4 -3
  63. package/src/components/HoverCard/HoverCard.tsx +7 -4
  64. package/src/components/Link/Link.tsx +1 -1
  65. package/src/components/Menu/Menu.stories.tsx +14 -8
  66. package/src/components/Modal/Modal.tsx +11 -4
  67. package/src/components/Panel/PANEL_IMPLEMENTATION_NOTES.md +64 -0
  68. package/src/components/Panel/Panel.stories.tsx +9 -9
  69. package/src/components/Panel/Panel.tsx +14 -9
  70. package/src/components/Radio/Radio.tsx +1 -0
  71. package/src/components/Radio/index.ts +1 -0
  72. package/src/components/ReadOnlyField/ReadOnlyField.tsx +1 -1
  73. package/src/components/SegmentedControl/SegmentedControl.tsx +5 -1
  74. package/src/components/Slider/Slider.tsx +12 -4
  75. package/src/components/Stack/Stack.tsx +4 -3
  76. package/src/components/Stepper/Stepper.tsx +6 -1
  77. package/src/components/Switch/Switch.tsx +2 -1
  78. package/src/components/Switch/SwitchGroup.tsx +5 -1
  79. package/src/components/Switch/index.ts +1 -0
  80. package/src/components/Table/Table.module.css +7 -0
  81. package/src/components/Table/Table.stories.tsx +35 -3
  82. package/src/components/Table/Table.tsx +61 -5
  83. package/src/components/Tabs/Tabs.tsx +5 -1
  84. package/src/components/Text/Text.tsx +5 -2
  85. package/src/components/TextArea/TextArea.tsx +11 -10
  86. package/src/components/TextField/TextField.tsx +2 -0
  87. package/src/components/Timeline/index.ts +1 -0
  88. package/src/components/Title/Title.tsx +4 -1
  89. package/src/components/Typography/index.ts +1 -0
  90. package/src/components/index.ts +1 -0
  91. package/src/index.ts +180 -1
  92. package/src/utils/filterStylingProps.ts +1 -0
@@ -65,7 +65,8 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(function ModalRoot(
65
65
  if (
66
66
  React.isValidElement(child) &&
67
67
  (child.type === ModalFooter ||
68
- (child.type as any)?.displayName === "Modal.Footer")
68
+ (child.type as unknown as { displayName?: string })?.displayName ===
69
+ "Modal.Footer")
69
70
  ) {
70
71
  footer = child;
71
72
  } else {
@@ -81,7 +82,11 @@ const ModalRoot = forwardRef<HTMLDivElement, ModalProps>(function ModalRoot(
81
82
  paper: styles.inner,
82
83
  }}
83
84
  {...(sanitizedProps as MuiDialogProps)}
84
- open={opened || (sanitizedProps as any).open}
85
+ open={
86
+ opened ||
87
+ ((sanitizedProps as Record<string, unknown>).open as boolean) ||
88
+ false
89
+ }
85
90
  onClose={onClose}
86
91
  >
87
92
  {(title || withCloseButton) && (
@@ -117,6 +122,7 @@ const ModalTitle = forwardRef<HTMLDivElement, ModalTitleProps>(
117
122
 
118
123
  return (
119
124
  <MuiDialogTitle
125
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
120
126
  ref={ref as any}
121
127
  className={`${styles.title} ${className || ""}`}
122
128
  {...(sanitizedProps as MuiDialogTitleProps)}
@@ -159,10 +165,10 @@ const ModalBody = forwardRef<HTMLDivElement, ModalBodyProps>(function ModalBody(
159
165
  return (
160
166
  <MuiDialogContent
161
167
  ref={(node) => {
162
- if (typeof ref === "function") ref(node as any);
168
+ if (typeof ref === "function") ref(node as HTMLDivElement);
163
169
  else if (ref)
164
170
  (ref as React.MutableRefObject<HTMLDivElement | null>).current =
165
- node as any;
171
+ node as HTMLDivElement;
166
172
  if (internalRef)
167
173
  (
168
174
  internalRef as React.MutableRefObject<HTMLDivElement | null>
@@ -193,6 +199,7 @@ const ModalFooter = forwardRef<HTMLDivElement, ModalFooterProps>(
193
199
 
194
200
  return (
195
201
  <MuiDialogActions
202
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
196
203
  ref={ref as any}
197
204
  className={`${styles.footer} ${className || ""}`}
198
205
  {...(sanitizedProps as MuiDialogActionsProps)}
@@ -0,0 +1,64 @@
1
+ # Panel – Implementation Notes
2
+
3
+ Decisions and design tweaks strictly tailored for the UI Kit's Panel wrapped against `@mui/material`. This is a living document that tracks _why_ specific logic decisions exist.
4
+
5
+ ---
6
+
7
+ ## 1. Mapping to MUI Drawer
8
+
9
+ **Decision:** Panel maps to MUI's `Drawer` component, not `Paper` or `Card`.
10
+
11
+ **Implementation:** Per the Recursica design system specification, "Panels slide in or expand from the edge of the screen to reveal additional content or functionality." This is the exact behavior of MUI's `Drawer` component, which provides:
12
+
13
+ - Slide-in animation from any screen edge (using the `anchor` prop)
14
+ - Backdrop/overlay support
15
+ - Focus trap and overlay portal management
16
+ - Internal scroll lock when open
17
+
18
+ ---
19
+
20
+ ## 2. Token Namespace: `panel`
21
+
22
+ **Decision:** The CSS module exclusively uses variables from the `--recursica_ui-kit_components_panel_*` namespace.
23
+
24
+ **Implementation:** The Recursica token system defines the `panel` namespace covering:
25
+
26
+ - Geometry: border-radius, border-size, min-width (200px), max-width (960px)
27
+ - Content padding: content-horizontal-padding (xl), content-vertical-padding (lg)
28
+ - Header/Footer padding: header-footer-horizontal-padding (xl), header-footer-vertical-padding (md)
29
+ - Spacing: header-close-gap (md), footer-button-gap (md)
30
+ - Divider: divider-size (1px), divider-color
31
+ - Elevation: elevation-3
32
+ - Colors (layer-aware): background, border-color, content, divider-color, header-footer-background, title
33
+ - Non-CSS: header-style ("h3")
34
+
35
+ No tokens from other component namespaces are referenced.
36
+
37
+ ---
38
+
39
+ ## 3. Default Placement Override
40
+
41
+ **Decision:** Use `placement` instead of `position` for configuring slide-out direction, and default it to `"right"`.
42
+
43
+ **Implementation:** The prop was renamed from `position` to `placement` to prevent collision with the CSS `position` keyword, which is strictly blocked by the styling gatekeeper (`BLOCKED_STYLING_KEYS`). This allows configuring the drawer direction natively while maintaining strict design-system boundaries. The `placement="right"` default is mapped internally to MUI Drawer's `anchor` prop before any other sanitized props are applied. Right-side panels are the most common pattern for supplementary content, settings, and detail views.
44
+
45
+ ---
46
+
47
+ ## 4. Custom Panel.Footer
48
+
49
+ **Decision:** A custom `Panel.Footer` sub-component is provided. MUI's Drawer does not have a native footer.
50
+
51
+ **Implementation:** `Panel.Footer` is a `<div>` with styling referencing Recursica CSS variables for:
52
+
53
+ - `header-footer-background` and `header-footer-padding` tokens
54
+ - Top divider using `divider-size` and `divider-color`
55
+ - `footer-button-gap` for action button spacing
56
+ - `margin-top: auto` to push the footer to the bottom
57
+
58
+ ---
59
+
60
+ ## 5. Visibility Mapping (`opened` -> `open`)
61
+
62
+ **Decision:** Accept `opened` prop to match the standard Recursica component API.
63
+
64
+ **Implementation:** MUI Drawer natively expects the `open` boolean prop. The wrapper maps the incoming framework-agnostic `opened` prop to MUI's `open={Boolean(opened)}`, allowing consistent usage across both adapter implementations.
@@ -12,7 +12,7 @@ const meta: Meta = {
12
12
  component: Panel,
13
13
  tags: ["autodocs"],
14
14
  argTypes: {
15
- position: {
15
+ placement: {
16
16
  control: "select",
17
17
  options: ["left", "right", "top", "bottom"],
18
18
  description: "Side of the screen the panel slides in from.",
@@ -45,7 +45,7 @@ const meta: Meta = {
45
45
  docs: {
46
46
  description: {
47
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.
48
+ The \`Panel\` component slides in or expands from the edge of the screen to reveal additional content or functionality. Built on Mui's \`Drawer\`, it enforces Recursica design tokens for styling.
49
49
 
50
50
  ### Anatomy
51
51
  1. **Header** — Title and close icon, remains fixed on scroll
@@ -58,7 +58,7 @@ The \`Panel\` component slides in or expands from the edge of the screen to reve
58
58
  const [opened, { open, close }] = useDisclosure(false);
59
59
 
60
60
  <Button onClick={open}>Open Panel</Button>
61
- <Panel opened={opened} onClose={close} title="Settings" position="right">
61
+ <Panel opened={opened} onClose={close} title="Settings" placement="right">
62
62
  Content goes here
63
63
  <Panel.Footer>
64
64
  <Button variant="outline">Cancel</Button>
@@ -78,7 +78,7 @@ type Story = StoryObj<PanelStoryArgs>;
78
78
 
79
79
  export const Default: Story = {
80
80
  args: {
81
- position: "right",
81
+ placement: "right",
82
82
  title: "Panel Title",
83
83
  withOverlay: true,
84
84
  withCloseButton: true,
@@ -98,7 +98,7 @@ export const Default: Story = {
98
98
  opened={opened}
99
99
  onClose={() => setOpened(false)}
100
100
  title="Panel Title"
101
- position="right"
101
+ placement="right"
102
102
  wrapHeaderText={wrapHeaderText}
103
103
  >
104
104
  <Text>
@@ -118,9 +118,9 @@ export const Default: Story = {
118
118
  },
119
119
  };
120
120
 
121
- export const LeftPosition: Story = {
121
+ export const LeftPlacement: Story = {
122
122
  args: {
123
- position: "left",
123
+ placement: "left",
124
124
  title: "Navigation",
125
125
  withOverlay: true,
126
126
  withCloseButton: true,
@@ -153,7 +153,7 @@ export const LeftPosition: Story = {
153
153
 
154
154
  export const ScrollableContent: Story = {
155
155
  args: {
156
- position: "right",
156
+ placement: "right",
157
157
  title: "Scrollable Panel",
158
158
  withOverlay: true,
159
159
  withCloseButton: true,
@@ -195,7 +195,7 @@ export const ScrollableContent: Story = {
195
195
 
196
196
  export const LongTitle: Story = {
197
197
  args: {
198
- position: "right",
198
+ placement: "right",
199
199
  title:
200
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
201
  withOverlay: true,
@@ -13,11 +13,18 @@ import styles from "./Panel.module.css";
13
13
  // PANEL (Drawer)
14
14
  // ============================================================
15
15
 
16
- import { type RecursicaPanelProps } from "@recursica/adapter-common";
16
+ import { type RecursicaPanelProps as BaseRecursicaPanelProps } from "@recursica/adapter-common";
17
17
 
18
18
  /**
19
19
  * Recursica Panel root props. Extends Mui Drawer.
20
20
  */
21
+ export interface RecursicaPanelProps
22
+ extends Omit<
23
+ MuiDrawerProps,
24
+ "classes" | "position" | "style" | "anchor" | "open"
25
+ >,
26
+ BaseRecursicaPanelProps {}
27
+
21
28
  export type PanelProps = RecursicaOverStyled<RecursicaPanelProps>;
22
29
 
23
30
  /**
@@ -29,7 +36,7 @@ export type PanelProps = RecursicaOverStyled<RecursicaPanelProps>;
29
36
  * cluttering the main interface.
30
37
  *
31
38
  * ```tsx
32
- * <Panel opened={opened} onClose={close} title="Panel Title" position="right">
39
+ * <Panel opened={opened} onClose={close} title="Panel Title" placement="right">
33
40
  * <Panel.Body>
34
41
  * Content goes here
35
42
  * </Panel.Body>
@@ -48,14 +55,12 @@ export type PanelProps = RecursicaOverStyled<RecursicaPanelProps>;
48
55
  */
49
56
  const PanelBase = function Panel({
50
57
  overStyled = false,
51
- position = "right",
58
+ placement = "right",
52
59
  keepMounted = true,
53
60
  wrapHeaderText = false,
61
+ opened,
54
62
  ...rest
55
- }: PanelProps & {
56
- position?: "left" | "right" | "top" | "bottom";
57
- opened?: boolean;
58
- }) {
63
+ }: PanelProps) {
59
64
  const sanitizedProps = filterStylingProps(rest, overStyled);
60
65
 
61
66
  // Bind CSS module classes to Mui's internal classNames API
@@ -85,9 +90,9 @@ const PanelBase = function Panel({
85
90
 
86
91
  return (
87
92
  <MuiDrawer
88
- anchor={position} /* Recursica default: right; Mui default: left */
93
+ anchor={placement} /* Recursica default: right; Mui default: left */
89
94
  keepMounted={keepMounted}
90
- open={Boolean(rest.opened)}
95
+ open={Boolean(opened)}
91
96
  {...(sanitizedProps as unknown as MuiDrawerProps)}
92
97
  classes={mergedClassNames}
93
98
  />
@@ -147,6 +147,7 @@ export const Radio = forwardRef<HTMLInputElement, RadioProps>(
147
147
  // We omit Mui's sizing/coloring so we rely strictly on variables from Radio.module.css
148
148
  const radioNode = (
149
149
  <MuiRadio
150
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
150
151
  ref={ref as any}
151
152
  icon={<RadioIcon />}
152
153
  checkedIcon={<RadioIcon className={styles.checked} />}
@@ -1 +1,2 @@
1
1
  export * from "./Radio";
2
+ export * from "./RadioGroup";
@@ -81,7 +81,7 @@ export const ReadOnlyField = React.forwardRef<
81
81
  }
82
82
 
83
83
  // Type coercions securely bounding the output values cleanly explicitly.
84
- let displayValue: React.ReactNode = value;
84
+ let displayValue: React.ReactNode = value as any;
85
85
 
86
86
  if (type === "boolean" || type === "switch") {
87
87
  displayValue = value ? "True" : "False";
@@ -108,7 +108,11 @@ const _SegmentedControl = forwardRef<HTMLDivElement, SegmentedControlProps>(
108
108
  data-orientation={orientation}
109
109
  exclusive
110
110
  value={value}
111
- onChange={handleChange as any}
111
+ onChange={
112
+ handleChange as React.ComponentProps<
113
+ typeof MuiSegmentedControl
114
+ >["onChange"]
115
+ }
112
116
  {...(sanitizedProps as Omit<
113
117
  MuiSegmentedControlProps,
114
118
  "variant" | "size" | "value" | "onChange"
@@ -129,7 +129,7 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>(
129
129
  if (!isNaN(parsed)) {
130
130
  // Clamp input value to bounds
131
131
  const clamped = Math.max(min, Math.min(max, parsed));
132
- handleValueChange(null as any, clamped);
132
+ handleValueChange(null as unknown as Event, clamped);
133
133
  }
134
134
  };
135
135
 
@@ -211,7 +211,7 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>(
211
211
  label={label}
212
212
  assistiveText={assistiveText}
213
213
  assistiveWithIcon={assistiveWithIcon}
214
- error={error as any}
214
+ error={!!error}
215
215
  required={required}
216
216
  id={id}
217
217
  readOnly={readOnly}
@@ -239,14 +239,22 @@ export const Slider = forwardRef<HTMLDivElement, SliderProps>(
239
239
  disabled={disabled}
240
240
  value={resolvedValue}
241
241
  onChange={handleValueChange}
242
- onChangeCommitted={onChangeEnd as any}
242
+ onChangeCommitted={
243
+ onChangeEnd as unknown as (
244
+ event: Event | React.SyntheticEvent,
245
+ value: number | number[],
246
+ ) => void
247
+ }
243
248
  min={min}
244
249
  max={max}
245
250
  step={step}
246
251
  valueLabelDisplay={tooltipLabel === null ? "off" : "auto"}
247
252
  valueLabelFormat={
248
253
  typeof tooltipLabel === "function"
249
- ? (tooltipLabel as any)
254
+ ? (tooltipLabel as unknown as (
255
+ value: number,
256
+ index: number,
257
+ ) => React.ReactNode)
250
258
  : undefined
251
259
  }
252
260
  {...(sanitizedProps as unknown as MuiSliderProps)}
@@ -1,4 +1,4 @@
1
- import React, { forwardRef } from "react";
1
+ import { forwardRef } from "react";
2
2
  import {
3
3
  Stack as MUIStack,
4
4
  type StackProps as MUIStackProps,
@@ -7,12 +7,13 @@ import {
7
7
  SPACING_MAP,
8
8
  type OmitSx,
9
9
  filterSxProp,
10
+ type WithRecursicaSpacing,
10
11
  } from "../../utils/filterStylingProps";
11
12
 
12
13
  import { type RecursicaStackProps } from "@recursica/adapter-common";
13
14
 
14
- export type StackProps = OmitSx<
15
- Omit<MUIStackProps, "spacing"> & RecursicaStackProps
15
+ export type StackProps = WithRecursicaSpacing<
16
+ OmitSx<Omit<MUIStackProps, "spacing"> & RecursicaStackProps>
16
17
  >;
17
18
 
18
19
  export const Stack = forwardRef<HTMLDivElement, StackProps>(function Stack(
@@ -19,7 +19,11 @@ import styles from "./Stepper.module.css";
19
19
 
20
20
  import { type RecursicaStepperProps } from "@recursica/adapter-common";
21
21
 
22
- export type StepperProps = RecursicaOverStyled<RecursicaStepperProps>;
22
+ export interface RecursicaStepperPropsExtended
23
+ extends Omit<Partial<MuiStepperProps>, "size">,
24
+ RecursicaStepperProps {}
25
+
26
+ export type StepperProps = RecursicaOverStyled<RecursicaStepperPropsExtended>;
23
27
 
24
28
  export const Stepper = forwardRef<HTMLDivElement, StepperProps>(
25
29
  function Stepper(props, ref) {
@@ -42,6 +46,7 @@ export const Stepper = forwardRef<HTMLDivElement, StepperProps>(
42
46
  data-orientation={orientation}
43
47
  >
44
48
  <MuiStepper
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
50
  ref={ref as any}
46
51
  orientation={orientation}
47
52
  connector={
@@ -37,7 +37,7 @@ export type RecursicaSwitchPropsAlias =
37
37
 
38
38
  export type SwitchProps = RecursicaOverStyled<RecursicaSwitchPropsAlias>;
39
39
 
40
- type SwitchComponent = any;
40
+ type SwitchComponent = React.ComponentType<SwitchProps>;
41
41
 
42
42
  export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
43
43
  function Switch(props, ref) {
@@ -137,6 +137,7 @@ export const Switch = forwardRef<HTMLInputElement, SwitchProps>(
137
137
  // We omit Mui's sizing/coloring so we rely strictly on variables from Switch.module.css
138
138
  const switchNode = (
139
139
  <MuiSwitch
140
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
140
141
  ref={ref as any}
141
142
  className={!label ? `${finalClass} ${styles.inner}` : styles.inner}
142
143
  classes={mergedClassNames}
@@ -28,7 +28,11 @@ export interface RecursicaSwitchGroupProps
28
28
  >,
29
29
  Omit<
30
30
  RecursicaFormControlWrapperProps,
31
- "controlMaxWidth" | "controlMinWidth" | "classes" | "onChange"
31
+ | "controlMaxWidth"
32
+ | "controlMinWidth"
33
+ | "classes"
34
+ | "onChange"
35
+ | "defaultValue"
32
36
  >,
33
37
  ReadOnlyControlProps,
34
38
  BaseRecursicaSwitchGroupProps {}
@@ -1 +1,2 @@
1
1
  export * from "./Switch";
2
+ export * from "./SwitchGroup";
@@ -0,0 +1,7 @@
1
+ /* HARDCODED VALUES:
2
+ * None. This is a generic layout wrapper.
3
+ */
4
+
5
+ .root {
6
+ /* MUI Table handles table layout natively. */
7
+ }
@@ -1,9 +1,8 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { Table } from "./Table";
3
- import { ComingSoon } from "@recursica/storybook-template";
4
3
 
5
4
  const meta: Meta<typeof Table> = {
6
- title: "UI-Kit/🚧 Table",
5
+ title: "UI-Kit/Table",
7
6
  component: Table,
8
7
  tags: ["autodocs"],
9
8
  };
@@ -12,6 +11,39 @@ export default meta;
12
11
 
13
12
  type Story = StoryObj<typeof Table>;
14
13
 
14
+ const elements = [
15
+ { position: 6, mass: 12.011, symbol: "C", name: "Carbon" },
16
+ { position: 7, mass: 14.007, symbol: "N", name: "Nitrogen" },
17
+ { position: 8, mass: 15.999, symbol: "O", name: "Oxygen" },
18
+ { position: 9, mass: 18.998, symbol: "F", name: "Fluorine" },
19
+ { position: 10, mass: 20.18, symbol: "Ne", name: "Neon" },
20
+ ];
21
+
15
22
  export const Default: Story = {
16
- render: () => <ComingSoon componentName="Table" />,
23
+ render: () => {
24
+ const rows = elements.map((element) => (
25
+ <Table.Row key={element.name}>
26
+ <Table.Cell>{element.position}</Table.Cell>
27
+ <Table.Cell>{element.name}</Table.Cell>
28
+ <Table.Cell>{element.symbol}</Table.Cell>
29
+ <Table.Cell>{element.mass}</Table.Cell>
30
+ </Table.Row>
31
+ ));
32
+
33
+ return (
34
+ <Table.Container>
35
+ <Table>
36
+ <Table.Head>
37
+ <Table.Row>
38
+ <Table.Cell>Element position</Table.Cell>
39
+ <Table.Cell>Element name</Table.Cell>
40
+ <Table.Cell>Symbol</Table.Cell>
41
+ <Table.Cell>Atomic mass</Table.Cell>
42
+ </Table.Row>
43
+ </Table.Head>
44
+ <Table.Body>{rows}</Table.Body>
45
+ </Table>
46
+ </Table.Container>
47
+ );
48
+ },
17
49
  };
@@ -1,9 +1,65 @@
1
- import React from "react";
1
+ import { forwardRef } from "react";
2
+ import {
3
+ Table as MuiTable,
4
+ type TableProps as MuiTableProps,
5
+ TableBody as MuiTableBody,
6
+ TableCell as MuiTableCell,
7
+ TableContainer as MuiTableContainer,
8
+ TableHead as MuiTableHead,
9
+ TableRow as MuiTableRow,
10
+ TableFooter as MuiTableFooter,
11
+ TableSortLabel as MuiTableSortLabel,
12
+ } from "@mui/material";
13
+ import {
14
+ filterStylingProps,
15
+ type RecursicaOverStyled,
16
+ } from "../../utils/filterStylingProps";
2
17
  import { type RecursicaTableProps } from "@recursica/adapter-common";
18
+ import styles from "./Table.module.css";
3
19
 
4
- export type TableProps = React.HTMLAttributes<HTMLDivElement> &
5
- RecursicaTableProps;
20
+ export type TableProps = RecursicaOverStyled<
21
+ MuiTableProps & RecursicaTableProps
22
+ >;
6
23
 
7
- export const Table: React.FC<TableProps> = (props) => {
8
- return <div {...props}>Table</div>;
24
+ const TableBase = forwardRef<HTMLTableElement, TableProps>(function Table(
25
+ { overStyled = false, ...rest },
26
+ ref,
27
+ ) {
28
+ const sanitizedProps = filterStylingProps(rest, overStyled);
29
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
30
+ .className as string | undefined;
31
+
32
+ const rootClass = styles.root;
33
+ const finalClass = classNameProp
34
+ ? `${rootClass} ${classNameProp}`
35
+ : rootClass;
36
+
37
+ return (
38
+ <MuiTable
39
+ ref={ref}
40
+ className={finalClass}
41
+ {...(sanitizedProps as unknown as MuiTableProps)}
42
+ />
43
+ );
44
+ });
45
+
46
+ TableBase.displayName = "Table";
47
+
48
+ type TableComponent = typeof TableBase & {
49
+ Body: typeof MuiTableBody;
50
+ Cell: typeof MuiTableCell;
51
+ Container: typeof MuiTableContainer;
52
+ Head: typeof MuiTableHead;
53
+ Row: typeof MuiTableRow;
54
+ Footer: typeof MuiTableFooter;
55
+ SortLabel: typeof MuiTableSortLabel;
9
56
  };
57
+
58
+ export const Table = TableBase as TableComponent;
59
+ Table.Body = MuiTableBody;
60
+ Table.Cell = MuiTableCell;
61
+ Table.Container = MuiTableContainer;
62
+ Table.Head = MuiTableHead;
63
+ Table.Row = MuiTableRow;
64
+ Table.Footer = MuiTableFooter;
65
+ Table.SortLabel = MuiTableSortLabel;
@@ -13,7 +13,11 @@ import styles from "./Tabs.module.css";
13
13
 
14
14
  import { type RecursicaTabsProps } from "@recursica/adapter-common";
15
15
 
16
- export type TabsProps = RecursicaOverStyled<RecursicaTabsProps>;
16
+ export interface RecursicaTabsPropsExtended
17
+ extends Omit<MuiTabsProps, "variant">,
18
+ RecursicaTabsProps {}
19
+
20
+ export type TabsProps = RecursicaOverStyled<RecursicaTabsPropsExtended>;
17
21
 
18
22
  export const Tabs = forwardRef<HTMLDivElement, TabsProps>(
19
23
  function Tabs(props, ref) {
@@ -1,9 +1,12 @@
1
- import React, { forwardRef } from "react";
1
+ import { forwardRef } from "react";
2
+ import { type TypographyProps as MuiTypographyProps } from "@mui/material";
2
3
  import { Typography } from "../Typography/Typography";
3
4
  import { type RecursicaOverStyled } from "../../utils/filterStylingProps";
4
5
  import { type RecursicaTextProps } from "@recursica/adapter-common";
5
6
 
6
- export type TextProps = RecursicaOverStyled<RecursicaTextProps>;
7
+ export type TextProps = RecursicaOverStyled<
8
+ Omit<MuiTypographyProps, "variant" | "classes"> & RecursicaTextProps
9
+ >;
7
10
 
8
11
  export const Text = forwardRef<HTMLElement, TextProps>(function Text(
9
12
  { variant = "body", ...rest },
@@ -18,18 +18,18 @@ import { type RecursicaTextAreaProps as BaseRecursicaTextAreaProps } from "@recu
18
18
  export interface RecursicaTextAreaProps
19
19
  extends Omit<
20
20
  MuiTextareaProps,
21
- "size" | "variant" | "radius" | "wrapperProps"
21
+ | keyof React.HTMLAttributes<HTMLDivElement>
22
+ | "size"
23
+ | "variant"
24
+ | "radius"
25
+ | "wrapperProps"
26
+ | "maxRows"
27
+ | "minRows"
28
+ | "classes"
22
29
  >,
23
- Pick<
30
+ Omit<
24
31
  RecursicaFormControlWrapperProps,
25
- | "assistiveText"
26
- | "assistiveWithIcon"
27
- | "formLayout"
28
- | "labelSize"
29
- | "labelAlignment"
30
- | "labelOptionalText"
31
- | "labelWithEditIcon"
32
- | "onLabelEditClick"
32
+ "controlMaxWidth" | "controlMinWidth"
33
33
  >,
34
34
  ReadOnlyControlProps,
35
35
  BaseRecursicaTextAreaProps {}
@@ -129,6 +129,7 @@ export const TextArea = forwardRef<HTMLTextAreaElement, TextAreaProps>(
129
129
  /* Naked Input execution safely decoupled from Mui's macro Input.Wrapper DOM hooks */
130
130
  <MuiTextarea
131
131
  multiline
132
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
133
  inputRef={ref as any}
133
134
  classes={mergedClassNames}
134
135
  disabled={disabled}
@@ -137,7 +137,9 @@ export const TextField = forwardRef<HTMLInputElement, TextFieldProps>(
137
137
  required={required}
138
138
  id={id}
139
139
  readOnly={readOnly}
140
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
140
141
  readOnlyComponent={readOnlyComponent as any}
142
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
143
  emptyValueComponent={(emptyValueComponent as any) || undefined}
142
144
  readOnlyType="text"
143
145
  readOnlyValue={value !== undefined ? value : defaultValue}
@@ -1 +1,2 @@
1
1
  export * from "./Timeline";
2
+ export * from "./TimelineItem";
@@ -1,9 +1,12 @@
1
1
  import React, { forwardRef } from "react";
2
+ import { type TypographyProps as MuiTypographyProps } from "@mui/material";
2
3
  import { Typography } from "../Typography/Typography";
3
4
  import { type RecursicaOverStyled } from "../../utils/filterStylingProps";
4
5
  import { type RecursicaTitleProps } from "@recursica/adapter-common";
5
6
 
6
- export type TitleProps = RecursicaOverStyled<RecursicaTitleProps>;
7
+ export type TitleProps = RecursicaOverStyled<
8
+ Omit<MuiTypographyProps, "variant" | "classes"> & RecursicaTitleProps
9
+ >;
7
10
 
8
11
  export const Title = forwardRef<HTMLElement, TitleProps>(function Title(
9
12
  { order = 1, component, ...rest },
@@ -0,0 +1 @@
1
+ export * from "./Typography";
@@ -45,3 +45,4 @@ export * from "./Title";
45
45
  export * from "./Toast";
46
46
  export * from "./Tooltip";
47
47
  export * from "./TransferList";
48
+ export * from "./Typography";