@recursica/mantine-adapter 0.42.0 → 0.43.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 (86) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/index.d.ts +93 -15
  3. package/dist/mantine-adapter.cjs +2 -2
  4. package/dist/mantine-adapter.cjs.map +1 -1
  5. package/dist/mantine-adapter.css +1 -1
  6. package/dist/mantine-adapter.js +3546 -3250
  7. package/dist/mantine-adapter.js.map +1 -1
  8. package/package.json +1 -1
  9. package/src/GlobalExemptions.modules.css +0 -6
  10. package/src/components/Accordion/Accordion.module.css +0 -8
  11. package/src/components/Accordion/Accordion.tsx +32 -33
  12. package/src/components/AssistiveElement/AssistiveElement.tsx +1 -1
  13. package/src/components/AutoComplete/AutoComplete.module.css +0 -7
  14. package/src/components/AutoComplete/AutoComplete.tsx +26 -38
  15. package/src/components/Avatar/Avatar.module.css +0 -8
  16. package/src/components/Avatar/Avatar.tsx +22 -23
  17. package/src/components/Badge/Badge.tsx +12 -18
  18. package/src/components/Breadcrumb/Breadcrumb.tsx +10 -18
  19. package/src/components/Button/Button.tsx +31 -30
  20. package/src/components/Card/Card.tsx +48 -27
  21. package/src/components/Checkbox/Checkbox.tsx +29 -42
  22. package/src/components/Checkbox/CheckboxGroup.tsx +12 -5
  23. package/src/components/Chip/Chip.module.css +0 -16
  24. package/src/components/Chip/Chip.tsx +34 -27
  25. package/src/components/DatePicker/DATEPICKER_IMPLEMENTATION_NOTES.md +14 -1
  26. package/src/components/DatePicker/DatePicker.icons.tsx +27 -0
  27. package/src/components/DatePicker/DatePicker.module.css +256 -7
  28. package/src/components/DatePicker/DatePicker.stories.tsx +0 -7
  29. package/src/components/DatePicker/DatePicker.tsx +68 -33
  30. package/src/components/DatePicker/USAGE.md +6 -2
  31. package/src/components/Dropdown/BareDropdown.tsx +25 -13
  32. package/src/components/Dropdown/Dropdown.module.css +0 -7
  33. package/src/components/Dropdown/Dropdown.tsx +25 -38
  34. package/src/components/FileInput/FileInput.module.css +0 -21
  35. package/src/components/FileInput/FileInput.tsx +1 -1
  36. package/src/components/FileUpload/FileUpload.module.css +0 -11
  37. package/src/components/FileUpload/FileUpload.tsx +1 -1
  38. package/src/components/HoverCard/HoverCard.module.css +1 -7
  39. package/src/components/HoverCard/HoverCard.tsx +18 -34
  40. package/src/components/Label/Label.module.css +0 -6
  41. package/src/components/Label/Label.tsx +20 -21
  42. package/src/components/Link/Link.module.css +1 -6
  43. package/src/components/Link/Link.tsx +6 -14
  44. package/src/components/Loader/Loader.tsx +8 -16
  45. package/src/components/Menu/Menu.module.css +0 -6
  46. package/src/components/Menu/Menu.tsx +55 -48
  47. package/src/components/Modal/Modal.module.css +0 -11
  48. package/src/components/Modal/Modal.tsx +134 -96
  49. package/src/components/NumberInput/NumberInput.module.css +0 -7
  50. package/src/components/NumberInput/NumberInput.tsx +14 -7
  51. package/src/components/Pagination/Pagination.module.css +0 -81
  52. package/src/components/Pagination/Pagination.tsx +25 -34
  53. package/src/components/Panel/Panel.tsx +19 -26
  54. package/src/components/Popover/Popover.tsx +19 -31
  55. package/src/components/Radio/Radio.tsx +32 -43
  56. package/src/components/Radio/RadioGroup.tsx +11 -4
  57. package/src/components/ReadOnlyField/ReadOnlyField.tsx +1 -1
  58. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +1 -1
  59. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +1 -1
  60. package/src/components/SegmentedControl/SegmentedControl.module.css +0 -1
  61. package/src/components/SegmentedControl/SegmentedControl.tsx +29 -33
  62. package/src/components/Slider/Slider.tsx +30 -43
  63. package/src/components/Stepper/Stepper.tsx +20 -13
  64. package/src/components/Switch/Switch.tsx +45 -45
  65. package/src/components/Switch/SwitchGroup.tsx +11 -4
  66. package/src/components/Table/Table.tsx +1 -1
  67. package/src/components/Tabs/Tabs.tsx +13 -6
  68. package/src/components/Text/Text.tsx +1 -1
  69. package/src/components/TextArea/TextArea.module.css +0 -7
  70. package/src/components/TextArea/TextArea.tsx +21 -25
  71. package/src/components/TextField/TextField.module.css +0 -7
  72. package/src/components/TextField/TextField.tsx +23 -30
  73. package/src/components/TimePicker/TimePicker.module.css +0 -16
  74. package/src/components/TimePicker/TimePicker.tsx +26 -12
  75. package/src/components/Timeline/Timeline.module.css +0 -6
  76. package/src/components/Timeline/Timeline.tsx +7 -21
  77. package/src/components/Timeline/TimelineItem.tsx +14 -26
  78. package/src/components/Title/Title.tsx +12 -2
  79. package/src/components/Toast/Toast.module.css +0 -7
  80. package/src/components/Toast/Toast.tsx +32 -31
  81. package/src/components/Tooltip/Tooltip.module.css +0 -9
  82. package/src/components/Tooltip/Tooltip.tsx +18 -34
  83. package/src/components/TransferList/TransferList.module.css +0 -8
  84. package/src/components/TransferList/TransferList.tsx +1 -1
  85. package/src/components/Tree/Tree.tsx +8 -8
  86. package/src/utils/filterStylingProps.ts +4 -0
