@recursica/mui-adapter 0.4.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 (177) hide show
  1. package/CHANGELOG.md +15 -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 +6651 -201
  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 +17 -0
  10. package/dist/src/components/Autocomplete/index.d.ts +1 -0
  11. package/dist/src/components/Avatar/Avatar.d.ts +14 -2
  12. package/dist/src/components/Badge/Badge.d.ts +13 -3
  13. package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +10 -3
  14. package/dist/src/components/Button/Button.d.ts +1 -1
  15. package/dist/src/components/Card/Card.d.ts +40 -3
  16. package/dist/src/components/Checkbox/Checkbox.d.ts +15 -2
  17. package/dist/src/components/Checkbox/CheckboxGroup.d.ts +16 -0
  18. package/dist/src/components/Checkbox/index.d.ts +5 -1
  19. package/dist/src/components/Chip/Chip.d.ts +18 -2
  20. package/dist/src/components/Dropdown/Dropdown.d.ts +23 -3
  21. package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +10 -2
  22. package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +24 -2
  23. package/dist/src/components/HoverCard/HoverCard.d.ts +55 -3
  24. package/dist/src/components/Label/Label.d.ts +13 -2
  25. package/dist/src/components/Link/Link.d.ts +1 -1
  26. package/dist/src/components/Menu/Menu.d.ts +99 -3
  27. package/dist/src/components/Modal/Modal.d.ts +36 -2
  28. package/dist/src/components/NumberInput/NumberInput.d.ts +14 -2
  29. package/dist/src/components/Pagination/Pagination.d.ts +44 -2
  30. package/dist/src/components/Pagination/Pagination.icons.d.ts +8 -0
  31. package/dist/src/components/Panel/Panel.d.ts +65 -3
  32. package/dist/src/components/Radio/Radio.d.ts +13 -3
  33. package/dist/src/components/Radio/RadioGroup.d.ts +13 -0
  34. package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
  35. package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +7 -2
  36. package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
  37. package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
  38. package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +28 -0
  39. package/dist/src/components/ReadOnlyField/index.d.ts +1 -0
  40. package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +31 -3
  41. package/dist/src/components/Slider/Slider.d.ts +28 -2
  42. package/dist/src/components/Stepper/Stepper.d.ts +21 -2
  43. package/dist/src/components/Switch/Switch.d.ts +10 -3
  44. package/dist/src/components/Switch/SwitchGroup.d.ts +13 -0
  45. package/dist/src/components/Tabs/Tabs.d.ts +35 -3
  46. package/dist/src/components/Text/Text.d.ts +1 -1
  47. package/dist/src/components/TextArea/TextArea.d.ts +18 -2
  48. package/dist/src/components/TextField/TextField.d.ts +16 -2
  49. package/dist/src/components/Timeline/Timeline.d.ts +14 -2
  50. package/dist/src/components/Timeline/TimelineItem.d.ts +29 -0
  51. package/dist/src/components/Title/Title.d.ts +1 -1
  52. package/dist/src/components/Toast/Toast.d.ts +25 -2
  53. package/dist/src/components/Tooltip/Tooltip.d.ts +42 -3
  54. package/dist/src/components/Typography/Typography.d.ts +1 -1
  55. package/dist/src/components/index.d.ts +1 -0
  56. package/package.json +12 -2
  57. package/src/GlobalExemptions.modules.css +13 -0
  58. package/src/components/Accordion/Accordion.module.css +295 -0
  59. package/src/components/Accordion/Accordion.stories.tsx +149 -21
  60. package/src/components/Accordion/Accordion.tsx +264 -4
  61. package/src/components/AssistiveElement/AssistiveElement.module.css +111 -0
  62. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +76 -19
  63. package/src/components/AssistiveElement/AssistiveElement.tsx +85 -4
  64. package/src/components/Autocomplete/Autocomplete.module.css +325 -0
  65. package/src/components/Autocomplete/Autocomplete.stories.tsx +196 -0
  66. package/src/components/Autocomplete/Autocomplete.tsx +201 -0
  67. package/src/components/Autocomplete/index.ts +1 -0
  68. package/src/components/Avatar/Avatar.module.css +344 -0
  69. package/src/components/Avatar/Avatar.stories.tsx +94 -22
  70. package/src/components/Avatar/Avatar.tsx +110 -5
  71. package/src/components/Badge/Badge.module.css +127 -0
  72. package/src/components/Badge/Badge.stories.tsx +65 -22
  73. package/src/components/Badge/Badge.tsx +70 -5
  74. package/src/components/Breadcrumb/Breadcrumb.module.css +26 -0
  75. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +57 -22
  76. package/src/components/Breadcrumb/Breadcrumb.tsx +62 -5
  77. package/src/components/Button/Button.module.css +113 -20
  78. package/src/components/Button/Button.stories.tsx +3 -0
  79. package/src/components/Button/Button.tsx +10 -3
  80. package/src/components/Card/Card.module.css +118 -0
  81. package/src/components/Card/Card.stories.tsx +117 -19
  82. package/src/components/Card/Card.tsx +179 -4
  83. package/src/components/Checkbox/Checkbox.module.css +232 -0
  84. package/src/components/Checkbox/Checkbox.stories.tsx +81 -19
  85. package/src/components/Checkbox/Checkbox.tsx +146 -4
  86. package/src/components/Checkbox/CheckboxGroup.tsx +117 -0
  87. package/src/components/Checkbox/index.ts +6 -1
  88. package/src/components/Chip/Chip.module.css +236 -0
  89. package/src/components/Chip/Chip.stories.tsx +101 -21
  90. package/src/components/Chip/Chip.tsx +138 -5
  91. package/src/components/DatePicker/DatePicker.module.css +42 -0
  92. package/src/components/DatePicker/DatePicker.tsx +1 -0
  93. package/src/components/Dropdown/Dropdown.module.css +296 -0
  94. package/src/components/Dropdown/Dropdown.stories.tsx +80 -21
  95. package/src/components/Dropdown/Dropdown.tsx +182 -5
  96. package/src/components/FileInput/FileInput.module.css +47 -0
  97. package/src/components/FileInput/FileInput.tsx +1 -0
  98. package/src/components/FileUpload/FileUpload.module.css +40 -0
  99. package/src/components/FileUpload/FileUpload.tsx +1 -0
  100. package/src/components/FormControlLayout/FormControlLayout.module.css +92 -0
  101. package/src/components/FormControlLayout/FormControlLayout.stories.tsx +74 -19
  102. package/src/components/FormControlLayout/FormControlLayout.tsx +58 -4
  103. package/src/components/FormControlWrapper/FormControlWrapper.module.css +96 -0
  104. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +122 -19
  105. package/src/components/FormControlWrapper/FormControlWrapper.tsx +145 -6
  106. package/src/components/HoverCard/HoverCard.module.css +91 -0
  107. package/src/components/HoverCard/HoverCard.stories.tsx +158 -21
  108. package/src/components/HoverCard/HoverCard.tsx +150 -4
  109. package/src/components/Label/Label.module.css +136 -0
  110. package/src/components/Label/Label.stories.tsx +98 -17
  111. package/src/components/Label/Label.tsx +88 -4
  112. package/src/components/Link/Link.module.css +8 -0
  113. package/src/components/Menu/Menu.module.css +287 -0
  114. package/src/components/Menu/Menu.stories.tsx +307 -21
  115. package/src/components/Menu/Menu.tsx +318 -4
  116. package/src/components/Modal/Modal.module.css +172 -0
  117. package/src/components/Modal/Modal.stories.tsx +66 -22
  118. package/src/components/Modal/Modal.tsx +216 -3
  119. package/src/components/NumberInput/NumberInput.module.css +281 -0
  120. package/src/components/NumberInput/NumberInput.stories.tsx +83 -20
  121. package/src/components/NumberInput/NumberInput.tsx +148 -5
  122. package/src/components/Pagination/Pagination.icons.tsx +66 -0
  123. package/src/components/Pagination/Pagination.module.css +307 -0
  124. package/src/components/Pagination/Pagination.stories.tsx +42 -21
  125. package/src/components/Pagination/Pagination.tsx +193 -4
  126. package/src/components/Panel/Panel.module.css +206 -0
  127. package/src/components/Panel/Panel.stories.tsx +214 -22
  128. package/src/components/Panel/Panel.tsx +153 -4
  129. package/src/components/Radio/Radio.module.css +220 -0
  130. package/src/components/Radio/Radio.stories.tsx +77 -19
  131. package/src/components/Radio/Radio.tsx +178 -4
  132. package/src/components/Radio/RadioGroup.tsx +111 -0
  133. package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
  134. package/src/components/ReadOnlyField/ReadOnlyField.module.css +54 -0
  135. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +175 -16
  136. package/src/components/ReadOnlyField/ReadOnlyField.tsx +109 -4
  137. package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
  138. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
  139. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
  140. package/src/components/ReadOnlyField/index.ts +1 -0
  141. package/src/components/SegmentedControl/SegmentedControl.module.css +203 -0
  142. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +102 -20
  143. package/src/components/SegmentedControl/SegmentedControl.tsx +110 -5
  144. package/src/components/Slider/Slider.module.css +542 -0
  145. package/src/components/Slider/Slider.stories.tsx +157 -19
  146. package/src/components/Slider/Slider.tsx +282 -4
  147. package/src/components/Stepper/Stepper.module.css +410 -0
  148. package/src/components/Stepper/Stepper.stories.tsx +132 -19
  149. package/src/components/Stepper/Stepper.tsx +145 -5
  150. package/src/components/Switch/Switch.module.css +238 -0
  151. package/src/components/Switch/Switch.stories.tsx +98 -19
  152. package/src/components/Switch/Switch.tsx +153 -5
  153. package/src/components/Switch/SwitchGroup.tsx +111 -0
  154. package/src/components/Tabs/Tabs.module.css +422 -0
  155. package/src/components/Tabs/Tabs.stories.tsx +138 -19
  156. package/src/components/Tabs/Tabs.tsx +103 -5
  157. package/src/components/TextArea/TextArea.module.css +160 -0
  158. package/src/components/TextArea/TextArea.stories.tsx +80 -21
  159. package/src/components/TextArea/TextArea.tsx +156 -5
  160. package/src/components/TextField/TextField.module.css +266 -0
  161. package/src/components/TextField/TextField.stories.tsx +168 -15
  162. package/src/components/TextField/TextField.tsx +171 -5
  163. package/src/components/TimePicker/TimePicker.module.css +41 -0
  164. package/src/components/TimePicker/TimePicker.tsx +1 -0
  165. package/src/components/Timeline/Timeline.module.css +367 -0
  166. package/src/components/Timeline/Timeline.stories.tsx +114 -21
  167. package/src/components/Timeline/Timeline.tsx +80 -4
  168. package/src/components/Timeline/TimelineItem.tsx +101 -0
  169. package/src/components/Toast/Toast.module.css +170 -0
  170. package/src/components/Toast/Toast.stories.tsx +54 -22
  171. package/src/components/Toast/Toast.tsx +90 -4
  172. package/src/components/Tooltip/Tooltip.module.css +90 -0
  173. package/src/components/Tooltip/Tooltip.stories.tsx +152 -22
  174. package/src/components/Tooltip/Tooltip.tsx +114 -4
  175. package/src/components/TransferList/TransferList.module.css +38 -0
  176. package/src/components/TransferList/TransferList.tsx +1 -0
  177. package/src/components/index.ts +1 -0
