@recursica/mantine-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.
- package/.storybook/commonArgTypes.ts +148 -0
- package/CHANGELOG.md +69 -0
- package/dist/mantine-adapter.cjs +1 -1
- package/dist/mantine-adapter.cjs.map +1 -1
- package/dist/mantine-adapter.css +1 -1
- package/dist/mantine-adapter.js +1076 -266
- package/dist/mantine-adapter.js.map +1 -1
- package/dist/src/components/Accordion/Accordion.d.ts +13 -28
- package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +10 -0
- package/dist/src/components/Avatar/Avatar.d.ts +1 -1
- package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +6 -3
- package/dist/src/components/Button/Button.d.ts +1 -1
- package/dist/src/components/Card/Card.d.ts +38 -3
- package/dist/src/components/Checkbox/Checkbox.d.ts +11 -3
- package/dist/src/components/Checkbox/CheckboxGroup.d.ts +9 -0
- package/dist/src/components/Chip/Chip.d.ts +14 -2
- package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +18 -0
- package/dist/src/components/Label/Label.d.ts +21 -0
- package/dist/src/components/Label/index.d.ts +1 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +19 -2
- package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
- package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +22 -0
- package/dist/src/components/ReadOnlyField/index.d.ts +3 -0
- package/dist/src/components/Text/Text.d.ts +17 -0
- package/dist/src/components/TextField/TextField.d.ts +8 -2
- package/dist/src/components/Title/Title.d.ts +13 -0
- package/dist/src/components/index.d.ts +3 -1
- package/dist/src/utils/ColorSchemeWrapper.d.ts +3 -0
- package/dist/src/utils/filterStylingProps.d.ts +6 -2
- package/package.json +9 -5
- package/src/OverStyling.stories.tsx +174 -0
- package/src/Version.stories.tsx +56 -0
- package/src/components/Accordion/ACCORDION_IMPLEMENTATION_NOTES.md +48 -0
- package/src/components/Accordion/Accordion.module.css +261 -0
- package/src/components/Accordion/Accordion.stories.tsx +145 -0
- package/src/components/Accordion/Accordion.tsx +200 -0
- package/src/components/Accordion/index.ts +1 -0
- package/src/components/AssistiveElement/AssistiveElement.module.css +111 -0
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +73 -0
- package/src/components/AssistiveElement/AssistiveElement.tsx +91 -0
- package/src/components/Avatar/AVATAR_IMPLEMENTATION_NOTES.md +24 -0
- package/src/components/Avatar/Avatar.module.css +340 -0
- package/src/components/Avatar/Avatar.stories.tsx +102 -0
- package/src/components/Avatar/Avatar.tsx +100 -0
- package/src/components/Avatar/index.ts +1 -0
- package/src/components/Badge/BADGE_IMPLEMENTATION_NOTES.md +18 -0
- package/src/components/Badge/Badge.module.css +124 -0
- package/src/components/Badge/Badge.stories.tsx +77 -0
- package/src/components/Badge/Badge.tsx +66 -0
- package/src/components/Breadcrumb/BREADCRUMB_IMPLEMENTATION_NOTES.md +14 -0
- package/src/components/Breadcrumb/Breadcrumb.module.css +26 -0
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +77 -0
- package/src/components/Breadcrumb/Breadcrumb.tsx +64 -0
- package/src/components/Button/Button.module.css +275 -0
- package/src/components/Button/Button.stories.tsx +105 -0
- package/src/components/Button/Button.tsx +122 -0
- package/src/components/Button/IMPLEMENTATION_NOTES.md +45 -0
- package/src/components/Button/index.ts +1 -0
- package/src/components/Card/CARD_IMPLEMENTATION_NOTES.md +10 -0
- package/src/components/Card/Card.module.css +60 -0
- package/src/components/Card/Card.stories.tsx +141 -0
- package/src/components/Card/Card.tsx +176 -0
- package/src/components/Checkbox/CHECKBOX_IMPLEMENTATION_NOTES.md +25 -0
- package/src/components/Checkbox/Checkbox.module.css +204 -0
- package/src/components/Checkbox/Checkbox.stories.tsx +73 -0
- package/src/components/Checkbox/Checkbox.tsx +111 -0
- package/src/components/Checkbox/CheckboxGroup.stories.tsx +138 -0
- package/src/components/Checkbox/CheckboxGroup.tsx +140 -0
- package/src/components/Chip/CHIP_IMPLEMENTATION_NOTES.md +35 -0
- package/src/components/Chip/Chip.module.css +218 -0
- package/src/components/Chip/Chip.stories.tsx +115 -0
- package/src/components/Chip/Chip.tsx +143 -0
- package/src/components/DatePicker/DatePicker.stories.tsx +22 -0
- package/src/components/DatePicker/DatePicker.tsx +7 -0
- package/src/components/Dropdown/Dropdown.stories.tsx +22 -0
- package/src/components/Dropdown/Dropdown.tsx +7 -0
- package/src/components/FileInput/FileInput.stories.tsx +22 -0
- package/src/components/FileInput/FileInput.tsx +7 -0
- package/src/components/FileUpload/FileUpload.stories.tsx +22 -0
- package/src/components/FileUpload/FileUpload.tsx +7 -0
- package/src/components/FormControlWrapper/FORMCONTROLWRAPPER_IMPLEMENTATION_NOTES.md +32 -0
- package/src/components/FormControlWrapper/FormControlWrapper.module.css +46 -0
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +116 -0
- package/src/components/FormControlWrapper/FormControlWrapper.tsx +170 -0
- package/src/components/HoverCard/HoverCard.stories.tsx +17 -0
- package/src/components/HoverCard/HoverCard.tsx +7 -0
- package/src/components/Label/LABEL_IMPLEMENTATION_NOTES.md +48 -0
- package/src/components/Label/Label.module.css +177 -0
- package/src/components/Label/Label.stories.tsx +123 -0
- package/src/components/Label/Label.tsx +132 -0
- package/src/components/Label/index.ts +1 -0
- package/src/components/Link/Link.stories.tsx +17 -0
- package/src/components/Link/Link.tsx +7 -0
- package/src/components/Loader/Loader.stories.tsx +17 -0
- package/src/components/Loader/Loader.tsx +7 -0
- package/src/components/Menu/Menu.stories.tsx +17 -0
- package/src/components/Menu/Menu.tsx +7 -0
- package/src/components/Modal/Modal.stories.tsx +17 -0
- package/src/components/Modal/Modal.tsx +7 -0
- package/src/components/NumberInput/NumberInput.stories.tsx +22 -0
- package/src/components/NumberInput/NumberInput.tsx +7 -0
- package/src/components/Pagination/Pagination.stories.tsx +17 -0
- package/src/components/Pagination/Pagination.tsx +7 -0
- package/src/components/Panel/Panel.stories.tsx +17 -0
- package/src/components/Panel/Panel.tsx +7 -0
- package/src/components/Popover/Popover.stories.tsx +17 -0
- package/src/components/Popover/Popover.tsx +7 -0
- package/src/components/Radio/Radio.stories.tsx +22 -0
- package/src/components/Radio/Radio.tsx +7 -0
- package/src/components/ReadOnlyField/READ_ONLY_FIELD_IMPLEMENTATION_NOTES.md +15 -0
- package/src/components/ReadOnlyField/ReadOnlyField.module.css +49 -0
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +120 -0
- package/src/components/ReadOnlyField/ReadOnlyField.tsx +75 -0
- package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +46 -0
- package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +85 -0
- package/src/components/ReadOnlyField/index.ts +3 -0
- package/src/components/Search/Search.stories.tsx +17 -0
- package/src/components/Search/Search.tsx +7 -0
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +22 -0
- package/src/components/SegmentedControl/SegmentedControl.tsx +7 -0
- package/src/components/Slider/Slider.stories.tsx +22 -0
- package/src/components/Slider/Slider.tsx +7 -0
- package/src/components/Stepper/Stepper.stories.tsx +17 -0
- package/src/components/Stepper/Stepper.tsx +7 -0
- package/src/components/Switch/Switch.stories.tsx +22 -0
- package/src/components/Switch/Switch.tsx +7 -0
- package/src/components/Table/Table.stories.tsx +17 -0
- package/src/components/Table/Table.tsx +7 -0
- package/src/components/Tabs/Tabs.stories.tsx +17 -0
- package/src/components/Tabs/Tabs.tsx +7 -0
- package/src/components/Text/Text.stories.tsx +88 -0
- package/src/components/Text/Text.tsx +54 -0
- package/src/components/TextArea/TextArea.stories.tsx +22 -0
- package/src/components/TextArea/TextArea.tsx +7 -0
- package/src/components/TextField/TEXTFIELD_IMPLEMENTATION_NOTES.md +19 -0
- package/src/components/TextField/TextField.module.css +225 -0
- package/src/components/TextField/TextField.stories.tsx +162 -0
- package/src/components/TextField/TextField.tsx +138 -0
- package/src/components/TimePicker/TimePicker.stories.tsx +22 -0
- package/src/components/TimePicker/TimePicker.tsx +7 -0
- package/src/components/Timeline/Timeline.stories.tsx +17 -0
- package/src/components/Timeline/Timeline.tsx +7 -0
- package/src/components/Title/Title.stories.tsx +63 -0
- package/src/components/Title/Title.tsx +45 -0
- package/src/components/Toast/Toast.stories.tsx +17 -0
- package/src/components/Toast/Toast.tsx +7 -0
- package/src/components/Tooltip/Tooltip.stories.tsx +17 -0
- package/src/components/Tooltip/Tooltip.tsx +7 -0
- package/src/components/TransferList/TransferList.stories.tsx +17 -0
- package/src/components/TransferList/TransferList.tsx +7 -0
- package/src/components/index.ts +39 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +3 -0
- package/src/types/index.ts +9 -0
- package/src/types/mantine.d.ts +7 -0
- package/src/types/scss-modules.d.ts +9 -0
- package/src/utils/ColorSchemeWrapper.tsx +27 -0
- package/src/utils/copyToClipboard.ts +36 -0
- package/src/utils/filterStylingProps.ts +139 -0
- package/src/utils/index.ts +1 -0
|
@@ -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";
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Chip Implementation Notes
|
|
2
|
+
|
|
3
|
+
## Architecture decisions
|
|
4
|
+
|
|
5
|
+
### Mantine DOM Structure & Label Overrides
|
|
6
|
+
|
|
7
|
+
Mantine's `<Chip>` behaves like an input element (`radio` or `checkbox`). Under the hood, it renders:
|
|
8
|
+
|
|
9
|
+
1. `.mantine-Chip-root` (wrapper)
|
|
10
|
+
2. `input` (hidden visual structure)
|
|
11
|
+
3. `.mantine-Chip-label` (The actual visible button-like pill)
|
|
12
|
+
|
|
13
|
+
Because the `.label` is the primary visual surface and handles Mantine's built-in `:hover` and active states, we direct our Recursica styling natively to `.label`.
|
|
14
|
+
|
|
15
|
+
### Icon and Remove Implementations
|
|
16
|
+
|
|
17
|
+
The Recursica specifications for the Chip component define `--chip-icon` and `--chip-close` which map to leading and trailing states.
|
|
18
|
+
To achieve this without breaking Mantine's `Chip` input architecture, we wrapped the internal `children` using a standard `span` DOM strategy:
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
<span className={styles.innerWrapper}>
|
|
22
|
+
{icon}
|
|
23
|
+
<span className={styles.children}>{children}</span>
|
|
24
|
+
{onRemove}
|
|
25
|
+
</span>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Accessibility of Remove Action
|
|
29
|
+
|
|
30
|
+
Because the Chip fundamentally functions as a `<label>` linked to an `<input>`, placing a raw interactive element like `<button>` directly inside the standard Chip sub-tree violates nested interactive element ARIA constraints in strict validators.
|
|
31
|
+
To accommodate this, the visual "close" icon uses a `<span>` element configured with `role="button"` and `tabIndex={0}` to hook into standard keyboard activations without triggering generic nested `<form>` conflicts native to Mantine's baseline constraints.
|
|
32
|
+
|
|
33
|
+
### Removing Sizing Properties
|
|
34
|
+
|
|
35
|
+
During implementation, the parsed Figma design tokens natively exported specific height/padding vectors dynamically (e.g., `--recursica_ui-kit_components_chip_properties_icon-size`) rather than explicit string variants (`sm`, `md`, `lg`). Therefore, we omitted `size` conceptually from the `RecursicaChipProps` wrapper to lock down size evaluation natively against the active layer variables.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/* HARDCODED VALUES
|
|
2
|
+
border-style: solid; (Baseline reset)
|
|
3
|
+
box-sizing: border-box; (Baseline reset)
|
|
4
|
+
display: inline-flex; (Baseline reset)
|
|
5
|
+
align-items: center; (Baseline reset)
|
|
6
|
+
outline: none; (Accessible fallback)
|
|
7
|
+
padding: 0; (Override mantine default)
|
|
8
|
+
background: transparent; (Override mantine default)
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
.root {
|
|
12
|
+
/* Set preset layout variables to override mantine defaults safely */
|
|
13
|
+
box-sizing: border-box;
|
|
14
|
+
display: inline-flex;
|
|
15
|
+
|
|
16
|
+
/* Token properties */
|
|
17
|
+
--chip-height: max-content; /* Sizing is driven by padding and line height */
|
|
18
|
+
--chip-bg: var(
|
|
19
|
+
--recursica_ui-kit_components_chip_variants_styles_unselected_properties_colors_background
|
|
20
|
+
);
|
|
21
|
+
--chip-border: var(
|
|
22
|
+
--recursica_ui-kit_components_chip_variants_styles_unselected_properties_colors_border-color
|
|
23
|
+
);
|
|
24
|
+
--chip-text: var(
|
|
25
|
+
--recursica_ui-kit_components_chip_variants_styles_unselected_properties_colors_text
|
|
26
|
+
);
|
|
27
|
+
--chip-icon: var(
|
|
28
|
+
--recursica_ui-kit_components_chip_variants_styles_unselected_properties_colors_leading-icon-color
|
|
29
|
+
);
|
|
30
|
+
--chip-close: var(
|
|
31
|
+
--recursica_ui-kit_components_chip_variants_styles_unselected_properties_colors_close-icon-color
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
border-radius: var(
|
|
35
|
+
--recursica_ui-kit_components_chip_properties_border-radius
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* We target the mantine label directly because that is the visible container in Mantine's Chip */
|
|
40
|
+
.label {
|
|
41
|
+
box-sizing: border-box;
|
|
42
|
+
display: inline-flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
justify-content: center;
|
|
45
|
+
|
|
46
|
+
/* Reset mantine styles */
|
|
47
|
+
background: transparent !important; /* HARDCODE: Mantine forces background styling on label */
|
|
48
|
+
border-style: solid;
|
|
49
|
+
border-width: var(--recursica_ui-kit_components_chip_properties_border-size);
|
|
50
|
+
padding: var(--recursica_ui-kit_components_chip_properties_vertical-padding)
|
|
51
|
+
var(--recursica_ui-kit_components_chip_properties_horizontal-padding) !important;
|
|
52
|
+
|
|
53
|
+
/* Typography */
|
|
54
|
+
font-family: var(
|
|
55
|
+
--recursica_ui-kit_components_chip_properties_text_font-family
|
|
56
|
+
);
|
|
57
|
+
font-size: var(--recursica_ui-kit_components_chip_properties_text_font-size);
|
|
58
|
+
font-weight: var(
|
|
59
|
+
--recursica_ui-kit_components_chip_properties_text_font-weight
|
|
60
|
+
);
|
|
61
|
+
letter-spacing: var(
|
|
62
|
+
--recursica_ui-kit_components_chip_properties_text_letter-spacing
|
|
63
|
+
);
|
|
64
|
+
line-height: var(
|
|
65
|
+
--recursica_ui-kit_components_chip_properties_text_line-height
|
|
66
|
+
);
|
|
67
|
+
text-decoration: var(
|
|
68
|
+
--recursica_ui-kit_components_chip_properties_text_text-decoration
|
|
69
|
+
);
|
|
70
|
+
text-transform: var(
|
|
71
|
+
--recursica_ui-kit_components_chip_properties_text_text-transform
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
/* Token evaluation */
|
|
75
|
+
background-color: var(--chip-bg) !important;
|
|
76
|
+
border-color: var(--chip-border) !important;
|
|
77
|
+
color: var(--chip-text) !important;
|
|
78
|
+
min-width: var(--recursica_ui-kit_components_chip_properties_min-width);
|
|
79
|
+
max-width: var(--recursica_ui-kit_components_chip_properties_max-width);
|
|
80
|
+
box-shadow: var(
|
|
81
|
+
--recursica_ui-kit_components_chip_properties_elevation,
|
|
82
|
+
none
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
transition: all 0.2s ease;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Default (Unselected) hover/active interactions (Mantine overrides) */
|
|
89
|
+
.label[data-checked] {
|
|
90
|
+
--chip-bg: var(
|
|
91
|
+
--recursica_ui-kit_components_chip_variants_styles_selected_properties_colors_background
|
|
92
|
+
);
|
|
93
|
+
--chip-border: var(
|
|
94
|
+
--recursica_ui-kit_components_chip_variants_styles_selected_properties_colors_border-color
|
|
95
|
+
);
|
|
96
|
+
--chip-text: var(
|
|
97
|
+
--recursica_ui-kit_components_chip_variants_styles_selected_properties_colors_text
|
|
98
|
+
);
|
|
99
|
+
--chip-icon: var(
|
|
100
|
+
--recursica_ui-kit_components_chip_variants_styles_selected_properties_colors_selected-icon-color
|
|
101
|
+
);
|
|
102
|
+
--chip-close: var(
|
|
103
|
+
--recursica_ui-kit_components_chip_variants_styles_selected_properties_colors_close-icon-color
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.root[data-error] .label {
|
|
108
|
+
--chip-bg: var(
|
|
109
|
+
--recursica_ui-kit_components_chip_variants_styles_error_properties_colors_background
|
|
110
|
+
);
|
|
111
|
+
--chip-border: var(
|
|
112
|
+
--recursica_ui-kit_components_chip_variants_styles_error_properties_colors_border-color
|
|
113
|
+
);
|
|
114
|
+
--chip-text: var(
|
|
115
|
+
--recursica_ui-kit_components_chip_variants_styles_error_properties_colors_text
|
|
116
|
+
);
|
|
117
|
+
--chip-icon: var(
|
|
118
|
+
--recursica_ui-kit_components_chip_variants_styles_error_properties_colors_icon
|
|
119
|
+
);
|
|
120
|
+
--chip-close: var(
|
|
121
|
+
--recursica_ui-kit_components_chip_variants_styles_error_properties_colors_close-icon-color
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.root[data-error] .label[data-checked] {
|
|
126
|
+
--chip-bg: var(
|
|
127
|
+
--recursica_ui-kit_components_chip_variants_styles_error-selected_properties_colors_background
|
|
128
|
+
);
|
|
129
|
+
--chip-border: var(
|
|
130
|
+
--recursica_ui-kit_components_chip_variants_styles_error-selected_properties_colors_border-color
|
|
131
|
+
);
|
|
132
|
+
--chip-text: var(
|
|
133
|
+
--recursica_ui-kit_components_chip_variants_styles_error-selected_properties_colors_text
|
|
134
|
+
);
|
|
135
|
+
--chip-icon: var(
|
|
136
|
+
--recursica_ui-kit_components_chip_variants_styles_error-selected_properties_colors_selected-icon-color
|
|
137
|
+
);
|
|
138
|
+
--chip-close: var(
|
|
139
|
+
--recursica_ui-kit_components_chip_variants_styles_error-selected_properties_colors_close-icon-color
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
.root[data-disabled] .label {
|
|
144
|
+
opacity: var(--recursica_brand_states_disabled, 0.5);
|
|
145
|
+
cursor: not-allowed;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Override Mantine's built-in check icon colors and size if we keep it */
|
|
149
|
+
.mantineIconWrapper {
|
|
150
|
+
color: var(--chip-icon) !important;
|
|
151
|
+
width: var(
|
|
152
|
+
--recursica_ui-kit_components_chip_properties_icon-size
|
|
153
|
+
) !important;
|
|
154
|
+
height: var(
|
|
155
|
+
--recursica_ui-kit_components_chip_properties_icon-size
|
|
156
|
+
) !important;
|
|
157
|
+
margin-right: var(
|
|
158
|
+
--recursica_ui-kit_components_chip_properties_icon-text-gap
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
.innerWrapper {
|
|
163
|
+
display: inline-flex;
|
|
164
|
+
align-items: center;
|
|
165
|
+
gap: var(--recursica_ui-kit_components_chip_properties_icon-text-gap);
|
|
166
|
+
width: 100%;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.children {
|
|
170
|
+
flex-grow: 1;
|
|
171
|
+
text-overflow: ellipsis;
|
|
172
|
+
overflow: hidden;
|
|
173
|
+
white-space: nowrap;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.leadingIcon {
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
justify-content: center;
|
|
180
|
+
color: var(--chip-icon);
|
|
181
|
+
width: var(--recursica_ui-kit_components_chip_properties_icon-size);
|
|
182
|
+
height: var(--recursica_ui-kit_components_chip_properties_icon-size);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.leadingIcon svg {
|
|
186
|
+
width: 100%;
|
|
187
|
+
height: 100%;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.removeIcon {
|
|
191
|
+
display: inline-flex;
|
|
192
|
+
align-items: center;
|
|
193
|
+
justify-content: center;
|
|
194
|
+
color: var(--chip-close);
|
|
195
|
+
width: var(--recursica_ui-kit_components_chip_properties_close-icon-size);
|
|
196
|
+
height: var(--recursica_ui-kit_components_chip_properties_close-icon-size);
|
|
197
|
+
cursor: pointer;
|
|
198
|
+
border-radius: 50%; /* HARDCODE: Make focus ring rounded */
|
|
199
|
+
outline: none;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.removeIcon:focus-visible {
|
|
203
|
+
box-shadow: 0 0 0 2px var(--chip-border); /* HARDCODE: Focus indication */
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
.removeIcon svg {
|
|
207
|
+
width: 100%;
|
|
208
|
+
height: 100%;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
.root[data-icon-only] .label {
|
|
212
|
+
padding-left: var(
|
|
213
|
+
--recursica_ui-kit_components_chip_properties_vertical-padding
|
|
214
|
+
) !important;
|
|
215
|
+
padding-right: var(
|
|
216
|
+
--recursica_ui-kit_components_chip_properties_vertical-padding
|
|
217
|
+
) !important;
|
|
218
|
+
}
|