@recursica/mantine-adapter 0.6.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 (140) hide show
  1. package/.storybook/commonArgTypes.ts +148 -0
  2. package/CHANGELOG.md +69 -0
  3. package/dist/mantine-adapter.cjs +1 -1
  4. package/dist/mantine-adapter.cjs.map +1 -1
  5. package/dist/mantine-adapter.css +1 -1
  6. package/dist/mantine-adapter.js +660 -586
  7. package/dist/mantine-adapter.js.map +1 -1
  8. package/dist/src/components/Checkbox/CheckboxGroup.d.ts +1 -1
  9. package/dist/src/components/Chip/Chip.d.ts +14 -2
  10. package/package.json +5 -2
  11. package/src/OverStyling.stories.tsx +174 -0
  12. package/src/Version.stories.tsx +56 -0
  13. package/src/components/Accordion/ACCORDION_IMPLEMENTATION_NOTES.md +48 -0
  14. package/src/components/Accordion/Accordion.module.css +261 -0
  15. package/src/components/Accordion/Accordion.stories.tsx +145 -0
  16. package/src/components/Accordion/Accordion.tsx +200 -0
  17. package/src/components/Accordion/index.ts +1 -0
  18. package/src/components/AssistiveElement/AssistiveElement.module.css +111 -0
  19. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +73 -0
  20. package/src/components/AssistiveElement/AssistiveElement.tsx +91 -0
  21. package/src/components/Avatar/AVATAR_IMPLEMENTATION_NOTES.md +24 -0
  22. package/src/components/Avatar/Avatar.module.css +340 -0
  23. package/src/components/Avatar/Avatar.stories.tsx +102 -0
  24. package/src/components/Avatar/Avatar.tsx +100 -0
  25. package/src/components/Avatar/index.ts +1 -0
  26. package/src/components/Badge/BADGE_IMPLEMENTATION_NOTES.md +18 -0
  27. package/src/components/Badge/Badge.module.css +124 -0
  28. package/src/components/Badge/Badge.stories.tsx +77 -0
  29. package/src/components/Badge/Badge.tsx +66 -0
  30. package/src/components/Breadcrumb/BREADCRUMB_IMPLEMENTATION_NOTES.md +14 -0
  31. package/src/components/Breadcrumb/Breadcrumb.module.css +26 -0
  32. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +77 -0
  33. package/src/components/Breadcrumb/Breadcrumb.tsx +64 -0
  34. package/src/components/Button/Button.module.css +275 -0
  35. package/src/components/Button/Button.stories.tsx +105 -0
  36. package/src/components/Button/Button.tsx +122 -0
  37. package/src/components/Button/IMPLEMENTATION_NOTES.md +45 -0
  38. package/src/components/Button/index.ts +1 -0
  39. package/src/components/Card/CARD_IMPLEMENTATION_NOTES.md +10 -0
  40. package/src/components/Card/Card.module.css +60 -0
  41. package/src/components/Card/Card.stories.tsx +141 -0
  42. package/src/components/Card/Card.tsx +176 -0
  43. package/src/components/Checkbox/CHECKBOX_IMPLEMENTATION_NOTES.md +25 -0
  44. package/src/components/Checkbox/Checkbox.module.css +204 -0
  45. package/src/components/Checkbox/Checkbox.stories.tsx +73 -0
  46. package/src/components/Checkbox/Checkbox.tsx +111 -0
  47. package/src/components/Checkbox/CheckboxGroup.stories.tsx +138 -0
  48. package/src/components/Checkbox/CheckboxGroup.tsx +140 -0
  49. package/src/components/Chip/CHIP_IMPLEMENTATION_NOTES.md +35 -0
  50. package/src/components/Chip/Chip.module.css +218 -0
  51. package/src/components/Chip/Chip.stories.tsx +115 -0
  52. package/src/components/Chip/Chip.tsx +143 -0
  53. package/src/components/DatePicker/DatePicker.stories.tsx +22 -0
  54. package/src/components/DatePicker/DatePicker.tsx +7 -0
  55. package/src/components/Dropdown/Dropdown.stories.tsx +22 -0
  56. package/src/components/Dropdown/Dropdown.tsx +7 -0
  57. package/src/components/FileInput/FileInput.stories.tsx +22 -0
  58. package/src/components/FileInput/FileInput.tsx +7 -0
  59. package/src/components/FileUpload/FileUpload.stories.tsx +22 -0
  60. package/src/components/FileUpload/FileUpload.tsx +7 -0
  61. package/src/components/FormControlWrapper/FORMCONTROLWRAPPER_IMPLEMENTATION_NOTES.md +32 -0
  62. package/src/components/FormControlWrapper/FormControlWrapper.module.css +46 -0
  63. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +116 -0
  64. package/src/components/FormControlWrapper/FormControlWrapper.tsx +170 -0
  65. package/src/components/HoverCard/HoverCard.stories.tsx +17 -0
  66. package/src/components/HoverCard/HoverCard.tsx +7 -0
  67. package/src/components/Label/LABEL_IMPLEMENTATION_NOTES.md +48 -0
  68. package/src/components/Label/Label.module.css +177 -0
  69. package/src/components/Label/Label.stories.tsx +123 -0
  70. package/src/components/Label/Label.tsx +132 -0
  71. package/src/components/Label/index.ts +1 -0
  72. package/src/components/Link/Link.stories.tsx +17 -0
  73. package/src/components/Link/Link.tsx +7 -0
  74. package/src/components/Loader/Loader.stories.tsx +17 -0
  75. package/src/components/Loader/Loader.tsx +7 -0
  76. package/src/components/Menu/Menu.stories.tsx +17 -0
  77. package/src/components/Menu/Menu.tsx +7 -0
  78. package/src/components/Modal/Modal.stories.tsx +17 -0
  79. package/src/components/Modal/Modal.tsx +7 -0
  80. package/src/components/NumberInput/NumberInput.stories.tsx +22 -0
  81. package/src/components/NumberInput/NumberInput.tsx +7 -0
  82. package/src/components/Pagination/Pagination.stories.tsx +17 -0
  83. package/src/components/Pagination/Pagination.tsx +7 -0
  84. package/src/components/Panel/Panel.stories.tsx +17 -0
  85. package/src/components/Panel/Panel.tsx +7 -0
  86. package/src/components/Popover/Popover.stories.tsx +17 -0
  87. package/src/components/Popover/Popover.tsx +7 -0
  88. package/src/components/Radio/Radio.stories.tsx +22 -0
  89. package/src/components/Radio/Radio.tsx +7 -0
  90. package/src/components/ReadOnlyField/READ_ONLY_FIELD_IMPLEMENTATION_NOTES.md +15 -0
  91. package/src/components/ReadOnlyField/ReadOnlyField.module.css +49 -0
  92. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +120 -0
  93. package/src/components/ReadOnlyField/ReadOnlyField.tsx +75 -0
  94. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +46 -0
  95. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +85 -0
  96. package/src/components/ReadOnlyField/index.ts +3 -0
  97. package/src/components/Search/Search.stories.tsx +17 -0
  98. package/src/components/Search/Search.tsx +7 -0
  99. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +22 -0
  100. package/src/components/SegmentedControl/SegmentedControl.tsx +7 -0
  101. package/src/components/Slider/Slider.stories.tsx +22 -0
  102. package/src/components/Slider/Slider.tsx +7 -0
  103. package/src/components/Stepper/Stepper.stories.tsx +17 -0
  104. package/src/components/Stepper/Stepper.tsx +7 -0
  105. package/src/components/Switch/Switch.stories.tsx +22 -0
  106. package/src/components/Switch/Switch.tsx +7 -0
  107. package/src/components/Table/Table.stories.tsx +17 -0
  108. package/src/components/Table/Table.tsx +7 -0
  109. package/src/components/Tabs/Tabs.stories.tsx +17 -0
  110. package/src/components/Tabs/Tabs.tsx +7 -0
  111. package/src/components/Text/Text.stories.tsx +88 -0
  112. package/src/components/Text/Text.tsx +54 -0
  113. package/src/components/TextArea/TextArea.stories.tsx +22 -0
  114. package/src/components/TextArea/TextArea.tsx +7 -0
  115. package/src/components/TextField/TEXTFIELD_IMPLEMENTATION_NOTES.md +19 -0
  116. package/src/components/TextField/TextField.module.css +225 -0
  117. package/src/components/TextField/TextField.stories.tsx +162 -0
  118. package/src/components/TextField/TextField.tsx +138 -0
  119. package/src/components/TimePicker/TimePicker.stories.tsx +22 -0
  120. package/src/components/TimePicker/TimePicker.tsx +7 -0
  121. package/src/components/Timeline/Timeline.stories.tsx +17 -0
  122. package/src/components/Timeline/Timeline.tsx +7 -0
  123. package/src/components/Title/Title.stories.tsx +63 -0
  124. package/src/components/Title/Title.tsx +45 -0
  125. package/src/components/Toast/Toast.stories.tsx +17 -0
  126. package/src/components/Toast/Toast.tsx +7 -0
  127. package/src/components/Tooltip/Tooltip.stories.tsx +17 -0
  128. package/src/components/Tooltip/Tooltip.tsx +7 -0
  129. package/src/components/TransferList/TransferList.stories.tsx +17 -0
  130. package/src/components/TransferList/TransferList.tsx +7 -0
  131. package/src/components/index.ts +39 -0
  132. package/src/env.d.ts +7 -0
  133. package/src/index.ts +3 -0
  134. package/src/types/index.ts +9 -0
  135. package/src/types/mantine.d.ts +7 -0
  136. package/src/types/scss-modules.d.ts +9 -0
  137. package/src/utils/ColorSchemeWrapper.tsx +27 -0
  138. package/src/utils/copyToClipboard.ts +36 -0
  139. package/src/utils/filterStylingProps.ts +139 -0
  140. package/src/utils/index.ts +1 -0