@@ -1,7 +1,182 @@
1
- import React from "react";
1
+ import { forwardRef } from "react";
2
+ import { Card as MuiCard, type CardProps as MuiCardProps } from "@mui/material";
3
+ import {
4
+ filterStylingProps,
5
+ type RecursicaOverStyled,
6
+ } from "../../utils/filterStylingProps";
7
+ import styles from "./Card.module.css";
2
8
 
3
- export type CardProps = React.HTMLAttributes<HTMLDivElement>;
9
+ // ==== CARD CONTAINER ====
10
+ export type CardProps = RecursicaOverStyled<MuiCardProps>;
4
11
 
5
- export const Card: React.FC<CardProps> = (props) => {
6
- return <div {...props}>Card</div>;
12
+ /**
13
+ * The root Card elevation box. It establishes the global background color, border radius, nested component gap, and outer shadow governed by the current `Layer` context.
14
+ */
15
+ const CardBase = forwardRef<HTMLDivElement, CardProps>(function Card(
16
+ { overStyled = false, ...rest },
17
+ ref,
18
+ ) {
19
+ const sanitizedProps = filterStylingProps(rest, overStyled);
20
+
21
+ const mergedClassNames: Partial<Record<string, string>> = {
22
+ root: styles.root,
23
+ };
24
+
25
+ const classNamesProp = (sanitizedProps as Record<string, unknown>).classNames;
26
+ if (
27
+ classNamesProp &&
28
+ typeof classNamesProp === "object" &&
29
+ !Array.isArray(classNamesProp)
30
+ ) {
31
+ const o = classNamesProp as Record<string, string>;
32
+ Object.keys(o).forEach((key) => {
33
+ if (mergedClassNames[key]) {
34
+ mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
35
+ } else {
36
+ mergedClassNames[key] = o[key];
37
+ }
38
+ });
39
+ }
40
+
41
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
42
+ .className as string | undefined;
43
+
44
+ return (
45
+ <MuiCard
46
+ ref={ref}
47
+ className={classNameProp}
48
+ classes={mergedClassNames}
49
+ {...(sanitizedProps as unknown as MuiCardProps)}
50
+ />
51
+ );
52
+ });
53
+ CardBase.displayName = "Card";
54
+
55
+ // ==== NATIVE MANTINE CARD.SECTION ====
56
+ export type RecursicaCardSectionProps = React.HTMLAttributes<HTMLDivElement> & {
57
+ children?: React.ReactNode;
58
+ };
59
+ export type CardSectionProps = RecursicaOverStyled<RecursicaCardSectionProps>;
60
+
61
+ /**
62
+ * A generalized edge-to-edge structural wrapper native to Mui. Strips typical boundary padding via negative margins to allow content (like maps or header images) to touch the border seamlessly.
63
+ */
64
+ export const CardSection = forwardRef<HTMLDivElement, CardSectionProps>(
65
+ function CardSection({ overStyled = false, ...rest }, ref) {
66
+ const sanitizedProps = filterStylingProps(rest, overStyled);
67
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
68
+ .className as string | undefined;
69
+
70
+ return (
71
+ <div
72
+ ref={ref}
73
+ className={
74
+ classNameProp ? `${styles.section} ${classNameProp}` : styles.section
75
+ }
76
+ {...(sanitizedProps as unknown as React.HTMLAttributes<HTMLDivElement>)}
77
+ />
78
+ );
79
+ },
80
+ );
81
+ CardSection.displayName = "CardSection";
82
+
83
+ // ==== EXPLICIT CARD.HEADER ====
84
+ export type CardHeaderProps = RecursicaOverStyled<RecursicaCardSectionProps>;
85
+
86
+ /**
87
+ * Replaces standard generic Mui `<Card.Section>` wrappers by directly injecting the strict Recursica design tokens for header sizing, background drops, and intrinsic padding limits.
88
+ */
89
+ export const CardHeader = forwardRef<HTMLDivElement, CardHeaderProps>(
90
+ function CardHeader({ overStyled = false, ...rest }, ref) {
91
+ const sanitizedProps = filterStylingProps(rest, overStyled);
92
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
93
+ .className as string | undefined;
94
+
95
+ return (
96
+ <div
97
+ ref={ref}
98
+ className={
99
+ classNameProp ? `${styles.header} ${classNameProp}` : styles.header
100
+ }
101
+ {...(sanitizedProps as unknown as React.HTMLAttributes<HTMLDivElement>)}
102
+ />
103
+ );
104
+ },
105
+ );
106
+ CardHeader.displayName = "CardHeader";
107
+
108
+ // ==== EXPLICIT CARD.FOOTER ====
109
+ export type CardFooterProps = RecursicaOverStyled<RecursicaCardSectionProps>;
110
+
111
+ /**
112
+ * Counterpart to `Card.Header`, applying specific footer elevation margins, sizes, and padding natively.
113
+ */
114
+ export const CardFooter = forwardRef<HTMLDivElement, CardFooterProps>(
115
+ function CardFooter({ overStyled = false, ...rest }, ref) {
116
+ const sanitizedProps = filterStylingProps(rest, overStyled);
117
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
118
+ .className as string | undefined;
119
+
120
+ return (
121
+ <div
122
+ ref={ref}
123
+ className={
124
+ classNameProp ? `${styles.footer} ${classNameProp}` : styles.footer
125
+ }
126
+ {...(sanitizedProps as unknown as React.HTMLAttributes<HTMLDivElement>)}
127
+ />
128
+ );
129
+ },
130
+ );
131
+ CardFooter.displayName = "CardFooter";
132
+
133
+ // ==== EXPLICIT CARD.CONTENT ====
134
+ export type CardContentProps = RecursicaOverStyled<
135
+ React.HTMLAttributes<HTMLDivElement>
136
+ >;
137
+
138
+ /**
139
+ * Internal container that safely binds typography and structural flex gaps driven by the active UI kit tokens natively, preserving correct padding blocks compared to rigid `Card.Section` edges.
140
+ */
141
+ export const CardContent = forwardRef<HTMLDivElement, CardContentProps>(
142
+ function CardContent({ overStyled = false, ...rest }, ref) {
143
+ const sanitizedProps = filterStylingProps(rest, overStyled);
144
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
145
+ .className as string | undefined;
146
+
147
+ return (
148
+ <div
149
+ ref={ref}
150
+ className={
151
+ classNameProp ? `${styles.content} ${classNameProp}` : styles.content
152
+ }
153
+ {...sanitizedProps}
154
+ />
155
+ );
156
+ },
157
+ );
158
+ CardContent.displayName = "CardContent";
159
+
160
+ // ==== DOT NOTATION EXPORT ====
161
+
162
+ /**
163
+ * Recursica Card component wrapping Mui's Card (Paper).
164
+ *
165
+ * Supports polymorphism via the `component` prop or `renderRoot` for custom element rendering.
166
+ * Use dot-notation sub-components for structured card layouts.
167
+ */
168
+ const PolymorphicCard = CardBase as any;
169
+
170
+ // Attach static components for dot-notation access
171
+ const _card = PolymorphicCard as unknown as Record<string, unknown>;
172
+ _card.Section = CardSection;
173
+ _card.Header = CardHeader;
174
+ _card.Footer = CardFooter;
175
+ _card.Content = CardContent;
176
+
177
+ export const Card = PolymorphicCard as typeof PolymorphicCard & {
178
+ Section: typeof CardSection;
179
+ Header: typeof CardHeader;
180
+ Footer: typeof CardFooter;
181
+ Content: typeof CardContent;
7
182
  };
@@ -0,0 +1,232 @@
1
+ /**
2
+ * Checkbox.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_checkbox-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_checkbox-group_properties_item-gap,
23
+ 16px
24
+ );
25
+ }
26
+
27
+ /* SideBySide Variant */
28
+ .groupRoot[data-layout="side-by-side"] {
29
+ flex-direction: column;
30
+ flex-wrap: wrap;
31
+ padding-top: var(
32
+ --recursica_ui-kit_components_checkbox-group_variants_layouts_side-by-side_properties_vertical-padding
33
+ );
34
+ padding-bottom: var(
35
+ --recursica_ui-kit_components_checkbox-group_variants_layouts_side-by-side_properties_vertical-padding
36
+ );
37
+ margin-top: var(
38
+ --recursica_ui-kit_components_checkbox-group_variants_layouts_side-by-side_properties_top-bottom-margin
39
+ );
40
+ margin-bottom: var(
41
+ --recursica_ui-kit_components_checkbox-group_variants_layouts_side-by-side_properties_top-bottom-margin
42
+ );
43
+ }
44
+
45
+ /* Stacked Variant */
46
+ .groupRoot[data-layout="stacked"] {
47
+ flex-direction: column;
48
+ margin-top: var(
49
+ --recursica_ui-kit_components_checkbox-group_variants_layouts_stacked_properties_top-bottom-margin
50
+ );
51
+ margin-bottom: var(
52
+ --recursica_ui-kit_components_checkbox-group_variants_layouts_stacked_properties_top-bottom-margin
53
+ );
54
+ }
55
+
56
+ /* --- PRIMITIVE CHECKBOX STYLES --- */
57
+
58
+ .root {
59
+ max-width: var(
60
+ --recursica_ui-kit_components_checkbox-item_properties_max-width
61
+ );
62
+ }
63
+
64
+ .body {
65
+ display: flex;
66
+ align-items: flex-start;
67
+ gap: var(--recursica_ui-kit_components_checkbox-item_properties_label-gap);
68
+ }
69
+
70
+ .inner {
71
+ display: flex;
72
+ align-items: center;
73
+ justify-content: center;
74
+ margin: 0; /* HARDCODE: structural reset */
75
+ width: var(--recursica_ui-kit_components_checkbox_properties_size);
76
+ height: var(--recursica_ui-kit_components_checkbox_properties_size);
77
+ /* Apply font-size to this element so that an 'em' based line-height token evaluates to the correct exact pixel value. */
78
+ font-size: var(
79
+ --recursica_ui-kit_components_checkbox-item_properties_text_font-size
80
+ );
81
+ /* Mathematically syncs the physical input checkbox bounding block visually with the center of the text's first line.
82
+ 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.
83
+ Instead, it naturally evaluates to the correct height because we applied the font-size above. */
84
+ margin-top: calc(
85
+ (
86
+ var(
87
+ --recursica_ui-kit_components_checkbox-item_properties_text_line-height
88
+ ) -
89
+ var(--recursica_ui-kit_components_checkbox_properties_size)
90
+ ) /
91
+ 2
92
+ );
93
+ }
94
+
95
+ /* Base explicit reset masking underlying framework defaults */
96
+ .input {
97
+ cursor: pointer; /* HARDCODE: core interaction behavior */
98
+ appearance: none; /* HARDCODE: reset native widget */
99
+ box-sizing: border-box; /* HARDCODE: ensures size includes borders */
100
+ width: var(--recursica_ui-kit_components_checkbox_properties_size);
101
+ height: var(--recursica_ui-kit_components_checkbox_properties_size);
102
+ border-radius: var(
103
+ --recursica_ui-kit_components_checkbox_properties_border-radius
104
+ );
105
+ border-width: var(
106
+ --recursica_ui-kit_components_checkbox_properties_border-size
107
+ );
108
+ border-style: solid; /* HARDCODE: normalize primitive border-style */
109
+
110
+ /* Standard unchecked state */
111
+ background-color: var(
112
+ --recursica_ui-kit_components_checkbox_properties_colors_background-unchecked
113
+ );
114
+ border-color: var(
115
+ --recursica_ui-kit_components_checkbox_properties_colors_border-unchecked
116
+ );
117
+ transition: all 0.2s ease; /* HARDCODE: consider global transition token */
118
+ }
119
+
120
+ /* Checked/Indeterminate State */
121
+ .input:checked {
122
+ background-color: var(
123
+ --recursica_ui-kit_components_checkbox_properties_colors_background-checked
124
+ );
125
+ border-color: var(
126
+ --recursica_ui-kit_components_checkbox_properties_colors_border-checked
127
+ );
128
+ }
129
+ .input[data-indeterminate] {
130
+ background-color: var(
131
+ --recursica_ui-kit_components_checkbox_properties_colors_background-indeterminate
132
+ );
133
+ border-color: var(
134
+ --recursica_ui-kit_components_checkbox_properties_colors_border-indeterminate
135
+ );
136
+ }
137
+
138
+ /* Disabled State */
139
+ .input:disabled {
140
+ cursor: not-allowed; /* HARDCODE: disabled state default */
141
+ opacity: var(
142
+ --recursica_ui-kit_components_checkbox_properties_disabled-opacity
143
+ );
144
+ background-color: var(
145
+ --recursica_ui-kit_components_checkbox_properties_colors_disabled-background
146
+ );
147
+ border-color: var(
148
+ --recursica_ui-kit_components_checkbox_properties_colors_disabled-border
149
+ );
150
+ }
151
+
152
+ .icon {
153
+ width: var(--recursica_ui-kit_components_checkbox_properties_icon-size);
154
+ height: var(--recursica_ui-kit_components_checkbox_properties_icon-size);
155
+ color: var(
156
+ --recursica_ui-kit_components_checkbox_properties_colors_icon-color
157
+ );
158
+ }
159
+ .input:disabled + .icon {
160
+ color: var(
161
+ --recursica_ui-kit_components_checkbox_properties_colors_disabled-icon
162
+ );
163
+ }
164
+
165
+ .labelWrapper {
166
+ padding: 0; /* HARDCODE: structural layout reset */
167
+ margin: 0; /* HARDCODE: structural layout reset */
168
+ flex: 1;
169
+ min-width: 0; /* HARDCODE: forces text wrap boundary in flex child */
170
+ }
171
+
172
+ .label {
173
+ cursor: pointer; /* HARDCODE: interactive affordance */
174
+ padding: 0; /* HARDCODE: reset layout */
175
+ margin: 0; /* HARDCODE: reset layout */
176
+ word-wrap: break-word;
177
+ white-space: normal;
178
+ color: var(
179
+ --recursica_ui-kit_components_checkbox-item_properties_colors_text
180
+ );
181
+ font-family: var(
182
+ --recursica_ui-kit_components_checkbox-item_properties_text_font-family
183
+ );
184
+ font-size: var(
185
+ --recursica_ui-kit_components_checkbox-item_properties_text_font-size
186
+ );
187
+ font-weight: var(
188
+ --recursica_ui-kit_components_checkbox-item_properties_text_font-weight
189
+ );
190
+ line-height: var(
191
+ --recursica_ui-kit_components_checkbox-item_properties_text_line-height
192
+ );
193
+ font-style: var(
194
+ --recursica_ui-kit_components_checkbox-item_properties_text_font-style
195
+ );
196
+ letter-spacing: var(
197
+ --recursica_ui-kit_components_checkbox-item_properties_text_letter-spacing
198
+ );
199
+ text-decoration: var(
200
+ --recursica_ui-kit_components_checkbox-item_properties_text_text-decoration
201
+ );
202
+ text-transform: var(
203
+ --recursica_ui-kit_components_checkbox-item_properties_text_text-transform
204
+ );
205
+ }
206
+ .root:has(input:disabled) .label {
207
+ cursor: not-allowed; /* HARDCODE: disabled interaction */
208
+ opacity: var(
209
+ --recursica_ui-kit_components_checkbox-item_properties_disabled-opacity
210
+ );
211
+ color: var(
212
+ --recursica_ui-kit_components_checkbox-item_properties_colors_disabled-text
213
+ );
214
+ }
215
+
216
+ /* CheckboxGroup Layouts */
217
+ .groupRoot {
218
+ display: flex;
219
+ }
220
+ .groupRoot[data-layout="stacked"] {
221
+ flex-direction: column;
222
+ gap: var(
223
+ --recursica_ui-kit_components_checkbox-group_variants_layouts_stacked_properties_label-field-gap
224
+ );
225
+ }
226
+ .groupRoot[data-layout="side-by-side"] {
227
+ flex-direction: row;
228
+ flex-wrap: wrap;
229
+ gap: var(
230
+ --recursica_ui-kit_components_checkbox-group_variants_layouts_side-by-side_properties_gutter
231
+ );
232
+ }
@@ -1,35 +1,97 @@
1
1
  import type { Meta, StoryObj } from "@storybook/react";
2
2
  import { Checkbox } from "./Checkbox";
3
- import { ComingSoon } from "@recursica/storybook-template";
4
3
 
5
4
  const meta: Meta<typeof Checkbox> = {
6
- title: "UI-Kit/🚧 Checkbox",
5
+ title: "UI-Kit/Checkbox",
7
6
  component: Checkbox,
8
7
  tags: ["autodocs"],
9
8
  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
- ],
9
+ docs: {
10
+ description: {
11
+ component: `
12
+ The \`Checkbox\` component is a precisely engineered, atomic form primitive representing boolean states natively aligned to the Recursica design system. It overrides Mantine's standard properties explicitly enforcing our variables natively across all structural boundaries.
13
+
14
+ > [!IMPORTANT]
15
+ > The atomic \`Checkbox\` is intended primarily as an internal primitive. **When wrapping multiple Checkbox elements together or rendering form controls, always utilize the \`<Checkbox.Group>\` component.** \`Checkbox.Group\` inherits the global \`FormControlWrapper\`, granting instantaneous access to macroscopic layout structuring, assistive descriptions, validation errors, and strict flex arrays.
16
+
17
+ ### Usage
18
+ To render a solitary component natively:
19
+ \`\`\`tsx
20
+ <Checkbox label="Acknowledge Terms" defaultChecked />
21
+ \`\`\`
22
+ `,
23
+ },
24
+ },
25
+ },
26
+ argTypes: {
27
+ disabled: {
28
+ control: "boolean",
29
+ },
30
+ readOnly: {
31
+ control: "boolean",
32
+ description:
33
+ "Toggles structural read-only data presentation bypassing interaction boundaries completely.",
26
34
  },
35
+ controlMaxWidth: { table: { disable: true } },
36
+ controlMinWidth: { table: { disable: true } },
27
37
  },
