@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.
Files changed (160) hide show
  1. package/.storybook/commonArgTypes.ts +148 -0
  2. package/CHANGELOG.md +69 -0
  3. package/dist/mantine-adapter.cjs +1 -1
  4. package/dist/mantine-adapter.cjs.map +1 -1
  5. package/dist/mantine-adapter.css +1 -1
  6. package/dist/mantine-adapter.js +1076 -266
  7. package/dist/mantine-adapter.js.map +1 -1
  8. package/dist/src/components/Accordion/Accordion.d.ts +13 -28
  9. package/dist/src/components/AssistiveElement/AssistiveElement.d.ts +10 -0
  10. package/dist/src/components/Avatar/Avatar.d.ts +1 -1
  11. package/dist/src/components/Breadcrumb/Breadcrumb.d.ts +6 -3
  12. package/dist/src/components/Button/Button.d.ts +1 -1
  13. package/dist/src/components/Card/Card.d.ts +38 -3
  14. package/dist/src/components/Checkbox/Checkbox.d.ts +11 -3
  15. package/dist/src/components/Checkbox/CheckboxGroup.d.ts +9 -0
  16. package/dist/src/components/Chip/Chip.d.ts +14 -2
  17. package/dist/src/components/FormControlWrapper/FormControlWrapper.d.ts +18 -0
  18. package/dist/src/components/Label/Label.d.ts +21 -0
  19. package/dist/src/components/Label/index.d.ts +1 -0
  20. package/dist/src/components/ReadOnlyField/ReadOnlyField.d.ts +19 -2
  21. package/dist/src/components/ReadOnlyField/ReadOnlyTextField.d.ts +8 -0
  22. package/dist/src/components/ReadOnlyField/WithReadOnlyWrapper.d.ts +22 -0
  23. package/dist/src/components/ReadOnlyField/index.d.ts +3 -0
  24. package/dist/src/components/Text/Text.d.ts +17 -0
  25. package/dist/src/components/TextField/TextField.d.ts +8 -2
  26. package/dist/src/components/Title/Title.d.ts +13 -0
  27. package/dist/src/components/index.d.ts +3 -1
  28. package/dist/src/utils/ColorSchemeWrapper.d.ts +3 -0
  29. package/dist/src/utils/filterStylingProps.d.ts +6 -2
  30. package/package.json +9 -5
  31. package/src/OverStyling.stories.tsx +174 -0
  32. package/src/Version.stories.tsx +56 -0
  33. package/src/components/Accordion/ACCORDION_IMPLEMENTATION_NOTES.md +48 -0
  34. package/src/components/Accordion/Accordion.module.css +261 -0
  35. package/src/components/Accordion/Accordion.stories.tsx +145 -0
  36. package/src/components/Accordion/Accordion.tsx +200 -0
  37. package/src/components/Accordion/index.ts +1 -0
  38. package/src/components/AssistiveElement/AssistiveElement.module.css +111 -0
  39. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +73 -0
  40. package/src/components/AssistiveElement/AssistiveElement.tsx +91 -0
  41. package/src/components/Avatar/AVATAR_IMPLEMENTATION_NOTES.md +24 -0
  42. package/src/components/Avatar/Avatar.module.css +340 -0
  43. package/src/components/Avatar/Avatar.stories.tsx +102 -0
  44. package/src/components/Avatar/Avatar.tsx +100 -0
  45. package/src/components/Avatar/index.ts +1 -0
  46. package/src/components/Badge/BADGE_IMPLEMENTATION_NOTES.md +18 -0
  47. package/src/components/Badge/Badge.module.css +124 -0
  48. package/src/components/Badge/Badge.stories.tsx +77 -0
  49. package/src/components/Badge/Badge.tsx +66 -0
  50. package/src/components/Breadcrumb/BREADCRUMB_IMPLEMENTATION_NOTES.md +14 -0
  51. package/src/components/Breadcrumb/Breadcrumb.module.css +26 -0
  52. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +77 -0
  53. package/src/components/Breadcrumb/Breadcrumb.tsx +64 -0
  54. package/src/components/Button/Button.module.css +275 -0
  55. package/src/components/Button/Button.stories.tsx +105 -0
  56. package/src/components/Button/Button.tsx +122 -0
  57. package/src/components/Button/IMPLEMENTATION_NOTES.md +45 -0
  58. package/src/components/Button/index.ts +1 -0
  59. package/src/components/Card/CARD_IMPLEMENTATION_NOTES.md +10 -0
  60. package/src/components/Card/Card.module.css +60 -0
  61. package/src/components/Card/Card.stories.tsx +141 -0
  62. package/src/components/Card/Card.tsx +176 -0
  63. package/src/components/Checkbox/CHECKBOX_IMPLEMENTATION_NOTES.md +25 -0
  64. package/src/components/Checkbox/Checkbox.module.css +204 -0
  65. package/src/components/Checkbox/Checkbox.stories.tsx +73 -0
  66. package/src/components/Checkbox/Checkbox.tsx +111 -0
  67. package/src/components/Checkbox/CheckboxGroup.stories.tsx +138 -0
  68. package/src/components/Checkbox/CheckboxGroup.tsx +140 -0
  69. package/src/components/Chip/CHIP_IMPLEMENTATION_NOTES.md +35 -0
  70. package/src/components/Chip/Chip.module.css +218 -0
  71. package/src/components/Chip/Chip.stories.tsx +115 -0
  72. package/src/components/Chip/Chip.tsx +143 -0
  73. package/src/components/DatePicker/DatePicker.stories.tsx +22 -0
  74. package/src/components/DatePicker/DatePicker.tsx +7 -0
  75. package/src/components/Dropdown/Dropdown.stories.tsx +22 -0
  76. package/src/components/Dropdown/Dropdown.tsx +7 -0
  77. package/src/components/FileInput/FileInput.stories.tsx +22 -0
  78. package/src/components/FileInput/FileInput.tsx +7 -0
  79. package/src/components/FileUpload/FileUpload.stories.tsx +22 -0
  80. package/src/components/FileUpload/FileUpload.tsx +7 -0
  81. package/src/components/FormControlWrapper/FORMCONTROLWRAPPER_IMPLEMENTATION_NOTES.md +32 -0
  82. package/src/components/FormControlWrapper/FormControlWrapper.module.css +46 -0
  83. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +116 -0
  84. package/src/components/FormControlWrapper/FormControlWrapper.tsx +170 -0
  85. package/src/components/HoverCard/HoverCard.stories.tsx +17 -0
  86. package/src/components/HoverCard/HoverCard.tsx +7 -0
  87. package/src/components/Label/LABEL_IMPLEMENTATION_NOTES.md +48 -0
  88. package/src/components/Label/Label.module.css +177 -0
  89. package/src/components/Label/Label.stories.tsx +123 -0
  90. package/src/components/Label/Label.tsx +132 -0
  91. package/src/components/Label/index.ts +1 -0
  92. package/src/components/Link/Link.stories.tsx +17 -0
  93. package/src/components/Link/Link.tsx +7 -0
  94. package/src/components/Loader/Loader.stories.tsx +17 -0
  95. package/src/components/Loader/Loader.tsx +7 -0
  96. package/src/components/Menu/Menu.stories.tsx +17 -0
  97. package/src/components/Menu/Menu.tsx +7 -0
  98. package/src/components/Modal/Modal.stories.tsx +17 -0
  99. package/src/components/Modal/Modal.tsx +7 -0
  100. package/src/components/NumberInput/NumberInput.stories.tsx +22 -0
  101. package/src/components/NumberInput/NumberInput.tsx +7 -0
  102. package/src/components/Pagination/Pagination.stories.tsx +17 -0
  103. package/src/components/Pagination/Pagination.tsx +7 -0
  104. package/src/components/Panel/Panel.stories.tsx +17 -0
  105. package/src/components/Panel/Panel.tsx +7 -0
  106. package/src/components/Popover/Popover.stories.tsx +17 -0
  107. package/src/components/Popover/Popover.tsx +7 -0
  108. package/src/components/Radio/Radio.stories.tsx +22 -0
  109. package/src/components/Radio/Radio.tsx +7 -0
  110. package/src/components/ReadOnlyField/READ_ONLY_FIELD_IMPLEMENTATION_NOTES.md +15 -0
  111. package/src/components/ReadOnlyField/ReadOnlyField.module.css +49 -0
  112. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +120 -0
  113. package/src/components/ReadOnlyField/ReadOnlyField.tsx +75 -0
  114. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +46 -0
  115. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +85 -0
  116. package/src/components/ReadOnlyField/index.ts +3 -0
  117. package/src/components/Search/Search.stories.tsx +17 -0
  118. package/src/components/Search/Search.tsx +7 -0
  119. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +22 -0
  120. package/src/components/SegmentedControl/SegmentedControl.tsx +7 -0
  121. package/src/components/Slider/Slider.stories.tsx +22 -0
  122. package/src/components/Slider/Slider.tsx +7 -0
  123. package/src/components/Stepper/Stepper.stories.tsx +17 -0
  124. package/src/components/Stepper/Stepper.tsx +7 -0
  125. package/src/components/Switch/Switch.stories.tsx +22 -0
  126. package/src/components/Switch/Switch.tsx +7 -0
  127. package/src/components/Table/Table.stories.tsx +17 -0
  128. package/src/components/Table/Table.tsx +7 -0
  129. package/src/components/Tabs/Tabs.stories.tsx +17 -0
  130. package/src/components/Tabs/Tabs.tsx +7 -0
  131. package/src/components/Text/Text.stories.tsx +88 -0
  132. package/src/components/Text/Text.tsx +54 -0
  133. package/src/components/TextArea/TextArea.stories.tsx +22 -0
  134. package/src/components/TextArea/TextArea.tsx +7 -0
  135. package/src/components/TextField/TEXTFIELD_IMPLEMENTATION_NOTES.md +19 -0
  136. package/src/components/TextField/TextField.module.css +225 -0
  137. package/src/components/TextField/TextField.stories.tsx +162 -0
  138. package/src/components/TextField/TextField.tsx +138 -0
  139. package/src/components/TimePicker/TimePicker.stories.tsx +22 -0
  140. package/src/components/TimePicker/TimePicker.tsx +7 -0
  141. package/src/components/Timeline/Timeline.stories.tsx +17 -0
  142. package/src/components/Timeline/Timeline.tsx +7 -0
  143. package/src/components/Title/Title.stories.tsx +63 -0
  144. package/src/components/Title/Title.tsx +45 -0
  145. package/src/components/Toast/Toast.stories.tsx +17 -0
  146. package/src/components/Toast/Toast.tsx +7 -0
  147. package/src/components/Tooltip/Tooltip.stories.tsx +17 -0
  148. package/src/components/Tooltip/Tooltip.tsx +7 -0
  149. package/src/components/TransferList/TransferList.stories.tsx +17 -0
  150. package/src/components/TransferList/TransferList.tsx +7 -0
  151. package/src/components/index.ts +39 -0
  152. package/src/env.d.ts +7 -0
  153. package/src/index.ts +3 -0
  154. package/src/types/index.ts +9 -0
  155. package/src/types/mantine.d.ts +7 -0
  156. package/src/types/scss-modules.d.ts +9 -0
  157. package/src/utils/ColorSchemeWrapper.tsx +27 -0
  158. package/src/utils/copyToClipboard.ts +36 -0
  159. package/src/utils/filterStylingProps.ts +139 -0
  160. package/src/utils/index.ts +1 -0
