@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.
Files changed (159) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/mui-adapter.cjs +74 -1
  3. package/dist/mui-adapter.cjs.map +1 -1
  4. package/dist/mui-adapter.css +1 -1
  5. package/dist/mui-adapter.js +6652 -204
  6. package/dist/mui-adapter.js.map +1 -1
  7. package/dist/src/components/Accordion/Accordion.d.ts +63 -2
  8. package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +9 -2
  9. package/dist/src/components/Autocomplete/Autocomplete.d.ts +16 -2
  10. package/dist/src/components/Avatar/Avatar.d.ts +14 -2
  11. package/dist/src/components/Badge/Badge.d.ts +13 -3
  12. package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +10 -3
  13. package/dist/src/components/Button/Button.d.ts +1 -1
  14. package/dist/src/components/Card/Card.d.ts +40 -3
  15. package/dist/src/components/Checkbox/Checkbox.d.ts +15 -2
  16. package/dist/src/components/Checkbox/CheckboxGroup.d.ts +16 -0
  17. package/dist/src/components/Checkbox/index.d.ts +5 -1
  18. package/dist/src/components/Chip/Chip.d.ts +18 -2
  19. package/dist/src/components/Dropdown/Dropdown.d.ts +23 -3
  20. package/dist/src/components/FormControlLayout/FormControlLayout.d.ts +10 -2
  21. package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +24 -2
  22. package/dist/src/components/HoverCard/HoverCard.d.ts +55 -3
  23. package/dist/src/components/Label/Label.d.ts +13 -2
  24. package/dist/src/components/Link/Link.d.ts +1 -1
  25. package/dist/src/components/Menu/Menu.d.ts +99 -3
  26. package/dist/src/components/Modal/Modal.d.ts +36 -2
  27. package/dist/src/components/NumberInput/NumberInput.d.ts +14 -2
  28. package/dist/src/components/Pagination/Pagination.d.ts +44 -2
  29. package/dist/src/components/Pagination/Pagination.icons.d.ts +8 -0
  30. package/dist/src/components/Panel/Panel.d.ts +65 -3
  31. package/dist/src/components/Radio/Radio.d.ts +13 -3
  32. package/dist/src/components/Radio/RadioGroup.d.ts +13 -0
  33. package/dist/src/components/ReadOnlyField/ReadOnlyBooleanField.d.ts +5 -0
  34. package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +7 -2
  35. package/dist/src/components/ReadOnlyField/ReadOnlySwitchField.d.ts +5 -0
  36. package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
  37. package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +28 -0
  38. package/dist/src/components/ReadOnlyField/index.d.ts +1 -0
  39. package/dist/src/components/SegmentedControl/SegmentedControl.d.ts +31 -3
  40. package/dist/src/components/Slider/Slider.d.ts +28 -2
  41. package/dist/src/components/Stepper/Stepper.d.ts +21 -2
  42. package/dist/src/components/Switch/Switch.d.ts +10 -3
  43. package/dist/src/components/Switch/SwitchGroup.d.ts +13 -0
  44. package/dist/src/components/Tabs/Tabs.d.ts +35 -3
  45. package/dist/src/components/Text/Text.d.ts +1 -1
  46. package/dist/src/components/TextArea/TextArea.d.ts +18 -2
  47. package/dist/src/components/TextField/TextField.d.ts +16 -2
  48. package/dist/src/components/Timeline/Timeline.d.ts +14 -2
  49. package/dist/src/components/Timeline/TimelineItem.d.ts +29 -0
  50. package/dist/src/components/Title/Title.d.ts +1 -1
  51. package/dist/src/components/Toast/Toast.d.ts +25 -2
  52. package/dist/src/components/Tooltip/Tooltip.d.ts +42 -3
  53. package/dist/src/components/Typography/Typography.d.ts +1 -1
  54. package/package.json +9 -2
  55. package/src/components/Accordion/Accordion.module.css +295 -56
  56. package/src/components/Accordion/Accordion.stories.tsx +149 -21
  57. package/src/components/Accordion/Accordion.tsx +264 -5
  58. package/src/components/AssistiveElement/AssistiveElement.module.css +111 -20
  59. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +76 -19
  60. package/src/components/AssistiveElement/AssistiveElement.tsx +85 -5
  61. package/src/components/Autocomplete/Autocomplete.module.css +320 -42
  62. package/src/components/Autocomplete/Autocomplete.stories.tsx +180 -19
  63. package/src/components/Autocomplete/Autocomplete.tsx +199 -6
  64. package/src/components/Avatar/Avatar.module.css +343 -73
  65. package/src/components/Avatar/Avatar.stories.tsx +94 -22
  66. package/src/components/Avatar/Avatar.tsx +110 -6
  67. package/src/components/Badge/Badge.module.css +127 -29
  68. package/src/components/Badge/Badge.stories.tsx +65 -22
  69. package/src/components/Badge/Badge.tsx +70 -6
  70. package/src/components/Breadcrumb/Breadcrumb.module.css +25 -5
  71. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +57 -22
  72. package/src/components/Breadcrumb/Breadcrumb.tsx +62 -6
  73. package/src/components/Button/Button.module.css +3 -3
  74. package/src/components/Card/Card.module.css +118 -38
  75. package/src/components/Card/Card.stories.tsx +117 -19
  76. package/src/components/Card/Card.tsx +179 -5
  77. package/src/components/Checkbox/Checkbox.module.css +232 -39
  78. package/src/components/Checkbox/Checkbox.stories.tsx +81 -19
  79. package/src/components/Checkbox/Checkbox.tsx +146 -5
  80. package/src/components/Checkbox/CheckboxGroup.tsx +117 -0
  81. package/src/components/Checkbox/index.ts +6 -1
  82. package/src/components/Chip/Chip.module.css +228 -43
  83. package/src/components/Chip/Chip.stories.tsx +101 -21
  84. package/src/components/Chip/Chip.tsx +138 -6
  85. package/src/components/Dropdown/Dropdown.module.css +291 -41
  86. package/src/components/Dropdown/Dropdown.stories.tsx +80 -21
  87. package/src/components/Dropdown/Dropdown.tsx +182 -6
  88. package/src/components/FormControlLayout/FormControlLayout.module.css +92 -0
  89. package/src/components/FormControlLayout/FormControlLayout.stories.tsx +74 -19
  90. package/src/components/FormControlLayout/FormControlLayout.tsx +58 -4
  91. package/src/components/FormControlWrapper/FormControlWrapper.module.css +96 -0
  92. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +122 -19
  93. package/src/components/FormControlWrapper/FormControlWrapper.tsx +145 -6
  94. package/src/components/HoverCard/HoverCard.module.css +90 -22
  95. package/src/components/HoverCard/HoverCard.stories.tsx +158 -21
  96. package/src/components/HoverCard/HoverCard.tsx +150 -5
  97. package/src/components/Label/Label.module.css +136 -35
  98. package/src/components/Label/Label.stories.tsx +98 -17
  99. package/src/components/Label/Label.tsx +88 -5
  100. package/src/components/Menu/Menu.module.css +285 -51
  101. package/src/components/Menu/Menu.stories.tsx +307 -21
  102. package/src/components/Menu/Menu.tsx +318 -5
  103. package/src/components/Modal/Modal.module.css +171 -35
  104. package/src/components/Modal/Modal.stories.tsx +66 -22
  105. package/src/components/Modal/Modal.tsx +216 -4
  106. package/src/components/NumberInput/NumberInput.module.css +276 -42
  107. package/src/components/NumberInput/NumberInput.stories.tsx +83 -20
  108. package/src/components/NumberInput/NumberInput.tsx +148 -6
  109. package/src/components/Pagination/Pagination.icons.tsx +66 -0
  110. package/src/components/Pagination/Pagination.module.css +246 -20
  111. package/src/components/Pagination/Pagination.stories.tsx +42 -21
  112. package/src/components/Pagination/Pagination.tsx +193 -5
  113. package/src/components/Panel/Panel.module.css +206 -38
  114. package/src/components/Panel/Panel.stories.tsx +214 -22
  115. package/src/components/Panel/Panel.tsx +153 -5
  116. package/src/components/Radio/Radio.module.css +220 -37
  117. package/src/components/Radio/Radio.stories.tsx +77 -19
  118. package/src/components/Radio/Radio.tsx +178 -5
  119. package/src/components/Radio/RadioGroup.tsx +111 -0
  120. package/src/components/ReadOnlyField/ReadOnlyBooleanField.tsx +21 -0
  121. package/src/components/ReadOnlyField/ReadOnlyField.module.css +53 -15
  122. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +175 -16
  123. package/src/components/ReadOnlyField/ReadOnlyField.tsx +109 -5
  124. package/src/components/ReadOnlyField/ReadOnlySwitchField.tsx +21 -0
  125. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +48 -0
  126. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +93 -0
  127. package/src/components/ReadOnlyField/index.ts +1 -0
  128. package/src/components/SegmentedControl/SegmentedControl.module.css +203 -47
  129. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +102 -20
  130. package/src/components/SegmentedControl/SegmentedControl.tsx +110 -6
  131. package/src/components/Slider/Slider.module.css +542 -86
  132. package/src/components/Slider/Slider.stories.tsx +157 -19
  133. package/src/components/Slider/Slider.tsx +282 -5
  134. package/src/components/Stepper/Stepper.module.css +410 -71
  135. package/src/components/Stepper/Stepper.stories.tsx +132 -19
  136. package/src/components/Stepper/Stepper.tsx +145 -6
  137. package/src/components/Switch/Switch.module.css +238 -39
  138. package/src/components/Switch/Switch.stories.tsx +98 -19
  139. package/src/components/Switch/Switch.tsx +153 -6
  140. package/src/components/Switch/SwitchGroup.tsx +111 -0
  141. package/src/components/Tabs/Tabs.module.css +422 -79
  142. package/src/components/Tabs/Tabs.stories.tsx +138 -19
  143. package/src/components/Tabs/Tabs.tsx +103 -6
  144. package/src/components/TextArea/TextArea.module.css +155 -32
  145. package/src/components/TextArea/TextArea.stories.tsx +80 -21
  146. package/src/components/TextArea/TextArea.tsx +156 -6
  147. package/src/components/TextField/TextField.module.css +261 -42
  148. package/src/components/TextField/TextField.stories.tsx +168 -15
  149. package/src/components/TextField/TextField.tsx +171 -6
  150. package/src/components/Timeline/Timeline.module.css +365 -76
  151. package/src/components/Timeline/Timeline.stories.tsx +114 -21
  152. package/src/components/Timeline/Timeline.tsx +80 -5
  153. package/src/components/Timeline/TimelineItem.tsx +101 -0
  154. package/src/components/Toast/Toast.module.css +166 -28
  155. package/src/components/Toast/Toast.stories.tsx +54 -22
  156. package/src/components/Toast/Toast.tsx +90 -5
  157. package/src/components/Tooltip/Tooltip.module.css +89 -24
  158. package/src/components/Tooltip/Tooltip.stories.tsx +152 -22
  159. package/src/components/Tooltip/Tooltip.tsx +114 -5
