@recursica/mui-adapter 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/dist/mui-adapter.cjs +74 -1
- package/dist/mui-adapter.cjs.map +1 -1
- package/dist/mui-adapter.css +1 -1
- package/dist/mui-adapter.js +6652 -204
- package/dist/mui-adapter.js.map +1 -1
- package/dist/src/components/Accordion/Accordion.d.ts +63 -2
- package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +9 -2
- package/dist/src/components/Autocomplete/Autocomplete.d.ts +16 -2
- package/dist/src/components/Avatar/Avatar.d.ts +14 -2
- package/dist/src/components/Badge/Badge.d.ts +13 -3
- package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +10 -3
- package/dist/src/components/Button/Button.d.ts +1 -1
- package/dist/src/components/Card/Card.d.ts +40 -3
- package/dist/src/components/Checkbox/Checkbox.d.ts +15 -2
- package/dist/src/components/Checkbox/CheckboxGroup.d.ts +16 -0
- package/dist/src/components/Checkbox/index.d.ts +5 -1
- package/dist/src/components/Chip/Chip.d.ts +18 -2
- package/dist/src/components/Dropdown/Dropdown.d.ts +23 -3
- package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +10 -2
- package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +24 -2
- package/dist/src/components/HoverCard/HoverCard.d.ts +55 -3
- package/dist/src/components/Label/Label.d.ts +13 -2
- package/dist/src/components/Link/Link.d.ts +1 -1
- package/dist/src/components/Menu/Menu.d.ts +99 -3
- package/dist/src/components/Modal/Modal.d.ts +36 -2
- package/dist/src/components/NumberInput/NumberInput.d.ts +14 -2
- package/dist/src/components/Pagination/Pagination.d.ts +44 -2
- package/dist/src/components/Pagination/Pagination.icons.d.ts +8 -0
- package/dist/src/components/Panel/Panel.d.ts +65 -3
- package/dist/src/components/Radio/Radio.d.ts +13 -3
- package/dist/src/components/Radio/RadioGroup.d.ts +13 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +7 -2
- package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
- package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
- package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +28 -0
- package/dist/src/components/ReadOnlyField/index.d.ts +1 -0
- package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +31 -3
- package/dist/src/components/Slider/Slider.d.ts +28 -2
- package/dist/src/components/Stepper/Stepper.d.ts +21 -2
- package/dist/src/components/Switch/Switch.d.ts +10 -3
- package/dist/src/components/Switch/SwitchGroup.d.ts +13 -0
- package/dist/src/components/Tabs/Tabs.d.ts +35 -3
- package/dist/src/components/Text/Text.d.ts +1 -1
- package/dist/src/components/TextArea/TextArea.d.ts +18 -2
- package/dist/src/components/TextField/TextField.d.ts +16 -2
- package/dist/src/components/Timeline/Timeline.d.ts +14 -2
- package/dist/src/components/Timeline/TimelineItem.d.ts +29 -0
- package/dist/src/components/Title/Title.d.ts +1 -1
- package/dist/src/components/Toast/Toast.d.ts +25 -2
- package/dist/src/components/Tooltip/Tooltip.d.ts +42 -3
- package/dist/src/components/Typography/Typography.d.ts +1 -1
- package/package.json +9 -2
- package/src/components/Accordion/Accordion.module.css +295 -56
- package/src/components/Accordion/Accordion.stories.tsx +149 -21
- package/src/components/Accordion/Accordion.tsx +264 -5
- package/src/components/AssistiveElement/AssistiveElement.module.css +111 -20
- package/src/components/AssistiveElement/AssistiveElement.stories.tsx +76 -19
- package/src/components/AssistiveElement/AssistiveElement.tsx +85 -5
- package/src/components/Autocomplete/Autocomplete.module.css +320 -42
- package/src/components/Autocomplete/Autocomplete.stories.tsx +180 -19
- package/src/components/Autocomplete/Autocomplete.tsx +199 -6
- package/src/components/Avatar/Avatar.module.css +343 -73
- package/src/components/Avatar/Avatar.stories.tsx +94 -22
- package/src/components/Avatar/Avatar.tsx +110 -6
- package/src/components/Badge/Badge.module.css +127 -29
- package/src/components/Badge/Badge.stories.tsx +65 -22
- package/src/components/Badge/Badge.tsx +70 -6
- package/src/components/Breadcrumb/Breadcrumb.module.css +25 -5
- package/src/components/Breadcrumb/Breadcrumb.stories.tsx +57 -22
- package/src/components/Breadcrumb/Breadcrumb.tsx +62 -6
- package/src/components/Button/Button.module.css +3 -3
- package/src/components/Card/Card.module.css +118 -38
- package/src/components/Card/Card.stories.tsx +117 -19
- package/src/components/Card/Card.tsx +179 -5
- package/src/components/Checkbox/Checkbox.module.css +232 -39
- package/src/components/Checkbox/Checkbox.stories.tsx +81 -19
- package/src/components/Checkbox/Checkbox.tsx +146 -5
- package/src/components/Checkbox/CheckboxGroup.tsx +117 -0
- package/src/components/Checkbox/index.ts +6 -1
- package/src/components/Chip/Chip.module.css +228 -43
- package/src/components/Chip/Chip.stories.tsx +101 -21
- package/src/components/Chip/Chip.tsx +138 -6
- package/src/components/Dropdown/Dropdown.module.css +291 -41
- package/src/components/Dropdown/Dropdown.stories.tsx +80 -21
- package/src/components/Dropdown/Dropdown.tsx +182 -6
- package/src/components/FormControlLayout/FormControlLayout.module.css +92 -0
- package/src/components/FormControlLayout/FormControlLayout.stories.tsx +74 -19
- package/src/components/FormControlLayout/FormControlLayout.tsx +58 -4
- package/src/components/FormControlWrapper/FormControlWrapper.module.css +96 -0
- package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +122 -19
- package/src/components/FormControlWrapper/FormControlWrapper.tsx +145 -6
- package/src/components/HoverCard/HoverCard.module.css +90 -22
- package/src/components/HoverCard/HoverCard.stories.tsx +158 -21
- package/src/components/HoverCard/HoverCard.tsx +150 -5
- package/src/components/Label/Label.module.css +136 -35
- package/src/components/Label/Label.stories.tsx +98 -17
- package/src/components/Label/Label.tsx +88 -5
- package/src/components/Menu/Menu.module.css +285 -51
- package/src/components/Menu/Menu.stories.tsx +307 -21
- package/src/components/Menu/Menu.tsx +318 -5
- package/src/components/Modal/Modal.module.css +171 -35
- package/src/components/Modal/Modal.stories.tsx +66 -22
- package/src/components/Modal/Modal.tsx +216 -4
- package/src/components/NumberInput/NumberInput.module.css +276 -42
- package/src/components/NumberInput/NumberInput.stories.tsx +83 -20
- package/src/components/NumberInput/NumberInput.tsx +148 -6
- package/src/components/Pagination/Pagination.icons.tsx +66 -0
- package/src/components/Pagination/Pagination.module.css +246 -20
- package/src/components/Pagination/Pagination.stories.tsx +42 -21
- package/src/components/Pagination/Pagination.tsx +193 -5
- package/src/components/Panel/Panel.module.css +206 -38
- package/src/components/Panel/Panel.stories.tsx +214 -22
- package/src/components/Panel/Panel.tsx +153 -5
- package/src/components/Radio/Radio.module.css +220 -37
- package/src/components/Radio/Radio.stories.tsx +77 -19
- package/src/components/Radio/Radio.tsx +178 -5
- package/src/components/Radio/RadioGroup.tsx +111 -0
- package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyField.module.css +53 -15
- package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +175 -16
- package/src/components/ReadOnlyField/ReadOnlyField.tsx +109 -5
- package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
- package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
- package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
- package/src/components/ReadOnlyField/index.ts +1 -0
- package/src/components/SegmentedControl/SegmentedControl.module.css +203 -47
- package/src/components/SegmentedControl/SegmentedControl.stories.tsx +102 -20
- package/src/components/SegmentedControl/SegmentedControl.tsx +110 -6
- package/src/components/Slider/Slider.module.css +542 -86
- package/src/components/Slider/Slider.stories.tsx +157 -19
- package/src/components/Slider/Slider.tsx +282 -5
- package/src/components/Stepper/Stepper.module.css +410 -71
- package/src/components/Stepper/Stepper.stories.tsx +132 -19
- package/src/components/Stepper/Stepper.tsx +145 -6
- package/src/components/Switch/Switch.module.css +238 -39
- package/src/components/Switch/Switch.stories.tsx +98 -19
- package/src/components/Switch/Switch.tsx +153 -6
- package/src/components/Switch/SwitchGroup.tsx +111 -0
- package/src/components/Tabs/Tabs.module.css +422 -79
- package/src/components/Tabs/Tabs.stories.tsx +138 -19
- package/src/components/Tabs/Tabs.tsx +103 -6
- package/src/components/TextArea/TextArea.module.css +155 -32
- package/src/components/TextArea/TextArea.stories.tsx +80 -21
- package/src/components/TextArea/TextArea.tsx +156 -6
- package/src/components/TextField/TextField.module.css +261 -42
- package/src/components/TextField/TextField.stories.tsx +168 -15
- package/src/components/TextField/TextField.tsx +171 -6
- package/src/components/Timeline/Timeline.module.css +365 -76
- package/src/components/Timeline/Timeline.stories.tsx +114 -21
- package/src/components/Timeline/Timeline.tsx +80 -5
- package/src/components/Timeline/TimelineItem.tsx +101 -0
- package/src/components/Toast/Toast.module.css +166 -28
- package/src/components/Toast/Toast.stories.tsx +54 -22
- package/src/components/Toast/Toast.tsx +90 -5
- package/src/components/Tooltip/Tooltip.module.css +89 -24
- package/src/components/Tooltip/Tooltip.stories.tsx +152 -22
- package/src/components/Tooltip/Tooltip.tsx +114 -5
|
@@ -1,8 +1,220 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
Modal as MuiModal,
|
|
4
|
+
type ModalProps as MuiModalProps,
|
|
5
|
+
} from "@mui/material";
|
|
6
|
+
import {
|
|
7
|
+
filterStylingProps,
|
|
8
|
+
type RecursicaOverStyled,
|
|
9
|
+
} from "../../utils/filterStylingProps";
|
|
10
|
+
import styles from "./Modal.module.css";
|
|
3
11
|
|
|
4
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Properties for the strictly-tokenized Modal component.
|
|
14
|
+
* Native Mui abstract properties like `size`, `radius`, and `shadow` have been stripped out
|
|
15
|
+
* because they directly conflict with the non-negotiable pixel boundaries defined in the Recursica UI Kit.
|
|
16
|
+
*/
|
|
17
|
+
export type ModalProps = RecursicaOverStyled<
|
|
18
|
+
Omit<MuiModalProps, "size" | "radius" | "shadow">
|
|
19
|
+
>;
|
|
5
20
|
|
|
6
|
-
|
|
7
|
-
|
|
21
|
+
/**
|
|
22
|
+
* The `Modal` component displays a window overlaid on the primary viewport.
|
|
23
|
+
*
|
|
24
|
+
* **Recursica Abstract:**
|
|
25
|
+
* This component acts as a strict structural wrapper around Mui's `<Modal>`.
|
|
26
|
+
* It automatically enforces Figma UI Kit geometries (`max-width: 960px`, `min-width: 304px`)
|
|
27
|
+
* and handles internal body scrolling explicitly via CSS module overrides.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```tsx
|
|
31
|
+
* <Modal opened={opened} onClose={close} title="Hello World">
|
|
32
|
+
* Modal Content
|
|
33
|
+
* </Modal>
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
const ModalInner = React.forwardRef<HTMLDivElement, ModalProps>(function Modal(
|
|
37
|
+
{
|
|
38
|
+
overStyled = false,
|
|
39
|
+
children,
|
|
40
|
+
title,
|
|
41
|
+
withCloseButton = true,
|
|
42
|
+
overlayProps,
|
|
43
|
+
withOverlay = true,
|
|
44
|
+
closeButtonProps,
|
|
45
|
+
...rest
|
|
46
|
+
},
|
|
47
|
+
ref,
|
|
48
|
+
) {
|
|
49
|
+
const sanitizedProps = filterStylingProps(rest, overStyled);
|
|
50
|
+
|
|
51
|
+
const mergedClassNames: Partial<Record<string, string>> = {
|
|
52
|
+
root: styles.root,
|
|
53
|
+
inner: styles.inner,
|
|
54
|
+
content: styles.content,
|
|
55
|
+
header: styles.header,
|
|
56
|
+
title: styles.title,
|
|
57
|
+
close: styles.close,
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const classNamesProp = (sanitizedProps as Record<string, unknown>).classNames;
|
|
61
|
+
if (
|
|
62
|
+
classNamesProp &&
|
|
63
|
+
typeof classNamesProp === "object" &&
|
|
64
|
+
!Array.isArray(classNamesProp)
|
|
65
|
+
) {
|
|
66
|
+
const o = classNamesProp as Record<string, string>;
|
|
67
|
+
Object.keys(o).forEach((key) => {
|
|
68
|
+
if (mergedClassNames[key]) {
|
|
69
|
+
mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
|
|
70
|
+
} else {
|
|
71
|
+
mergedClassNames[key] = o[key];
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div
|
|
78
|
+
ref={ref}
|
|
79
|
+
classes={mergedClassNames}
|
|
80
|
+
{...(sanitizedProps as unknown as Omit<
|
|
81
|
+
MuiModalProps,
|
|
82
|
+
"size" | "radius" | "shadow"
|
|
83
|
+
>)}
|
|
84
|
+
>
|
|
85
|
+
{withOverlay && <div {...overlayProps} />}
|
|
86
|
+
<div>
|
|
87
|
+
{(title || withCloseButton) && (
|
|
88
|
+
<div>
|
|
89
|
+
{title && <div>{title}</div>}
|
|
90
|
+
{withCloseButton && <div {...closeButtonProps} />}
|
|
91
|
+
</div>
|
|
92
|
+
)}
|
|
93
|
+
<ModalBody>{children}</ModalBody>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
ModalInner.displayName = "Modal";
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* The `Modal.Footer` provides a perfectly padded container for modal actions.
|
|
103
|
+
*
|
|
104
|
+
* **Recursica Abstract:**
|
|
105
|
+
* By default, this container aligns its children to the right (`justify-content: flex-end`)
|
|
106
|
+
* and applies the standard Figma button-gap spacing.
|
|
107
|
+
*
|
|
108
|
+
* > [!IMPORTANT]
|
|
109
|
+
* > **Button Hierarchy:** Recursica design language explicitly requires that the
|
|
110
|
+
* > primary action button MUST be the right-most element, with the secondary
|
|
111
|
+
* > (outline variant) action placed immediately to the left of it.
|
|
112
|
+
*/
|
|
113
|
+
const ModalFooter = React.forwardRef<
|
|
114
|
+
HTMLDivElement,
|
|
115
|
+
React.ComponentPropsWithoutRef<"div">
|
|
116
|
+
>(function ModalFooter({ className, ...rest }, ref) {
|
|
117
|
+
return (
|
|
118
|
+
<div
|
|
119
|
+
ref={ref}
|
|
120
|
+
className={`${styles.footer} ${className || ""}`}
|
|
121
|
+
{...rest}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
ModalFooter.displayName = "Modal.Footer";
|
|
126
|
+
|
|
127
|
+
const ModalBody = React.forwardRef<
|
|
128
|
+
HTMLDivElement,
|
|
129
|
+
React.ComponentPropsWithoutRef<typeof MuiModal.Body>
|
|
130
|
+
>(function ModalBody({ className, onScroll, children, ...rest }, ref) {
|
|
131
|
+
const internalRef = React.useRef<HTMLDivElement>(null);
|
|
132
|
+
const [scrolledTop, setScrolledTop] = React.useState(false);
|
|
133
|
+
const [scrolledBottom, setScrolledBottom] = React.useState(false);
|
|
134
|
+
|
|
135
|
+
const checkScroll = React.useCallback(() => {
|
|
136
|
+
if (internalRef.current) {
|
|
137
|
+
const { scrollTop, scrollHeight, clientHeight } = internalRef.current;
|
|
138
|
+
setScrolledTop(scrollTop > 0);
|
|
139
|
+
setScrolledBottom(Math.ceil(scrollTop + clientHeight) < scrollHeight);
|
|
140
|
+
}
|
|
141
|
+
}, []);
|
|
142
|
+
|
|
143
|
+
// Re-check scroll on mount and window resize
|
|
144
|
+
React.useEffect(() => {
|
|
145
|
+
checkScroll();
|
|
146
|
+
window.addEventListener("resize", checkScroll);
|
|
147
|
+
return () => window.removeEventListener("resize", checkScroll);
|
|
148
|
+
}, [checkScroll, children]);
|
|
149
|
+
|
|
150
|
+
const handleScroll = (e: React.UIEvent<HTMLDivElement>) => {
|
|
151
|
+
checkScroll();
|
|
152
|
+
onScroll?.(e);
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Intercept children to pull Modal.Footer out of the scrolling container
|
|
156
|
+
let footer: React.ReactNode = null;
|
|
157
|
+
const bodyChildren: React.ReactNode[] = [];
|
|
158
|
+
|
|
159
|
+
React.Children.forEach(children, (child) => {
|
|
160
|
+
if (
|
|
161
|
+
React.isValidElement(child) &&
|
|
162
|
+
(child.type === ModalFooter ||
|
|
163
|
+
(child.type as React.ComponentType)?.displayName === "Modal.Footer")
|
|
164
|
+
) {
|
|
165
|
+
footer = child;
|
|
166
|
+
} else {
|
|
167
|
+
bodyChildren.push(child);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div
|
|
173
|
+
{...rest}
|
|
174
|
+
ref={(node) => {
|
|
175
|
+
if (typeof ref === "function") ref(node);
|
|
176
|
+
else if (ref)
|
|
177
|
+
(ref as React.MutableRefObject<HTMLDivElement | null>).current = node;
|
|
178
|
+
}}
|
|
179
|
+
className={`${styles.bodyWrapper} ${className || ""}`}
|
|
180
|
+
>
|
|
181
|
+
<div
|
|
182
|
+
ref={internalRef}
|
|
183
|
+
onScroll={handleScroll}
|
|
184
|
+
data-scrolled-top={scrolledTop || undefined}
|
|
185
|
+
data-scrolled-bottom={scrolledBottom || undefined}
|
|
186
|
+
className={styles.scrollArea}
|
|
187
|
+
>
|
|
188
|
+
{bodyChildren}
|
|
189
|
+
</div>
|
|
190
|
+
{footer}
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
});
|
|
194
|
+
ModalBody.displayName = "Modal.Body";
|
|
195
|
+
|
|
196
|
+
interface ModalComponent
|
|
197
|
+
extends React.ForwardRefExoticComponent<
|
|
198
|
+
ModalProps & React.RefAttributes<HTMLDivElement>
|
|
199
|
+
> {
|
|
200
|
+
Root: typeof MuiModal.Root;
|
|
201
|
+
Overlay: typeof MuiModal.Overlay;
|
|
202
|
+
Content: typeof MuiModal.Content;
|
|
203
|
+
Header: typeof MuiModal.Header;
|
|
204
|
+
Title: typeof MuiModal.Title;
|
|
205
|
+
CloseButton: typeof MuiModal.CloseButton;
|
|
206
|
+
Body: typeof ModalBody;
|
|
207
|
+
Footer: typeof ModalFooter;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export const Modal = ModalInner as ModalComponent & {
|
|
211
|
+
Footer: typeof ModalFooter;
|
|
8
212
|
};
|
|
213
|
+
Modal.Root = MuiModal.Root;
|
|
214
|
+
Modal.Overlay = MuiModal.Overlay;
|
|
215
|
+
Modal.Content = MuiModal.Content;
|
|
216
|
+
Modal.Header = MuiModal.Header;
|
|
217
|
+
Modal.Title = MuiModal.Title;
|
|
218
|
+
Modal.CloseButton = MuiModal.CloseButton;
|
|
219
|
+
Modal.Body = ModalBody;
|
|
220
|
+
Modal.Footer = ModalFooter;
|
|
@@ -1,47 +1,281 @@
|
|
|
1
1
|
/* EXEMPTIONS:
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_border-radius */
|
|
6
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_horizontal-padding */
|
|
7
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_icon-size */
|
|
8
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_icon-text-gap */
|
|
9
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_max-width */
|
|
10
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_min-height */
|
|
11
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_min-width */
|
|
12
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_placeholder-opacity */
|
|
13
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_text_font-family */
|
|
14
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_text_font-size */
|
|
15
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_text_font-style */
|
|
16
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_text_font-weight */
|
|
17
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_text_letter-spacing */
|
|
18
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_text_line-height */
|
|
19
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_text_text-decoration */
|
|
20
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_text_text-transform */
|
|
21
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_properties_vertical-padding */
|
|
22
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_layouts_side-by-side_properties_top-bottom-margin */
|
|
23
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_layouts_stacked_properties_top-bottom-margin */
|
|
2
|
+
- state-specific border-size variables are ignored because the input box uses a uniform static boundary to prevent
|
|
3
|
+
unexpected layout shifts or flickering when switching between active, focused, or error states. */
|
|
24
4
|
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_default_properties_border-size */
|
|
25
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_default_properties_colors_background */
|
|
26
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_default_properties_colors_border-color */
|
|
27
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_default_properties_colors_leading-icon */
|
|
28
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_default_properties_colors_text */
|
|
29
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_default_properties_colors_trailing-icon */
|
|
30
5
|
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_disabled_properties_border-size */
|
|
31
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_disabled_properties_colors_background */
|
|
32
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_disabled_properties_colors_border-color */
|
|
33
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_disabled_properties_colors_leading-icon */
|
|
34
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_disabled_properties_colors_text */
|
|
35
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_disabled_properties_colors_trailing-icon */
|
|
36
6
|
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_error_properties_border-size */
|
|
37
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_error_properties_colors_background */
|
|
38
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_error_properties_colors_border-color */
|
|
39
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_error_properties_colors_leading-icon */
|
|
40
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_error_properties_colors_text */
|
|
41
|
-
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_error_properties_colors_trailing-icon */
|
|
42
7
|
/* recursica-ignore: --recursica_ui-kit_components_number-input_variants_states_focus_properties_border-size */
|
|
43
|
-
|
|
44
|
-
/*
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
8
|
+
|
|
9
|
+
/* LAYOUT SPACING OVERRIDES:
|
|
10
|
+
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens. */
|
|
11
|
+
.layoutOverride {
|
|
12
|
+
--form-control-margin-bottom: var(
|
|
13
|
+
--recursica_ui-kit_components_number-input_variants_layouts_stacked_properties_top-bottom-margin
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.layoutOverride[data-form-layout="side-by-side"] {
|
|
18
|
+
--form-control-margin-bottom: var(
|
|
19
|
+
--recursica_ui-kit_components_number-input_variants_layouts_side-by-side_properties_top-bottom-margin
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/* HARDCODED VALUES:
|
|
24
|
+
- border-width: 1px. Native geometric boundary for the input box.
|
|
25
|
+
- border-style: solid. Native structural rendering rule.
|
|
26
|
+
- outline: none. Bypassing browser focus rings to rely strictly on Recursica focus states natively.
|
|
27
|
+
- flex/layout: display: flex on the root wrapper safely encapsulating internal section rendering.
|
|
28
|
+
- Controls: Hardcoded to bypass Mantine's default borders and backgrounds on the increment/decrement arrows.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
.root {
|
|
32
|
+
display: flex;
|
|
33
|
+
position: relative;
|
|
34
|
+
width: 100%;
|
|
35
|
+
min-height: var(
|
|
36
|
+
--recursica_ui-kit_components_number-input_properties_min-height
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
/* Override Mantine native Input variables governing section spacing padding mathematics safely */
|
|
40
|
+
--input-left-section-size: calc(
|
|
41
|
+
var(
|
|
42
|
+
--recursica_ui-kit_components_number-input_properties_horizontal-padding
|
|
43
|
+
) +
|
|
44
|
+
var(--recursica_ui-kit_components_number-input_properties_icon-size) +
|
|
45
|
+
var(--recursica_ui-kit_components_number-input_properties_icon-text-gap)
|
|
46
|
+
);
|
|
47
|
+
--input-right-section-size: calc(
|
|
48
|
+
var(
|
|
49
|
+
--recursica_ui-kit_components_number-input_properties_horizontal-padding
|
|
50
|
+
) +
|
|
51
|
+
var(--recursica_ui-kit_components_number-input_properties_icon-size) +
|
|
52
|
+
var(--recursica_ui-kit_components_number-input_properties_icon-text-gap)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.input {
|
|
57
|
+
/* Structural Overrides */
|
|
58
|
+
width: 100%;
|
|
59
|
+
box-sizing: border-box;
|
|
60
|
+
margin: 0;
|
|
61
|
+
|
|
62
|
+
/* Box Geometry */
|
|
63
|
+
min-height: var(
|
|
64
|
+
--recursica_ui-kit_components_number-input_properties_min-height
|
|
65
|
+
);
|
|
66
|
+
padding-top: var(
|
|
67
|
+
--recursica_ui-kit_components_number-input_properties_vertical-padding
|
|
68
|
+
);
|
|
69
|
+
padding-bottom: var(
|
|
70
|
+
--recursica_ui-kit_components_number-input_properties_vertical-padding
|
|
71
|
+
);
|
|
72
|
+
padding-left: var(
|
|
73
|
+
--recursica_ui-kit_components_number-input_properties_horizontal-padding
|
|
74
|
+
);
|
|
75
|
+
padding-right: var(
|
|
76
|
+
--recursica_ui-kit_components_number-input_properties_horizontal-padding
|
|
77
|
+
);
|
|
78
|
+
border-radius: var(
|
|
79
|
+
--recursica_ui-kit_components_number-input_properties_border-radius
|
|
80
|
+
);
|
|
81
|
+
border-width: 1px;
|
|
82
|
+
border-style: solid;
|
|
83
|
+
|
|
84
|
+
/* Strict Typography Unification */
|
|
85
|
+
font-family: var(
|
|
86
|
+
--recursica_ui-kit_components_number-input_properties_text_font-family
|
|
87
|
+
);
|
|
88
|
+
font-size: var(
|
|
89
|
+
--recursica_ui-kit_components_number-input_properties_text_font-size
|
|
90
|
+
);
|
|
91
|
+
font-style: var(
|
|
92
|
+
--recursica_ui-kit_components_number-input_properties_text_font-style
|
|
93
|
+
);
|
|
94
|
+
font-weight: var(
|
|
95
|
+
--recursica_ui-kit_components_number-input_properties_text_font-weight
|
|
96
|
+
);
|
|
97
|
+
letter-spacing: var(
|
|
98
|
+
--recursica_ui-kit_components_number-input_properties_text_letter-spacing
|
|
99
|
+
);
|
|
100
|
+
line-height: var(
|
|
101
|
+
--recursica_ui-kit_components_number-input_properties_text_line-height
|
|
102
|
+
);
|
|
103
|
+
text-decoration: var(
|
|
104
|
+
--recursica_ui-kit_components_number-input_properties_text_text-decoration
|
|
105
|
+
);
|
|
106
|
+
text-transform: var(
|
|
107
|
+
--recursica_ui-kit_components_number-input_properties_text_text-transform
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
/* Base State Default Execution */
|
|
111
|
+
background-color: var(
|
|
112
|
+
--recursica_ui-kit_components_number-input_variants_states_default_properties_colors_background
|
|
113
|
+
);
|
|
114
|
+
border-color: var(
|
|
115
|
+
--recursica_ui-kit_components_number-input_variants_states_default_properties_colors_border-color
|
|
116
|
+
);
|
|
117
|
+
color: var(
|
|
118
|
+
--recursica_ui-kit_components_number-input_variants_states_default_properties_colors_text
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
outline: none;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.input::placeholder {
|
|
125
|
+
opacity: var(
|
|
126
|
+
--recursica_ui-kit_components_number-input_properties_placeholder-opacity
|
|
127
|
+
);
|
|
128
|
+
color: inherit;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Dynamic Padding Overrides */
|
|
132
|
+
.root[data-with-left-section] .input {
|
|
133
|
+
padding-left: var(--input-left-section-size);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
.root[data-with-right-section] .input {
|
|
137
|
+
padding-right: var(--input-right-section-size);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Flexible End-Caps (Icons, actions) */
|
|
141
|
+
.section {
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
height: 100%;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.section[data-position="left"] {
|
|
148
|
+
justify-content: flex-start;
|
|
149
|
+
padding-left: var(
|
|
150
|
+
--recursica_ui-kit_components_number-input_properties_horizontal-padding
|
|
151
|
+
);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.section[data-position="right"] {
|
|
155
|
+
justify-content: flex-end;
|
|
156
|
+
padding-right: var(
|
|
157
|
+
--recursica_ui-kit_components_number-input_properties_horizontal-padding
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.section :global(svg) {
|
|
162
|
+
width: var(--recursica_ui-kit_components_number-input_properties_icon-size);
|
|
163
|
+
height: var(--recursica_ui-kit_components_number-input_properties_icon-size);
|
|
164
|
+
color: var(
|
|
165
|
+
--recursica_ui-kit_components_number-input_variants_states_default_properties_colors_leading-icon
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
.section[data-position="right"] :global(svg) {
|
|
170
|
+
color: var(
|
|
171
|
+
--recursica_ui-kit_components_number-input_variants_states_default_properties_colors_trailing-icon
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* -------------------------------------
|
|
176
|
+
CONTROLS (Mantine Specific Up/Down Arrows)
|
|
177
|
+
-------------------------------------- */
|
|
178
|
+
|
|
179
|
+
.controls {
|
|
180
|
+
/* Adjust right padding so controls do not overlap borders */
|
|
181
|
+
padding-right: var(
|
|
182
|
+
--recursica_ui-kit_components_number-input_properties_horizontal-padding
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
.control {
|
|
187
|
+
/* Override Mantine borders for the inner controls */
|
|
188
|
+
border: none;
|
|
189
|
+
background: transparent;
|
|
190
|
+
color: var(
|
|
191
|
+
--recursica_ui-kit_components_number-input_variants_states_default_properties_colors_trailing-icon
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.control:hover {
|
|
196
|
+
background: transparent;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* -------------------------------------
|
|
200
|
+
STATE CASCADE ARCHITECTURE
|
|
201
|
+
-------------------------------------- */
|
|
202
|
+
|
|
203
|
+
/* Focus State Mapping (Triggered internally via browser pseudo-pseudo-class) */
|
|
204
|
+
.input:focus-within,
|
|
205
|
+
.input:focus {
|
|
206
|
+
border-color: var(
|
|
207
|
+
--recursica_ui-kit_components_number-input_variants_states_focus_properties_colors_border-color
|
|
208
|
+
);
|
|
209
|
+
background-color: var(
|
|
210
|
+
--recursica_ui-kit_components_number-input_variants_states_focus_properties_colors_background
|
|
211
|
+
);
|
|
212
|
+
color: var(
|
|
213
|
+
--recursica_ui-kit_components_number-input_variants_states_focus_properties_colors_text
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.root:focus-within .section[data-position="left"] :global(svg) {
|
|
218
|
+
color: var(
|
|
219
|
+
--recursica_ui-kit_components_number-input_variants_states_focus_properties_colors_leading-icon
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
.root:focus-within .section[data-position="right"] :global(svg),
|
|
224
|
+
.root:focus-within .control {
|
|
225
|
+
color: var(
|
|
226
|
+
--recursica_ui-kit_components_number-input_variants_states_focus_properties_colors_trailing-icon
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* Error State Mapping (Propagated strictly down from the wrapper DOM context) */
|
|
231
|
+
.root[data-error] .input {
|
|
232
|
+
border-color: var(
|
|
233
|
+
--recursica_ui-kit_components_number-input_variants_states_error_properties_colors_border-color
|
|
234
|
+
) !important;
|
|
235
|
+
background-color: var(
|
|
236
|
+
--recursica_ui-kit_components_number-input_variants_states_error_properties_colors_background
|
|
237
|
+
) !important;
|
|
238
|
+
color: var(
|
|
239
|
+
--recursica_ui-kit_components_number-input_variants_states_error_properties_colors_text
|
|
240
|
+
) !important;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
.root[data-error] .section[data-position="left"] :global(svg) {
|
|
244
|
+
color: var(
|
|
245
|
+
--recursica_ui-kit_components_number-input_variants_states_error_properties_colors_leading-icon
|
|
246
|
+
) !important;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
.root[data-error] .section[data-position="right"] :global(svg),
|
|
250
|
+
.root[data-error] .control {
|
|
251
|
+
color: var(
|
|
252
|
+
--recursica_ui-kit_components_number-input_variants_states_error_properties_colors_trailing-icon
|
|
253
|
+
) !important;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/* Disabled State Mapping (Propagated strictly down from the wrapper DOM context) */
|
|
257
|
+
.root[data-disabled] .input {
|
|
258
|
+
border-color: var(
|
|
259
|
+
--recursica_ui-kit_components_number-input_variants_states_disabled_properties_colors_border-color
|
|
260
|
+
) !important;
|
|
261
|
+
background-color: var(
|
|
262
|
+
--recursica_ui-kit_components_number-input_variants_states_disabled_properties_colors_background
|
|
263
|
+
) !important;
|
|
264
|
+
color: var(
|
|
265
|
+
--recursica_ui-kit_components_number-input_variants_states_disabled_properties_colors_text
|
|
266
|
+
) !important;
|
|
267
|
+
cursor: not-allowed;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
.root[data-disabled] .section[data-position="left"] :global(svg) {
|
|
271
|
+
color: var(
|
|
272
|
+
--recursica_ui-kit_components_number-input_variants_states_disabled_properties_colors_leading-icon
|
|
273
|
+
) !important;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.root[data-disabled] .section[data-position="right"] :global(svg),
|
|
277
|
+
.root[data-disabled] .control {
|
|
278
|
+
color: var(
|
|
279
|
+
--recursica_ui-kit_components_number-input_variants_states_disabled_properties_colors_trailing-icon
|
|
280
|
+
) !important;
|
|
281
|
+
}
|
|
@@ -1,35 +1,98 @@
|
|
|
1
1
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
2
|
import { NumberInput } from "./NumberInput";
|
|
3
|
-
import { ComingSoon } from "@recursica/storybook-template";
|
|
4
3
|
|
|
5
4
|
const meta: Meta<typeof NumberInput> = {
|
|
6
|
-
title: "UI-Kit
|
|
5
|
+
title: "UI-Kit/NumberInput",
|
|
7
6
|
component: NumberInput,
|
|
8
7
|
tags: ["autodocs"],
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
"href",
|
|
21
|
-
"onClick",
|
|
22
|
-
"onChange",
|
|
23
|
-
"value",
|
|
24
|
-
"checked",
|
|
25
|
-
],
|
|
8
|
+
argTypes: {
|
|
9
|
+
label: { control: "text" },
|
|
10
|
+
assistiveText: { control: "text" },
|
|
11
|
+
disabled: { control: "boolean" },
|
|
12
|
+
error: { control: "boolean" },
|
|
13
|
+
readOnly: { control: "boolean" },
|
|
14
|
+
required: { control: "boolean" },
|
|
15
|
+
hideControls: { control: "boolean" },
|
|
16
|
+
formLayout: {
|
|
17
|
+
control: "select",
|
|
18
|
+
options: ["stacked", "side-by-side"],
|
|
26
19
|
},
|
|
20
|
+
labelSize: {
|
|
21
|
+
control: "select",
|
|
22
|
+
options: ["small", "default", "large"],
|
|
23
|
+
},
|
|
24
|
+
checked: { table: { disable: true } },
|
|
25
|
+
defaultChecked: { table: { disable: true } },
|
|
26
|
+
assistiveWithIcon: { table: { disable: true } },
|
|
27
|
+
labelOptionalText: { table: { disable: true } },
|
|
28
|
+
labelWithEditIcon: { table: { disable: true } },
|
|
29
|
+
onLabelEditClick: { table: { disable: true } },
|
|
30
|
+
emptyValueComponent: { table: { disable: true } },
|
|
27
31
|
},
|
|
28
32
|
};
|
|
29
33
|
|
|
30
34
|
export default meta;
|
|
35
|
+
|
|
31
36
|
type Story = StoryObj<typeof NumberInput>;
|
|
32
37
|
|
|
33
38
|
export const Default: Story = {
|
|
34
|
-
|
|
39
|
+
args: {
|
|
40
|
+
label: "Amount",
|
|
41
|
+
placeholder: "Enter an amount",
|
|
42
|
+
assistiveText: "Must be greater than 0",
|
|
43
|
+
defaultValue: 10,
|
|
44
|
+
min: 0,
|
|
45
|
+
max: 100,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const SideBySideLayout: Story = {
|
|
50
|
+
args: {
|
|
51
|
+
...Default.args,
|
|
52
|
+
formLayout: "side-by-side",
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export const States: Story = {
|
|
57
|
+
render: () => (
|
|
58
|
+
<div
|
|
59
|
+
style={{
|
|
60
|
+
display: "flex",
|
|
61
|
+
flexDirection: "column",
|
|
62
|
+
gap: "1rem",
|
|
63
|
+
maxWidth: 400,
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
<NumberInput label="Default" placeholder="Enter a number" />
|
|
67
|
+
<NumberInput label="Disabled" placeholder="Disabled input" disabled />
|
|
68
|
+
<NumberInput label="Error" placeholder="Error state" error />
|
|
69
|
+
<NumberInput label="Read Only" value={42} readOnly />
|
|
70
|
+
<NumberInput label="Required" required />
|
|
71
|
+
</div>
|
|
72
|
+
),
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
export const WithLeftIcon: Story = {
|
|
76
|
+
args: {
|
|
77
|
+
label: "Price",
|
|
78
|
+
placeholder: "0.00",
|
|
79
|
+
leftSection: <span>$</span>,
|
|
80
|
+
},
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const WithRightIcon: Story = {
|
|
84
|
+
args: {
|
|
85
|
+
label: "Percentage",
|
|
86
|
+
placeholder: "0",
|
|
87
|
+
rightSection: <span>%</span>,
|
|
88
|
+
hideControls: true, // Typically hiding controls if rightSection is occupied
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
export const HiddenControls: Story = {
|
|
93
|
+
args: {
|
|
94
|
+
label: "Zip Code",
|
|
95
|
+
placeholder: "Enter zip code",
|
|
96
|
+
hideControls: true,
|
|
97
|
+
},
|
|
35
98
|
};
|