@@ -0,0 +1,275 @@
1
+ /* Baseline and Reset */
2
+ .root {
3
+ box-sizing: border-box;
4
+ margin: 0;
5
+ border-style: solid;
6
+ overflow: hidden; /* Truncation bounding box */
7
+ position: relative;
8
+ transition: all 0.2s ease;
9
+
10
+ /* Shared Defaults */
11
+ font-family: var(
12
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_text_font-family
13
+ );
14
+ font-size: var(
15
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_text_font-size
16
+ );
17
+ font-style: var(
18
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_text_font-style
19
+ );
20
+ font-weight: var(
21
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_text_font-weight
22
+ );
23
+ letter-spacing: var(
24
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_text_letter-spacing
25
+ );
26
+ line-height: var(
27
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_text_line-height
28
+ );
29
+ text-decoration: var(
30
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_text_text-decoration
31
+ );
32
+ text-transform: var(
33
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_text_text-transform
34
+ );
35
+ }
36
+
37
+ /* Internal Mantine reset & layout logic for Truncation */
38
+ .root > * {
39
+ min-width: 0;
40
+ width: 100%;
41
+ position: relative;
42
+ z-index: 1;
43
+ }
44
+
45
+ .label {
46
+ display: flex;
47
+ align-items: center;
48
+ min-width: 0;
49
+ flex: 1 1 0;
50
+ }
51
+
52
+ .labelText {
53
+ display: block;
54
+ min-width: 0;
55
+ width: 100%;
56
+ overflow: hidden;
57
+ text-overflow: ellipsis;
58
+ white-space: nowrap;
59
+ font-size: inherit;
60
+ line-height: inherit;
61
+ }
62
+
63
+ /* ==== SIZES ==== */
64
+ .root[data-size="default"] {
65
+ border-radius: var(
66
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_border-radius
67
+ );
68
+ height: var(
69
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_height
70
+ );
71
+ min-width: var(
72
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_min-width
73
+ );
74
+ padding: 0
75
+ var(
76
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_horizontal-padding
77
+ );
78
+ }
79
+ .root[data-size="small"] {
80
+ border-radius: var(
81
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_border-radius
82
+ );
83
+ height: var(
84
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_height
85
+ );
86
+ min-width: var(
87
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_min-width
88
+ );
89
+ padding: 0
90
+ var(
91
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_horizontal-padding
92
+ );
93
+
94
+ /* Override specific typography for small */
95
+ font-family: var(
96
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_text_font-family,
97
+ var(
98
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_text_font-family
99
+ )
100
+ );
101
+ font-size: var(
102
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_text_font-size,
103
+ var(
104
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_text_font-size
105
+ )
106
+ );
107
+ font-weight: var(
108
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_text_font-weight,
109
+ var(
110
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_text_font-weight
111
+ )
112
+ );
113
+ }
114
+
115
+ /* Icon Resets */
116
+ .iconWrapper {
117
+ display: flex;
118
+ align-items: center;
119
+ justify-content: center;
120
+ flex-shrink: 0;
121
+ }
122
+ .iconWrapper > * {
123
+ width: 100%;
124
+ height: 100%;
125
+ object-fit: contain;
126
+ }
127
+ .root[data-size="default"] .iconWrapper {
128
+ width: var(
129
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_icon
130
+ );
131
+ height: var(
132
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_icon
133
+ );
134
+ }
135
+ .root[data-size="small"] .iconWrapper {
136
+ width: var(
137
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_icon
138
+ );
139
+ height: var(
140
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_icon
141
+ );
142
+ }
143
+
144
+ .section {
145
+ min-width: 0;
146
+ }
147
+ .root[data-size="default"] .section {
148
+ gap: var(
149
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_icon-text-gap
150
+ );
151
+ }
152
+ .root[data-size="small"] .section {
153
+ gap: var(
154
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_icon-text-gap
155
+ );
156
+ }
157
+
158
+ /* Icon-only logic overrides */
159
+ .root[data-icon-only] .section {
160
+ gap: 0;
161
+ }
162
+ .root[data-icon-only] .section[data-position="left"] {
163
+ margin-inline-end: 0;
164
+ }
165
+ .root[data-icon-only] .section[data-position="right"] {
166
+ margin-inline-start: 0;
167
+ }
168
+
169
+ /* Disabled */
170
+ .root:disabled {
171
+ opacity: var(--recursica_brand_states_disabled);
172
+ cursor: not-allowed;
173
+ }
174
+
175
+ /* Global Hover Hack Overlay */
176
+ .root::after {
177
+ content: "";
178
+ position: absolute;
179
+ inset: 0;
180
+ border-radius: inherit;
181
+ z-index: 0;
182
+ pointer-events: none;
183
+ transition: opacity 150ms ease;
184
+ opacity: 0;
185
+ }
186
+ .root[data-size="default"]::after {
187
+ background-color: var(
188
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_hover-color
189
+ );
190
+ }
191
+ .root[data-size="small"]::after {
192
+ background-color: var(
193
+ --recursica_ui-kit_components_button_variants_sizes_small_properties_hover-color
194
+ );
195
+ }
196
+ .root:hover:not(:disabled)::after {
197
+ opacity: var(
198
+ --recursica_ui-kit_components_button_variants_sizes_default_properties_hover-opacity,
199
+ 0.1
200
+ );
201
+ }
202
+
203
+ /* ==== VARIANTS ==== */
204
+
205
+ /* SOLID */
206
+ .root[data-variant="solid"] {
207
+ border-width: var(
208
+ --recursica_ui-kit_components_button_variants_styles_solid_properties_border-size
209
+ );
210
+ box-shadow: var(
211
+ --recursica_ui-kit_components_button_variants_styles_solid_properties_elevation
212
+ );
213
+ background-color: var(
214
+ --recursica_ui-kit_components_button_variants_styles_solid_properties_colors_background
215
+ );
216
+ border-color: var(
217
+ --recursica_ui-kit_components_button_variants_styles_solid_properties_colors_border-color
218
+ );
219
+ color: var(
220
+ --recursica_ui-kit_components_button_variants_styles_solid_properties_colors_text
221
+ );
222
+ }
223
+ .root[data-variant="solid"]:hover:not(:disabled) {
224
+ color: var(
225
+ --recursica_ui-kit_components_button_variants_styles_solid_properties_colors_text-hover
226
+ );
227
+ }
228
+
229
+ /* OUTLINE */
230
+ .root[data-variant="outline"] {
231
+ border-width: var(
232
+ --recursica_ui-kit_components_button_variants_styles_outline_properties_border-size
233
+ );
234
+ box-shadow: var(
235
+ --recursica_ui-kit_components_button_variants_styles_outline_properties_elevation
236
+ );
237
+ background-color: var(
238
+ --recursica_ui-kit_components_button_variants_styles_outline_properties_colors_background
239
+ );
240
+ border-color: var(
241
+ --recursica_ui-kit_components_button_variants_styles_outline_properties_colors_border-color
242
+ );
243
+ color: var(
244
+ --recursica_ui-kit_components_button_variants_styles_outline_properties_colors_text
245
+ );
246
+ }
247
+ .root[data-variant="outline"]:hover:not(:disabled) {
248
+ color: var(
249
+ --recursica_ui-kit_components_button_variants_styles_outline_properties_colors_text-hover
250
+ );
251
+ }
252
+
253
+ /* TEXT */
254
+ .root[data-variant="text"] {
255
+ border-width: var(
256
+ --recursica_ui-kit_components_button_variants_styles_text_properties_border-size
257
+ );
258
+ box-shadow: var(
259
+ --recursica_ui-kit_components_button_variants_styles_text_properties_elevation
260
+ );
261
+ background-color: var(
262
+ --recursica_ui-kit_components_button_variants_styles_text_properties_colors_background
263
+ );
264
+ border-color: var(
265
+ --recursica_ui-kit_components_button_variants_styles_text_properties_colors_border-color
266
+ );
267
+ color: var(
268
+ --recursica_ui-kit_components_button_variants_styles_text_properties_colors_text
269
+ );
270
+ }
271
+ .root[data-variant="text"]:hover:not(:disabled) {
272
+ color: var(
273
+ --recursica_ui-kit_components_button_variants_styles_text_properties_colors_text-hover
274
+ );
275
+ }
@@ -0,0 +1,105 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { Button } from "./Button";
4
+ import { Layer } from "@recursica/adapter-common";
5
+
6
+ type ButtonStoryProps = React.ComponentProps<typeof Button>;
7
+
8
+ const meta: Meta<ButtonStoryProps> = {
9
+ title: "UI-Kit/Button",
10
+ component: Button,
11
+ tags: ["autodocs"],
12
+ argTypes: {
13
+ variant: {
14
+ control: "select",
15
+ options: ["solid", "outline", "text"],
16
+ description: "The visual variant of the button",
17
+ },
18
+ size: {
19
+ control: "radio",
20
+ options: ["default", "small"],
21
+ description: "The size of the button",
22
+ },
23
+ disabled: {
24
+ control: "boolean",
25
+ },
26
+ },
27
+ };
28
+
29
+ export default meta;
30
+
31
+ type Story = StoryObj<ButtonStoryProps>;
32
+
33
+ export const Default: Story = {
34
+ args: {
35
+ children: "Explore Button",
36
+ variant: "solid",
37
+ size: "default",
38
+ disabled: false,
39
+ },
40
+ render: (args) => {
41
+ return <Button {...args} />;
42
+ },
43
+ };
44
+
45
+ export const SolidDefault: Story = {
46
+ args: {
47
+ children: "Solid Default",
48
+ variant: "solid",
49
+ size: "default",
50
+ },
51
+ };
52
+
53
+ export const OutlineSmall: Story = {
54
+ args: {
55
+ children: "Outline Small",
56
+ variant: "outline",
57
+ size: "small",
58
+ },
59
+ };
60
+
61
+ export const TextWithIcon: Story = {
62
+ args: {
63
+ children: "Text With Icon",
64
+ variant: "text",
65
+ size: "default",
66
+ icon: (
67
+ <svg
68
+ xmlns="http://www.w3.org/2000/svg"
69
+ width="24"
70
+ height="24"
71
+ viewBox="0 0 24 24"
72
+ fill="none"
73
+ stroke="currentColor"
74
+ strokeWidth="2"
75
+ strokeLinecap="round"
76
+ strokeLinejoin="round"
77
+ >
78
+ <circle cx="11" cy="11" r="8"></circle>
79
+ <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
80
+ </svg>
81
+ ),
82
+ },
83
+ };
84
+
85
+ export const LayerOneSolid: Story = {
86
+ args: {
87
+ children: "Layer 1 Solid",
88
+ variant: "solid",
89
+ size: "default",
90
+ },
91
+ render: (args: ButtonStoryProps) => (
92
+ <Layer layer={1} style={{ padding: "24px" }}>
93
+ <Button {...args} />
94
+ </Layer>
95
+ ),
96
+ };
97
+
98
+ export const DisabledSolid: Story = {
99
+ args: {
100
+ children: "Disabled Solid",
101
+ variant: "solid",
102
+ size: "default",
103
+ disabled: true,
104
+ },
105
+ };
@@ -0,0 +1,122 @@
1
+ import React, { forwardRef } from "react";
2
+ import {
3
+ Button as MantineButton,
4
+ type ButtonProps as MantineButtonProps,
5
+ } from "@mantine/core";
6
+ import {
7
+ filterStylingProps,
8
+ type RecursicaOverStyled,
9
+ } from "../../utils/filterStylingProps";
10
+ import styles from "./Button.module.css";
11
+
12
+ export interface RecursicaButtonProps {
13
+ variant?: "solid" | "outline" | "text";
14
+ size?: "default" | "small";
15
+ icon?: React.ReactNode;
16
+ }
17
+
18
+ export type ButtonProps = RecursicaOverStyled<
19
+ Omit<MantineButtonProps, "variant" | "size" | "leftSection" | "fullWidth"> &
20
+ RecursicaButtonProps
21
+ >;
22
+
23
+ function hasVisibleChildren(children: React.ReactNode): boolean {
24
+ if (children == null || children === "") return false;
25
+ if (typeof children === "string") return children.trim() !== "";
26
+ return true;
27
+ }
28
+
29
+ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
30
+ function Button(
31
+ {
32
+ variant = "solid",
33
+ size = "default",
34
+ icon,
35
+ children,
36
+ overStyled = false,
37
+ ...rest
38
+ },
39
+ ref,
40
+ ) {
41
+ const mapVariant = {
42
+ solid: "filled",
43
+ outline: "outline",
44
+ text: "subtle",
45
+ } as const;
46
+
47
+ const mapSize = {
48
+ default: "md",
49
+ small: "sm",
50
+ } as const;
51
+
52
+ const sanitizedProps = filterStylingProps(rest, overStyled);
53
+ const restRecord = sanitizedProps as Record<string, unknown>;
54
+
55
+ // Explicitly delete blocked semantic expansion dimension props
56
+ delete restRecord["fullWidth"];
57
+
58
+ const hasLeftSection = !!icon || !!restRecord["leftSection"];
59
+ const hasRightSection = !!restRecord["rightSection"];
60
+ const isIconOnly =
61
+ (hasLeftSection || hasRightSection) && !hasVisibleChildren(children);
62
+
63
+ if (
64
+ typeof process !== "undefined" &&
65
+ process.env.NODE_ENV !== "production" &&
66
+ isIconOnly &&
67
+ !restRecord["aria-label"]
68
+ ) {
69
+ console.warn(
70
+ '[Recursica Button] Icon-only buttons must provide an accessible name. Pass aria-label (e.g. aria-label="Submit").',
71
+ );
72
+ }
73
+
74
+ const mergedClassNames: Partial<Record<string, string>> = {
75
+ root: styles.root,
76
+ section: styles.section,
77
+ label: styles.label,
78
+ };
79
+
80
+ const classNamesProp = restRecord.classNames;
81
+ if (
82
+ classNamesProp &&
83
+ typeof classNamesProp === "object" &&
84
+ !Array.isArray(classNamesProp)
85
+ ) {
86
+ const o = classNamesProp as Partial<Record<string, string>>;
87
+ mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
88
+ mergedClassNames.section = o.section ?? styles.section;
89
+ mergedClassNames.label = o.label ?? 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
+ return (
98
+ <MantineButton
99
+ ref={ref}
100
+ className={finalClass}
101
+ classNames={mergedClassNames}
102
+ variant={mapVariant[variant]}
103
+ size={mapSize[size]}
104
+ leftSection={
105
+ icon != null ? (
106
+ <span className={styles.iconWrapper} aria-hidden>
107
+ {icon}
108
+ </span>
109
+ ) : undefined
110
+ }
111
+ data-variant={variant}
112
+ data-size={size}
113
+ {...(isIconOnly ? { "data-icon-only": "" } : {})}
114
+ {...sanitizedProps}
115
+ >
116
+ <span className={styles.labelText}>{children}</span>
117
+ </MantineButton>
118
+ );
119
+ },
120
+ );
121
+
122
+ Button.displayName = "Button";
@@ -0,0 +1,45 @@
1
+ # Button – implementation notes
2
+
3
+ Decisions and design tweaks for the Recursica Button that go beyond the Component Development Guide. Use this when maintaining the Button or when adding similar behavior to other components.
4
+
5
+ ---
6
+
7
+ ## Icon size: Recursica defines it
8
+
9
+ **Decision:** Icon size is **not** left to the developer. Recursica defines it via the design tokens; the Button enforces it so callers cannot pass an arbitrarily sized icon.
10
+
11
+ **Implementation:**
12
+
13
+ - When `icon` is provided, the Button wraps it in a single element with class `iconWrapper` before passing it to Mantine’s `leftSection`.
14
+ - In `Button.module.css`, `.iconWrapper` has explicit `width` and `height` from the Recursica tokens mapping natively based on `data-size`.
15
+ - The rule `.iconWrapper > *` sets `width: 100%`, `height: 100%`, and `object-fit: contain` so whatever the caller passes scales cleanly with the token constraints.
16
+
17
+ ---
18
+
19
+ ## Icon-only buttons: accessibility and width
20
+
21
+ **Decision:** When the button has an icon and no visible label (icon-only), callers must provide an accessible name, and the button must not show extra space to the right of the icon.
22
+
23
+ **Accessibility:** We document that icon-only buttons must pass `aria-label` (e.g. `aria-label="Submit"`). In development we log a console warning if `icon` is set, `children` is empty, and `aria-label` is missing.
24
+
25
+ **Width:** Mantine’s layout natively applies structural section gaps. We detect icon-only and set a `data-icon-only` hook so that `Button.module.css` zeros out the sections spacing allowing the button to precisely hit `min-width` perfectly centered.
26
+
27
+ ---
28
+
29
+ ## Label truncation at max-width
30
+
31
+ **Decision:** When the button hits its Recursica max-width (500px), the label truncates with an ellipsis instead of wrapping.
32
+
33
+ **Implementation:**
34
+ Mantine's `.mantine-Button-label` flex centering breaks primitive truncation logic. To combat this:
35
+
36
+ - **`.root`** has `overflow: hidden`.
37
+ - **`.root > *`** forces `min-width: 0`.
38
+ - The structural children wrap into `<span className={styles.labelText}>`.
39
+ - **`.labelText`** binds `overflow: hidden; text-overflow: ellipsis; white-space: nowrap;` creating flawless string cutoffs strictly at exact UI constraints.
40
+
41
+ ---
42
+
43
+ ## Disabled state: brand theme opacity (implicit)
44
+
45
+ **Decision:** The UI kit enforces global brand theme disabled opacities. The `.root:disabled` logic implicitly overrides visibility locally via `var(--recursica_brand_states_disabled)`.
@@ -0,0 +1 @@
1
+ export * from "./Button";
@@ -0,0 +1,10 @@
1
+ # Card Implementation Notes
2
+
3
+ ## Architecture Overrides
4
+
5
+ Because Mantine natively constructs `Card` bounding boxes with generic inner `<Card.Section>` elements that depend on explicit user styling (and lack implicit native designations for "Header" vs "Footer"), we implemented an explicit component wrapper departure:
6
+
7
+ - `<Card.Header>` explicitly hooks `--recursica_ui-kit_components_card_properties_header-background` and corresponding padding variables.
8
+ - `<Card.Footer>` explicitly hooks `--recursica_ui-kit_components_card_properties_footer-background` and corresponding padding variables.
9
+
10
+ Mantine's generic `<Card.Section>` calculates negative margins implicitly. Because of this, it is crucial that our local CSS module declares `--card-padding: var(--recursica_ui-kit_components_card_properties_padding)` directly on `.root` so that all generic or explicit section wrappers natively stretch across the bounding box properly.
@@ -0,0 +1,60 @@
1
+ .root {
2
+ /* HARDCODE: Map Recursica explicitly into Mantine's variable format so internal components know the margins */
3
+ --card-padding: var(--recursica_ui-kit_components_card_properties_padding);
4
+
5
+ background-color: var(
6
+ --recursica_ui-kit_components_card_properties_colors_background
7
+ );
8
+ border-radius: var(
9
+ --recursica_ui-kit_components_card_properties_borders_border-radius
10
+ );
11
+ border: var(--recursica_ui-kit_components_card_properties_borders_border-size)
12
+ solid var(--recursica_ui-kit_components_card_properties_colors_border-color);
13
+ box-shadow: var(
14
+ --recursica_ui-kit_components_card_properties_elevations_elevation
15
+ );
16
+
17
+ padding: var(--card-padding);
18
+ min-width: var(--recursica_ui-kit_components_card_properties_min-width);
19
+ max-width: var(--recursica_ui-kit_components_card_properties_max-width);
20
+ color: var(--recursica_ui-kit_components_card_properties_colors_content);
21
+
22
+ display: flex;
23
+ flex-direction: column;
24
+ gap: var(--recursica_ui-kit_components_card_properties_section-gap);
25
+ }
26
+
27
+ .header {
28
+ background-color: var(
29
+ --recursica_ui-kit_components_card_properties_colors_header-background
30
+ );
31
+ padding: var(--recursica_ui-kit_components_card_properties_header-padding);
32
+ color: var(--recursica_ui-kit_components_card_properties_colors_title);
33
+ border-bottom: var(--recursica_ui-kit_components_card_properties_divider-size)
34
+ solid
35
+ var(--recursica_ui-kit_components_card_properties_colors_divider-color);
36
+ }
37
+
38
+ .footer {
39
+ background-color: var(
40
+ --recursica_ui-kit_components_card_properties_colors_footer-background
41
+ );
42
+ padding: var(--recursica_ui-kit_components_card_properties_footer-padding);
43
+ border-top: var(--recursica_ui-kit_components_card_properties_divider-size)
44
+ solid
45
+ var(--recursica_ui-kit_components_card_properties_colors_divider-color);
46
+ }
47
+
48
+ .section {
49
+ /* Just maps standard section spacing overrides if passed explicitly */
50
+ margin-top: var(
51
+ --recursica_ui-kit_components_card_properties_vertical-gutter
52
+ );
53
+ }
54
+
55
+ .content {
56
+ display: flex;
57
+ flex-direction: column;
58
+ gap: var(--recursica_ui-kit_components_card_properties_vertical-gutter);
59
+ padding: 0;
60
+ }