@recursica/mantine-adapter 0.6.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 (140) 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 +660 -586
  7. package/dist/mantine-adapter.js.map +1 -1
  8. package/dist/src/components/Checkbox/CheckboxGroup.d.ts +1 -1
  9. package/dist/src/components/Chip/Chip.d.ts +14 -2
  10. package/package.json +5 -2
  11. package/src/OverStyling.stories.tsx +174 -0
  12. package/src/Version.stories.tsx +56 -0
  13. package/src/components/Accordion/ACCORDION_IMPLEMENTATION_NOTES.md +48 -0
  14. package/src/components/Accordion/Accordion.module.css +261 -0
  15. package/src/components/Accordion/Accordion.stories.tsx +145 -0
  16. package/src/components/Accordion/Accordion.tsx +200 -0
  17. package/src/components/Accordion/index.ts +1 -0
  18. package/src/components/AssistiveElement/AssistiveElement.module.css +111 -0
  19. package/src/components/AssistiveElement/AssistiveElement.stories.tsx +73 -0
  20. package/src/components/AssistiveElement/AssistiveElement.tsx +91 -0
  21. package/src/components/Avatar/AVATAR_IMPLEMENTATION_NOTES.md +24 -0
  22. package/src/components/Avatar/Avatar.module.css +340 -0
  23. package/src/components/Avatar/Avatar.stories.tsx +102 -0
  24. package/src/components/Avatar/Avatar.tsx +100 -0
  25. package/src/components/Avatar/index.ts +1 -0
  26. package/src/components/Badge/BADGE_IMPLEMENTATION_NOTES.md +18 -0
  27. package/src/components/Badge/Badge.module.css +124 -0
  28. package/src/components/Badge/Badge.stories.tsx +77 -0
  29. package/src/components/Badge/Badge.tsx +66 -0
  30. package/src/components/Breadcrumb/BREADCRUMB_IMPLEMENTATION_NOTES.md +14 -0
  31. package/src/components/Breadcrumb/Breadcrumb.module.css +26 -0
  32. package/src/components/Breadcrumb/Breadcrumb.stories.tsx +77 -0
  33. package/src/components/Breadcrumb/Breadcrumb.tsx +64 -0
  34. package/src/components/Button/Button.module.css +275 -0
  35. package/src/components/Button/Button.stories.tsx +105 -0
  36. package/src/components/Button/Button.tsx +122 -0
  37. package/src/components/Button/IMPLEMENTATION_NOTES.md +45 -0
  38. package/src/components/Button/index.ts +1 -0
  39. package/src/components/Card/CARD_IMPLEMENTATION_NOTES.md +10 -0
  40. package/src/components/Card/Card.module.css +60 -0
  41. package/src/components/Card/Card.stories.tsx +141 -0
  42. package/src/components/Card/Card.tsx +176 -0
  43. package/src/components/Checkbox/CHECKBOX_IMPLEMENTATION_NOTES.md +25 -0
  44. package/src/components/Checkbox/Checkbox.module.css +204 -0
  45. package/src/components/Checkbox/Checkbox.stories.tsx +73 -0
  46. package/src/components/Checkbox/Checkbox.tsx +111 -0
  47. package/src/components/Checkbox/CheckboxGroup.stories.tsx +138 -0
  48. package/src/components/Checkbox/CheckboxGroup.tsx +140 -0
  49. package/src/components/Chip/CHIP_IMPLEMENTATION_NOTES.md +35 -0
  50. package/src/components/Chip/Chip.module.css +218 -0
  51. package/src/components/Chip/Chip.stories.tsx +115 -0
  52. package/src/components/Chip/Chip.tsx +143 -0
  53. package/src/components/DatePicker/DatePicker.stories.tsx +22 -0
  54. package/src/components/DatePicker/DatePicker.tsx +7 -0
  55. package/src/components/Dropdown/Dropdown.stories.tsx +22 -0
  56. package/src/components/Dropdown/Dropdown.tsx +7 -0
  57. package/src/components/FileInput/FileInput.stories.tsx +22 -0
  58. package/src/components/FileInput/FileInput.tsx +7 -0
  59. package/src/components/FileUpload/FileUpload.stories.tsx +22 -0
  60. package/src/components/FileUpload/FileUpload.tsx +7 -0
  61. package/src/components/FormControlWrapper/FORMCONTROLWRAPPER_IMPLEMENTATION_NOTES.md +32 -0
  62. package/src/components/FormControlWrapper/FormControlWrapper.module.css +46 -0
  63. package/src/components/FormControlWrapper/FormControlWrapper.stories.tsx +116 -0
  64. package/src/components/FormControlWrapper/FormControlWrapper.tsx +170 -0
  65. package/src/components/HoverCard/HoverCard.stories.tsx +17 -0
  66. package/src/components/HoverCard/HoverCard.tsx +7 -0
  67. package/src/components/Label/LABEL_IMPLEMENTATION_NOTES.md +48 -0
  68. package/src/components/Label/Label.module.css +177 -0
  69. package/src/components/Label/Label.stories.tsx +123 -0
  70. package/src/components/Label/Label.tsx +132 -0
  71. package/src/components/Label/index.ts +1 -0
  72. package/src/components/Link/Link.stories.tsx +17 -0
  73. package/src/components/Link/Link.tsx +7 -0
  74. package/src/components/Loader/Loader.stories.tsx +17 -0
  75. package/src/components/Loader/Loader.tsx +7 -0
  76. package/src/components/Menu/Menu.stories.tsx +17 -0
  77. package/src/components/Menu/Menu.tsx +7 -0
  78. package/src/components/Modal/Modal.stories.tsx +17 -0
  79. package/src/components/Modal/Modal.tsx +7 -0
  80. package/src/components/NumberInput/NumberInput.stories.tsx +22 -0
  81. package/src/components/NumberInput/NumberInput.tsx +7 -0
  82. package/src/components/Pagination/Pagination.stories.tsx +17 -0
  83. package/src/components/Pagination/Pagination.tsx +7 -0
  84. package/src/components/Panel/Panel.stories.tsx +17 -0
  85. package/src/components/Panel/Panel.tsx +7 -0
  86. package/src/components/Popover/Popover.stories.tsx +17 -0
  87. package/src/components/Popover/Popover.tsx +7 -0
  88. package/src/components/Radio/Radio.stories.tsx +22 -0
  89. package/src/components/Radio/Radio.tsx +7 -0
  90. package/src/components/ReadOnlyField/READ_ONLY_FIELD_IMPLEMENTATION_NOTES.md +15 -0
  91. package/src/components/ReadOnlyField/ReadOnlyField.module.css +49 -0
  92. package/src/components/ReadOnlyField/ReadOnlyField.stories.tsx +120 -0
  93. package/src/components/ReadOnlyField/ReadOnlyField.tsx +75 -0
  94. package/src/components/ReadOnlyField/ReadOnlyTextField.tsx +46 -0
  95. package/src/components/ReadOnlyField/WithReadOnlyWrapper.tsx +85 -0
  96. package/src/components/ReadOnlyField/index.ts +3 -0
  97. package/src/components/Search/Search.stories.tsx +17 -0
  98. package/src/components/Search/Search.tsx +7 -0
  99. package/src/components/SegmentedControl/SegmentedControl.stories.tsx +22 -0
  100. package/src/components/SegmentedControl/SegmentedControl.tsx +7 -0
  101. package/src/components/Slider/Slider.stories.tsx +22 -0
  102. package/src/components/Slider/Slider.tsx +7 -0
  103. package/src/components/Stepper/Stepper.stories.tsx +17 -0
  104. package/src/components/Stepper/Stepper.tsx +7 -0
  105. package/src/components/Switch/Switch.stories.tsx +22 -0
  106. package/src/components/Switch/Switch.tsx +7 -0
  107. package/src/components/Table/Table.stories.tsx +17 -0
  108. package/src/components/Table/Table.tsx +7 -0
  109. package/src/components/Tabs/Tabs.stories.tsx +17 -0
  110. package/src/components/Tabs/Tabs.tsx +7 -0
  111. package/src/components/Text/Text.stories.tsx +88 -0
  112. package/src/components/Text/Text.tsx +54 -0
  113. package/src/components/TextArea/TextArea.stories.tsx +22 -0
  114. package/src/components/TextArea/TextArea.tsx +7 -0
  115. package/src/components/TextField/TEXTFIELD_IMPLEMENTATION_NOTES.md +19 -0
  116. package/src/components/TextField/TextField.module.css +225 -0
  117. package/src/components/TextField/TextField.stories.tsx +162 -0
  118. package/src/components/TextField/TextField.tsx +138 -0
  119. package/src/components/TimePicker/TimePicker.stories.tsx +22 -0
  120. package/src/components/TimePicker/TimePicker.tsx +7 -0
  121. package/src/components/Timeline/Timeline.stories.tsx +17 -0
  122. package/src/components/Timeline/Timeline.tsx +7 -0
  123. package/src/components/Title/Title.stories.tsx +63 -0
  124. package/src/components/Title/Title.tsx +45 -0
  125. package/src/components/Toast/Toast.stories.tsx +17 -0
  126. package/src/components/Toast/Toast.tsx +7 -0
  127. package/src/components/Tooltip/Tooltip.stories.tsx +17 -0
  128. package/src/components/Tooltip/Tooltip.tsx +7 -0
  129. package/src/components/TransferList/TransferList.stories.tsx +17 -0
  130. package/src/components/TransferList/TransferList.tsx +7 -0
  131. package/src/components/index.ts +39 -0
  132. package/src/env.d.ts +7 -0
  133. package/src/index.ts +3 -0
  134. package/src/types/index.ts +9 -0
  135. package/src/types/mantine.d.ts +7 -0
  136. package/src/types/scss-modules.d.ts +9 -0
  137. package/src/utils/ColorSchemeWrapper.tsx +27 -0
  138. package/src/utils/copyToClipboard.ts +36 -0
  139. package/src/utils/filterStylingProps.ts +139 -0
  140. package/src/utils/index.ts +1 -0