@@ -6,6 +6,8 @@ import {
6
6
  import { type ReadOnlyControlProps } from "@recursica/adapter-common";
7
7
  import {
8
8
  filterStylingProps,
9
+ omitUnsupportedProps,
10
+ mergeClassNames,
9
11
  type RecursicaOverStyled,
10
12
  } from "../../utils/filterStylingProps";
11
13
  import { type RecursicaFormControlWrapperProps } from "../FormControlWrapper/FormControlWrapper";
@@ -28,6 +30,14 @@ export interface RecursicaDropdownProps
28
30
 
29
31
  export type DropdownProps = RecursicaOverStyled<RecursicaDropdownProps>;
30
32
 
33
+ // Props this component intentionally doesn't support — deleted at runtime so they can't leak
34
+ // through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
35
+ const UNSUPPORTED_PROPS = [
36
+ "size", // Recursica controls sizing via the `size` variant + design tokens, not raw dimensions.
37
+ "variant", // Colors/variants are token-driven; the library's native palette isn't exposed.
38
+ "radius", // Recursica does not expose the library's native corner-radius system.
39
+ ] as const satisfies readonly (keyof MantineSelectProps)[];
40
+
31
41
  export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
32
42
  function Dropdown(props, ref) {
33
43
  const {
@@ -60,46 +70,23 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
60
70
  data,
61
71
  ...rest
62
72
  } = props;
63
- const sanitizedProps = filterStylingProps(rest, overStyled);
73
+ const sanitizedProps = omitUnsupportedProps(
74
+ filterStylingProps(rest, overStyled) as Record<string, unknown>,
75
+ UNSUPPORTED_PROPS,
76
+ );
64
77
  const restRecord = sanitizedProps as Record<string, unknown>;
65
78
 
66
- // Delete prohibited sizing hooks from bypassing variables natively
67
- delete restRecord["size"];
68
- delete restRecord["variant"];
69
- delete restRecord["radius"];
70
-
71
79
  // Securely map core native blocks down ensuring nested CSS modules map precisely
72
- const mergedClassNames: Partial<Record<string, string>> = {
73
- wrapper: styles.root, // The nested Input internal relative wrapper bounding box
74
- input: styles.input,
75
- section: styles.section,
76
- dropdown: styles.dropdown,
77
- option: styles.option,
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.wrapper = o.wrapper
88
- ? `${styles.root} ${o.wrapper}`
89
- : styles.root;
90
- mergedClassNames.input = o.input
91
- ? `${styles.input} ${o.input}`
92
- : styles.input;
93
- mergedClassNames.section = o.section
94
- ? `${styles.section} ${o.section}`
95
- : styles.section;
96
- mergedClassNames.dropdown = o.dropdown
97
- ? `${styles.dropdown} ${o.dropdown}`
98
- : styles.dropdown;
99
- mergedClassNames.option = o.option
100
- ? `${styles.option} ${o.option}`
101
- : styles.option;
102
- }
80
+ const mergedClassNames = mergeClassNames(
81
+ {
82
+ wrapper: styles.root, // The nested Input internal relative wrapper bounding box
83
+ input: styles.input,
84
+ section: styles.section,
85
+ dropdown: styles.dropdown,
86
+ option: styles.option,
87
+ },
88
+ restRecord.classNames as Partial<Record<string, string>> | undefined,
89
+ );
103
90
 
104
91
  const injectedStyles = {
105
92
  ...((style as React.CSSProperties) || {}),
@@ -146,6 +133,7 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
146
133
  /* Naked Select execution safely decoupled from Mantine's macro Input.Wrapper DOM hooks */
147
134
  <MantineSelect
148
135
  ref={ref}
136
+ {...(sanitizedProps as unknown as MantineSelectProps)}
149
137
  classNames={mergedClassNames}
150
138
  disabled={disabled}
151
139
  value={value}
@@ -169,7 +157,6 @@ export const Dropdown = forwardRef<HTMLInputElement, DropdownProps>(
169
157
  "data-error": error ? "true" : undefined,
170
158
  },
171
159
  }}
172
- {...(sanitizedProps as unknown as MantineSelectProps)}
173
160
  />
174
161
  }
