@recursica/mui-adapter 0.5.0 → 0.7.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 (212) hide show
  1. package/ARCHITECTURE.md +15 -0
  2. package/CHANGELOG.md +15 -0
  3. package/SETUP.md +77 -0
  4. package/USAGE.md +55 -0
  5. package/dist/mui-adapter.cjs +74 -1
  6. package/dist/mui-adapter.cjs.map +1 -1
  7. package/dist/mui-adapter.css +1 -1
  8. package/dist/mui-adapter.js +6863 -205
  9. package/dist/mui-adapter.js.map +1 -1
  10. package/dist/src/components/Accordion/Accordion.d.ts +43 -2
  11. package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +6 -2
  12. package/dist/src/components/Autocomplete/Autocomplete.d.ts +12 -2
  13. package/dist/src/components/Avatar/Avatar.d.ts +10 -3
  14. package/dist/src/components/Badge/Badge.d.ts +10 -3
  15. package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +9 -3
  16. package/dist/src/components/Button/Button.d.ts +2 -17
  17. package/dist/src/components/Card/Card.d.ts +38 -3
  18. package/dist/src/components/Checkbox/Checkbox.d.ts +13 -2
  19. package/dist/src/components/Checkbox/CheckboxGroup.d.ts +18 -0
  20. package/dist/src/components/Checkbox/index.d.ts +5 -1
  21. package/dist/src/components/Chip/Chip.d.ts +9 -2
  22. package/dist/src/components/Container/Container.d.ts +1 -5
  23. package/dist/src/components/DatePicker/DatePicker.d.ts +2 -1
  24. package/dist/src/components/Dropdown/Dropdown.d.ts +12 -2
  25. package/dist/src/components/Flex/Flex.d.ts +2 -22
  26. package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +5 -2
  27. package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +9 -2
  28. package/dist/src/components/Group/Group.d.ts +2 -7
  29. package/dist/src/components/HoverCard/HoverCard.d.ts +28 -2
  30. package/dist/src/components/Label/Label.d.ts +6 -2
  31. package/dist/src/components/Link/Link.d.ts +2 -6
  32. package/dist/src/components/Loader/Loader.d.ts +2 -7
  33. package/dist/src/components/Menu/Menu.d.ts +25 -3
  34. package/dist/src/components/Modal/Modal.d.ts +37 -2
  35. package/dist/src/components/NumberInput/NumberInput.d.ts +12 -2
  36. package/dist/src/components/Pagination/Pagination.d.ts +12 -3
  37. package/dist/src/components/Pagination/Pagination.icons.d.ts +8 -0
  38. package/dist/src/components/Panel/Panel.d.ts +52 -3
  39. package/dist/src/components/Radio/Radio.d.ts +13 -3
  40. package/dist/src/components/Radio/RadioGroup.d.ts +9 -0
  41. package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
  42. package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +4 -2
  43. package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
  44. package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
  45. package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +28 -0
  46. package/dist/src/components/ReadOnlyField/index.d.ts +1 -0
  47. package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +21 -3
  48. package/dist/src/components/Slider/Slider.d.ts +18 -2
  49. package/dist/src/components/Stack/Stack.d.ts +2 -5
  50. package/dist/src/components/Stepper/Stepper.d.ts +41 -2
  51. package/dist/src/components/Switch/Switch.d.ts +10 -3
  52. package/dist/src/components/Switch/SwitchGroup.d.ts +13 -0
  53. package/dist/src/components/Table/Table.d.ts +2 -1
  54. package/dist/src/components/Tabs/Tabs.d.ts +20 -3
  55. package/dist/src/components/Text/Text.d.ts +2 -16
  56. package/dist/src/components/TextArea/TextArea.d.ts +12 -2
  57. package/dist/src/components/TextField/TextField.d.ts +12 -2
  58. package/dist/src/components/TimePicker/TimePicker.d.ts +2 -1
  59. package/dist/src/components/Timeline/Timeline.d.ts +15 -2
  60. package/dist/src/components/Timeline/TimelineItem.d.ts +18 -0
  61. package/dist/src/components/Title/Title.d.ts +2 -15
  62. package/dist/src/components/Toast/Toast.d.ts +20 -2
  63. package/dist/src/components/Tooltip/Tooltip.d.ts +28 -3
  64. package/dist/src/components/TransferList/TransferList.d.ts +2 -1
  65. package/dist/src/components/Typography/Typography.d.ts +1 -1
  66. package/dist/src/utils/RequireAccessibleLabel.d.ts +1 -18
  67. package/dist/src/utils/filterStylingProps.d.ts +1 -11
  68. package/llms.txt +16 -0
  69. package/package.json +14 -3
  70. package/src/components/Accordion/Accordion.module.css +294 -56
  71. package/src/components/Accordion/Accordion.stories.tsx +131 -21
  72. package/src/components/Accordion/Accordion.tsx +260 -5
  73. package/src/components/AssistiveElement/AssistiveElement.module.css +114 -20
  74. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +60 -20
  75. package/src/components/AssistiveElement/AssistiveElement.tsx +81 -5
  76. package/src/components/Autocomplete/Autocomplete.module.css +319 -42
  77. package/src/components/Autocomplete/Autocomplete.stories.tsx +175 -23
  78. package/src/components/Autocomplete/Autocomplete.tsx +181 -6
  79. package/src/components/Avatar/Avatar.module.css +343 -73
  80. package/src/components/Avatar/Avatar.stories.tsx +94 -22
  81. package/src/components/Avatar/Avatar.tsx +99 -6
  82. package/src/components/Badge/Badge.module.css +127 -29
  83. package/src/components/Badge/Badge.stories.tsx +65 -22
  84. package/src/components/Badge/Badge.tsx +67 -6
  85. package/src/components/Breadcrumb/Breadcrumb.module.css +25 -5
  86. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +57 -22
  87. package/src/components/Breadcrumb/Breadcrumb.tsx +59 -6
  88. package/src/components/Button/Button.module.css +19 -61
  89. package/src/components/Button/Button.stories.tsx +24 -23
  90. package/src/components/Button/Button.tsx +7 -18
  91. package/src/components/Card/Card.module.css +154 -38
  92. package/src/components/Card/Card.stories.tsx +117 -19
  93. package/src/components/Card/Card.tsx +181 -5
  94. package/src/components/Checkbox/Checkbox.module.css +249 -39
  95. package/src/components/Checkbox/Checkbox.stories.tsx +81 -19
  96. package/src/components/Checkbox/Checkbox.tsx +246 -5
  97. package/src/components/Checkbox/CheckboxGroup.stories.tsx +139 -0
  98. package/src/components/Checkbox/CheckboxGroup.tsx +137 -0
  99. package/src/components/Checkbox/index.ts +6 -1
  100. package/src/components/Chip/Chip.module.css +246 -43
  101. package/src/components/Chip/Chip.stories.tsx +101 -21
  102. package/src/components/Chip/Chip.tsx +136 -6
  103. package/src/components/Container/Container.stories.tsx +103 -20
  104. package/src/components/Container/Container.tsx +1 -13
  105. package/src/components/DatePicker/DatePicker.stories.tsx +125 -19
  106. package/src/components/DatePicker/DatePicker.tsx +3 -1
  107. package/src/components/Dropdown/Dropdown.module.css +291 -41
  108. package/src/components/Dropdown/Dropdown.stories.tsx +80 -21
  109. package/src/components/Dropdown/Dropdown.tsx +180 -6
  110. package/src/components/FileInput/FileInput.stories.tsx +2 -19
  111. package/src/components/FileUpload/FileUpload.stories.tsx +2 -19
  112. package/src/components/Flex/Flex.stories.tsx +113 -20
  113. package/src/components/Flex/Flex.tsx +16 -26
  114. package/src/components/FormControlLayout/FormControlLayout.module.css +92 -0
  115. package/src/components/FormControlLayout/FormControlLayout.stories.tsx +67 -20
  116. package/src/components/FormControlLayout/FormControlLayout.tsx +69 -4
  117. package/src/components/FormControlWrapper/FormControlWrapper.module.css +87 -0
  118. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +116 -22
  119. package/src/components/FormControlWrapper/FormControlWrapper.tsx +126 -6
  120. package/src/components/Group/Group.stories.tsx +6 -30
  121. package/src/components/Group/Group.tsx +14 -9
  122. package/src/components/HoverCard/HoverCard.module.css +90 -22
  123. package/src/components/HoverCard/HoverCard.stories.tsx +158 -21
  124. package/src/components/HoverCard/HoverCard.tsx +147 -5
  125. package/src/components/Label/Label.module.css +140 -35
  126. package/src/components/Label/Label.stories.tsx +108 -21
  127. package/src/components/Label/Label.tsx +82 -5
  128. package/src/components/Link/Link.module.css +4 -3
  129. package/src/components/Link/Link.stories.tsx +2 -8
  130. package/src/components/Link/Link.tsx +1 -5
  131. package/src/components/Loader/Loader.module.css +1 -2
  132. package/src/components/Loader/Loader.tsx +1 -7
  133. package/src/components/Menu/IMPLEMENTATION_NOTES.md +4 -0
  134. package/src/components/Menu/Menu.module.css +285 -51
  135. package/src/components/Menu/Menu.stories.tsx +229 -25
  136. package/src/components/Menu/Menu.tsx +73 -6
  137. package/src/components/Modal/Modal.module.css +171 -35
  138. package/src/components/Modal/Modal.stories.tsx +66 -22
  139. package/src/components/Modal/Modal.tsx +215 -5
  140. package/src/components/NumberInput/NumberInput.module.css +276 -42
  141. package/src/components/NumberInput/NumberInput.stories.tsx +83 -20
  142. package/src/components/NumberInput/NumberInput.tsx +136 -6
  143. package/src/components/Pagination/IMPLEMENTATION_NOTES.md +3 -0
  144. package/src/components/Pagination/Pagination.icons.tsx +66 -0
  145. package/src/components/Pagination/Pagination.module.css +234 -20
  146. package/src/components/Pagination/Pagination.stories.tsx +34 -21
  147. package/src/components/Pagination/Pagination.tsx +74 -6
  148. package/src/components/Panel/Panel.module.css +206 -38
  149. package/src/components/Panel/Panel.stories.tsx +214 -22
  150. package/src/components/Panel/Panel.tsx +130 -5
  151. package/src/components/Radio/Radio.module.css +235 -37
  152. package/src/components/Radio/Radio.stories.tsx +77 -19
  153. package/src/components/Radio/Radio.tsx +210 -5
  154. package/src/components/Radio/RadioGroup.stories.tsx +117 -0
  155. package/src/components/Radio/RadioGroup.tsx +125 -0
  156. package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
  157. package/src/components/ReadOnlyField/ReadOnlyField.module.css +53 -15
  158. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +147 -19
  159. package/src/components/ReadOnlyField/ReadOnlyField.tsx +105 -5
  160. package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
  161. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
  162. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
  163. package/src/components/ReadOnlyField/index.ts +1 -0
  164. package/src/components/SegmentedControl/SegmentedControl.module.css +179 -47
  165. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +102 -20
  166. package/src/components/SegmentedControl/SegmentedControl.tsx +140 -6
  167. package/src/components/Slider/Slider.module.css +539 -86
  168. package/src/components/Slider/Slider.stories.tsx +157 -19
  169. package/src/components/Slider/Slider.tsx +283 -5
  170. package/src/components/Stack/Stack.stories.tsx +4 -25
  171. package/src/components/Stack/Stack.tsx +1 -5
  172. package/src/components/Stepper/IMPLEMENTATION_NOTES.md +4 -0
  173. package/src/components/Stepper/Stepper.module.css +410 -71
  174. package/src/components/Stepper/Stepper.stories.tsx +146 -20
  175. package/src/components/Stepper/Stepper.tsx +147 -6
  176. package/src/components/Switch/Switch.module.css +259 -39
  177. package/src/components/Switch/Switch.stories.tsx +98 -19
  178. package/src/components/Switch/Switch.tsx +200 -6
  179. package/src/components/Switch/SwitchGroup.stories.tsx +132 -0
  180. package/src/components/Switch/SwitchGroup.tsx +120 -0
  181. package/src/components/Table/Table.stories.tsx +1 -19
  182. package/src/components/Table/Table.tsx +3 -1
  183. package/src/components/Tabs/IMPLEMENTATION_NOTES.md +4 -0
  184. package/src/components/Tabs/Tabs.module.css +422 -79
  185. package/src/components/Tabs/Tabs.stories.tsx +156 -20
  186. package/src/components/Tabs/Tabs.tsx +86 -6
  187. package/src/components/Text/Text.stories.tsx +5 -17
  188. package/src/components/Text/Text.tsx +1 -15
  189. package/src/components/TextArea/TextArea.module.css +155 -32
  190. package/src/components/TextArea/TextArea.stories.tsx +80 -21
  191. package/src/components/TextArea/TextArea.tsx +146 -6
  192. package/src/components/TextField/TextField.module.css +261 -42
  193. package/src/components/TextField/TextField.stories.tsx +147 -20
  194. package/src/components/TextField/TextField.tsx +169 -6
  195. package/src/components/TimePicker/TimePicker.stories.tsx +2 -19
  196. package/src/components/TimePicker/TimePicker.tsx +3 -1
  197. package/src/components/Timeline/Timeline.module.css +365 -76
  198. package/src/components/Timeline/Timeline.stories.tsx +114 -21
  199. package/src/components/Timeline/Timeline.tsx +83 -5
  200. package/src/components/Timeline/TimelineItem.tsx +90 -0
  201. package/src/components/Title/Title.stories.tsx +0 -18
  202. package/src/components/Title/Title.tsx +1 -7
  203. package/src/components/Toast/Toast.module.css +166 -28
  204. package/src/components/Toast/Toast.stories.tsx +54 -22
  205. package/src/components/Toast/Toast.tsx +124 -5
  206. package/src/components/Tooltip/Tooltip.module.css +89 -24
  207. package/src/components/Tooltip/Tooltip.stories.tsx +152 -22
  208. package/src/components/Tooltip/Tooltip.tsx +88 -5
  209. package/src/components/TransferList/TransferList.stories.tsx +1 -19
  210. package/src/components/TransferList/TransferList.tsx +3 -1
  211. package/src/utils/RequireAccessibleLabel.ts +1 -12
  212. package/src/utils/filterStylingProps.ts +7 -20