28
38
  };
29
39
 
30
40
  export default meta;
41
+
31
42
  type Story = StoryObj<typeof Checkbox>;
32
43
 
33
44
  export const Default: Story = {
34
- render: () => <ComingSoon componentName="Checkbox" />,
45
+ args: {
46
+ disabled: false,
47
+ label: "Standard Unchecked Property",
48
+ },
49
+ };
50
+
51
+ export const SideBySideLayout: Story = {
52
+ args: {
53
+ label: "Opt-in form alignment",
54
+ formLayout: "side-by-side",
55
+ },
56
+ };
57
+
58
+ export const LongLabelWrap: Story = {
59
+ args: {
60
+ label:
61
+ "A meticulously long Checkbox label property demonstrating the absolute maximum 400px wrapper constraints actively snapping the text engine down onto a secondary wrapping line automatically without blowing out the visual boundaries.",
62
+ },
63
+ };
64
+
65
+ export const StaticVariations: Story = {
66
+ args: {},
67
+ render: () => (
68
+ <div
69
+ style={{
70
+ display: "flex",
71
+ flexDirection: "column",
72
+ gap: "24px",
73
+ }}
74
+ >
75
+ <Checkbox label="Default Unchecked State" />
76
+ <Checkbox label="Acknowledge Configuration" defaultChecked />
77
+ <Checkbox label="Indeterminate Master" indeterminate />
78
+ <Checkbox label="Disabled Variant" disabled />
79
+ <Checkbox label="Disabled Checked Variant" checked disabled />
80
+ </div>
81
+ ),
82
+ };
83
+
84
+ export const ReadOnly: Story = {
85
+ args: {},
86
+ render: () => (
87
+ <div
88
+ style={{
89
+ display: "flex",
90
+ flexDirection: "column",
91
+ gap: "24px",
92
+ }}
93
+ >
94
+ <Checkbox label="Accept Terms & Conditions" defaultChecked readOnly />
95
+ </div>
96
+ ),
35
97
  };