175
162
  />
@@ -1,24 +1,3 @@
1
- /* EXEMPTIONS:
2
- - The disabled/error states' own border-size variables are ignored so the border never changes
3
- thickness across states and shifts the layout — same house policy as TextField/DatePicker.
4
- The flat, state-agnostic properties_border-size token below is applied uniformly instead.
5
- - There is no forge-defined focus state for file-input (no `states.focus` axis, unlike its
6
- disabled/error siblings) — the generic recursica_brand_states_focus_* ring is used instead,
7
- same fallback TextField/DatePicker use for the same reason.
8
- - properties_icon-text-gap doubles as the gap between chips: file-input has no dedicated
9
- chip-gap token (unlike file-upload's properties_item-gap).
10
- - The trailing clear-all affordance is now a real shared `Button` (icon-only, "text" variant)
11
- rather than a bespoke span, so it gets its own real button semantics/keyboard handling —
12
- same pattern as Tree's expand/collapse button. It renders through Button's own tokened
13
- color states (including disabled), so file-input's own trailing-icon color tokens — which
14
- have no "clear button is disabled/errored" equivalent state of their own anyway — are no
15
- longer consumed here. */
16
- /* recursica-ignore: --recursica_ui-kit_components_file-input_variants_states_disabled_properties_border-size */
17
- /* recursica-ignore: --recursica_ui-kit_components_file-input_variants_states_error_properties_border-size */
18
- /* recursica-ignore: --recursica_ui-kit_components_file-input_properties_colors_trailing-icon */
19
- /* recursica-ignore: --recursica_ui-kit_components_file-input_variants_states_error_properties_colors_trailing-icon */
20
- /* recursica-ignore: --recursica_ui-kit_components_file-input_variants_states_disabled_properties_colors_trailing-icon */
21
-
22
1
  /* LAYOUT SPACING OVERRIDES:
23
2
  - Sets the --form-control-margin-bottom spacing hook to map component-specific layout tokens.
24
3
  - Also sets the --file-input-control-{max,min}-width hooks consumed inline in FileInput.tsx,
@@ -309,6 +309,7 @@ export const FileInput = forwardRef<HTMLDivElement, FileInputProps>(
309
309
  >
310
310
  <div
311
311
  ref={ref}
312
+ {...restRecord}
312
313
  className={styles.root}
313
314
  role="button"
314
315
  aria-label={browseLabel}
@@ -324,7 +325,6 @@ export const FileInput = forwardRef<HTMLDivElement, FileInputProps>(
324
325
  onDragLeave={interactive ? handleDragLeave : undefined}
325
326
  onDragOver={interactive ? handleDragOver : undefined}
326
327
  onDrop={interactive ? handleDrop : undefined}
327
- {...restRecord}
328
328
  >
329
329
  <span className={styles.leadingIcon} aria-hidden>
330
330
  {icon ?? <UploadIcon />}
@@ -1,14 +1,3 @@
1
- /* EXEMPTIONS:
2
- - border-style has no usable var(...) form: the Figma-exported token's value is the literal
3
- string `"dashed"` (quotes included), which is not a valid CSS <line-style> keyword — the same
4
- class of "structural, not tokenized" property the canonical guide already treats border-style
5
- as (see .root's own HARDCODED VALUES note below and TimePicker/DatePicker's identical
6
- hardcoded `border-style: solid`). Hardcoded to the keyword `dashed` instead.
7
- - properties_icon-size has no equivalent: unlike TextField/DatePicker, file-upload has no
8
- dedicated icon-size token. Sized to visually match the Figma reference instead (see
9
- HARDCODED VALUES below). */
10
- /* recursica-ignore: --recursica_ui-kit_components_file-upload_properties_border-style */
11
-
12
1
  /* HARDCODED VALUES:
13
2
  - .root: display: flex; flex-direction: column; width: 100% — structural layout, not a design
14
3
  token concern (matches how other block-level components like Layer are laid out).
@@ -268,10 +268,10 @@ export const FileUpload = forwardRef<HTMLDivElement, FileUploadProps>(
268
268
  >
269
269
  <div
270
270
  ref={ref}
271
+ {...restRecord}
271
272
  className={styles.root}
272
273
  data-disabled={disabled ? "true" : undefined}
273
274
  data-error={effectiveError ? "true" : undefined}
274
- {...restRecord}
275
275
  >
276
276
  {!readOnly && (
277
277
  <div
@@ -5,13 +5,7 @@
5
5
  and positioning (-arrowSize/2) calculations that cannot be CSS-driven. The default matches
6
6
  the Recursica beak-size token (16px). See COMPONENT_ISSUES.md for details.
7
7
  - All structural layout (display, position, overflow) is deferred to Mantine's native
8
- behavior. We only override visual design tokens (colors, typography, spacing, borders).
9
- /* EXEMPTIONS:
10
- - beak-size is ignored here because Mantine's popover arrows compute their sizing and positional offsets
11
- dynamically within the React rendering engine (requires inline pixel number rather than standard CSS variables).
12
- The beak size is checked in JS against a fixed value of 16px. If this variable changes, the TSX components
13
- need to be reviewed/re-compiled. */
14
- /* recursica-ignore: --recursica_ui-kit_components_hover-card-popover_properties_beak-size = 16px */
8
+ behavior. We only override visual design tokens (colors, typography, spacing, borders). */
15
9
 