@@ -0,0 +1,204 @@
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 !important;
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 !important;
72
+ align-items: center !important;
73
+ justify-content: center !important;
74
+ margin: 0 !important; /* HARDCODE: structural reset */
75
+ width: var(--recursica_ui-kit_components_checkbox_properties_size) !important;
76
+ height: var(
77
+ --recursica_ui-kit_components_checkbox_properties_size
78
+ ) !important;
79
+ /* Mathematically syncs the physical input checkbox bounding block visually with the center of the text's first line natively. */
80
+ margin-top: calc(
81
+ (
82
+ var(
83
+ --recursica_ui-kit_components_checkbox-item_properties_text_line-height
84
+ ) *
85
+ var(
86
+ --recursica_ui-kit_components_checkbox-item_properties_text_font-size
87
+ ) -
88
+ var(--recursica_ui-kit_components_checkbox_properties_size)
89
+ ) /
90
+ 2
91
+ ) !important;
92
+ }
93
+
94
+ /* Base explicit reset masking underlying framework defaults */
95
+ .input {
96
+ cursor: pointer; /* HARDCODE: core interaction behavior */
97
+ appearance: none; /* HARDCODE: reset native widget */
98
+ box-sizing: border-box !important; /* HARDCODE: ensures size includes borders */
99
+ width: var(--recursica_ui-kit_components_checkbox_properties_size) !important;
100
+ height: var(
101
+ --recursica_ui-kit_components_checkbox_properties_size
102
+ ) !important;
103
+ border-radius: var(
104
+ --recursica_ui-kit_components_checkbox_properties_border-radius
105
+ ) !important;
106
+ border-width: var(
107
+ --recursica_ui-kit_components_checkbox_properties_border-size
108
+ ) !important;
109
+ border-style: solid; /* HARDCODE: normalize primitive border-style */
110
+
111
+ /* Standard unchecked state */
112
+ background-color: var(
113
+ --recursica_ui-kit_components_checkbox_properties_colors_background-unchecked
114
+ ) !important;
115
+ border-color: var(
116
+ --recursica_ui-kit_components_checkbox_properties_colors_border-unchecked
117
+ ) !important;
118
+ transition: all 0.2s ease; /* HARDCODE: consider global transition token */
119
+ }
120
+
121
+ /* Checked/Indeterminate State */
122
+ .input:checked {
123
+ background-color: var(
124
+ --recursica_ui-kit_components_checkbox_properties_colors_background-checked
125
+ ) !important;
126
+ border-color: var(
127
+ --recursica_ui-kit_components_checkbox_properties_colors_border-checked
128
+ ) !important;
129
+ }
130
+ .input[data-indeterminate] {
131
+ background-color: var(
132
+ --recursica_ui-kit_components_checkbox_properties_colors_background-indeterminate
133
+ ) !important;
134
+ border-color: var(
135
+ --recursica_ui-kit_components_checkbox_properties_colors_border-indeterminate
136
+ ) !important;
137
+ }
138
+
139
+ /* Disabled State */
140
+ .input:disabled {
141
+ cursor: not-allowed; /* HARDCODE: disabled state default */
142
+ opacity: var(
143
+ --recursica_ui-kit_components_checkbox_properties_disabled-opacity
144
+ ) !important;
145
+ background-color: var(
146
+ --recursica_ui-kit_components_checkbox_properties_colors_disabled-background
147
+ ) !important;
148
+ border-color: var(
149
+ --recursica_ui-kit_components_checkbox_properties_colors_disabled-border
150
+ ) !important;
151
+ }
152
+
153
+ .icon {
154
+ width: var(
155
+ --recursica_ui-kit_components_checkbox_properties_icon-size
156
+ ) !important;
157
+ height: var(
158
+ --recursica_ui-kit_components_checkbox_properties_icon-size
159
+ ) !important;
160
+ color: var(
161
+ --recursica_ui-kit_components_checkbox_properties_colors_icon-color
162
+ ) !important;
163
+ }
164
+ .input:disabled + .iconWrapper .icon {
165
+ color: var(
166
+ --recursica_ui-kit_components_checkbox_properties_colors_disabled-icon
167
+ ) !important;
168
+ }
169
+
170
+ .labelWrapper {
171
+ padding: 0 !important; /* HARDCODE: structural layout reset */
172
+ margin: 0 !important; /* HARDCODE: structural layout reset */
173
+ flex: 1;
174
+ min-width: 0; /* HARDCODE: forces text wrap boundary in flex child */
175
+ }
176
+
177
+ .label {
178
+ cursor: pointer; /* HARDCODE: interactive affordance */
179
+ padding: 0 !important; /* HARDCODE: reset layout */
180
+ margin: 0 !important; /* HARDCODE: reset layout */
181
+ word-wrap: break-word;
182
+ white-space: normal;
183
+ color: var(
184
+ --recursica_ui-kit_components_checkbox-item_properties_text_color
185
+ ) !important;
186
+ font-family: var(
187
+ --recursica_ui-kit_components_checkbox-item_properties_text_font-family
188
+ );
189
+ font-size: var(
190
+ --recursica_ui-kit_components_checkbox-item_properties_text_font-size
191
+ );
192
+ font-weight: var(
193
+ --recursica_ui-kit_components_checkbox-item_properties_text_font-weight
194
+ );
195
+ line-height: var(
196
+ --recursica_ui-kit_components_checkbox-item_properties_text_line-height
197
+ );
198
+ }
199
+ .root[data-disabled] .label {
200
+ cursor: not-allowed; /* HARDCODE: disabled interaction */
201
+ opacity: var(
202
+ --recursica_ui-kit_components_checkbox_properties_disabled-opacity
203
+ );
204
+ }
@@ -0,0 +1,73 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { Checkbox } from "./Checkbox";
3
+
4
+ const meta: Meta<typeof Checkbox> = {
5
+ title: "UI-Kit/Checkbox",
6
+ component: Checkbox,
7
+ tags: ["autodocs"],
8
+ parameters: {
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.",
34
+ },
35
+ },
36
+ };
37
+
38
+ export default meta;
39
+
40
+ type Story = StoryObj<typeof Checkbox>;
41
+
42
+ export const Default: Story = {
43
+ args: {
44
+ disabled: false,
45
+ label: "Standard Unchecked Property",
46
+ },
47
+ };
48
+
49
+ export const LongLabelWrap: Story = {
50
+ args: {
51
+ label:
52
+ "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.",
53
+ },
54
+ };
55
+
56
+ export const StaticVariations: Story = {
57
+ args: {},
58
+ render: () => (
59
+ <div
60
+ style={{
61
+ display: "flex",
62
+ flexDirection: "column",
63
+ gap: "24px",
64
+ }}
65
+ >
66
+ <Checkbox label="Default Unchecked State" />
67
+ <Checkbox label="Acknowledge Configuration" defaultChecked />
68
+ <Checkbox label="Indeterminate Master" indeterminate />
69
+ <Checkbox label="Disabled Variant" disabled />
70
+ <Checkbox label="Disabled Checked Variant" checked disabled />
71
+ </div>
72
+ ),
73
+ };
@@ -0,0 +1,111 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { forwardRef } from "react";
3
+ import {
4
+ Checkbox as MantineCheckbox,
5
+ type CheckboxProps as MantineCheckboxProps,
6
+ } from "@mantine/core";
7
+ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
8
+ import { CheckboxGroup } from "./CheckboxGroup";
9
+ import { FormControlWrapper } from "../FormControlWrapper/FormControlWrapper";
10
+ import {
11
+ filterStylingProps,
12
+ type RecursicaOverStyled,
13
+ } from "../../utils/filterStylingProps";
14
+ import styles from "./Checkbox.module.css";
15
+
16
+ export type RecursicaCheckboxProps = Omit<
17
+ MantineCheckboxProps,
18
+ "size" | "color" | "radius" | "iconColor" | "variant"
19
+ > &
20
+ ReadOnlyControlProps;
21
+
22
+ export type CheckboxProps = RecursicaOverStyled<RecursicaCheckboxProps>;
23
+
24
+ type CheckboxComponent = React.ForwardRefExoticComponent<
25
+ CheckboxProps & React.RefAttributes<HTMLInputElement>
26
+ > & {
27
+ Group: typeof CheckboxGroup;
28
+ };
29
+
30
+ export const Checkbox = forwardRef<HTMLInputElement, CheckboxProps>(
31
+ function Checkbox(
32
+ { overStyled = false, readOnly, readOnlyComponent, disabled, ...rest },
33
+ ref,
34
+ ) {
35
+ // Checkbox doesn't use Label onLabelEditClick natively since it isn't mapped inside FormControlWrapper intrinsically.
36
+ const sanitizedProps = filterStylingProps(rest, overStyled);
37
+ const restRecord = sanitizedProps as Record<string, unknown>;
38
+
39
+ // Actively delete dimension bindings that bypass the abstraction
40
+ delete restRecord["size"];
41
+ delete restRecord["color"];
42
+ delete restRecord["radius"];
43
+ delete restRecord["variant"];
44
+ delete restRecord["iconColor"];
45
+
46
+ if (readOnly && readOnlyComponent) {
47
+ // Safely fallback explicitly returning mapped elements natively
48
+ return (
49
+ <FormControlWrapper
50
+ overStyled={overStyled as true}
51
+ {...(sanitizedProps as any)}
52
+ >
53
+ {readOnlyComponent}
54
+ </FormControlWrapper>
55
+ );
56
+ }
57
+
58
+ const mergedClassNames: Partial<Record<string, string>> = {
59
+ root: styles.root,
60
+ body: styles.body,
61
+ inner: styles.inner,
62
+ input: styles.input,
63
+ icon: styles.icon,
64
+ labelWrapper: styles.labelWrapper,
65
+ label: styles.label,
66
+ };
67
+
68
+ const classNamesProp = restRecord.classNames;
69
+ if (
70
+ classNamesProp &&
71
+ typeof classNamesProp === "object" &&
72
+ !Array.isArray(classNamesProp)
73
+ ) {
74
+ const o = classNamesProp as Partial<Record<string, string>>;
75
+ mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
76
+ mergedClassNames.body = o.body ? `${styles.body} ${o.body}` : styles.body;
77
+ mergedClassNames.inner = o.inner
78
+ ? `${styles.inner} ${o.inner}`
79
+ : styles.inner;
80
+ mergedClassNames.input = o.input
81
+ ? `${styles.input} ${o.input}`
82
+ : styles.input;
83
+ mergedClassNames.icon = o.icon ? `${styles.icon} ${o.icon}` : styles.icon;
84
+ mergedClassNames.labelWrapper = o.labelWrapper
85
+ ? `${styles.labelWrapper} ${o.labelWrapper}`
86
+ : styles.labelWrapper;
87
+ mergedClassNames.label = o.label
88
+ ? `${styles.label} ${o.label}`
89
+ : styles.label;
90
+ }
91
+
92
+ const classNameProp = restRecord.className as string | undefined;
93
+ const finalClass = classNameProp
94
+ ? `${styles.root} ${classNameProp}`
95
+ : styles.root;
96
+
97
+ // We omit Mantine's sizing/coloring so we rely strictly on variables from Checkbox.module.css
98
+ return (
99
+ <MantineCheckbox
100
+ ref={ref}
101
+ className={finalClass}
102
+ classNames={mergedClassNames}
103
+ disabled={readOnly || disabled}
104
+ {...(sanitizedProps as unknown as MantineCheckboxProps)}
105
+ />
106
+ );
107
+ },
108
+ ) as CheckboxComponent;
109
+
110
+ Checkbox.displayName = "Checkbox";
111
+ Checkbox.Group = CheckboxGroup;
@@ -0,0 +1,138 @@
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 GroupSideBySide: Story = {
50
+ args: {
51
+ formLayout: "side-by-side",
52
+ labelOptionalText: "Recommended",
53
+ labelWithEditIcon: true,
54
+ label: "Frontend Frameworks",
55
+ assistiveText:
56
+ "Select all libraries currently in use for this specific workspace configuration.",
57
+ },
58
+ render: function GroupSideBySideRender(args) {
59
+ const [value, setValue] = useState<string[]>(["react"]);
60
+ return (
61
+ <Checkbox.Group {...args} value={value} onChange={setValue}>
62
+ <Checkbox value="react" label="React (Standard Build)" />
63
+ <Checkbox
64
+ value="svelte"
65
+ 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."
66
+ />
67
+ <Checkbox value="vue" label="Vue Configuration Map" />
68
+ </Checkbox.Group>
69
+ );
70
+ },
71
+ };
72
+
73
+ export const GroupStacked: Story = {
74
+ args: {
75
+ formLayout: "stacked",
76
+ required: true,
77
+ label: "Execution Targets",
78
+ error: "You must select at least one deployment target to compile.",
79
+ },
80
+ render: function GroupStackedRender(args) {
81
+ const [value, setValue] = useState<string[]>(["react"]);
82
+ return (
83
+ <Checkbox.Group {...args} value={value} onChange={setValue}>
84
+ <Checkbox value="react" label="Browser Execution Context" />
85
+ <Checkbox
86
+ value="svelte"
87
+ label="Highly distributed Edge computing environments seamlessly bridging local runtime boundaries."
88
+ />
89
+ <Checkbox value="vue" label="Serverless Cloud Providers" />
90
+ </Checkbox.Group>
91
+ );
92
+ },
93
+ };
94
+
95
+ export const StaticReadOnlyGroup: Story = {
96
+ args: {
97
+ readOnly: true,
98
+ formLayout: "stacked",
99
+ required: true,
100
+ label: "Static ReadOnly Execution Locks",
101
+ assistiveText:
102
+ "This structure explicitly validates native component-level DOM preservation natively mapping lock bounds safely over interaction.",
103
+ },
104
+ render: function StaticReadOnlyGroupRender(args) {
105
+ const [value, setValue] = useState<string[]>(["disabledNode"]);
106
+ return (
107
+ <Checkbox.Group {...args} value={value} onChange={setValue}>
108
+ <Checkbox
109
+ value="disabledNode"
110
+ label="Structurally Checked Node natively"
111
+ />
112
+ <Checkbox
113
+ value="disabledNodeEmpty"
114
+ label="Unchecked Configuration Limit"
115
+ />
116
+ </Checkbox.Group>
117
+ );
118
+ },
119
+ };
120
+
121
+ export const EditableReadOnlyGroup: Story = {
122
+ args: {
123
+ readOnly: true,
124
+ formLayout: "side-by-side",
125
+ labelWithEditIcon: true,
126
+ label: "Interactively Editable ReadOnly Group",
127
+ assistiveText: "Users explicitly unlock DOM structures dynamically.",
128
+ },
129
+ render: function EditableReadOnlyGroupRender(args) {
130
+ const [value, setValue] = useState<string[]>(["activeState"]);
131
+ return (
132
+ <Checkbox.Group {...args} value={value} onChange={setValue}>
133
+ <Checkbox value="activeState" label="Locked active state" />
134
+ <Checkbox value="unlockedNode" label="Locked inactive map" />
135
+ </Checkbox.Group>
136
+ );
137
+ },
138
+ };
@@ -0,0 +1,140 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React, { forwardRef } from "react";
3
+ import {
4
+ Checkbox as MantineCheckbox,
5
+ type CheckboxGroupProps as MantineCheckboxGroupProps,
6
+ } from "@mantine/core";
7
+ import { type ReadOnlyControlProps } from "@recursica/adapter-common";
8
+ import {
9
+ filterStylingProps,
10
+ type RecursicaOverStyled,
11
+ } from "../../utils/filterStylingProps";
12
+ import {
13
+ FormControlWrapper,
14
+ type RecursicaFormControlWrapperProps,
15
+ } from "../FormControlWrapper/FormControlWrapper";
16
+ import styles from "./Checkbox.module.css";
17
+
18
+ export interface RecursicaCheckboxGroupProps
19
+ extends Omit<MantineCheckboxGroupProps, "size" | "labelProps">,
20
+ Omit<
21
+ RecursicaFormControlWrapperProps,
22
+ "controlMaxWidth" | "controlMinWidth"
23
+ >,
24
+ ReadOnlyControlProps {}
25
+
26
+ export type CheckboxGroupProps =
27
+ RecursicaOverStyled<RecursicaCheckboxGroupProps>;
28
+
29
+ export const CheckboxGroup = forwardRef<HTMLDivElement, CheckboxGroupProps>(
30
+ function CheckboxGroup(
31
+ {
32
+ overStyled = false,
33
+ formLayout = "stacked",
34
+
35
+ // Label Wrappers
36
+ labelSize,
37
+ labelAlignment,
38
+ labelOptionalText,
39
+ labelWithEditIcon,
40
+ onLabelEditClick,
41
+
42
+ // Base Mantine Extracted Attributes
43
+ label,
44
+ description,
45
+ assistiveText,
46
+ assistiveWithIcon,
47
+ error,
48
+ required,
49
+ withAsterisk,
50
+ id,
51
+ className,
52
+ style,
53
+ children,
54
+ readOnly,
55
+ readOnlyComponent,
56
+ ...rest
57
+ },
58
+ ref,
59
+ ) {
60
+ const sanitizedProps = filterStylingProps(rest, overStyled);
61
+ const restRecord = sanitizedProps as Record<string, unknown>;
62
+
63
+ // Delete prohibited sizing hooks from bypassing the variables
64
+ delete restRecord["size"];
65
+
66
+ if (readOnly && readOnlyComponent) {
67
+ return (
68
+ <FormControlWrapper
69
+ className={className}
70
+ style={style as React.CSSProperties}
71
+ controlMaxWidth="var(--recursica_ui-kit_components_checkbox-item_properties_max-width)"
72
+ controlMinWidth={undefined}
73
+ overStyled={overStyled as true}
74
+ labelElement="div"
75
+ formLayout={formLayout}
76
+ labelSize={labelSize}
77
+ labelAlignment={labelAlignment}
78
+ labelOptionalText={labelOptionalText}
79
+ labelWithEditIcon={labelWithEditIcon}
80
+ onLabelEditClick={onLabelEditClick}
81
+ label={label}
82
+ description={description}
83
+ assistiveText={assistiveText}
84
+ assistiveWithIcon={assistiveWithIcon}
85
+ error={error}
86
+ required={required}
87
+ withAsterisk={withAsterisk}
88
+ id={id}
89
+ >
90
+ {readOnlyComponent}
91
+ </FormControlWrapper>
92
+ );
93
+ }
94
+
95
+ return (
96
+ <FormControlWrapper
97
+ className={className}
98
+ style={style as React.CSSProperties}
99
+ controlMaxWidth="var(--recursica_ui-kit_components_checkbox-item_properties_max-width)"
100
+ controlMinWidth={undefined}
101
+ overStyled={overStyled as true}
102
+ labelElement="div" // Strictly override. ARIA grouping prohibits interactive checkboxes nested natively inside <label>.
103
+ formLayout={formLayout}
104
+ labelSize={labelSize}
105
+ labelAlignment={labelAlignment}
106
+ labelOptionalText={labelOptionalText}
107
+ labelWithEditIcon={labelWithEditIcon}
108
+ onLabelEditClick={onLabelEditClick}
109
+ label={label}
110
+ description={description}
111
+ assistiveText={assistiveText}
112
+ assistiveWithIcon={assistiveWithIcon}
113
+ error={error}
114
+ required={required}
115
+ withAsterisk={withAsterisk}
116
+ id={id}
117
+ >
118
+ <MantineCheckbox.Group
119
+ ref={ref}
120
+ /* Natively bind local disabled lock dynamically */
121
+ {...(sanitizedProps as unknown as MantineCheckboxGroupProps)}
122
+ >
123
+ <div className={styles.groupRoot} data-layout={formLayout}>
124
+ {/* If the exact group is read-only natively, we inject disabled hooks dynamically over children? Mantine handles native context directly! */}
125
+ {React.Children.map(children, (child) => {
126
+ if (React.isValidElement(child)) {
127
+ return React.cloneElement(child as React.ReactElement<any>, {
128
+ disabled: readOnly || child.props.disabled,
129
+ });
130
+ }
131
+ return child;
132
+ })}
133
+ </div>
134
+ </MantineCheckbox.Group>
135
+ </FormControlWrapper>
136
+ );
137
+ },
138
+ );
139
+
140
+ CheckboxGroup.displayName = "CheckboxGroup";