@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,102 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { Avatar } from "./Avatar";
4
+ import { Layer } from "@recursica/adapter-common";
5
+
6
+ type AvatarStoryProps = React.ComponentProps<typeof Avatar>;
7
+
8
+ const meta: Meta<AvatarStoryProps> = {
9
+ title: "UI-Kit/Avatar",
10
+ component: Avatar,
11
+ tags: ["autodocs"],
12
+ argTypes: {
13
+ variant: {
14
+ control: "select",
15
+ options: ["solid", "outline", "ghost"],
16
+ description:
17
+ "The visual variant of the avatar (applies to icon/text styles)",
18
+ },
19
+ size: {
20
+ control: "radio",
21
+ options: ["default", "small", "large"],
22
+ description: "The size of the avatar",
23
+ },
24
+ src: {
25
+ control: "text",
26
+ description: "Image URL for the image style avatar",
27
+ },
28
+ icon: {
29
+ table: {
30
+ disable: true,
31
+ },
32
+ },
33
+ },
34
+ };
35
+
36
+ export default meta;
37
+
38
+ type Story = StoryObj<AvatarStoryProps>;
39
+
40
+ export const Default: Story = {
41
+ args: {
42
+ size: "default",
43
+ variant: "solid",
44
+ },
45
+ render: (args) => {
46
+ return <Avatar {...args} />;
47
+ },
48
+ };
49
+
50
+ export const TextSolidDefault: Story = {
51
+ args: {
52
+ children: "JD",
53
+ variant: "solid",
54
+ size: "default",
55
+ },
56
+ render: (args) => <Avatar {...args} />,
57
+ };
58
+
59
+ export const ImageLarge: Story = {
60
+ args: {
61
+ src: "https://images.unsplash.com/photo-1535713875002-d1d0cf377fde?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=250&q=80",
62
+ size: "large",
63
+ },
64
+ render: (args) => <Avatar {...args} />,
65
+ };
66
+
67
+ export const IconSmallGhost: Story = {
68
+ args: {
69
+ size: "small",
70
+ variant: "ghost",
71
+ icon: (
72
+ <svg
73
+ xmlns="http://www.w3.org/2000/svg"
74
+ width="16"
75
+ height="16"
76
+ viewBox="0 0 24 24"
77
+ fill="none"
78
+ stroke="currentColor"
79
+ strokeWidth="2"
80
+ strokeLinecap="round"
81
+ strokeLinejoin="round"
82
+ >
83
+ <path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"></path>
84
+ <circle cx="12" cy="7" r="4"></circle>
85
+ </svg>
86
+ ),
87
+ },
88
+ render: (args) => <Avatar {...args} />,
89
+ };
90
+
91
+ export const LayerOneOutline: Story = {
92
+ args: {
93
+ children: "L1",
94
+ variant: "outline",
95
+ size: "default",
96
+ },
97
+ render: (args) => (
98
+ <Layer layer={1} style={{ padding: "24px", display: "inline-block" }}>
99
+ <Avatar {...args} />
100
+ </Layer>
101
+ ),
102
+ };
@@ -0,0 +1,100 @@
1
+ import React, { forwardRef } from "react";
2
+ import {
3
+ Avatar as MantineAvatar,
4
+ type AvatarProps as MantineAvatarProps,
5
+ } from "@mantine/core";
6
+ import styles from "./Avatar.module.css";
7
+
8
+ export interface RecursicaAvatarProps {
9
+ size?: "small" | "default" | "large";
10
+ variant?: "solid" | "outline" | "ghost";
11
+ icon?: React.ReactNode;
12
+ }
13
+
14
+ export type AvatarProps = Omit<
15
+ MantineAvatarProps,
16
+ "variant" | "size" | "color" | "radius"
17
+ > &
18
+ RecursicaAvatarProps;
19
+
20
+ export const Avatar = forwardRef<HTMLDivElement, AvatarProps>(function Avatar(
21
+ {
22
+ size = "default",
23
+ variant = "solid",
24
+ icon,
25
+ className,
26
+ classNames,
27
+ children,
28
+ style,
29
+ src,
30
+ ...rest
31
+ },
32
+ ref,
33
+ ) {
34
+ const mapVariant = {
35
+ solid: "filled",
36
+ outline: "outline",
37
+ ghost: "transparent",
38
+ } as const;
39
+
40
+ const mapSize = {
41
+ default: "md",
42
+ small: "sm",
43
+ large: "lg",
44
+ } as const;
45
+
46
+ // Determine semantic style based on provided props
47
+ let computedStyle = "text";
48
+ if (src) {
49
+ computedStyle = "image";
50
+ } else if (icon) {
51
+ computedStyle = "icon";
52
+ }
53
+
54
+ const mergedClassNames: Partial<Record<string, string>> = {
55
+ root: styles.root,
56
+ image: styles.image,
57
+ placeholder: styles.placeholder,
58
+ };
59
+
60
+ if (
61
+ classNames &&
62
+ typeof classNames === "object" &&
63
+ !Array.isArray(classNames)
64
+ ) {
65
+ const o = classNames as Partial<Record<string, string>>;
66
+ mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
67
+ mergedClassNames.image = o.image
68
+ ? `${styles.image} ${o.image}`
69
+ : styles.image;
70
+ mergedClassNames.placeholder = o.placeholder
71
+ ? `${styles.placeholder} ${o.placeholder}`
72
+ : styles.placeholder;
73
+ }
74
+
75
+ return (
76
+ <MantineAvatar
77
+ ref={ref}
78
+ className={className}
79
+ classNames={mergedClassNames}
80
+ variant={mapVariant[variant]}
81
+ size={mapSize[size]}
82
+ style={style}
83
+ src={src}
84
+ data-variant={variant}
85
+ data-size={size}
86
+ data-style={computedStyle}
87
+ {...rest}
88
+ >
89
+ {icon != null ? (
90
+ <span className={styles.iconWrapper} aria-hidden>
91
+ {icon}
92
+ </span>
93
+ ) : children != null ? (
94
+ <span className={styles.textWrapper}>{children}</span>
95
+ ) : undefined}
96
+ </MantineAvatar>
97
+ );
98
+ });
99
+
100
+ Avatar.displayName = "Avatar";
@@ -0,0 +1 @@
1
+ export * from "./Avatar";
@@ -0,0 +1,18 @@
1
+ # Badge Implementation Notes
2
+
3
+ This document captures specific decisions, regressions, and structural compromises made when integrating Mantine's `Badge` component into the Recursica styling system.
4
+
5
+ ## 1. Stripped Size Properties
6
+
7
+ As logged in `COMPONENT_ISSUES.md`, there are currently no Figma variables mapped for different `size` variants (`small`, `default`, etc.). The component explicitly `Omit`s the Mantine `size` property from its signature to prevent integrators from attempting to drive sizes that do not exist in the tokens.
8
+
9
+ ## 2. Intent-Based Variants
10
+
11
+ Mantine supports multiple visual variants (`outline`, `filled`, `light`). However, the existing variable schema for `Badge` only defines "Styles" which act as intents (`alert`, `primary-color`, `success`, `warning`).
12
+
13
+ - Default is arbitrarily mapped to `primary-color` as we lack a pure `neutral` schema right now.
14
+ - `variant` mapped to underlying Mantine prop has been hardcoded to `filled`, since the Recursica coloring fully replaces the Mantine DOM.
15
+
16
+ ## 3. The `overStyled` Prop
17
+
18
+ This component implements the newly added `overStyled` architectural standard. By default, passing inline `{style}` React properties to `Badge` will be ignored. This prevents accidental cascading regressions of our strict CSS-variables layouts unless a developer explicitly forces `overStyled={true}`.
@@ -0,0 +1,124 @@
1
+ /* HARDCODED VALUES:
2
+ - border-style: solid (needs a token if we support other border styles)
3
+ - box-sizing: border-box
4
+ - display: inline-flex
5
+ - align-items: center
6
+ - justify-content: center
7
+ - text-transform: uppercase (Mantine does this, but our tokens also specify it so we map it)
8
+ */
9
+
10
+ .root {
11
+ /* HARDCODE: Basic structural assumptions */
12
+ box-sizing: border-box;
13
+ display: inline-flex;
14
+ align-items: center;
15
+ justify-content: center;
16
+ border-style: solid;
17
+
18
+ /* Base style resets to fully clear Mantine's defaults before applying ours */
19
+ background-color: transparent;
20
+ color: inherit;
21
+ border-color: transparent;
22
+
23
+ /* Apply global badge properties */
24
+ border-radius: var(
25
+ --recursica_ui-kit_components_badge_properties_border-radius
26
+ );
27
+ border-width: var(--recursica_ui-kit_components_badge_properties_border-size);
28
+ box-shadow: var(--recursica_ui-kit_components_badge_properties_elevation);
29
+
30
+ padding-left: var(
31
+ --recursica_ui-kit_components_badge_properties_padding-horizontal
32
+ );
33
+ padding-right: var(
34
+ --recursica_ui-kit_components_badge_properties_padding-horizontal
35
+ );
36
+ padding-top: var(
37
+ --recursica_ui-kit_components_badge_properties_padding-vertical
38
+ );
39
+ padding-bottom: var(
40
+ --recursica_ui-kit_components_badge_properties_padding-vertical
41
+ );
42
+
43
+ font-family: var(
44
+ --recursica_ui-kit_components_badge_properties_text_font-family
45
+ );
46
+ font-size: var(--recursica_ui-kit_components_badge_properties_text_font-size);
47
+ font-weight: var(
48
+ --recursica_ui-kit_components_badge_properties_text_font-weight
49
+ );
50
+ letter-spacing: var(
51
+ --recursica_ui-kit_components_badge_properties_text_letter-spacing
52
+ );
53
+ line-height: var(
54
+ --recursica_ui-kit_components_badge_properties_text_line-height
55
+ );
56
+ text-decoration: var(
57
+ --recursica_ui-kit_components_badge_properties_text_text-decoration
58
+ );
59
+ text-transform: var(
60
+ --recursica_ui-kit_components_badge_properties_text_text-transform
61
+ );
62
+ }
63
+
64
+ /* Variant Definitions */
65
+
66
+ .root[data-variant="alert"] {
67
+ background-color: var(
68
+ --recursica_ui-kit_components_badge_variants_styles_alert_properties_colors_background
69
+ );
70
+ border-color: var(
71
+ --recursica_ui-kit_components_badge_variants_styles_alert_properties_colors_border-color
72
+ );
73
+ color: var(
74
+ --recursica_ui-kit_components_badge_variants_styles_alert_properties_colors_text
75
+ );
76
+ }
77
+
78
+ .root[data-variant="primary-color"] {
79
+ background-color: var(
80
+ --recursica_ui-kit_components_badge_variants_styles_primary-color_properties_colors_background
81
+ );
82
+ border-color: var(
83
+ --recursica_ui-kit_components_badge_variants_styles_primary-color_properties_colors_border-color
84
+ );
85
+ color: var(
86
+ --recursica_ui-kit_components_badge_variants_styles_primary-color_properties_colors_text
87
+ );
88
+ }
89
+
90
+ .root[data-variant="success"] {
91
+ background-color: var(
92
+ --recursica_ui-kit_components_badge_variants_styles_success_properties_colors_background
93
+ );
94
+ border-color: var(
95
+ --recursica_ui-kit_components_badge_variants_styles_success_properties_colors_border-color
96
+ );
97
+ color: var(
98
+ --recursica_ui-kit_components_badge_variants_styles_success_properties_colors_text
99
+ );
100
+ }
101
+
102
+ .root[data-variant="warning"] {
103
+ background-color: var(
104
+ --recursica_ui-kit_components_badge_variants_styles_warning_properties_colors_background
105
+ );
106
+ border-color: var(
107
+ --recursica_ui-kit_components_badge_variants_styles_warning_properties_colors_border-color
108
+ );
109
+ color: var(
110
+ --recursica_ui-kit_components_badge_variants_styles_warning_properties_colors_text
111
+ );
112
+ }
113
+
114
+ /* Target Mantine internal children if needed for typography constraints */
115
+ .root :global(.mantine-Badge-label),
116
+ .root :global(.mantine-Badge-section) {
117
+ /* HARDCODE: Prevent Mantine from overriding our typography setup inside inner wrappers */
118
+ font-size: inherit;
119
+ font-weight: inherit;
120
+ line-height: inherit;
121
+ font-family: inherit;
122
+ letter-spacing: inherit;
123
+ text-transform: inherit;
124
+ }
@@ -0,0 +1,77 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { Badge } from "./Badge";
4
+ import { Layer } from "@recursica/adapter-common";
5
+
6
+ type BadgeStoryProps = React.ComponentProps<typeof Badge>;
7
+
8
+ const meta: Meta<BadgeStoryProps> = {
9
+ title: "UI-Kit/Badge",
10
+ component: Badge,
11
+ tags: ["autodocs"],
12
+ argTypes: {
13
+ variant: {
14
+ control: "select",
15
+ options: ["alert", "primary-color", "success", "warning"],
16
+ description: "The style / intent mapping for the badge",
17
+ },
18
+ },
19
+ };
20
+
21
+ export default meta;
22
+
23
+ type Story = StoryObj<BadgeStoryProps>;
24
+
25
+ export const Default: Story = {
26
+ args: {
27
+ children: "Badge Label",
28
+ variant: "primary-color",
29
+ },
30
+ render: (args) => {
31
+ return <Badge {...args} />;
32
+ },
33
+ };
34
+
35
+ export const StaticAlert: Story = {
36
+ args: {
37
+ children: "Alert Badge",
38
+ variant: "alert",
39
+ },
40
+ render: (args: BadgeStoryProps) => <Badge {...args} />,
41
+ };
42
+
43
+ export const StaticPrimary: Story = {
44
+ args: {
45
+ children: "Primary Badge",
46
+ variant: "primary-color",
47
+ },
48
+ render: (args: BadgeStoryProps) => <Badge {...args} />,
49
+ };
50
+
51
+ export const StaticSuccess: Story = {
52
+ args: {
53
+ children: "Success Badge",
54
+ variant: "success",
55
+ },
56
+ render: (args: BadgeStoryProps) => <Badge {...args} />,
57
+ };
58
+
59
+ export const StaticWarning: Story = {
60
+ args: {
61
+ children: "Warning Badge",
62
+ variant: "warning",
63
+ },
64
+ render: (args: BadgeStoryProps) => <Badge {...args} />,
65
+ };
66
+
67
+ export const LayerOneAlert: Story = {
68
+ args: {
69
+ children: "Layer 1 Alert",
70
+ variant: "alert",
71
+ },
72
+ render: (args: BadgeStoryProps) => (
73
+ <Layer layer={1} style={{ padding: "24px" }}>
74
+ <Badge {...args} />
75
+ </Layer>
76
+ ),
77
+ };
@@ -0,0 +1,66 @@
1
+ import { forwardRef } from "react";
2
+ import {
3
+ Badge as MantineBadge,
4
+ type BadgeProps as MantineBadgeProps,
5
+ } from "@mantine/core";
6
+ import {
7
+ filterStylingProps,
8
+ type RecursicaOverStyled,
9
+ } from "../../utils/filterStylingProps";
10
+ import styles from "./Badge.module.css";
11
+
12
+ export interface RecursicaBadgeProps {
13
+ /** Map to the component styles defined in variables */
14
+ variant?: "alert" | "primary-color" | "success" | "warning";
15
+ }
16
+
17
+ export type BadgeProps = RecursicaOverStyled<
18
+ Omit<MantineBadgeProps, "variant" | "size" | "color" | "radius"> &
19
+ RecursicaBadgeProps
20
+ >;
21
+
22
+ export const Badge = forwardRef<HTMLDivElement, BadgeProps>(function Badge(
23
+ { variant = "primary-color", overStyled = false, ...rest },
24
+ ref,
25
+ ) {
26
+ // Strip all visual override injections unless developer has specifically opted into overStyling.
27
+ // External layout props like margins are safely preserved.
28
+ const sanitizedProps = filterStylingProps(rest, overStyled);
29
+
30
+ const mergedClassNames: Partial<Record<string, string>> = {
31
+ root: styles.root,
32
+ section: styles.section,
33
+ label: styles.label,
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 Partial<Record<string, string>>;
43
+ mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
44
+ mergedClassNames.section = o.section ?? styles.section;
45
+ mergedClassNames.label = o.label ?? styles.label;
46
+ }
47
+
48
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
49
+ .className as string | undefined;
50
+ const finalClass = classNameProp
51
+ ? `${styles.root} ${classNameProp}`
52
+ : styles.root;
53
+
54
+ return (
55
+ <MantineBadge
56
+ ref={ref}
57
+ variant="filled" // We manage visual style purely through our CSS variables mapping
58
+ data-variant={variant}
59
+ {...sanitizedProps}
60
+ className={finalClass}
61
+ classNames={mergedClassNames}
62
+ />
63
+ );
64
+ });
65
+
66
+ Badge.displayName = "Badge";
@@ -0,0 +1,14 @@
1
+ # Breadcrumb Implementation Notes
2
+
3
+ ## Missing Variants and Sizes
4
+
5
+ The `Breadcrumb` currently does not establish any size (`xs`, `sm`, etc.) or variant variables in the underlying design tokens (`recursica_ui-kit.json`). Only basic structural definitions for `padding` and `item-gap` exist. Because of this, the `size` and `variant` properties have been explicitly omitted from the passed mantine props.
6
+
7
+ ## Gap Styling
8
+
9
+ We attach `gap` to `.root` directly within `.module.css`. Mantine's inner `separator` divs can natively accept our CSS variables for structural layout.
10
+
11
+ ## Composition and Separators
12
+
13
+ We preserve standard hierarchical composition based on Mantine's defaults. The component natively accepts typical anchor tags as children without wrapping them in structural spans, allowing developers to utilize `Link` wrappers as needed contextually.
14
+ The separator character is inherited natively from Mantine's default configuration (`/`) unless overridden by passing `separator={<Icon />}` to the component props.
@@ -0,0 +1,26 @@
1
+ /* HARDCODED VALUES:
2
+ - align-items: center
3
+ */
4
+
5
+ .root {
6
+ /* HARDCODE: Basic structural flex layout for breadcrumbs */
7
+ display: flex;
8
+ align-items: center;
9
+
10
+ /* Base style resets */
11
+ background-color: transparent;
12
+ color: inherit;
13
+
14
+ /* Apply global breadcrumb properties */
15
+ padding: var(--recursica_ui-kit_components_breadcrumb_properties_padding);
16
+
17
+ /* Mantine's breadcrumbs uses gap on the root or pseudo classes, but standard gap is safest since flex is used */
18
+ gap: var(--recursica_ui-kit_components_breadcrumb_properties_item-gap);
19
+ }
20
+
21
+ .separator {
22
+ /* Separator base resets if needed */
23
+ display: flex;
24
+ align-items: center;
25
+ justify-content: center;
26
+ }
@@ -0,0 +1,77 @@
1
+ import React from "react";
2
+ import type { Meta, StoryObj } from "@storybook/react";
3
+ import { Breadcrumb } from "./Breadcrumb";
4
+
5
+ type BreadcrumbStoryProps = React.ComponentProps<typeof Breadcrumb> & {
6
+ items?: string[];
7
+ };
8
+
9
+ const meta: Meta<BreadcrumbStoryProps> = {
10
+ title: "UI-Kit/Breadcrumb",
11
+ component: Breadcrumb,
12
+ tags: ["autodocs"],
13
+ argTypes: {
14
+ children: {
15
+ table: {
16
+ disable: true,
17
+ },
18
+ },
19
+ items: {
20
+ control: "object",
21
+ description:
22
+ "Array of string labels used to dynamically generate the interactive Breadcrumb nodes.",
23
+ table: {
24
+ category: "Story Controls",
25
+ },
26
+ },
27
+ separator: {
28
+ control: "text",
29
+ description: "Custom separator between items",
30
+ },
31
+ },
32
+ args: {
33
+ items: ["Home", "Components", "Breadcrumbs"],
34
+ },
35
+ render: ({ items, children, ...args }) => {
36
+ const mappedChildren = items
37
+ ? items.map((label, index) => (
38
+ <a
39
+ href="#"
40
+ key={index}
41
+ style={{
42
+ color:
43
+ "var(--recursica_brand_palettes_primary_default_color_tone)",
44
+ textDecoration: "none",
45
+ }}
46
+ >
47
+ {label}
48
+ </a>
49
+ ))
50
+ : children;
51
+
52
+ return <Breadcrumb children={mappedChildren} {...args} />;
53
+ },
54
+ };
55
+
56
+ export default meta;
57
+
58
+ type Story = StoryObj<BreadcrumbStoryProps>;
59
+
60
+ export const Default: Story = {
61
+ args: {
62
+ items: ["Dashboard", "Settings", "Security"],
63
+ },
64
+ };
65
+
66
+ export const StaticExample: Story = {
67
+ args: {
68
+ items: ["Store", "Electronics", "Computers", "Laptops"],
69
+ },
70
+ };
71
+
72
+ export const CustomSeparator: Story = {
73
+ args: {
74
+ items: ["Root", "Branch", "Leaf"],
75
+ separator: "→",
76
+ },
77
+ };
@@ -0,0 +1,64 @@
1
+ import { forwardRef } from "react";
2
+ import {
3
+ Breadcrumbs as MantineBreadcrumbs,
4
+ type BreadcrumbsProps as MantineBreadcrumbsProps,
5
+ } from "@mantine/core";
6
+ import {
7
+ filterStylingProps,
8
+ type RecursicaOverStyled,
9
+ } from "../../utils/filterStylingProps";
10
+ import styles from "./Breadcrumb.module.css";
11
+
12
+ // Currently there are no Recursica prop additions specific to Breadcrumbs (like size or variant)
13
+ // according to recursica_ui-kit.json
14
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
15
+ export interface RecursicaBreadcrumbProps {}
16
+
17
+ export type BreadcrumbProps = RecursicaOverStyled<
18
+ Omit<MantineBreadcrumbsProps, "variant" | "size"> & RecursicaBreadcrumbProps
19
+ >;
20
+
21
+ export const Breadcrumb = forwardRef<HTMLDivElement, BreadcrumbProps>(
22
+ function Breadcrumb({ overStyled = false, separator = ">", ...rest }, ref) {
23
+ const sanitizedProps = filterStylingProps(
24
+ { separator, ...rest },
25
+ overStyled,
26
+ );
27
+
28
+ const mergedClassNames: Partial<Record<string, string>> = {
29
+ root: styles.root,
30
+ separator: styles.separator,
31
+ };
32
+
33
+ const classNamesProp = (sanitizedProps as Record<string, unknown>)
34
+ .classNames;
35
+ if (
36
+ classNamesProp &&
37
+ typeof classNamesProp === "object" &&
38
+ !Array.isArray(classNamesProp)
39
+ ) {
40
+ const o = classNamesProp as Partial<Record<string, string>>;
41
+ mergedClassNames.root = o.root ? `${styles.root} ${o.root}` : styles.root;
42
+ mergedClassNames.separator = o.separator
43
+ ? `${styles.separator} ${o.separator}`
44
+ : styles.separator;
45
+ }
46
+
47
+ const classNameProp = (sanitizedProps as Record<string, unknown>)
48
+ .className as string | undefined;
49
+ const finalClass = classNameProp
50
+ ? `${styles.root} ${classNameProp}`
51
+ : styles.root;
52
+
53
+ return (
54
+ <MantineBreadcrumbs
55
+ ref={ref}
56
+ {...(sanitizedProps as unknown as MantineBreadcrumbsProps)}
57
+ className={finalClass}
58
+ classNames={mergedClassNames}
59
+ />
60
+ );
61
+ },
62
+ );
63
+
64
+ Breadcrumb.displayName = "Breadcrumb";