16
10
  /* ======================================
17
11
  DROPDOWN CONTAINER
@@ -6,6 +6,7 @@ import {
6
6
  } from "@mantine/core";
7
7
  import {
8
8
  filterStylingProps,
9
+ mergeClassNames,
9
10
  type RecursicaOverStyled,
10
11
  } from "../../utils/filterStylingProps";
11
12
  import styles from "./HoverCard.module.css";
@@ -42,54 +43,37 @@ export type HoverCardProps = RecursicaOverStyled<
42
43
  const HoverCardBase = function HoverCard({
43
44
  overStyled = false,
44
45
  withBeak = true,
46
+ position = "top", // Recursica default; Mantine defaults to "bottom". Consumers can still override.
47
+ // arrowSize must be a JS number prop — Mantine uses it for inline width/height
48
+ // and positioning offset (-arrowSize/2) calculations that cannot be CSS-driven.
49
+ // Default to 16 to match the Recursica beak-size token (16px). Consumers can still override.
50
+ arrowSize = 16,
51
+ withArrow,
45
52
  ...rest
46
53
  }: HoverCardProps) {
47
54
  const sanitizedProps = filterStylingProps(rest, overStyled);
55
+ const restRecord = sanitizedProps as Record<string, unknown>;
48
56
 
49
57
  // Bind CSS module classes to Mantine's internal classNames API
50
- const mergedClassNames: Partial<Record<string, string>> = {
51
- dropdown: styles.dropdown,
52
- arrow: styles.arrow,
53
- };
54
-
55
- const classNamesProp = (sanitizedProps as Record<string, unknown>).classNames;
56
- if (
57
- classNamesProp &&
58
- typeof classNamesProp === "object" &&
59
- !Array.isArray(classNamesProp)
60
- ) {
61
- const o = classNamesProp as Record<string, string>;
62
- Object.keys(o).forEach((key) => {
63
- if (mergedClassNames[key]) {
64
- mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
65
- } else {
66
- mergedClassNames[key] = o[key];
67
- }
68
- });
69
- }
70
-
71
- // arrowSize must be a JS number prop — Mantine uses it for inline width/height
72
- // and positioning offset (-arrowSize/2) calculations that cannot be CSS-driven.
73
- // Default to 16 to match the Recursica beak-size token (16px).
74
- const arrowSize =
75
- ((sanitizedProps as Record<string, unknown>).arrowSize as
76
- | number
77
- | undefined) ?? 16;
58
+ const mergedClassNames = mergeClassNames(
59
+ {
60
+ dropdown: styles.dropdown,
61
+ arrow: styles.arrow,
62
+ },
63
+ restRecord.classNames as Partial<Record<string, string>> | undefined,
64
+ );
78
65
 
79
66
  // Resolve withBeak (Recursica) vs withArrow (Mantine).
80
67
  // withBeak takes precedence when both are provided.
81
- const withArrow = (sanitizedProps as Record<string, unknown>).withArrow as
82
- | boolean
83
- | undefined;
84
68
  const resolvedWithArrow = withBeak ?? withArrow;
85
69
 
86
70
  return (
87
71
  <MantineHoverCard
88
- position="top" /* Recursica default; Mantine defaults to "bottom" */
72
+ {...(sanitizedProps as unknown as MantineHoverCardProps)}
73
+ position={position}
89
74
  arrowSize={arrowSize}
