@recursica/mantine-adapter 0.48.0 → 0.49.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 +18 -0
- package/dist/index.d.ts +42 -7
- package/dist/mantine-adapter.cjs +2 -2
- package/dist/mantine-adapter.cjs.map +1 -1
- package/dist/mantine-adapter.css +1 -1
- package/dist/mantine-adapter.js +1662 -1613
- package/dist/mantine-adapter.js.map +1 -1
- package/package.json +2 -2
- package/src/components/Accordion/Accordion.module.css +11 -0
- package/src/components/AutoComplete/AUTOCOMPLETE_IMPLEMENTATION_NOTES.md +18 -2
- package/src/components/AutoComplete/AutoComplete.module.css +127 -12
- package/src/components/AutoComplete/AutoComplete.stories.tsx +149 -0
- package/src/components/AutoComplete/AutoComplete.tsx +29 -1
- package/src/components/AutoComplete/USAGE.md +32 -2
- package/src/components/Button/Button.module.css +10 -0
- package/src/components/Card/Card.stories.tsx +2 -4
- package/src/components/Chip/Chip.module.css +9 -0
- package/src/components/DatePicker/DatePicker.module.css +16 -0
- package/src/components/Dropdown/DROPDOWN_IMPLEMENTATION_NOTES.md +3 -0
- package/src/components/Dropdown/Dropdown.module.css +124 -6
- package/src/components/Dropdown/Dropdown.stories.tsx +149 -0
- package/src/components/Dropdown/Dropdown.tsx +30 -2
- package/src/components/Dropdown/USAGE.md +21 -0
- package/src/components/FileInput/FILEINPUT_IMPLEMENTATION_NOTES.md +4 -1
- package/src/components/FileInput/FileInput.module.css +17 -0
- package/src/components/FileUpload/FileUpload.module.css +6 -0
- package/src/components/Link/Link.module.css +5 -0
- package/src/components/Menu/Menu.module.css +6 -0
- package/src/components/NumberInput/NumberInput.module.css +8 -0
- package/src/components/Pagination/Pagination.module.css +7 -0
- package/src/components/Slider/Slider.module.css +8 -0
- package/src/components/Switch/Switch.module.css +8 -0
- package/src/components/Table/Table.module.css +6 -0
- package/src/components/Tabs/Tabs.module.css +7 -0
- package/src/components/TextArea/TextArea.module.css +8 -0
- package/src/components/TextField/TextField.module.css +8 -0
- package/src/components/TimePicker/TimePicker.module.css +8 -0
- package/src/components/Tree/Tree.module.css +11 -0
- package/src/utils/renderRichOption.tsx +65 -0
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_focus_blur
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_focus_border-size
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_focus_color
|
|
6
|
+
* recursica-allow-brand: --recursica_brand_states_focus_margin
|
|
7
|
+
* recursica-allow-brand: --recursica_brand_states_hover_color
|
|
8
|
+
* recursica-allow-brand: --recursica_brand_states_hover_opacity
|
|
9
|
+
*/
|
|
10
|
+
|
|
1
11
|
/* LAYOUT SPACING OVERRIDES:
|
|
2
12
|
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
|
|
3
13
|
- Also sets the --dropdown-control-{max,min}-width hooks consumed inline in Dropdown.tsx, since
|
|
@@ -313,14 +323,21 @@
|
|
|
313
323
|
color: var(
|
|
314
324
|
--recursica_ui-kit_components_dropdown_properties_colors_text-color
|
|
315
325
|
);
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
326
|
+
/* Padding/inter-item gap reuse the menu-item component's tokens (same reasoning as the icon/
|
|
327
|
+
supporting-text tokens below) instead of an arbitrary scaled dropdown padding — matches
|
|
328
|
+
Menu.module.css's `.item` padding + `margin-bottom` gap exactly. */
|
|
329
|
+
padding: var(
|
|
330
|
+
--recursica_ui-kit_components_menu-item_properties_vertical-padding
|
|
319
331
|
)
|
|
320
|
-
var(--recursica_ui-
|
|
332
|
+
var(--recursica_ui-kit_components_menu-item_properties_horizontal-padding);
|
|
333
|
+
margin-bottom: var(--recursica_ui-kit_components_menu_properties_item-gap);
|
|
321
334
|
cursor: pointer;
|
|
322
335
|
}
|
|
323
336
|
|
|
337
|
+
.option:last-of-type {
|
|
338
|
+
margin-bottom: 0;
|
|
339
|
+
}
|
|
340
|
+
|
|
324
341
|
/* No per-option hovered token exists in the schema; use the generic overlay tint (same technique
|
|
325
342
|
as Table's row hover) to highlight a hovered-but-not-selected option. */
|
|
326
343
|
.option[data-hovered="true"],
|
|
@@ -337,9 +354,15 @@
|
|
|
337
354
|
component's selected-state colors are the closest real token family for the same concept (a
|
|
338
355
|
selected row in a list) — reused here rather than the neutral hover tint above, which is what
|
|
339
356
|
previously made the selected option read as grey instead of Recursica's brand color. Matches
|
|
340
|
-
the mui-adapter fix for the same gap.
|
|
357
|
+
the mui-adapter fix for the same gap. Despite the name, Mantine's `data-combobox-active` — not
|
|
358
|
+
`data-combobox-selected` — is the attribute it sets when an option's value matches the current
|
|
359
|
+
field value (see OptionsDropdown.mjs: `active: checked`); `data-combobox-selected` is an
|
|
360
|
+
unrelated transient keyboard-navigation highlight (imperatively set/cleared via arrow keys —
|
|
361
|
+
use-combobox.mjs's `selectOption`/`clearSelectedItem`). Keying this off `data-combobox-selected`
|
|
362
|
+
meant the real selected option only showed the brand color while arrow-key navigating, and lost
|
|
363
|
+
it as soon as the dropdown was reopened normally. */
|
|
341
364
|
.option[data-selected="true"],
|
|
342
|
-
.option[data-combobox-
|
|
365
|
+
.option[data-combobox-active="true"] {
|
|
343
366
|
background-color: var(
|
|
344
367
|
--recursica_ui-kit_components_menu-item_variants_selection-states_selected_properties_colors_background-color
|
|
345
368
|
);
|
|
@@ -347,3 +370,98 @@
|
|
|
347
370
|
--recursica_ui-kit_components_menu-item_variants_selection-states_selected_properties_colors_text-color
|
|
348
371
|
);
|
|
349
372
|
}
|
|
373
|
+
|
|
374
|
+
/* Rich option content (leadingIcon/supportingText — see MANTINE_ADAPTER_RICH_OPTION_DATA.md). No
|
|
375
|
+
dedicated dropdown-option icon or supporting-text token exists in the schema either; reuse the
|
|
376
|
+
menu-item component's tokens for the same reason the selected-state colors above do. */
|
|
377
|
+
.optionContent {
|
|
378
|
+
display: flex;
|
|
379
|
+
align-items: center;
|
|
380
|
+
gap: var(--recursica_ui-kit_components_menu-item_properties_icon-text-gap);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.optionIcon {
|
|
384
|
+
display: flex;
|
|
385
|
+
flex-shrink: 0;
|
|
386
|
+
width: var(
|
|
387
|
+
--recursica_ui-kit_components_menu-item_properties_icon-leading-size
|
|
388
|
+
);
|
|
389
|
+
height: var(
|
|
390
|
+
--recursica_ui-kit_components_menu-item_properties_icon-leading-size
|
|
391
|
+
);
|
|
392
|
+
color: var(
|
|
393
|
+
--recursica_ui-kit_components_menu-item_variants_selection-states_unselected_properties_colors_leading-icon-color
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
.optionIcon :global(svg) {
|
|
398
|
+
width: 100%;
|
|
399
|
+
height: 100%;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.optionText {
|
|
403
|
+
display: flex;
|
|
404
|
+
flex-direction: column;
|
|
405
|
+
min-width: 0;
|
|
406
|
+
gap: var(--recursica_ui-kit_components_menu-item_properties_text-gap);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/* Default: label/supportingText each truncate to a single line with an ellipsis. */
|
|
410
|
+
.optionText > * {
|
|
411
|
+
overflow: hidden;
|
|
412
|
+
text-overflow: ellipsis;
|
|
413
|
+
white-space: nowrap;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/* Applied alongside .optionText when `wrapItemText` is true — long values wrap onto additional
|
|
417
|
+
lines instead of overflowing the fixed-width dropdown. */
|
|
418
|
+
.optionTextWrap > * {
|
|
419
|
+
overflow: visible;
|
|
420
|
+
text-overflow: clip;
|
|
421
|
+
white-space: normal;
|
|
422
|
+
overflow-wrap: anywhere;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.optionSupportingText {
|
|
426
|
+
font-family: var(
|
|
427
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_font-family
|
|
428
|
+
);
|
|
429
|
+
font-size: var(
|
|
430
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_font-size
|
|
431
|
+
);
|
|
432
|
+
font-style: var(
|
|
433
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_font-style
|
|
434
|
+
);
|
|
435
|
+
font-weight: var(
|
|
436
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_font-weight
|
|
437
|
+
);
|
|
438
|
+
letter-spacing: var(
|
|
439
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_letter-spacing
|
|
440
|
+
);
|
|
441
|
+
line-height: var(
|
|
442
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_line-height
|
|
443
|
+
);
|
|
444
|
+
text-decoration: var(
|
|
445
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_text-decoration
|
|
446
|
+
);
|
|
447
|
+
text-transform: var(
|
|
448
|
+
--recursica_ui-kit_components_menu-item_properties_supporting-text_text-transform
|
|
449
|
+
);
|
|
450
|
+
color: var(
|
|
451
|
+
--recursica_ui-kit_components_menu-item_variants_selection-states_unselected_properties_colors_supporting-text-color
|
|
452
|
+
);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
.option[data-selected="true"] .optionIcon,
|
|
456
|
+
.option[data-combobox-active="true"] .optionIcon {
|
|
457
|
+
color: var(
|
|
458
|
+
--recursica_ui-kit_components_menu-item_variants_selection-states_selected_properties_colors_leading-icon-color
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
.option[data-selected="true"] .optionSupportingText,
|
|
463
|
+
.option[data-combobox-active="true"] .optionSupportingText {
|
|
464
|
+
color: var(
|
|
465
|
+
--recursica_ui-kit_components_menu-item_variants_selection-states_selected_properties_colors_supporting-text-color
|
|
466
|
+
);
|
|
467
|
+
}
|
|
@@ -2,6 +2,8 @@ import React from "react";
|
|
|
2
2
|
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
3
|
import { Dropdown } from "./Dropdown";
|
|
4
4
|
import { formControlArgTypes } from "../../../.storybook/commonArgTypes";
|
|
5
|
+
import { renderRichOption } from "../../utils/renderRichOption";
|
|
6
|
+
import styles from "./Dropdown.module.css";
|
|
5
7
|
|
|
6
8
|
type DropdownStoryProps = React.ComponentProps<typeof Dropdown>;
|
|
7
9
|
|
|
@@ -42,6 +44,11 @@ const meta: Meta<DropdownStoryProps> = {
|
|
|
42
44
|
clearable: {
|
|
43
45
|
control: "boolean",
|
|
44
46
|
},
|
|
47
|
+
wrapItemText: {
|
|
48
|
+
control: "boolean",
|
|
49
|
+
description:
|
|
50
|
+
"Wraps option label/supportingText onto additional lines instead of truncating with an ellipsis.",
|
|
51
|
+
},
|
|
45
52
|
containerWidth: {
|
|
46
53
|
table: { disable: true },
|
|
47
54
|
},
|
|
@@ -86,6 +93,148 @@ export const WithLeadingIcon: Story = {
|
|
|
86
93
|
},
|
|
87
94
|
};
|
|
88
95
|
|
|
96
|
+
const UserIcon = (
|
|
97
|
+
<svg
|
|
98
|
+
width="16"
|
|
99
|
+
height="16"
|
|
100
|
+
viewBox="0 0 24 24"
|
|
101
|
+
fill="none"
|
|
102
|
+
stroke="currentColor"
|
|
103
|
+
strokeWidth="2"
|
|
104
|
+
strokeLinecap="round"
|
|
105
|
+
strokeLinejoin="round"
|
|
106
|
+
>
|
|
107
|
+
<path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
|
|
108
|
+
<circle cx="12" cy="7" r="4"></circle>
|
|
109
|
+
</svg>
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
export const WithRichOptions: Story = {
|
|
113
|
+
args: {
|
|
114
|
+
label: "Assignee",
|
|
115
|
+
placeholder: "Pick a team member",
|
|
116
|
+
assistiveText:
|
|
117
|
+
"Each option can show a leading icon and supporting text — see MANTINE_ADAPTER_RICH_OPTION_DATA.md.",
|
|
118
|
+
data: [
|
|
119
|
+
{
|
|
120
|
+
value: "jdoe",
|
|
121
|
+
label: "Jane Doe",
|
|
122
|
+
leadingIcon: UserIcon,
|
|
123
|
+
supportingText: "jane.doe@example.com",
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
value: "asmith",
|
|
127
|
+
label: "Alex Smith",
|
|
128
|
+
leadingIcon: UserIcon,
|
|
129
|
+
supportingText: "alex.smith@example.com",
|
|
130
|
+
},
|
|
131
|
+
{ value: "unassigned", label: "Unassigned" },
|
|
132
|
+
],
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
export const WithRichOptionsWrapped: Story = {
|
|
137
|
+
args: {
|
|
138
|
+
label: "Assignee",
|
|
139
|
+
placeholder: "Pick a team member",
|
|
140
|
+
wrapItemText: true,
|
|
141
|
+
data: [
|
|
142
|
+
{
|
|
143
|
+
value: "jdoe",
|
|
144
|
+
label: "Jane Doe, Senior Staff Engineer, Platform Infrastructure",
|
|
145
|
+
leadingIcon: UserIcon,
|
|
146
|
+
supportingText:
|
|
147
|
+
"jane.doe@example.com — Platform Infrastructure team, on-call rotation lead",
|
|
148
|
+
},
|
|
149
|
+
{ value: "unassigned", label: "Unassigned" },
|
|
150
|
+
],
|
|
151
|
+
assistiveText:
|
|
152
|
+
"wrapItemText=true — long label/supportingText wrap instead of truncating.",
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const optionRowPreviewClassNames = {
|
|
157
|
+
optionContent: styles.optionContent,
|
|
158
|
+
optionIcon: styles.optionIcon,
|
|
159
|
+
optionText: styles.optionText,
|
|
160
|
+
optionTextWrap: styles.optionTextWrap,
|
|
161
|
+
optionSupportingText: styles.optionSupportingText,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const OPTION_ROW_PREVIEW_ITEMS = [
|
|
165
|
+
{
|
|
166
|
+
value: "icon-and-supporting",
|
|
167
|
+
label: "Jane Doe",
|
|
168
|
+
leadingIcon: UserIcon,
|
|
169
|
+
supportingText: "jane.doe@example.com",
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
value: "no-icon",
|
|
173
|
+
label: "Alex Smith",
|
|
174
|
+
supportingText:
|
|
175
|
+
"No leadingIcon — label/supportingText shift left, no reserved icon space",
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
value: "no-supporting-text",
|
|
179
|
+
label: "Taylor Rivera",
|
|
180
|
+
leadingIcon: UserIcon,
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
value: "plain",
|
|
184
|
+
label: "Plain option — no leadingIcon, no supportingText",
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
value: "long-text",
|
|
188
|
+
label:
|
|
189
|
+
"A very long option label that, with wrapItemText, wraps onto a second line instead of overflowing the fixed-width dropdown — otherwise it truncates with an ellipsis",
|
|
190
|
+
leadingIcon: UserIcon,
|
|
191
|
+
supportingText:
|
|
192
|
+
"A similarly long supporting text string, to confirm the same wrap-or-truncate behavior applies to it too",
|
|
193
|
+
},
|
|
194
|
+
];
|
|
195
|
+
|
|
196
|
+
// Renders the option row content directly — outside the floating/portal menu — inside a
|
|
197
|
+
// container sized to Dropdown's own max-width token. Spacing between rows, icon/supportingText
|
|
198
|
+
// presence-or-absence alignment, and long-text wrapping/truncation are all much easier to inspect
|
|
199
|
+
// this way than by opening the real (portal-rendered) combobox dropdown. See
|
|
200
|
+
// MANTINE_ADAPTER_RICH_OPTION_DATA.md.
|
|
201
|
+
const renderOptionRowPreview = (wrapItemText: boolean) => (
|
|
202
|
+
<div
|
|
203
|
+
className={styles.dropdown}
|
|
204
|
+
style={{
|
|
205
|
+
width:
|
|
206
|
+
"var(--recursica_ui-kit_components_dropdown_variants_layouts_stacked_properties_max-width)",
|
|
207
|
+
}}
|
|
208
|
+
>
|
|
209
|
+
{OPTION_ROW_PREVIEW_ITEMS.map((item) => (
|
|
210
|
+
<div key={item.value} className={styles.option}>
|
|
211
|
+
{renderRichOption(
|
|
212
|
+
{ option: item },
|
|
213
|
+
optionRowPreviewClassNames,
|
|
214
|
+
wrapItemText,
|
|
215
|
+
)}
|
|
216
|
+
</div>
|
|
217
|
+
))}
|
|
218
|
+
</div>
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
// Default: `wrapItemText` is false — label/supportingText truncate to a single line with an
|
|
222
|
+
// ellipsis instead of wrapping.
|
|
223
|
+
export const RichOptionRowPreview: Story = {
|
|
224
|
+
parameters: {
|
|
225
|
+
controls: { disable: true },
|
|
226
|
+
},
|
|
227
|
+
render: () => renderOptionRowPreview(false),
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
// `wrapItemText: true` — label/supportingText wrap onto additional lines instead of truncating.
|
|
231
|
+
export const RichOptionRowPreviewWrapped: Story = {
|
|
232
|
+
parameters: {
|
|
233
|
+
controls: { disable: true },
|
|
234
|
+
},
|
|
235
|
+
render: () => renderOptionRowPreview(true),
|
|
236
|
+
};
|
|
237
|
+
|
|
89
238
|
export const StaticError: Story = {
|
|
90
239
|
args: {
|
|
91
240
|
error: "You must choose a valid destination.",
|
|
@@ -3,7 +3,10 @@ import {
|
|
|
3
3
|
Select as MantineSelect,
|
|
4
4
|
type SelectProps as MantineSelectProps,
|
|
5
5
|
} from "@mantine/core";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
type ReadOnlyControlProps,
|
|
8
|
+
normalizeComboboxData,
|
|
9
|
+
} from "@recursica/adapter-common";
|
|
7
10
|
import {
|
|
8
11
|
filterStylingProps,
|
|
9
12
|
omitUnsupportedProps,
|
|
@@ -12,6 +15,7 @@ import {
|
|
|
12
15
|
} from "../../utils/filterStylingProps";
|
|
13
16
|
import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
|
|
14
17
|
import { WithReadOnlyWrapper } from "../ReadOnlyField/WithReadOnlyWrapper";
|
|
18
|
+
import { renderRichOption } from "../../utils/renderRichOption";
|
|
15
19
|
import styles from "./Dropdown.module.css";
|
|
16
20
|
|
|
17
21
|
import { type RecursicaDropdownProps as BaseRecursicaDropdownProps } from "@recursica/adapter-common";
|
|
@@ -69,6 +73,8 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
69
73
|
defaultValue,
|
|
70
74
|
data,
|
|
71
75
|
clearButtonProps,
|
|
76
|
+
renderOption,
|
|
77
|
+
wrapItemText = false,
|
|
72
78
|
...rest
|
|
73
79
|
} = props;
|
|
74
80
|
const sanitizedProps = omitUnsupportedProps(
|
|
@@ -77,6 +83,13 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
77
83
|
);
|
|
78
84
|
const restRecord = sanitizedProps as Record<string, unknown>;
|
|
79
85
|
|
|
86
|
+
// Mantine's own data parser only preserves extra fields (`leadingIcon`/`supportingText`) when
|
|
87
|
+
// an item already has both `value` and `label` — an item with `value` only is rebuilt into a
|
|
88
|
+
// bare `{value, label: value, disabled}` object, silently dropping them (see
|
|
89
|
+
// get-parsed-combobox-data.mjs, and AutoComplete.tsx's identical use of this). Matters here
|
|
90
|
+
// too now that `label` is optional (shared `RecursicaComboboxItem` — see adapter-common).
|
|
91
|
+
const normalizedData = normalizeComboboxData(data);
|
|
92
|
+
|
|
80
93
|
// Mantine's own clear button (rendered when `clearable` + a value are both present) otherwise
|
|
81
94
|
// renders unstyled — bare `CloseButton` defaults, no Recursica icon-button treatment. Merge in
|
|
82
95
|
// our own class (see `.clearButton` in Dropdown.module.css) alongside anything the caller
|
|
@@ -104,6 +117,14 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
104
117
|
restRecord.classNames as Partial<Record<string, string>> | undefined,
|
|
105
118
|
);
|
|
106
119
|
|
|
120
|
+
const optionClassNames = {
|
|
121
|
+
optionContent: styles.optionContent,
|
|
122
|
+
optionIcon: styles.optionIcon,
|
|
123
|
+
optionText: styles.optionText,
|
|
124
|
+
optionTextWrap: styles.optionTextWrap,
|
|
125
|
+
optionSupportingText: styles.optionSupportingText,
|
|
126
|
+
};
|
|
127
|
+
|
|
107
128
|
const injectedStyles = {
|
|
108
129
|
...((style as React.CSSProperties) || {}),
|
|
109
130
|
width: containerWidth || "100%",
|
|
@@ -154,7 +175,14 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
|
|
|
154
175
|
disabled={disabled}
|
|
155
176
|
value={value}
|
|
156
177
|
defaultValue={defaultValue}
|
|
157
|
-
data={
|
|
178
|
+
data={
|
|
179
|
+
(normalizedData as unknown as MantineSelectProps["data"]) || []
|
|
180
|
+
}
|
|
181
|
+
renderOption={
|
|
182
|
+
renderOption ??
|
|
183
|
+
((input) =>
|
|
184
|
+
renderRichOption(input, optionClassNames, wrapItemText))
|
|
185
|
+
}
|
|
158
186
|
clearButtonProps={mergedClearButtonProps}
|
|
159
187
|
label={undefined}
|
|
160
188
|
description={undefined}
|
|
@@ -46,3 +46,24 @@ All Recursica components in the `@recursica/mantine-adapter` package adhere stri
|
|
|
46
46
|
|
|
47
47
|
- `Dropdown` is styled independently from `TextField`; even though they look similar, they are themed using separate design tokens.
|
|
48
48
|
- Pass `leftSection` for a leading icon, and `clearable` (with a value present) to show a clear button — both render using the dropdown's own icon-color tokens.
|
|
49
|
+
- `data` items can carry a `leadingIcon` and `supportingText`, rendered inside each option row:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
<Dropdown
|
|
53
|
+
label="Assignee"
|
|
54
|
+
data={[
|
|
55
|
+
{
|
|
56
|
+
value: "jdoe",
|
|
57
|
+
label: "Jane Doe",
|
|
58
|
+
leadingIcon: <UserIcon />,
|
|
59
|
+
supportingText: "jane.doe@example.com",
|
|
60
|
+
},
|
|
61
|
+
{ value: "asmith", label: "Alex Smith" },
|
|
62
|
+
]}
|
|
63
|
+
/>
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Pass your own `renderOption` to opt out of this default rendering for a given instance.
|
|
67
|
+
|
|
68
|
+
By default `label`/`supportingText` truncate to a single line with an ellipsis. Set
|
|
69
|
+
`wrapItemText` to wrap them onto additional lines instead: `<Dropdown data={data} wrapItemText />`.
|
|
@@ -136,7 +136,10 @@ roving group, _then_ the trailing clear button as its own stop.
|
|
|
136
136
|
wraps onto multiple lines below the dropzone — `FileInput` is a fixed single-line, `min-height`d
|
|
137
137
|
control, so wrapping would grow it vertically. Enough chips to overflow the control's own width
|
|
138
138
|
scroll horizontally within it instead (mouse wheel/trackpad or a native scrollbar), same tradeoff
|
|
139
|
-
already made for `.value`'s ellipsis truncation.
|
|
139
|
+
already made for `.value`'s ellipsis truncation. The native scrollbar itself is hidden
|
|
140
|
+
(`scrollbar-width: none`, `-ms-overflow-style: none`, `::-webkit-scrollbar { display: none }`) —
|
|
141
|
+
the control's `min-height` isn't tall enough to host a visible horizontal scrollbar without
|
|
142
|
+
clipping the chips, and scrolling (wheel/trackpad/drag) still works with it hidden.
|
|
140
143
|
|
|
141
144
|
## Read-only vs disabled
|
|
142
145
|
|
|
@@ -1,3 +1,13 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_focus_blur
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_focus_border-size
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_focus_color
|
|
6
|
+
* recursica-allow-brand: --recursica_brand_states_focus_margin
|
|
7
|
+
* recursica-allow-brand: --recursica_brand_states_hover_color
|
|
8
|
+
* recursica-allow-brand: --recursica_brand_states_hover_opacity
|
|
9
|
+
*/
|
|
10
|
+
|
|
1
11
|
/* LAYOUT SPACING OVERRIDES:
|
|
2
12
|
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
|
|
3
13
|
- Also sets the --file-input-control-{max,min}-width hooks consumed inline in FileInput.tsx,
|
|
@@ -178,6 +188,13 @@
|
|
|
178
188
|
overflow-x: auto;
|
|
179
189
|
overflow-y: hidden;
|
|
180
190
|
gap: var(--recursica_ui-kit_components_file-input_properties_icon-text-gap);
|
|
191
|
+
/* HARDCODE: scrollbar stays functional (still scrolls via wheel/trackpad/drag) but hidden —
|
|
192
|
+
the control isn't tall enough to host a visible scrollbar without clipping the chips. */
|
|
193
|
+
scrollbar-width: none; /* Firefox */
|
|
194
|
+
-ms-overflow-style: none; /* legacy Edge */
|
|
195
|
+
}
|
|
196
|
+
.chipRow::-webkit-scrollbar {
|
|
197
|
+
display: none; /* Chrome, Safari */
|
|
181
198
|
}
|
|
182
199
|
|
|
183
200
|
.chipWrapper {
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_hover_color
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_hover_opacity
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
/* HARDCODED VALUES:
|
|
2
8
|
- .root: display: flex; flex-direction: column; width: 100% — structural layout, not a design
|
|
3
9
|
token concern (matches how other block-level components like Layer are laid out).
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Only source for link hover-underline; no per-component token exists (see IMPLEMENTATION_NOTES.md).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_link_decoration
|
|
4
|
+
*/
|
|
5
|
+
|
|
1
6
|
/*
|
|
2
7
|
HARDCODED VALUES:
|
|
3
8
|
- display: inline-flex; (Base layout to align icon and text correctly)
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_hover_color
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_hover_opacity
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
/* HARDCODED VALUES:
|
|
2
8
|
- border-style: solid. Structural rendering rule for the dropdown border (Mantine uses Paper which may not set border natively).
|
|
3
9
|
- NOTE: overflow is intentionally NOT set on .dropdown. Mantine renders sub-menu dropdowns
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_focus_blur
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_focus_border-size
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_focus_color
|
|
6
|
+
* recursica-allow-brand: --recursica_brand_states_focus_margin
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/* LAYOUT SPACING OVERRIDES:
|
|
2
10
|
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
|
|
3
11
|
- Also sets the --number-input-control-{max,min}-width hooks consumed inline in NumberInput.tsx,
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_disabled
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_hover_color
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_hover_opacity
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
.root {
|
|
2
9
|
display: flex;
|
|
3
10
|
align-items: center;
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_focus_blur
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_focus_border-size
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_focus_color
|
|
6
|
+
* recursica-allow-brand: --recursica_brand_states_focus_margin
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/*
|
|
2
10
|
* HARDCODED VALUES:
|
|
3
11
|
* - display: flex; align-items: center; width: 100%; (Standard CSS flexbox layouts for bidirectional components)
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_focus_blur
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_focus_border-size
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_focus_color
|
|
6
|
+
* recursica-allow-brand: --recursica_brand_states_focus_margin
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/* HARDCODED VALUES
|
|
2
10
|
*
|
|
3
11
|
* 1. track border: none; (we do not use border for the switch track)
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_hover_color
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_hover_opacity
|
|
5
|
+
*/
|
|
6
|
+
|
|
1
7
|
/* HARDCODED VALUES:
|
|
2
8
|
* - border-collapse: separate; border-spacing: 0; (To support custom border-radius on table elements)
|
|
3
9
|
* - overflow: hidden; (To prevent content from bleeding outside the rounded table border)
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_hover_color
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_hover_opacity
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_disabled
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
/*
|
|
2
9
|
Recursica Tabs CSS module natively binding to Figma variables.
|
|
3
10
|
*/
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_focus_blur
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_focus_border-size
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_focus_color
|
|
6
|
+
* recursica-allow-brand: --recursica_brand_states_focus_margin
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/* LAYOUT SPACING OVERRIDES:
|
|
2
10
|
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
|
|
3
11
|
- Also sets the --textarea-control-{max,min}-width hooks consumed inline in TextArea.tsx, since
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_focus_blur
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_focus_border-size
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_focus_color
|
|
6
|
+
* recursica-allow-brand: --recursica_brand_states_focus_margin
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/* LAYOUT SPACING OVERRIDES:
|
|
2
10
|
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
|
|
3
11
|
- Also sets the --text-field-control-{max,min}-width hooks consumed inline in TextField.tsx,
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_focus_blur
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_focus_border-size
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_focus_color
|
|
6
|
+
* recursica-allow-brand: --recursica_brand_states_focus_margin
|
|
7
|
+
*/
|
|
8
|
+
|
|
1
9
|
/* LAYOUT SPACING OVERRIDES:
|
|
2
10
|
- Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens. */
|
|
3
11
|
.layoutOverride {
|
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
/* Brand-layer exemptions (recursica-allow-brand) — see recursica-token-analyzer README.md.
|
|
2
|
+
* Global hover/focus/disabled state tokens (recursica_variables_scoped.css header, 'Hover & Focus states' / 'Disabled state' — implicit rule for every interactive element; components must not define their own per-component treatment).
|
|
3
|
+
* recursica-allow-brand: --recursica_brand_states_disabled
|
|
4
|
+
* recursica-allow-brand: --recursica_brand_states_focus_blur
|
|
5
|
+
* recursica-allow-brand: --recursica_brand_states_focus_border-size
|
|
6
|
+
* recursica-allow-brand: --recursica_brand_states_focus_color
|
|
7
|
+
* recursica-allow-brand: --recursica_brand_states_focus_margin
|
|
8
|
+
* recursica-allow-brand: --recursica_brand_states_hover_color
|
|
9
|
+
* recursica-allow-brand: --recursica_brand_states_hover_opacity
|
|
10
|
+
*/
|
|
11
|
+
|
|
1
12
|
/* HARDCODED VALUES:
|
|
2
13
|
* - list-style/margin/padding resets on .root/.subtree: layout resets, no corresponding
|
|
3
14
|
* design tokens (structural, not visual design values).
|