@@ -1,7 +1,149 @@
1
- import React from "react";
1
+ import React, { forwardRef } from "react";
2
+ import {
3
+ Checkbox as MuiCheckbox,
4
+ type CheckboxProps as MuiCheckboxProps,
5
+ FormControlLabel,
6
+ } from "@mui/material";
7
+ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
8
+ import { CheckboxGroup } from "./CheckboxGroup";
9
+ import {
10
+ filterStylingProps,
11
+ type RecursicaOverStyled,
12
+ } from "../../utils/filterStylingProps";
13
+ import { type RequireAccessibleLabel } from "../../utils/RequireAccessibleLabel";
14
+ import {
15
+ FormControlLayout,
16
+ type FormControlLayoutProps,
17
+ } from "../FormControlLayout/FormControlLayout";
2
18
 
3
- export type CheckboxProps = React.HTMLAttributes<HTMLDivElement>;
19
+ import styles from "./Checkbox.module.css";
4
20
 
5
- export const Checkbox: React.FC<CheckboxProps> = (props) => {
6
- return <div {...props}>Checkbox</div>;
21
+ export type RecursicaCheckboxProps = RequireAccessibleLabel<
22
+ Omit<MuiCheckboxProps, "size" | "color"> & {
23
+ label?: React.ReactNode;
24
+ } & ReadOnlyControlProps &
25
+ Pick<
26
+ FormControlLayoutProps,
27
+ "formLayout" | "labelSize" | "controlMaxWidth" | "controlMinWidth"
28
+ >
29
+ >;
30
+
31
+ export type CheckboxProps = RecursicaOverStyled<RecursicaCheckboxProps>;
32
+
33
+ type CheckboxComponent = React.ForwardRefExoticComponent<
34
+ CheckboxProps & React.RefAttributes<HTMLButtonElement>
35
+ > & {
36
+ Group: typeof CheckboxGroup;
7
37
  };
38
+
39
+ export const Checkbox = forwardRef<HTMLButtonElement, CheckboxProps>(
40
+ function Checkbox(props, ref) {
41
+ const {
42
+ overStyled = false,
43
+ readOnly,
44
+ readOnlyComponent,
45
+ disabled,
46
+ formLayout,
47
+ labelSize,
48
+ controlMaxWidth,
49
+ controlMinWidth,
50
+ label,
51
+ ...rest
52
+ } = props;
53
+
54
+ const sanitizedProps = filterStylingProps(rest, overStyled);
55
+ const restRecord = sanitizedProps as Record<string, unknown>;
56
+
57
+ delete restRecord["size"];
58
+ delete restRecord["color"];
59
+
60
+ const mergedClassNames: Partial<Record<string, string>> = {
61
+ root: styles.root,
62
+ checked: styles.checked,
63
+ disabled: styles.disabled,
64
+ };
65
+
66
+ const classesProp = restRecord.classes;
67
+ if (
68
+ classesProp &&
69
+ typeof classesProp === "object" &&
70
+ !Array.isArray(classesProp)
71
+ ) {
72
+ const o = classesProp as Partial<Record<string, string>>;
73
+ mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
74
+ mergedClassNames.checked = o.checked
75
+ ? `${styles.checked} ${o.checked}`
76
+ : styles.checked;
77
+ mergedClassNames.disabled = o.disabled
78
+ ? `${styles.disabled} ${o.disabled}`
79
+ : styles.disabled;
80
+ }
81
+
82
+ const classNameProp = restRecord.className as string | undefined;
83
+ const finalClass = classNameProp
84
+ ? `${styles.root} ${classNameProp}`
85
+ : styles.root;
86
+
87
+ if (readOnly && !!readOnlyComponent) {
88
+ const isChecked = !!(restRecord.checked ?? restRecord.defaultChecked);
89
+ const ReadOnlyComp = readOnlyComponent;
90
+ const roNode = (
91
+ <ReadOnlyComp {...props} checked={isChecked} label={label} />
92
+ );
93
+
94
+ if (formLayout) {
95
+ return (
96
+ <FormControlLayout
97
+ formLayout={formLayout}
98
+ labelSize={labelSize}
99
+ controlMaxWidth={controlMaxWidth}
100
+ controlMinWidth={controlMinWidth}
101
+ >
102
+ {roNode}
103
+ </FormControlLayout>
104
+ );
105
+ }
106
+
107
+ return <>{roNode}</>;
108
+ }
109
+
110
+ const checkboxNode = (
111
+ <MuiCheckbox
112
+ ref={ref}
113
+ className={finalClass}
114
+ classes={mergedClassNames}
115
+ disabled={readOnly || disabled}
116
+ {...(sanitizedProps as unknown as MuiCheckboxProps)}
117
+ />
118
+ );
119
+
120
+ const finalNode = label ? (
121
+ <FormControlLabel
122
+ control={checkboxNode}
123
+ label={label}
124
+ className={styles.labelWrapper}
125
+ classes={{ label: styles.label }}
126
+ />
127
+ ) : (
128
+ checkboxNode
129
+ );
130
+
131
+ if (formLayout) {
132
+ return (
133
+ <FormControlLayout
134
+ formLayout={formLayout}
135
+ labelSize={labelSize}
136
+ controlMaxWidth={controlMaxWidth}
137
+ controlMinWidth={controlMinWidth}
138
+ >
139
+ {finalNode}
140
+ </FormControlLayout>
141
+ );
142
+ }
143
+
144
+ return finalNode;
145
+ },
146
+ ) as CheckboxComponent;
147
+
148
+ Checkbox.displayName = "Checkbox";
149
+ Checkbox.Group = CheckboxGroup;