@@ -0,0 +1,200 @@
1
+ import React, { forwardRef } from "react";
2
+ import {
3
+ Accordion as MantineAccordion,
4
+ type AccordionProps as MantineAccordionProps,
5
+ type AccordionItemProps,
6
+ type AccordionControlProps,
7
+ type AccordionPanelProps,
8
+ } from "@mantine/core";
9
+ import {
10
+ filterStylingProps,
11
+ type RecursicaOverStyled,
12
+ } from "../../utils/filterStylingProps";
13
+ import styles from "./Accordion.module.css";
14
+
15
+ // ==== ACCORDION CONTAINER ====
16
+ export type AccordionProps = RecursicaOverStyled<MantineAccordionProps>;
17
+
18
+ const AccordionBase = function Accordion({
19
+ variant = "unstyled",
20
+ overStyled = false,
21
+ ...rest
22
+ }: AccordionProps) {
23
+ const sanitizedProps = filterStylingProps(rest, overStyled);
24
+
25
+ // Bind all deep CSS module references natively into the global class mapping schema
26
+ const mergedClassNames: Partial<Record<string, string>> = {
27
+ root: styles.root,
28
+ item: styles.item,
29
+ control: styles.control,
30
+ label: styles.label,
31
+ chevron: styles.chevron,
32
+ panel: styles.panel,
33
+ content: styles.content,
34
+ };
35
+
36
+ const classNamesProp = (sanitizedProps as Record<string, unknown>).classNames;
37
+ if (
38
+ classNamesProp &&
39
+ typeof classNamesProp === "object" &&
40
+ !Array.isArray(classNamesProp)
41
+ ) {
42
+ const o = classNamesProp as Record<string, string>;
43
+ Object.keys(o).forEach((key) => {
44
+ if (mergedClassNames[key]) {
45
+ mergedClassNames[key] = `${mergedClassNames[key]} ${o[key]}`;
46
+ } else {
47
+ mergedClassNames[key] = o[key];
48
+ }
49
+ });
50
+ }
51
+
52
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
53
+ .className as string | undefined;
54
+
55
+ return (
56
+ <MantineAccordion
57
+ variant={variant}
58
+ className={classNameProp}
59
+ classNames={mergedClassNames}
60
+ {...(sanitizedProps as unknown as MantineAccordionProps)}
61
+ />
62
+ );
63
+ };
64
+ AccordionBase.displayName = "Accordion";
65
+
66
+ // ==== ACCORDION ITEM ====
67
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
68
+ export interface RecursicaAccordionItemProps
69
+ extends Omit<AccordionItemProps, "className"> {}
70
+ // We need to omit and re-merge native props like in Badge
71
+ export type AccordionItemWrapperProps = RecursicaOverStyled<
72
+ AccordionItemProps & {
73
+ /**
74
+ * When provided alongside `leftIcon` or independently, this auto-generates the Accordion Control and Panel DOM layers natively.
75
+ */
76
+ title?: React.ReactNode;
77
+
78
+ /**
79
+ * Leading icon explicitly mapped into the Mantine `Accordion.Control` leftSection boundary natively.
80
+ */
81
+ leftIcon?: React.ReactNode;
82
+
83
+ /**
84
+ * Toggles the presence of the bottom trailing divider native to AccordionItems.
85
+ * @default true
86
+ */
87
+ divider?: boolean;
88
+ }
89
+ >;
90
+
91
+ export const AccordionItem = forwardRef<
92
+ HTMLDivElement,
93
+ AccordionItemWrapperProps
94
+ >(function AccordionItem(
95
+ { title, leftIcon, divider = true, children, overStyled = false, ...rest },
96
+ ref,
97
+ ) {
98
+ const sanitizedProps = filterStylingProps(rest, overStyled);
99
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
100
+ .className as string | undefined;
101
+
102
+ const finalClass =
103
+ [divider ? undefined : styles.noDivider, classNameProp]
104
+ .filter(Boolean)
105
+ .join(" ") || undefined;
106
+
107
+ // If the user utilizes the explicit 'title' prop from Recursica, we securely auto-construct the Mantine sub-hierarchy natively!
108
+ // If not, we defer to raw composable children (meaning the integrator maps `<Accordion.Control>` manually).
109
+ return (
110
+ <MantineAccordion.Item
111
+ ref={ref}
112
+ className={finalClass}
113
+ {...(sanitizedProps as unknown as AccordionItemProps)}
114
+ >
115
+ {title ? (
116
+ <>
117
+ <AccordionControl leftIcon={leftIcon}>{title}</AccordionControl>
118
+ <AccordionPanel>{children}</AccordionPanel>
119
+ </>
120
+ ) : (
121
+ children
122
+ )}
123
+ </MantineAccordion.Item>
124
+ );
125
+ });
126
+ AccordionItem.displayName = "AccordionItem";
127
+
128
+ // ==== ACCORDION CONTROL ====
129
+ export type AccordionControlWrapperProps = RecursicaOverStyled<
130
+ AccordionControlProps & {
131
+ /**
132
+ * Leading icon explicitly mapped into the Mantine `Accordion.Control` leftSection boundary natively.
133
+ */
134
+ leftIcon?: React.ReactNode;
135
+ }
136
+ >;
137
+
138
+ export const AccordionControl = forwardRef<
139
+ HTMLButtonElement,
140
+ AccordionControlWrapperProps
141
+ >(function AccordionControl(
142
+ { leftIcon, children, overStyled = false, ...rest },
143
+ ref,
144
+ ) {
145
+ const sanitizedProps = filterStylingProps(rest, overStyled);
146
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
147
+ .className as string | undefined;
148
+
149
+ return (
150
+ <MantineAccordion.Control
151
+ ref={ref}
152
+ className={classNameProp}
153
+ icon={
154
+ leftIcon ? (
155
+ <span className={styles.iconLeftWrapper} aria-hidden>
156
+ {leftIcon}
157
+ </span>
158
+ ) : undefined
159
+ }
160
+ {...(sanitizedProps as unknown as AccordionControlProps)}
161
+ >
162
+ {children}
163
+ </MantineAccordion.Control>
164
+ );
165
+ });
166
+ AccordionControl.displayName = "AccordionControl";
167
+
168
+ // ==== ACCORDION PANEL ====
169
+ export type AccordionPanelWrapperProps =
170
+ RecursicaOverStyled<AccordionPanelProps>;
171
+
172
+ export const AccordionPanel = forwardRef<
173
+ HTMLDivElement,
174
+ AccordionPanelWrapperProps
175
+ >(function AccordionPanel({ overStyled = false, ...rest }, ref) {
176
+ const sanitizedProps = filterStylingProps(rest, overStyled);
177
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
178
+ .className as string | undefined;
179
+
180
+ return (
181
+ <MantineAccordion.Panel
182
+ ref={ref}
183
+ className={classNameProp}
184
+ {...(sanitizedProps as unknown as AccordionPanelProps)}
185
+ />
186
+ );
187
+ });
188
+ AccordionPanel.displayName = "AccordionPanel";
189
+
190
+ // ==== DOT NOTATION EXPORT ====
191
+ type AccordionComponent = typeof AccordionBase & {
192
+ Item: typeof AccordionItem;
193
+ Control: typeof AccordionControl;
194
+ Panel: typeof AccordionPanel;
195
+ };
196
+
197
+ export const Accordion = AccordionBase as AccordionComponent;
198
+ Accordion.Item = AccordionItem;
199
+ Accordion.Control = AccordionControl;
200
+ Accordion.Panel = AccordionPanel;
@@ -0,0 +1 @@
1
+ export * from "./Accordion";
@@ -0,0 +1,111 @@
1
+ /**
2
+ * AssistiveElement.module.css
3
+ *
4
+ * HARDCODED VALUES:
5
+ * - margin / padding: 0 — Structural resets.
6
+ * - display: flex / align-items: flex-start — layout alignment.
7
+ */
8
+
9
+ .root {
10
+ display: flex !important;
11
+ align-items: flex-start !important; /* Forces the icon to align with the first line of text */
12
+ margin: 0 !important; /* HARDCODE: structural reset */
13
+ padding: 0 !important; /* HARDCODE: structural reset */
14
+
15
+ gap: var(
16
+ --recursica_ui-kit_components_assistive-element_properties_icon-text-gap
17
+ );
18
+ margin-top: var(
19
+ --recursica_ui-kit_components_assistive-element_properties_top-margin
20
+ ) !important;
21
+ max-width: var(
22
+ --recursica_ui-kit_components_assistive-element_properties_max-width
23
+ );
24
+ }
25
+
26
+ .textWrapper {
27
+ /* Text properties securely governed via layout mappings */
28
+ font-family: var(
29
+ --recursica_ui-kit_components_assistive-element_properties_text_font-family
30
+ );
31
+ font-size: var(
32
+ --recursica_ui-kit_components_assistive-element_properties_text_font-size
33
+ );
34
+ font-style: var(
35
+ --recursica_ui-kit_components_assistive-element_properties_text_font-style
36
+ );
37
+ font-weight: var(
38
+ --recursica_ui-kit_components_assistive-element_properties_text_font-weight
39
+ );
40
+ letter-spacing: var(
41
+ --recursica_ui-kit_components_assistive-element_properties_text_letter-spacing
42
+ );
43
+ line-height: var(
44
+ --recursica_ui-kit_components_assistive-element_properties_text_line-height
45
+ );
46
+ text-decoration: var(
47
+ --recursica_ui-kit_components_assistive-element_properties_text_text-decoration
48
+ );
49
+ text-transform: var(
50
+ --recursica_ui-kit_components_assistive-element_properties_text_text-transform
51
+ );
52
+
53
+ /* Fallback text wrap structures */
54
+ overflow-wrap: break-word;
55
+ white-space: normal;
56
+ }
57
+
58
+ .iconWrapper {
59
+ flex-shrink: 0 !important; /* Prevent squishing */
60
+ display: flex !important;
61
+ align-items: center !important;
62
+ justify-content: center !important;
63
+
64
+ width: var(
65
+ --recursica_ui-kit_components_assistive-element_properties_icon-size
66
+ ) !important;
67
+ height: calc(
68
+ var(
69
+ --recursica_ui-kit_components_assistive-element_properties_text_line-height
70
+ ) *
71
+ var(
72
+ --recursica_ui-kit_components_assistive-element_properties_text_font-size
73
+ )
74
+ ) !important; /* Forces exact centering bounds within the first text line vertically */
75
+ }
76
+
77
+ .iconWrapper svg {
78
+ width: var(
79
+ --recursica_ui-kit_components_assistive-element_properties_icon-size
80
+ ) !important;
81
+ height: var(
82
+ --recursica_ui-kit_components_assistive-element_properties_icon-size
83
+ ) !important;
84
+ }
85
+
86
+ /*
87
+ * VARIANT MAPPINGS
88
+ * Inherits colors natively mapped per variant.
89
+ */
90
+
91
+ .root[data-variant="help"] .textWrapper {
92
+ color: var(
93
+ --recursica_ui-kit_components_assistive-element_variants_types_help_properties_colors_text-color
94
+ ) !important;
95
+ }
96
+ .root[data-variant="help"] .iconWrapper svg {
97
+ color: var(
98
+ --recursica_ui-kit_components_assistive-element_variants_types_help_properties_colors_icon-color
99
+ ) !important;
100
+ }
101
+
102
+ .root[data-variant="error"] .textWrapper {
103
+ color: var(
104
+ --recursica_ui-kit_components_assistive-element_variants_types_error_properties_colors_text-color
105
+ ) !important;
106
+ }
107
+ .root[data-variant="error"] .iconWrapper svg {
108
+ color: var(
109
+ --recursica_ui-kit_components_assistive-element_variants_types_error_properties_colors_icon-color
110
+ ) !important;
111
+ }
@@ -0,0 +1,73 @@
1
+ import type { Meta, StoryObj } from "@storybook/react";
2
+ import { AssistiveElement } from "./AssistiveElement";
3
+ import { Layer } from "@recursica/adapter-common";
4
+ import React from "react";
5
+
6
+ const meta: Meta<typeof AssistiveElement> = {
7
+ title: "UI-Kit/AssistiveElement",
8
+ component: AssistiveElement,
9
+ tags: ["autodocs"],
10
+ parameters: {
11
+ docs: {
12
+ description: {
13
+ component:
14
+ "The `AssistiveElement` is a semantic structural primitive designed to standardize Helper and Error descriptive blocks natively beneath form components globally. By explicitly wiring to the `--recursica_ui-kit_components_assistive-element` layout tokens, this component safely injects custom SVGs (Alerts vs Info circles) alongside constrained flex-wrapping typography strings, preserving flawless line-height and alignment logic entirely decoupled from underlying input engine frameworks.",
15
+ },
16
+ },
17
+ },
18
+ argTypes: {
19
+ assistiveVariant: {
20
+ control: "radio",
21
+ options: ["help", "error"],
22
+ },
23
+ assistiveWithIcon: {
24
+ control: "boolean",
25
+ },
26
+ },
27
+ };
28
+
29
+ export default meta;
30
+
31
+ type Story = StoryObj<typeof AssistiveElement>;
32
+
33
+ export const DefaultHelp: Story = {
34
+ args: {
35
+ children:
36
+ "This is a standard assistive layout explaining specific configurations.",
37
+ assistiveVariant: "help",
38
+ assistiveWithIcon: true,
39
+ },
40
+ render: (args) => (
41
+ <Layer layer={0} style={{ padding: "48px" }}>
42
+ <AssistiveElement {...args} />
43
+ </Layer>
44
+ ),
45
+ };
46
+
47
+ export const ErrorState: Story = {
48
+ args: {
49
+ children:
50
+ "Invalid property. You must satisfy the constraints outlined above.",
51
+ assistiveVariant: "error",
52
+ assistiveWithIcon: true,
53
+ },
54
+ render: (args) => (
55
+ <Layer layer={0} style={{ padding: "48px" }}>
56
+ <AssistiveElement {...args} />
57
+ </Layer>
58
+ ),
59
+ };
60
+
61
+ export const NoIconHelp: Story = {
62
+ args: {
63
+ children:
64
+ "Fallback textual representation without visual injection targets.",
65
+ assistiveVariant: "help",
66
+ assistiveWithIcon: false,
67
+ },
68
+ render: (args) => (
69
+ <Layer layer={0} style={{ padding: "48px" }}>
70
+ <AssistiveElement {...args} />
71
+ </Layer>
72
+ ),
73
+ };
@@ -0,0 +1,91 @@
1
+ import React, { forwardRef } from "react";
2
+ import {
3
+ filterStylingProps,
4
+ type RecursicaOverStyled,
5
+ } from "../../utils/filterStylingProps";
6
+ import styles from "./AssistiveElement.module.css";
7
+
8
+ export interface RecursicaAssistiveElementProps {
9
+ /** The semantic variant driving the icon and text colors natively mapped across the UI Kit tokens. */
10
+ assistiveVariant?: "help" | "error";
11
+ /** Explicitly toggle the rendering of the variant-specific SVG bounding box. */
12
+ assistiveWithIcon?: boolean;
13
+ }
14
+
15
+ export type AssistiveElementProps = RecursicaOverStyled<
16
+ React.HTMLAttributes<HTMLDivElement> & RecursicaAssistiveElementProps
17
+ >;
18
+
19
+ const InfoIcon = () => (
20
+ <svg
21
+ xmlns="http://www.w3.org/2000/svg"
22
+ viewBox="0 0 24 24"
23
+ fill="none"
24
+ stroke="currentColor"
25
+ strokeWidth="2"
26
+ strokeLinecap="round"
27
+ strokeLinejoin="round"
28
+ >
29
+ <circle cx="12" cy="12" r="10" />
30
+ <path d="M12 16v-4" />
31
+ <path d="M12 8h.01" />
32
+ </svg>
33
+ );
34
+
35
+ const AlertIcon = () => (
36
+ <svg
37
+ xmlns="http://www.w3.org/2000/svg"
38
+ viewBox="0 0 24 24"
39
+ fill="none"
40
+ stroke="currentColor"
41
+ strokeWidth="2"
42
+ strokeLinecap="round"
43
+ strokeLinejoin="round"
44
+ >
45
+ <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" />
46
+ <path d="M12 9v4" />
47
+ <path d="M12 17h.01" />
48
+ </svg>
49
+ );
50
+
51
+ export const AssistiveElement = forwardRef<
52
+ HTMLDivElement,
53
+ AssistiveElementProps
54
+ >(function AssistiveElement(
55
+ {
56
+ assistiveVariant = "help",
57
+ assistiveWithIcon = true,
58
+ children,
59
+ className,
60
+ overStyled = false,
61
+ ...rest
62
+ },
63
+ ref,
64
+ ) {
65
+ const sanitizedProps = filterStylingProps(rest, overStyled);
66
+ const restRecord = sanitizedProps as Record<string, unknown>;
67
+
68
+ const rootClass = styles.root;
69
+ const finalClass = className ? `${rootClass} ${className}` : rootClass;
70
+
71
+ const IconComponent = assistiveVariant === "error" ? AlertIcon : InfoIcon;
72
+
73
+ return (
74
+ <div
75
+ ref={ref}
76
+ className={finalClass}
77
+ data-variant={assistiveVariant}
78
+ style={restRecord.style as React.CSSProperties}
79
+ {...restRecord} // Spread standard HTML attributes (like id, aria-*, role) natively.
80
+ >
81
+ {assistiveWithIcon && (
82
+ <span className={styles.iconWrapper}>
83
+ <IconComponent />
84
+ </span>
85
+ )}
86
+ <span className={styles.textWrapper}>{children}</span>
87
+ </div>
88
+ );
89
+ });
90
+
91
+ AssistiveElement.displayName = "AssistiveElement";
@@ -0,0 +1,24 @@
1
+ # Avatar Component Implementation Notes
2
+
3
+ ## Architecture Decisions
4
+
5
+ The `Avatar` component is an adapter over Mantine's `Avatar`. To ensure adherence to the `COMPONENT_GUIDE_WALKTHROUGH.md`:
6
+
7
+ - We do not wrap `MantineAvatar` in any custom standard `div` elements, preserving DOM structure.
8
+ - All styles strictly pull from explicit `--recursica_ui-kit_components_avatar_*` CSS tokens.
9
+
10
+ ## Structural Workarounds
11
+
12
+ ### Implicit `data-style`
13
+
14
+ Mantine's Avatar implicitly renders an image, an icon, or a text node based on the properties passed (`src`, `var`, `children`).
15
+ Recursica Tokens split Avatar styling distinctly across three separate categories: `image`, `icon`, and `text`.
16
+ To correctly map these variables, our React component observes standard prop states and manually injects a `data-style="image|icon|text"` onto the root. The `Avatar.module.css` explicitly gates padding and generic sizing modifiers under these `data-style` attributes.
17
+
18
+ ### Flex Layout & Internal Spans
19
+
20
+ Since Avatar children (icons or initials) require robust centering that might differ heavily across Recursica size mappings, all child content defaults to being wrapped in `span` elements (either `.textWrapper` or `.iconWrapper`). These spans enforce 100% height and flex formatting independent of the Mantine container constraints.
21
+
22
+ ### CSS Reset Hacks
23
+
24
+ Noticeable `/* HARDCODE: ... */` hacks are deployed within `.root` to completely zero-out Mantine's `--avatar-bg` and internal variables statically since Recursica handles background-colors inherently via the CSS variants cascade.