@@ -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,8 +1,249 @@
1
- import React from "react";
2
- import "./Checkbox.module.css";
1
+ import React, { forwardRef } from "react";
2
+ import {
3
+ Checkbox as MuiCheckbox,
4
+ type CheckboxProps as MuiCheckboxProps,
5
+ } from "@mui/material";
6
+ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
7
+ import { CheckboxGroup, CheckboxGroupContext } from "./CheckboxGroup";
8
+ import {
9
+ filterStylingProps,
10
+ type RecursicaOverStyled,
11
+ } from "../../utils/filterStylingProps";
12
+ import {
13
+ type RequireAccessibleLabel,
14
+ type RecursicaCheckboxProps,
15
+ } from "@recursica/adapter-common";
16
+ import {
17
+ FormControlLayout,
18
+ type FormControlLayoutProps,
19
+ } from "../FormControlLayout/FormControlLayout";
3
20
 
4
- export type CheckboxProps = React.HTMLAttributes<HTMLDivElement>;
21
+ import styles from "./Checkbox.module.css";
5
22
 
6
- export const Checkbox: React.FC<CheckboxProps> = (props) => {
7
- return <div {...props}>Checkbox</div>;
23
+ export type CheckboxWrapperProps = Omit<MuiCheckboxProps, "size" | "color"> &
24
+ RecursicaCheckboxProps &
25
+ ReadOnlyControlProps &
26
+ Pick<
27
+ FormControlLayoutProps,
28
+ "formLayout" | "labelSize" | "controlMaxWidth" | "controlMinWidth"
29
+ >;
30
+
31
+ export type RecursicaCheckboxPropsAlias =
32
+ RequireAccessibleLabel<CheckboxWrapperProps>;
33
+
34
+ export type CheckboxProps = RecursicaOverStyled<RecursicaCheckboxPropsAlias>;
35
+
36
+ type CheckboxComponent = React.ForwardRefExoticComponent<
37
+ CheckboxProps & React.RefAttributes<HTMLButtonElement>
38
+ > & {
39
+ Group: typeof CheckboxGroup;
8
40
  };
41
+
42
+ export const Checkbox = forwardRef<HTMLButtonElement, CheckboxProps>(
43
+ function Checkbox(props, ref) {
44
+ const {
45
+ overStyled = false,
46
+ readOnly,
47
+ readOnlyComponent,
48
+ disabled,
49
+ formLayout,
50
+ labelSize,
51
+ controlMaxWidth,
52
+ controlMinWidth,
53
+ label,
54
+ description,
55
+ error,
56
+ style,
57
+ ...rest
58
+ } = props;
59
+
60
+ const sanitizedProps = filterStylingProps(rest, overStyled);
61
+ const restRecord = sanitizedProps as Record<string, unknown>;
62
+
63
+ delete restRecord["size"];
64
+ delete restRecord["color"];
65
+
66
+ const mergedClassNames: Partial<Record<string, string>> = {
67
+ root: styles.root,
68
+ checked: styles.checked,
69
+ disabled: styles.disabled,
70
+ };
71
+
72
+ const classesProp = restRecord.classes;
73
+ if (
74
+ classesProp &&
75
+ typeof classesProp === "object" &&
76
+ !Array.isArray(classesProp)
77
+ ) {
78
+ const o = classesProp as Partial<Record<string, string>>;
79
+ mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
80
+ mergedClassNames.checked = o.checked
81
+ ? `${styles.checked} ${o.checked}`
82
+ : styles.checked;
83
+ mergedClassNames.disabled = o.disabled
84
+ ? `${styles.disabled} ${o.disabled}`
85
+ : styles.disabled;
86
+ }
87
+
88
+ const classNameProp = restRecord.className as string | undefined;
89
+ const finalClass = classNameProp
90
+ ? `${styles.root} ${classNameProp}`
91
+ : styles.root;
92
+
93
+ const groupContext = React.useContext(CheckboxGroupContext);
94
+ const isGrouped = groupContext !== null;
95
+ const isGroupReadOnly = isGrouped ? groupContext.readOnly : false;
96
+ const isChecked = isGrouped
97
+ ? (groupContext.value || []).includes(restRecord.value)
98
+ : !!(restRecord.checked ?? restRecord.defaultChecked);
99
+
100
+ if (readOnly && !!readOnlyComponent) {
101
+ const ReadOnlyComp = readOnlyComponent;
102
+ const roNode = (
103
+ <ReadOnlyComp
104
+ {...props}
105
+ checked={isChecked}
106
+ label={label as React.ReactNode}
107
+ />
108
+ );
109
+
110
+ if (formLayout) {
111
+ return (
112
+ <FormControlLayout
113
+ formLayout={formLayout}
114
+ labelSize={labelSize}
115
+ controlMaxWidth={controlMaxWidth}
116
+ controlMinWidth={controlMinWidth}
117
+ >
118
+ {roNode}
119
+ </FormControlLayout>
120
+ );
121
+ }
122
+
123
+ return <>{roNode}</>;
124
+ }
125
+
126
+ const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
127
+ if (isGrouped) {
128
+ const newValue = e.target.checked
129
+ ? [...(groupContext.value || []), restRecord.value]
130
+ : (groupContext.value || []).filter((v) => v !== restRecord.value);
131
+ if (groupContext.onChange) {
132
+ groupContext.onChange(e, newValue);
133
+ }
134
+ } else {
135
+ if (restRecord.onChange) {
136
+ (restRecord.onChange as any)(e, e.target.checked);
137
+ }
138
+ }
139
+ };
140
+
141
+ const CheckIcon = (props: React.ComponentProps<"svg">) => (
142
+ <svg
143
+ viewBox="0 0 10 7"
144
+ fill="none"
145
+ xmlns="http://www.w3.org/2000/svg"
146
+ {...props}
147
+ >
148
+ <path
149
+ d="M4 4.586L1.707 2.293A1 1 0 1 0 .293 3.707l3 3a.997.997 0 0 0 1.414 0l5-5A1 1 0 1 0 8.293.293L4 4.586z"
150
+ fill="currentColor"
151
+ fillRule="evenodd"
152
+ clipRule="evenodd"
153
+ />
154
+ </svg>
155
+ );
156
+
157
+ const checkboxNode = (
158
+ <MuiCheckbox
159
+ ref={ref}
160
+ className={!label ? `${finalClass} ${styles.inner}` : styles.inner}
161
+ classes={mergedClassNames}
162
+ disabled={readOnly || disabled || isGroupReadOnly}
163
+ {...(sanitizedProps as unknown as MuiCheckboxProps)}
164
+ checked={isChecked as boolean}
165
+ onChange={handleChange}
166
+ sx={label ? { padding: 0 } : undefined}
167
+ icon={<div className={styles.input} />}
168
+ checkedIcon={
169
+ <div className={`${styles.input} ${styles.inputChecked}`}>
170
+ <CheckIcon className={styles.icon} />
171
+ </div>
172
+ }
173
+ indeterminateIcon={
174
+ <div className={`${styles.input} ${styles.inputIndeterminate}`}>
175
+ <svg
176
+ className={styles.icon}
177
+ xmlns="http://www.w3.org/2000/svg"
178
+ fill="none"
179
+ viewBox="0 0 32 32"
180
+ >
181
+ <rect x="6" y="14" width="20" height="4" fill="currentColor" />
182
+ </svg>
183
+ </div>
184
+ }
185
+ />
186
+ );
187
+
188
+ const finalNode = label ? (
189
+ <div className={finalClass} style={style as React.CSSProperties}>
190
+ <div className={styles.body}>
191
+ {checkboxNode}
192
+ <div className={styles.labelWrapper}>
193
+ <label
194
+ className={styles.label}
195
+ htmlFor={restRecord.id as string}
196
+ data-disabled={
197
+ readOnly || disabled || isGroupReadOnly ? true : undefined
198
+ }
199
+ >
200
+ {label as React.ReactNode}
201
+ </label>
202
+ {description && (
203
+ <div
204
+ className={styles.description}
205
+ data-disabled={
206
+ readOnly || disabled || isGroupReadOnly ? true : undefined
207
+ }
208
+ >
209
+ {description}
210
+ </div>
211
+ )}
212
+ {error && (
213
+ <div
214
+ className={styles.error}
215
+ data-disabled={
216
+ readOnly || disabled || isGroupReadOnly ? true : undefined
217
+ }
218
+ >
219
+ {error}
220
+ </div>
221
+ )}
222
+ </div>
223
+ </div>
224
+ </div>
225
+ ) : (
226
+ <div className={finalClass} style={style as React.CSSProperties}>
227
+ {checkboxNode}
228
+ </div>
229
+ );
230
+
231
+ if (formLayout) {
232
+ return (
233
+ <FormControlLayout
234
+ formLayout={formLayout}
235
+ labelSize={labelSize}
236
+ controlMaxWidth={controlMaxWidth}
237
+ controlMinWidth={controlMinWidth}
238
+ >
239
+ {finalNode}
240
+ </FormControlLayout>
241
+ );
242
+ }
243
+
244
+ return finalNode;
245
+ },
246
+ ) as CheckboxComponent;
247
+
248
+ Checkbox.displayName = "Checkbox";
249
+ Checkbox.Group = CheckboxGroup;
@@ -0,0 +1,139 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Checkbox } from "./Checkbox";
3
+ import { CheckboxGroup } from "./CheckboxGroup";
4
+ import { useState } from "react";
5
+ import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
6
+
7
+ const meta: Meta<typeof CheckboxGroup> = {
8
+ title: "UI-Kit/CheckboxGroup",
9
+ component: CheckboxGroup,
10
+ tags: ["autodocs"],
11
+ parameters: {
12
+ docs: {
13
+ description: {
14
+ component: `
15
+ The \`CheckboxGroup\` component is the mandatory organizational wrapper aggregating multiple \`Checkbox\` primitives into structurally bound unified arrays. It inherently leverages the \`FormControlWrapper\` granting native access to macroscopic layout structuring, assistive descriptions, and strict flex arrays.
16
+
17
+ We exclusively utilize the \`formLayout\` parameter to control macro-level form flow:
18
+ - **\`formLayout="stacked"\`**: Top-to-bottom layout cascading the Label bounding box down vertically into a standard stacked checkbox column array.
19
+ - **\`formLayout="side-by-side"\`**: Flow architecture pulling the grouping Label dynamically to the left while structurally organizing the internal checkboxes cleanly alongside it horizontally.
20
+
21
+ \`\`\`tsx
22
+ <Checkbox.Group
23
+ label="Execution Targets"
24
+ assistiveText="Strictly mapped structural grouping dynamically applied natively."
25
+ formLayout="stacked"
26
+ >
27
+ <Checkbox value="react" label="Browser Execution Context" />
28
+ <Checkbox value="svelte" label="Edge Nodes" />
29
+ </Checkbox.Group>
30
+ \`\`\`
31
+ `,
32
+ },
33
+ },
34
+ },
35
+ argTypes: {
36
+ ...formControlArgTypes,
37
+ readOnly: {
38
+ control: "boolean",
39
+ description:
40
+ "Toggles structural read-only data presentation bypassing interaction boundaries completely.",
41
+ },
42
+ },
43
+ };
44
+
45
+ export default meta;
46
+
47
+ type Story = StoryObj<typeof CheckboxGroup>;
48
+
49
+ export const Default: Story = {
50
+ args: {
51
+ formLayout: "stacked",
52
+ label: "Standard Group",
53
+ },
54
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
55
+ render: function StoryRender({ withLayer, layer, ...args }: any) {
56
+ const [value, setValue] = useState<string[]>([]);
57
+ return (
58
+ <Checkbox.Group {...args} value={value} onChange={setValue}>
59
+ <Checkbox value="1" label="Option 1" />
60
+ <Checkbox value="2" label="Option 2" />
61
+ </Checkbox.Group>
62
+ );
63
+ },
64
+ };
65
+
66
+ export const SideBySideLayout: Story = {
67
+ args: {
68
+ formLayout: "side-by-side",
69
+ labelOptionalText: "Recommended",
70
+ labelWithEditIcon: true,
71
+ label: "Frontend Frameworks",
72
+ assistiveText:
73
+ "Select all libraries currently in use for this specific workspace configuration.",
74
+ },
75
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
76
+ render: function StoryRender({ withLayer, layer, ...args }: any) {
77
+ const [value, setValue] = useState<string[]>(["react"]);
78
+ return (
79
+ <Checkbox.Group {...args} value={value} onChange={setValue}>
80
+ <Checkbox value="react" label="React (Standard Build)" />
81
+ <Checkbox
82
+ value="svelte"
83
+ label="The Svelte architecture which provides a highly optimized, completely compiler-driven framework avoiding virtual DOM boundaries. This massively extended text explicitly guarantees accurate wrapper constraint checking and multi-line flex alignment."
84
+ />
85
+ <Checkbox value="vue" label="Vue Configuration Map" />
86
+ </Checkbox.Group>
87
+ );
88
+ },
89
+ };
90
+
91
+ export const StackedLayout: Story = {
92
+ args: {
93
+ formLayout: "stacked",
94
+ required: true,
95
+ label: "Execution Targets",
96
+ error: "You must select at least one deployment target to compile.",
97
+ },
98
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
99
+ render: function StoryRender({ withLayer, layer, ...args }: any) {
100
+ const [value, setValue] = useState<string[]>(["react"]);
101
+ return (
102
+ <Checkbox.Group {...args} value={value} onChange={setValue}>
103
+ <Checkbox value="react" label="Browser Execution Context" />
104
+ <Checkbox
105
+ value="svelte"
106
+ label="Highly distributed Edge computing environments seamlessly bridging local runtime boundaries."
107
+ />
108
+ <Checkbox value="vue" label="Serverless Cloud Providers" />
109
+ </Checkbox.Group>
110
+ );
111
+ },
112
+ };
113
+
114
+ export const ReadOnly: Story = {
115
+ args: {
116
+ readOnly: true,
117
+ formLayout: "stacked",
118
+ required: true,
119
+ label: "Static ReadOnly Execution Locks",
120
+ assistiveText:
121
+ "This structure explicitly validates native component-level DOM preservation natively mapping lock bounds safely over interaction.",
122
+ },
123
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any
124
+ render: function StoryRender({ withLayer, layer, ...args }: any) {
125
+ const [value, setValue] = useState<string[]>(["disabledNode"]);
126
+ return (
127
+ <Checkbox.Group {...args} value={value} onChange={setValue}>
128
+ <Checkbox
129
+ value="disabledNode"
130
+ label="Structurally Checked Node natively"
131
+ />
132
+ <Checkbox
133
+ value="disabledNodeEmpty"
134
+ label="Unchecked Configuration Limit"
135
+ />
136
+ </Checkbox.Group>
137
+ );
138
+ },
139
+ };
@@ -0,0 +1,137 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React, { forwardRef } from "react";
3
+ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
4
+ import {
5
+ filterStylingProps,
6
+ type RecursicaOverStyled,
7
+ } from "../../utils/filterStylingProps";
8
+ import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
9
+ import { WithReadOnlyWrapper } from "../ReadOnlyField/WithReadOnlyWrapper";
10
+ import styles from "./Checkbox.module.css";
11
+
12
+ import { FormGroup as MuiFormGroup } from "@mui/material";
13
+
14
+ export const CheckboxGroupContext = React.createContext<{
15
+ value?: any[];
16
+ onChange?: (event: React.SyntheticEvent, value: any) => void;
17
+ name?: string;
18
+ readOnly?: boolean;
19
+ } | null>(null);
20
+
21
+ import { type RecursicaCheckboxGroupProps as BaseRecursicaCheckboxGroupProps } from "@recursica/adapter-common";
22
+
23
+ export interface RecursicaCheckboxGroupProps
24
+ extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange">,
25
+ Omit<
26
+ RecursicaFormControlWrapperProps,
27
+ | "controlMaxWidth"
28
+ | "controlMinWidth"
29
+ | "onChange"
30
+ | "classes"
31
+ | "withAsterisk"
32
+ >,
33
+ ReadOnlyControlProps,
34
+ BaseRecursicaCheckboxGroupProps {}
35
+
36
+ export type CheckboxGroupProps =
37
+ RecursicaOverStyled<RecursicaCheckboxGroupProps>;
38
+
39
+ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
40
+ function CheckboxGroup(props, ref) {
41
+ const {
42
+ overStyled = false,
43
+ formLayout = "stacked",
44
+
45
+ // Label Wrappers
46
+ labelSize,
47
+ labelAlignment,
48
+ labelOptionalText,
49
+ labelWithEditIcon,
50
+ onLabelEditClick,
51
+
52
+ // Base Mantine Extracted Attributes
53
+ label,
54
+ description,
55
+ assistiveText,
56
+ assistiveWithIcon,
57
+ error,
58
+ required,
59
+ // removed withAsterisk
60
+ id,
61
+ className,
62
+ style,
63
+ children,
64
+ readOnly,
65
+ readOnlyComponent,
66
+ emptyValueComponent,
67
+ value,
68
+ defaultValue,
69
+ onChange,
70
+ row,
71
+ ...rest
72
+ } = props;
73
+ const sanitizedProps = filterStylingProps(rest, overStyled);
74
+ const restRecord = sanitizedProps as Record<string, unknown>;
75
+
76
+ // Delete prohibited sizing hooks
77
+ delete restRecord["size"];
78
+
79
+ const handleChange = (_event: React.SyntheticEvent, childValue: any) => {
80
+ if (onChange) {
81
+ onChange(childValue);
82
+ }
83
+ };
84
+
85
+ return (
86
+ <WithReadOnlyWrapper
87
+ className={className}
88
+ style={style as React.CSSProperties}
89
+ controlMaxWidth="var(--recursica_ui-kit_components_checkbox-item_properties_max-width)"
90
+ controlMinWidth={undefined}
91
+ overStyled={overStyled as true}
92
+ // strictly override
93
+ formLayout={formLayout}
94
+ labelSize={labelSize}
95
+ labelAlignment={labelAlignment}
96
+ labelOptionalText={labelOptionalText}
97
+ labelWithEditIcon={labelWithEditIcon}
98
+ onLabelEditClick={onLabelEditClick}
99
+ label={label}
100
+ description={description}
101
+ assistiveText={assistiveText}
102
+ assistiveWithIcon={assistiveWithIcon}
103
+ error={error}
104
+ required={required}
105
+ id={id}
106
+ readOnly={readOnly && !!readOnlyComponent}
107
+ readOnlyComponent={readOnlyComponent}
108
+ emptyValueComponent={emptyValueComponent}
109
+ readOnlyType="text"
110
+ readOnlyValue={value !== undefined ? value : defaultValue}
111
+ readOnlyNativeProps={props}
112
+ activeComponent={
113
+ <CheckboxGroupContext.Provider
114
+ value={{
115
+ value: value !== undefined ? value : defaultValue,
116
+ onChange: handleChange,
117
+ name: restRecord.name as string | undefined,
118
+ readOnly: readOnly || !!(restRecord as any).disabled,
119
+ }}
120
+ >
121
+ <MuiFormGroup
122
+ ref={ref}
123
+ row={row}
124
+ {...(sanitizedProps as any)}
125
+ className={`${styles.groupRoot} ${(sanitizedProps as any).className || ""}`.trim()}
126
+ data-layout={formLayout}
127
+ >
128
+ {children}
129
+ </MuiFormGroup>
130
+ </CheckboxGroupContext.Provider>
131
+ }
132
+ />
133
+ );
134
+ },
135
+ );
136
+
137
+ CheckboxGroup.displayName = "CheckboxGroup";
@@ -1 +1,6 @@
1
- export * from "./Checkbox";
1
+ import { CheckboxGroup } from "./CheckboxGroup";
2
+
3
+ export { Checkbox } from "./Checkbox";
4
+ export type { CheckboxProps } from "./Checkbox";
5
+ export { CheckboxGroup };
6
+ export type { CheckboxGroupProps } from "./CheckboxGroup";