90
75
  withArrow={resolvedWithArrow}
91
76
  classNames={mergedClassNames}
92
- {...(sanitizedProps as unknown as MantineHoverCardProps)}
93
77
  />
94
78
  );
95
79
  };
@@ -128,8 +112,8 @@ const HoverCardDropdown = function HoverCardDropdown({
128
112
 
129
113
  return (
130
114
  <MantineHoverCard.Dropdown
131
- className={classNameProp}
132
115
  {...(sanitizedProps as unknown as MantineHoverCardDropdownProps)}
116
+ className={classNameProp}
133
117
  />
134
118
  );
135
119
  };
@@ -1,14 +1,8 @@
1
- /* EXEMPTIONS:
2
- - layouts_stacked sizes width variables are ignored because stacked labels naturally occupy 100% width block-level
3
- real estate in HTML rendering, making explicit width properties redundant. */
4
-
5
1
  /* HARDCODED VALUES:
6
2
  - display: flex helps us leverage flex 'order' to perfectly position Mantine's injected asterisk.
7
3
  - align-items: baseline ensures text logic lines up nicely if font sizes differ slightly.
8
4
  - margin/padding resets.
9
5
  */
10
- /* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_stacked_variants_sizes_default_properties_width */
11
- /* recursica-ignore: --recursica_ui-kit_components_label_variants_layouts_stacked_variants_sizes_small_properties_width */
12
6
 
13
7
  .root {
14
8
  display: flex;
@@ -2,6 +2,8 @@ import React, { forwardRef } from "react";
2
2
  import { Input, type InputLabelProps } from "@mantine/core";
3
3
  import {
4
4
  filterStylingProps,
5
+ omitUnsupportedProps,
6
+ mergeClassNames,
5
7
  type RecursicaOverStyled,
6
8
  } from "../../utils/filterStylingProps";
7
9
  import styles from "./Label.module.css";
@@ -35,28 +37,25 @@ export const Label = forwardRef<HTMLLabelElement, LabelProps>(function Label(
35
37
  else if (labelOptionalText) resolvedOptionalText = labelOptionalText;
36
38
  }
37
39
 
38
- const sanitizedProps = filterStylingProps(rest, overStyled);
39
- const restRecord = sanitizedProps as Record<string, unknown>;
40
+ // Props this component intentionally doesn't support — deleted at runtime so they can't leak
41
+ // through even if a caller forces them via plain JavaScript, bypassing the `Omit<>` above.
42
+ const UNSUPPORTED_PROPS = [
43
+ "size", // Recursica controls label sizing via `labelSize` + design tokens, not Mantine's native `size`.
44
+ ] as const satisfies readonly (keyof InputLabelProps)[];
40
45
 
41
- const mergedClassNames: Partial<Record<string, string>> = {
42
- label: styles.root,
43
- required: styles.required,
44
- };
46
+ const sanitizedProps = omitUnsupportedProps(
47
+ filterStylingProps(rest, overStyled),
48
+ UNSUPPORTED_PROPS,
49
+ );
50
+ const restRecord = sanitizedProps as Record<string, unknown>;
45
51
 
46
- const classNamesProp = restRecord.classNames;
47
- if (
48
- classNamesProp &&
49
- typeof classNamesProp === "object" &&
50
- !Array.isArray(classNamesProp)
51
- ) {
52
- const o = classNamesProp as Partial<Record<string, string>>;
53
- mergedClassNames.label = o.label
54
- ? `${styles.root} ${o.label}`
55
- : styles.root;
56
- mergedClassNames.required = o.required
57
- ? `${styles.required} ${o.required}`
58
- : styles.required;
59
- }
52
+ const mergedClassNames = mergeClassNames(
53
+ {
54
+ label: styles.root,
55
+ required: styles.required,
56
+ },
57
+ restRecord.classNames as Partial<Record<string, string>> | undefined,
58
+ );
60
59
 
61
60
  const classNameProp = restRecord.className as string | undefined;
62
61
  const finalClass = classNameProp
@@ -66,12 +65,12 @@ export const Label = forwardRef<HTMLLabelElement, LabelProps>(function Label(
66
65
  return (
67
66
  <Input.Label
68
67
  ref={ref}
68
+ {...sanitizedProps}
69
69
  className={finalClass}
70
70
  classNames={mergedClassNames}
71
71
  data-size={labelSize}
72
72
  data-alignment={resolvedAlignment}
73
73
  required={required && !labelWithEditIcon}
74
- {...sanitizedProps}
75
74
  >
76
75
  <span className={styles.labelText}>{children}</span>
77
76
  {resolvedOptionalText && (
@@ -1,9 +1,4 @@
1
- /* EXEMPTIONS:
2
- - text_text-transform is ignored because links naturally inherit casing directly from text
3
- children rather than requiring custom CSS transforms. */
4
- /* recursica-ignore: --recursica_ui-kit_components_link_properties_text_text-transform */
5
-
6
- /*
1
+ /*
7
2
  HARDCODED VALUES:
8
3
  - display: inline-flex; (Base layout to align icon and text correctly)
9
4
  - align-items: center;
@@ -6,6 +6,7 @@ import {
6
6
  } from "@mantine/core";
7
7
  import {
8
8
  filterStylingProps,
9
+ mergeClassNames,
9
10
  type RecursicaOverStyled,
10
11
  } from "../../utils/filterStylingProps";
11
12
  import styles from "./Link.module.css";
@@ -23,19 +24,10 @@ const _Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
23
24
  const sanitizedProps = filterStylingProps(rest, overStyled);
24
25
  const restRecord = sanitizedProps as Record<string, unknown>;
25
26
 
26
- const mergedClassNames: Partial<Record<string, string>> = {
27
- root: styles.root,
28
- };
29
-
30
- const classNamesProp = restRecord.classNames;
31
- if (
32
- classNamesProp &&
33
- typeof classNamesProp === "object" &&
34
- !Array.isArray(classNamesProp)
35
- ) {
36
- const o = classNamesProp as Partial<Record<string, string>>;
37
- mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
38
- }
27
+ const mergedClassNames = mergeClassNames(
28
+ { root: styles.root },
29
+ restRecord.classNames as Partial<Record<string, string>> | undefined,
30
+ );
39
31
 
40
32
  const classNameProp = restRecord.className as string | undefined;
41
33
  const finalClass = classNameProp
@@ -45,11 +37,11 @@ const _Link = forwardRef<HTMLAnchorElement, LinkProps>(function Link(
45
37
  return (
46
38
  <MantineAnchor
47
39
  ref={ref}
40
+ {...(sanitizedProps as Record<string, unknown>)}
48
41
  className={finalClass}
49
42
  classNames={mergedClassNames}
50
43
  underline="never"
51
44
  {...(icon ? { "data-has-icon": "" } : {})}
52
- {...(sanitizedProps as Record<string, unknown>)}
53
45
  >
54
46
  {icon && (
55
47
  <span className={styles.iconWrapper} aria-hidden>
@@ -5,6 +5,7 @@ import {
5
5
  } from "@mantine/core";
6
6
  import {
7
7
  filterStylingProps,
8
+ mergeClassNames,
8
9
  type RecursicaOverStyled,
9
10
  } from "../../utils/filterStylingProps";
10
11
  import styles from "./Loader.module.css";
@@ -33,23 +34,14 @@ export const Loader = forwardRef<HTMLSpanElement, LoaderProps>(function Loader(
33
34
  // Strip all visual override injections unless developer has specifically opted into overStyling.
34
35
  // External layout props like margins are safely preserved.
35
36
  const sanitizedProps = filterStylingProps(rest, overStyled);
37
+ const restRecord = sanitizedProps as Record<string, unknown>;
36
38
 
37
- const mergedClassNames: Partial<Record<string, string>> = {
38
- root: styles.root,
39
- };
40
-
41
- const classNamesProp = (sanitizedProps as Record<string, unknown>).classNames;
42
- if (
43
- classNamesProp &&
44
- typeof classNamesProp === "object" &&
45
- !Array.isArray(classNamesProp)
46
- ) {
47
- const o = classNamesProp as Partial<Record<string, string>>;
48
- mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
49
- }
39
+ const mergedClassNames = mergeClassNames(
40
+ { root: styles.root },
41
+ restRecord.classNames as Partial<Record<string, string>> | undefined,
42
+ );
50
43
 
51
- const classNameProp = (sanitizedProps as Record<string, unknown>)
52
- .className as string | undefined;
44
+ const classNameProp = restRecord.className as string | undefined;
53
45
  const finalClass = classNameProp
54
46
  ? `${styles.root} ${classNameProp}`
55
47
  : styles.root;
@@ -57,10 +49,10 @@ export const Loader = forwardRef<HTMLSpanElement, LoaderProps>(function Loader(
57
49
  return (
58
50
  <MantineLoader
59
51
  ref={ref}
52
+ {...sanitizedProps}
60
53
  type={variant}
61
54
  data-variant={variant}
62
55
  data-size={resolvedSize}
63
- {...sanitizedProps}
64
56
  className={finalClass}
65
57
  classNames={mergedClassNames}
66
58
  />
@@ -1,9 +1,3 @@
1
- /* EXEMPTIONS:
2
- - colors_supporting-text-color (selected variant) is ignored because selected menu items do not
3
- render supporting secondary text, making the selected-state override for supporting text
4
- colors obsolete. */
5
- /* recursica-ignore: --recursica_ui-kit_components_menu-item_variants_selection-states_selected_properties_colors_supporting-text-color */
6
-
7
1
  /* HARDCODED VALUES:
8
2
  - border-style: solid. Structural rendering rule for the dropdown border (Mantine uses Paper which may not set border natively).
9
3
  - NOTE: overflow is intentionally NOT set on .dropdown. Mantine renders sub-menu dropdowns