@@ -1,8 +1,153 @@
1
- import React from "react";
2
- import "./HoverCard.module.css";
1
+ import {
2
+ Popover as MuiHoverCard,
3
+ type PopoverProps as MuiHoverCardProps,
4
+ } from "@mui/material";
5
+ import {
6
+ filterStylingProps,
7
+ type RecursicaOverStyled,
8
+ } from "../../utils/filterStylingProps";
9
+ import styles from "./HoverCard.module.css";
3
10
 
4
- export type HoverCardProps = React.HTMLAttributes<HTMLDivElement>;
11
+ // ============================================================
12
+ // HOVERCARD ROOT
13
+ // ============================================================
5
14
 
6
- export const HoverCard: React.FC<HoverCardProps> = (props) => {
7
- return <div {...props}>HoverCard</div>;
15
+ /**
16
+ * Recursica-specific props for HoverCard.
17
+ */
18
+ export interface RecursicaHoverCardProps {
19
+ /**
20
+ * Whether to display a beak (arrow) pointing from the dropdown to the target.
21
+ * This is the Recursica equivalent of Mui's `withArrow`.
22
+ * When both `withBeak` and `withArrow` are provided, `withBeak` takes precedence.
23
+ */
24
+ withBeak?: boolean;
25
+ }
26
+
27
+ /**
28
+ * Recursica HoverCard component wrapping Mui's composable HoverCard.
29
+ *
30
+ * Displays a dropdown panel when the user hovers over a target element.
31
+ * Uses the composable dot-notation pattern:
32
+ * ```tsx
33
+ * <HoverCard withBeak>
34
+ * <HoverCard.Target>
35
+ * <Button>Hover me</Button>
36
+ * </HoverCard.Target>
37
+ * <HoverCard.Dropdown>
38
+ * Content displayed on hover
39
+ * </HoverCard.Dropdown>
40
+ * </HoverCard>
41
+ * ```
42
+ */
43
+ export type HoverCardProps = RecursicaOverStyled<
44
+ MuiHoverCardProps & RecursicaHoverCardProps
45
+ >;
46
+
47
+ const HoverCardBase = function HoverCard({
48
+ overStyled = false,
49
+ withBeak = true,
50
+ ...rest
51
+ }: HoverCardProps) {
52
+ const sanitizedProps = filterStylingProps(rest, overStyled);
53
+
54
+ // Bind CSS module classes to Mui's internal classNames API
55
+ const mergedClassNames: Partial<Record<string, string>> = {
56
+ dropdown: styles.dropdown,
57
+ arrow: styles.arrow,
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
+ // arrowSize must be a JS number prop — Mui uses it for inline width/height
77
+ // and positioning offset (-arrowSize/2) calculations that cannot be CSS-driven.
78
+ // Default to 16 to match the Recursica beak-size token (16px).
79
+ const arrowSize =
80
+ ((sanitizedProps as Record<string, unknown>).arrowSize as
81
+ | number
82
+ | undefined) ?? 16;
83
+
84
+ // Resolve withBeak (Recursica) vs withArrow (Mui).
85
+ // withBeak takes precedence when both are provided.
86
+ const withArrow = (sanitizedProps as Record<string, unknown>).withArrow as
87
+ | boolean
88
+ | undefined;
89
+ const resolvedWithArrow = withBeak ?? withArrow;
90
+
91
+ return (
92
+ <MuiHoverCard
93
+ position="top" /* Recursica default; Mui defaults to "bottom" */
94
+ arrowSize={arrowSize}
95
+ withArrow={resolvedWithArrow}
96
+ classes={mergedClassNames}
97
+ {...(sanitizedProps as unknown as MuiHoverCardProps)}
98
+ />
99
+ );
100
+ };
101
+ HoverCardBase.displayName = "HoverCard";
102
+
103
+ // ============================================================
104
+ // HOVERCARD TARGET
105
+ // ============================================================
106
+
107
+ /**
108
+ * Wrapper for the element that triggers the hover card.
109
+ * Requires a single child element that supports ref forwarding.
110
+ */
111
+ export type HoverCardTargetProps = any;
112
+
113
+ const HoverCardTarget = function HoverCardTarget(props: HoverCardTargetProps) {
114
+ return <div {...props} />;
115
+ };
116
+ HoverCardTarget.displayName = "HoverCardTarget";
117
+
118
+ // ============================================================
119
+ // HOVERCARD DROPDOWN
120
+ // ============================================================
121
+
122
+ /** The dropdown panel displayed when hovering over the target. */
123
+ export type HoverCardDropdownProps = RecursicaOverStyled<any>;
124
+
125
+ const HoverCardDropdown = function HoverCardDropdown({
126
+ overStyled = false,
127
+ ...rest
128
+ }: HoverCardDropdownProps) {
129
+ const sanitizedProps = filterStylingProps(rest, overStyled);
130
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
131
+ .className as string | undefined;
132
+
133
+ return (
134
+ <div
135
+ className={classNameProp}
136
+ {...(sanitizedProps as unknown as MuiHoverCardDropdownProps)}
137
+ />
138
+ );
8
139
  };
140
+ HoverCardDropdown.displayName = "HoverCardDropdown";
141
+
142
+ // ============================================================
143
+ // DOT NOTATION EXPORT
144
+ // ============================================================
145
+
146
+ type HoverCardComponent = typeof HoverCardBase & {
147
+ Target: typeof HoverCardTarget;
148
+ Dropdown: typeof HoverCardDropdown;
149
+ };
150
+
151
+ export const HoverCard = HoverCardBase as HoverCardComponent;
152
+ HoverCard.Target = HoverCardTarget;
153
+ HoverCard.Dropdown = HoverCardDropdown;
@@ -1,35 +1,136 @@
1
- /* EXEMPTIONS:
2
- This component is a placeholder stub. Once fully implemented, its specific
3
- tokens will be fully wired up to direct styles. */
4
-
5
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_colors_asterisk */
6
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_colors_text */
7
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_edit-icon-gap */
8
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_label-optional-text-gap */
9
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_label-text_font-family */
10
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_label-text_font-size */
11
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_label-text_font-style */
12
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_label-text_font-weight */
13
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_label-text_letter-spacing */
14
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_label-text_line-height */
15
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_label-text_text-decoration */
16
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_label-text_text-transform */
17
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_optional-text-opacity */
18
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_optional-text_font-family */
19
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_optional-text_font-size */
20
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_optional-text_font-style */
21
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_optional-text_font-weight */
22
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_optional-text_letter-spacing */
23
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_optional-text_line-height */
24
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_optional-text_text-decoration */
25
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_optional-text_text-transform */
26
- /* recursica-ignore: --recursica_ui-kit_components_label_properties_required-indicator-gap */
27
- /* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_side-by-side_properties_gutter */
28
- /* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_side-by-side_properties_min-height */
29
- /* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_side-by-side_properties_vertical-padding */
30
- /* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_side-by-side_variants_sizes_default_properties_width */
31
- /* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_side-by-side_variants_sizes_small_properties_width */
32
- /* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_stacked_properties_bottom-padding */
33
- /* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_stacked_properties_min-height */
34
- /* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_stacked_variants_sizes_default_properties_width */
35
- /* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_stacked_variants_sizes_small_properties_width */
1
+ .root {
2
+ /* Prevent default MUI absolute positioning logic */
3
+ position: relative;
4
+ transform: none;
5
+ pointer-events: auto;
6
+ white-space: normal;
7
+
8
+ /* Reset MUI structural overrides */
9
+ margin: 0;
10
+ padding: 0;
11
+ display: flex;
12
+ width: 100%;
13
+
14
+ /* Recursica Font Map */
15
+ font-family: var(
16
+ --recursica_ui-kit_components_label_properties_label-text_font-family
17
+ );
18
+ font-size: var(
19
+ --recursica_ui-kit_components_label_properties_label-text_font-size
20
+ );
21
+ font-style: var(
22
+ --recursica_ui-kit_components_label_properties_label-text_font-style
23
+ );
24
+ font-weight: var(
25
+ --recursica_ui-kit_components_label_properties_label-text_font-weight
26
+ );
27
+ letter-spacing: var(
28
+ --recursica_ui-kit_components_label_properties_label-text_letter-spacing
29
+ );
30
+ line-height: var(
31
+ --recursica_ui-kit_components_label_properties_label-text_line-height
32
+ );
33
+ text-decoration: var(
34
+ --recursica_ui-kit_components_label_properties_label-text_text-decoration
35
+ );
36
+ text-transform: var(
37
+ --recursica_ui-kit_components_label_properties_label-text_text-transform
38
+ );
39
+
40
+ color: var(--recursica_ui-kit_components_label_properties_colors_text);
41
+ }
42
+
43
+ .innerLayout {
44
+ display: flex;
45
+ align-items: center;
46
+ gap: var(
47
+ --recursica_ui-kit_components_label_properties_label-optional-text-gap
48
+ );
49
+ width: 100%;
50
+ }
51
+
52
+ .text {
53
+ /* Natively intercepts MUI asterisk rendering securely wrapping inline with typography */
54
+ display: inline;
55
+ }
56
+
57
+ .required {
58
+ /* Override the `.MuiInputLabel-asterisk` natively */
59
+ color: var(--recursica_ui-kit_components_label_properties_colors_asterisk);
60
+ margin-left: var(
61
+ --recursica_ui-kit_components_label_properties_required-indicator-gap
62
+ );
63
+ }
64
+
65
+ /* MUI propagates .Mui-error to the label automatically on FormControl error.
66
+ We explicitly override this bleed to prevent the main label text from turning red,
67
+ while allowing the asterisk to take the error color! */
68
+ .root:global(.Mui-error) {
69
+ color: var(--recursica_ui-kit_components_label_properties_colors_text);
70
+ }
71
+ .root:global(.Mui-error) .required {
72
+ color: var(
73
+ --recursica_ui-kit_components_assistive-element_variants_types_error_properties_colors_text-color
74
+ );
75
+ }
76
+
77
+ .endNodes {
78
+ display: flex;
79
+ align-items: center;
80
+ gap: var(
81
+ --recursica_ui-kit_components_label_properties_label-optional-text-gap
82
+ );
83
+ }
84
+
85
+ .optionalText {
86
+ color: inherit;
87
+ font-family: var(
88
+ --recursica_ui-kit_components_label_properties_optional-text_font-family
89
+ );
90
+ font-size: var(
91
+ --recursica_ui-kit_components_label_properties_optional-text_font-size
92
+ );
93
+ font-style: var(
94
+ --recursica_ui-kit_components_label_properties_optional-text_font-style
95
+ );
96
+ font-weight: var(
97
+ --recursica_ui-kit_components_label_properties_optional-text_font-weight
98
+ );
99
+ letter-spacing: var(
100
+ --recursica_ui-kit_components_label_properties_optional-text_letter-spacing
101
+ );
102
+ line-height: var(
103
+ --recursica_ui-kit_components_label_properties_optional-text_line-height
104
+ );
105
+ text-decoration: var(
106
+ --recursica_ui-kit_components_label_properties_optional-text_text-decoration
107
+ );
108
+ text-transform: var(
109
+ --recursica_ui-kit_components_label_properties_optional-text_text-transform
110
+ );
111
+ }
112
+
113
+ .actionArea {
114
+ display: flex;
115
+ align-items: center;
116
+ }
117
+
118
+ .editIcon {
119
+ display: flex;
120
+ align-items: center;
121
+ justify-content: center;
122
+ cursor: pointer;
123
+ background: none;
124
+ border: none;
125
+ padding: 0;
126
+
127
+ width: 16px;
128
+ height: 16px;
129
+ color: inherit;
130
+ }
131
+
132
+ .editIcon :global(svg) {
133
+ width: 100%;
134
+ height: 100%;
135
+ stroke-width: 2px;
136
+ }
@@ -1,35 +1,116 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import React from "react";
1
3
  import type { Meta, StoryObj } from "@storybook/react";
2
4
  import { Label } from "./Label";
3
- import { ComingSoon } from "@recursica/storybook-template";
5
+ import { TextField } from "../TextField/TextField";
6
+ import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
4
7
 
5
- const meta: Meta<typeof Label> = {
6
- title: "UI-Kit/🚧 Label",
8
+ type LabelStoryProps = React.ComponentProps<typeof Label>;
9
+
10
+ const meta: Meta<LabelStoryProps> = {
11
+ title: "UI-Kit/Label",
7
12
  component: Label,
8
13
  tags: ["autodocs"],
9
14
  parameters: {
15
+ docs: {
16
+ description: {
17
+ component:
18
+ "The `Label` component is a strict Recursica-styled wrapper around MUI's native `InputLabel`. It serves as the primary compositional primitive for all form fields, preserving MUI's accessibility associations and context while strictly enforcing the Recursica atomic design system.\n\n### Usage with Form Inputs\nWhen working with form structures, render this `Label` component directly above your inputs or supply it to a component's overriding properties. The component automatically maps structural layout dimensions, dynamic alignment (`left` vs `right`), custom indicator gaps, and integrates a customized `optionalText` and `withEditIcon` flow that safely bypasses MUI's native required asterisk mechanisms.",
19
+ },
20
+ },
10
21
  controls: {
11
22
  include: [
12
- "layer",
13
- "withLayer",
23
+ "labelSize",
24
+ "labelAlignment",
25
+ "required",
26
+ "labelOptionalText",
27
+ "labelWithEditIcon",
14
28
  "children",
15
- "component",
16
- "variant",
17
- "size",
18
- "icon",
19
- "disabled",
20
- "href",
21
- "onClick",
22
- "onChange",
23
- "value",
24
- "checked",
25
29
  ],
26
30
  },
27
31
  },
32
+ argTypes: {
33
+ ...formControlArgTypes,
34
+ },
28
35
  };
29
36
 
30
37
  export default meta;
31
- type Story = StoryObj<typeof Label>;
38
+
39
+ type Story = StoryObj<LabelStoryProps>;
40
+
41
+ // Utility mapping to pipe raw Label args structurally into TextField accurately
42
+ const renderWithTextField = ({ children, ...args }: any) => (
43
+ <TextField
44
+ label={children as React.ReactNode}
45
+ placeholder="Form Control primitive mapped..."
46
+ {...(args as any)}
47
+ />
48
+ );
32
49
 
33
50
  export const Default: Story = {
34
- render: () => <ComingSoon componentName="Label" />,
51
+ args: {
52
+ children: "Dynamic Label (Controls)",
53
+ labelSize: "default",
54
+ labelAlignment: "left",
55
+ required: false,
56
+ labelOptionalText: "",
57
+ labelWithEditIcon: false,
58
+ },
59
+ render: renderWithTextField,
60
+ };
61
+
62
+ export const StackedDefault: Story = {
63
+ args: {
64
+ children: "Email Address",
65
+ },
66
+ render: renderWithTextField,
67
+ };
68
+
69
+ export const StackedRequired: Story = {
70
+ args: {
71
+ children: "Primary Network Node",
72
+ required: true,
73
+ },
74
+ render: renderWithTextField,
75
+ };
76
+
77
+ export const StackedWithEditIcon: Story = {
78
+ args: {
79
+ children: "Environment Variables",
80
+ labelWithEditIcon: true,
81
+ },
82
+ render: renderWithTextField,
83
+ };
84
+
85
+ export const SideBySideDefault: Story = {
86
+ args: {
87
+ children: "Status",
88
+ labelSize: "default",
89
+ },
90
+ render: renderWithTextField,
91
+ };
92
+
93
+ export const RequiredSuppressesOptionalText: Story = {
94
+ args: {
95
+ children: "Full Name",
96
+ required: true,
97
+ labelOptionalText: "This should not render",
98
+ },
99
+ render: renderWithTextField,
100
+ };
101
+
102
+ export const BooleanOptionalText: Story = {
103
+ args: {
104
+ children: "Middle Initial",
105
+ labelOptionalText: true,
106
+ },
107
+ render: renderWithTextField,
108
+ };
109
+
110
+ export const WithEditIcon: Story = {
111
+ args: {
112
+ children: "Shipping Address",
113
+ labelWithEditIcon: true,
114
+ },
115
+ render: renderWithTextField,
35
116
  };
@@ -1,8 +1,91 @@
1
1
  import React from "react";
2
- import "./Label.module.css";
2
+ import { InputLabel, type InputLabelProps } from "@mui/material";
3
+ import { filterStylingProps } from "../../utils/filterStylingProps";
4
+ import styles from "./Label.module.css";
3
5
 
4
- export type LabelProps = React.HTMLAttributes<HTMLDivElement>;
6
+ export interface LabelProps extends InputLabelProps {
7
+ overStyled?: boolean;
8
+ /** Explicit optional text to render safely mapping alongside the label. Hidden natively if 'required' is flagged true. */
9
+ labelOptionalText?: React.ReactNode | true;
10
+ /** Triggers the inline edit action icon native layout hook beside the label element. */
11
+ labelWithEditIcon?: boolean;
12
+ /** Fires specifically on icon click natively. */
13
+ onLabelEditClick?: (e: React.MouseEvent<HTMLButtonElement>) => void;
14
+ /** Replaces the right side optional text block natively with any custom React node (e.g. actions, toggles). */
15
+ labelActionArea?: React.ReactNode;
16
+ }
5
17
 
6
- export const Label: React.FC<LabelProps> = (props) => {
7
- return <div {...props}>Label</div>;
8
- };
18
+ export const Label = React.forwardRef<HTMLLabelElement, LabelProps>(
19
+ function Label(props, ref) {
20
+ const {
21
+ overStyled = false,
22
+ required = false,
23
+ labelOptionalText = "",
24
+ labelWithEditIcon = false,
25
+ onLabelEditClick,
26
+ labelActionArea,
27
+ children,
28
+ className,
29
+ ...rest
30
+ } = props;
31
+
32
+ const sanitizedProps = filterStylingProps(rest, overStyled);
33
+
34
+ const PenIcon = () => (
35
+ <svg
36
+ xmlns="http://www.w3.org/2000/svg"
37
+ width="24"
38
+ height="24"
39
+ viewBox="0 0 24 24"
40
+ fill="none"
41
+ stroke="currentColor"
42
+ strokeWidth="2"
43
+ strokeLinecap="round"
44
+ strokeLinejoin="round"
45
+ >
46
+ <path d="M12 20h9"></path>
47
+ <path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4L16.5 3.5z"></path>
48
+ </svg>
49
+ );
50
+
51
+ // Bypassing MUI's floating functionality by forcing shrink=true to static inline positioning.
52
+ return (
53
+ <InputLabel
54
+ ref={ref}
55
+ shrink={true}
56
+ className={className ? `${styles.root} ${className}` : styles.root}
57
+ {...(sanitizedProps as InputLabelProps)}
58
+ >
59
+ <div className={styles.innerLayout}>
60
+ <span className={styles.textWrapper}>
61
+ <span className={styles.text}>{children}</span>
62
+ {required && <span className={styles.required}> *</span>}
63
+ </span>
64
+
65
+ <div className={styles.endNodes}>
66
+ {labelActionArea && (
67
+ <span className={styles.actionArea}>{labelActionArea}</span>
68
+ )}
69
+ {!required && labelOptionalText && !labelActionArea && (
70
+ <span className={styles.optionalText}>
71
+ {labelOptionalText === true ? "Optional" : labelOptionalText}
72
+ </span>
73
+ )}
74
+ {labelWithEditIcon && (
75
+ <button
76
+ type="button"
77
+ className={styles.editIcon}
78
+ onClick={onLabelEditClick}
79
+ aria-label="Edit label"
80
+ >
81
+ <PenIcon />
82
+ </button>
83
+ )}
84
+ </div>
85
+ </div>
86
+ </InputLabel>
87
+ );
88
+ },
89
+ );
90
+
91
+ Label.displayName = "Label";