@syscore/ui-library 1.25.0 → 2.0.0-beta-publish-workflow-beta.1

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 (168) hide show
  1. package/client/App.tsx +0 -2
  2. package/client/assets/alert/icon-clear.svg +4 -0
  3. package/client/assets/alert/icon-done.svg +3 -0
  4. package/client/assets/avatar-identity/photo.png +0 -0
  5. package/client/assets/avatar-identity/placeholder-icon.svg +8 -0
  6. package/client/assets/breadcrumb/icon-ellipsis.svg +5 -0
  7. package/client/assets/breadcrumb/icon-home.svg +3 -0
  8. package/client/assets/breadcrumb/icon-separator.svg +3 -0
  9. package/client/assets/button-v01/icon-back.svg +4 -0
  10. package/client/assets/button-v01/icon-check.svg +3 -0
  11. package/client/assets/button-v01/icon-close.svg +4 -0
  12. package/client/assets/button-v01/icon-delete.svg +3 -0
  13. package/client/assets/button-v01/icon-device.svg +3 -0
  14. package/client/assets/dropdown/icon-carrot-contract.svg +3 -0
  15. package/client/assets/dropdown/icon-carrot-expand.svg +3 -0
  16. package/client/assets/dropdown/icon-clear.svg +4 -0
  17. package/client/assets/dropdown/icon-enter.svg +3 -0
  18. package/client/components/icons/UtilityChangeType.tsx +21 -0
  19. package/client/components/ui/alert-dialog-panel.tsx +246 -0
  20. package/client/components/ui/alert-v02.tsx +214 -0
  21. package/client/components/ui/app-bar.tsx +299 -0
  22. package/client/components/ui/avatar-identity.tsx +122 -0
  23. package/client/components/ui/banner.tsx +108 -0
  24. package/client/components/ui/bottom-navigation.tsx +223 -0
  25. package/client/components/ui/breadcrumb-trail.tsx +170 -0
  26. package/client/components/ui/button-v01.tsx +238 -0
  27. package/client/components/ui/button.tsx +2 -0
  28. package/client/components/ui/card.tsx +108 -26
  29. package/client/components/ui/code-badge.tsx +62 -0
  30. package/client/components/ui/date-picker.tsx +188 -0
  31. package/client/components/ui/dropdown.tsx +448 -0
  32. package/client/components/ui/empty-state.tsx +197 -0
  33. package/client/components/ui/file-upload.tsx +214 -0
  34. package/client/components/ui/footer.tsx +179 -0
  35. package/client/components/ui/layout.tsx +1182 -0
  36. package/client/components/ui/pagination-v01.tsx +157 -0
  37. package/client/components/ui/password-field-v01.tsx +67 -0
  38. package/client/components/ui/search-field-v01.tsx +192 -0
  39. package/client/components/ui/section-heading.tsx +49 -0
  40. package/client/components/ui/sidebar.tsx +13 -8
  41. package/client/components/ui/stepper.tsx +148 -0
  42. package/client/components/ui/switch-v01.tsx +71 -0
  43. package/client/components/ui/system-bar.tsx +119 -0
  44. package/client/components/ui/table-v01.tsx +230 -0
  45. package/client/components/ui/tabs-v01.tsx +99 -0
  46. package/client/components/ui/tag-v02.tsx +113 -0
  47. package/client/components/ui/text-area-v01.tsx +33 -0
  48. package/client/components/ui/text-field-v01.tsx +108 -0
  49. package/client/components/ui/timeline.tsx +181 -0
  50. package/client/components/ui/toast-v01.tsx +206 -0
  51. package/client/components/ui/tooltip.tsx +264 -71
  52. package/client/global.css +9565 -4378
  53. package/client/storybook.css +125 -0
  54. package/client/ui/Accordion.stories.tsx +1 -1
  55. package/client/ui/AlertDialogPanel/AlertDialogPanel.stories.tsx +150 -0
  56. package/client/ui/AlertV02/AlertV02.stories.tsx +188 -0
  57. package/client/ui/AppBar/app-bar.stories.tsx +280 -0
  58. package/client/ui/AspectRatio.stories.tsx +1 -1
  59. package/client/ui/AvatarIdentity/AvatarIdentity.stories.tsx +77 -0
  60. package/client/ui/Banner/banner.stories.tsx +238 -0
  61. package/client/ui/BottomNavigation/bottom-navigation.stories.tsx +285 -0
  62. package/client/ui/BreadcrumbTrail/BreadcrumbTrail.stories.tsx +232 -0
  63. package/client/ui/Button.stories.tsx +1 -1
  64. package/client/ui/ButtonV01/ButtonV01.stories.tsx +417 -0
  65. package/client/ui/Calendar/Calendar.stories.tsx +1 -1
  66. package/client/ui/Card.stories.tsx +285 -168
  67. package/client/ui/Carousel/Carousel.stories.tsx +1 -1
  68. package/client/ui/Chart/Chart.stories.tsx +1 -1
  69. package/client/ui/Checkbox.stories.tsx +1 -1
  70. package/client/ui/Collapsible/Collapsible.stories.tsx +1 -1
  71. package/client/ui/Colors.stories.tsx +1 -1
  72. package/client/ui/Command/Command.stories.tsx +1 -1
  73. package/client/ui/ContextMenu/ContextMenu.stories.tsx +1 -1
  74. package/client/ui/DatePicker/DatePicker.stories.tsx +293 -0
  75. package/client/ui/Dialog.stories.tsx +1 -1
  76. package/client/ui/Drawer/Drawer.stories.tsx +1 -1
  77. package/client/ui/Dropdown/Dropdown.stories.tsx +205 -0
  78. package/client/ui/EmptyState/empty-state.stories.tsx +251 -0
  79. package/client/ui/FileUpload/FileUpload.stories.tsx +218 -0
  80. package/client/ui/Footer/footer.stories.tsx +194 -0
  81. package/client/ui/Form/Form.stories.tsx +1 -1
  82. package/client/ui/Hero.stories.tsx +1 -1
  83. package/client/ui/HoverCard/HoverCard.stories.tsx +1 -1
  84. package/client/ui/Icons.stories.tsx +1 -1
  85. package/client/ui/Input.stories.tsx +1 -1
  86. package/client/ui/InputOTP/InputOTP.stories.tsx +1 -1
  87. package/client/ui/Label.stories.tsx +1 -1
  88. package/client/ui/Layout/Center.stories.tsx +100 -0
  89. package/client/ui/Layout/Columns.stories.tsx +136 -0
  90. package/client/ui/Layout/Container.stories.tsx +104 -0
  91. package/client/ui/Layout/FormLayout.stories.tsx +144 -0
  92. package/client/ui/Layout/Grid.stories.tsx +158 -0
  93. package/client/ui/Layout/GridSpan.stories.tsx +75 -0
  94. package/client/ui/Layout/HStack.stories.tsx +128 -0
  95. package/client/ui/Layout/Layout.stories.tsx +287 -0
  96. package/client/ui/Layout/LayoutContent.stories.tsx +91 -0
  97. package/client/ui/Layout/LayoutFooter.stories.tsx +77 -0
  98. package/client/ui/Layout/LayoutHeader.stories.tsx +104 -0
  99. package/client/ui/Layout/LayoutPanel.stories.tsx +143 -0
  100. package/client/ui/Layout/Positioned.stories.tsx +113 -0
  101. package/client/ui/Layout/Section.stories.tsx +84 -0
  102. package/client/ui/Layout/ShowHide.stories.tsx +99 -0
  103. package/client/ui/Layout/Spacer.stories.tsx +149 -0
  104. package/client/ui/Layout/Stack.stories.tsx +69 -0
  105. package/client/ui/Layout/StackItem.stories.tsx +124 -0
  106. package/client/ui/Layout/VStack.stories.tsx +101 -0
  107. package/client/ui/Layout/controls.ts +33 -0
  108. package/client/ui/Layout/demo.tsx +37 -0
  109. package/client/ui/Menubar/Menubar.stories.tsx +1 -1
  110. package/client/ui/MobileNav.stories.tsx +1 -1
  111. package/client/ui/Navigation.stories.tsx +1 -1
  112. package/client/ui/NavigationMenu/NavigationMenu.stories.tsx +1 -1
  113. package/client/ui/PageHeader.stories.tsx +13 -13
  114. package/client/ui/Pagination/Pagination.stories.tsx +178 -98
  115. package/client/ui/Pagination/PaginationLegacy.stories.tsx +121 -0
  116. package/client/ui/Panel.stories.tsx +1 -1
  117. package/client/ui/Popover/Popover.stories.tsx +1 -1
  118. package/client/ui/Progress/Progress.stories.tsx +1 -1
  119. package/client/ui/RadioGroup/RadioGroup.stories.tsx +1 -1
  120. package/client/ui/Resizable/Resizable.stories.tsx +1 -1
  121. package/client/ui/ScrollArea/ScrollArea.stories.tsx +1 -1
  122. package/client/ui/SearchField/SearchField.stories.tsx +153 -0
  123. package/client/ui/{SearchField.stories.tsx → SearchField/SearchFieldLegacy.stories.tsx} +2 -2
  124. package/client/ui/SectionBadge.stories.tsx +1 -1
  125. package/client/ui/Select.stories.tsx +1 -1
  126. package/client/ui/Separator/Separator.stories.tsx +1 -1
  127. package/client/ui/Sheet/Sheet.stories.tsx +1 -1
  128. package/client/ui/Sidebar/Sidebar.stories.tsx +57 -8
  129. package/client/ui/Skeleton/Skeleton.stories.tsx +1 -1
  130. package/client/ui/Slider/Slider.stories.tsx +1 -1
  131. package/client/ui/StandardNavigation.stories.tsx +1 -1
  132. package/client/ui/Stepper/Stepper.stories.tsx +344 -0
  133. package/client/ui/Switch.stories.tsx +1 -1
  134. package/client/ui/SwitchV01/SwitchV01.stories.tsx +152 -0
  135. package/client/ui/SystemBar/system-bar.stories.tsx +179 -0
  136. package/client/ui/Table/TableV01.stories.tsx +256 -0
  137. package/client/ui/Tabs.stories.tsx +1 -1
  138. package/client/ui/TabsV01/TabsV01.stories.tsx +144 -0
  139. package/client/ui/Tag.stories.tsx +1 -1
  140. package/client/ui/TagV02/TagStatusV02.stories.tsx +132 -0
  141. package/client/ui/TagV02/TagV02.stories.tsx +142 -0
  142. package/client/ui/TextAreaV01/TextAreaV01.stories.tsx +146 -0
  143. package/client/ui/TextFieldV01/PasswordFieldV01.stories.tsx +161 -0
  144. package/client/ui/TextFieldV01/TextFieldV01.stories.tsx +160 -0
  145. package/client/ui/Textarea.stories.tsx +1 -1
  146. package/client/ui/Timeline/timeline.stories.tsx +404 -0
  147. package/client/ui/Toast/Toast.stories.tsx +1 -1
  148. package/client/ui/ToastV01/ToastV01.stories.tsx +201 -0
  149. package/client/ui/Toaster/Toaster.stories.tsx +1 -1
  150. package/client/ui/Toggle.stories.tsx +1 -1
  151. package/client/ui/Tooltip.stories.tsx +287 -114
  152. package/client/ui/Typography.stories.tsx +1 -1
  153. package/dist/index.cjs.js +1 -1
  154. package/dist/index.d.ts +649 -13
  155. package/dist/index.es.js +6109 -2360
  156. package/package.json +1 -1
  157. package/client/components/ui/badge.tsx +0 -33
  158. package/client/components/ui/sonner.tsx +0 -26
  159. package/client/components/ui/toggle-group.tsx +0 -76
  160. package/client/ui/Alert/Alert.stories.tsx +0 -82
  161. package/client/ui/AlertDialog/AlertDialog.stories.tsx +0 -106
  162. package/client/ui/Avatar/Avatar.stories.tsx +0 -94
  163. package/client/ui/Badge/Badge.stories.tsx +0 -60
  164. package/client/ui/Breadcrumb/Breadcrumb.stories.tsx +0 -97
  165. package/client/ui/DropdownMenu/DropdownMenu.stories.tsx +0 -139
  166. package/client/ui/Sonner/Sonner.stories.tsx +0 -48
  167. package/client/ui/Table/Table.stories.tsx +0 -135
  168. package/client/ui/ToggleGroup/ToggleGroup.stories.tsx +0 -88
@@ -0,0 +1,223 @@
1
+ import * as React from "react";
2
+ import { cn } from "@/lib/utils";
3
+
4
+ // ─── Context ─────────────────────────────────────────────────────────────────
5
+
6
+ interface BottomNavContextValue {
7
+ value: string | number | null;
8
+ onChange: (val: string | number) => void;
9
+ grow: boolean;
10
+ horizontal: boolean;
11
+ shift: boolean;
12
+ activeColor: string | undefined;
13
+ }
14
+
15
+ const BottomNavContext = React.createContext<BottomNavContextValue>({
16
+ value: null,
17
+ onChange: () => {},
18
+ grow: false,
19
+ horizontal: false,
20
+ shift: false,
21
+ activeColor: undefined,
22
+ });
23
+
24
+ // ─── BottomNavigation Root ───────────────────────────────────────────────────
25
+
26
+ export interface BottomNavigationProps
27
+ extends Omit<React.HTMLAttributes<HTMLElement>, "onChange"> {
28
+ /**
29
+ * The currently active item value.
30
+ * Controlled: pass value + onChange together.
31
+ */
32
+ value?: string | number;
33
+ /** Default selected value for uncontrolled usage */
34
+ defaultValue?: string | number;
35
+ /** Fires when the active item changes */
36
+ onChange?: (value: string | number) => void;
37
+ /**
38
+ * Active item highlight color (CSS color or var token).
39
+ * Applies to icon + label of the active BottomNavigationItem.
40
+ */
41
+ color?: string;
42
+ /**
43
+ * Forces each item to fill all available width (max 168px per MD spec).
44
+ */
45
+ grow?: boolean;
46
+ /**
47
+ * Positions icon and label side-by-side (inline) instead of stacked.
48
+ */
49
+ horizontal?: boolean;
50
+ /**
51
+ * Hides labels of inactive items; only the active item shows its label.
52
+ * Labels must be wrapped in a <span> inside BottomNavigationItem.
53
+ */
54
+ shift?: boolean;
55
+ /**
56
+ * Controls visibility of the bar.
57
+ * When false the bar slides off-screen (bottom).
58
+ */
59
+ active?: boolean;
60
+ /** Elevates with a shadow */
61
+ elevated?: boolean;
62
+ }
63
+
64
+ const BottomNavigation = React.forwardRef<
65
+ HTMLElement,
66
+ BottomNavigationProps
67
+ >(
68
+ (
69
+ {
70
+ className,
71
+ children,
72
+ value: valueProp,
73
+ defaultValue,
74
+ onChange,
75
+ color,
76
+ grow = false,
77
+ horizontal = false,
78
+ shift = false,
79
+ active = true,
80
+ elevated = false,
81
+ ...props
82
+ },
83
+ ref
84
+ ) => {
85
+ const [internalValue, setInternalValue] = React.useState<
86
+ string | number | null
87
+ >(defaultValue ?? null);
88
+
89
+ const isControlled = valueProp !== undefined;
90
+ const currentValue = isControlled ? valueProp : internalValue;
91
+
92
+ const handleChange = React.useCallback(
93
+ (val: string | number) => {
94
+ if (!isControlled) setInternalValue(val);
95
+ onChange?.(val);
96
+ },
97
+ [isControlled, onChange]
98
+ );
99
+
100
+ return (
101
+ <BottomNavContext.Provider
102
+ value={{
103
+ value: currentValue ?? null,
104
+ onChange: handleChange,
105
+ grow,
106
+ horizontal,
107
+ shift,
108
+ activeColor: color,
109
+ }}
110
+ >
111
+ <nav
112
+ ref={ref}
113
+ role="navigation"
114
+ aria-label="Bottom navigation"
115
+ className={cn(
116
+ "bottom-nav",
117
+ grow && "bottom-nav--grow",
118
+ horizontal && "bottom-nav--horizontal",
119
+ shift && "bottom-nav--shift",
120
+ !active && "bottom-nav--hidden",
121
+ elevated && "bottom-nav--elevated",
122
+ className
123
+ )}
124
+ {...props}
125
+ >
126
+ {children}
127
+ </nav>
128
+ </BottomNavContext.Provider>
129
+ );
130
+ }
131
+ );
132
+ BottomNavigation.displayName = "BottomNavigation";
133
+
134
+ // ─── BottomNavigationItem ─────────────────────────────────────────────────────
135
+
136
+ export interface BottomNavigationItemProps
137
+ extends React.ButtonHTMLAttributes<HTMLButtonElement> {
138
+ /**
139
+ * Unique value for this item. If omitted, the item's index is used
140
+ * (assigned automatically by BottomNavigationGroup, see below).
141
+ */
142
+ value?: string | number;
143
+ /** Index injected by BottomNavigationGroup — do not set manually */
144
+ _index?: number;
145
+ }
146
+
147
+ const BottomNavigationItem = React.forwardRef<
148
+ HTMLButtonElement,
149
+ BottomNavigationItemProps
150
+ >(({ className, children, value: valueProp, _index, style, ...props }, ref) => {
151
+ const { value, onChange, grow, horizontal, shift, activeColor } =
152
+ React.useContext(BottomNavContext);
153
+
154
+ const itemValue = valueProp ?? _index ?? 0;
155
+ const isActive = value === itemValue;
156
+
157
+ const activeStyle =
158
+ isActive && activeColor ? ({ "--bottom-nav-active-color": activeColor } as React.CSSProperties) : {};
159
+
160
+ return (
161
+ <button
162
+ ref={ref}
163
+ type="button"
164
+ role="tab"
165
+ aria-selected={isActive}
166
+ aria-current={isActive ? "page" : undefined}
167
+ onClick={() => onChange(itemValue)}
168
+ style={{ ...activeStyle, ...style }}
169
+ className={cn(
170
+ "bottom-nav-item",
171
+ isActive && "bottom-nav-item--active",
172
+ grow && "bottom-nav-item--grow",
173
+ horizontal && "bottom-nav-item--horizontal",
174
+ shift && "bottom-nav-item--shift",
175
+ className
176
+ )}
177
+ {...props}
178
+ >
179
+ {children}
180
+ </button>
181
+ );
182
+ });
183
+ BottomNavigationItem.displayName = "BottomNavigationItem";
184
+
185
+ // ─── BottomNavigationGroup ───────────────────────────────────────────────────
186
+ // Thin wrapper that injects _index into each BottomNavigationItem child
187
+ // so unvalued items still participate in selection by index.
188
+
189
+ interface BottomNavigationGroupProps
190
+ extends React.HTMLAttributes<HTMLDivElement> {}
191
+
192
+ const BottomNavigationGroup = React.forwardRef<
193
+ HTMLDivElement,
194
+ BottomNavigationGroupProps
195
+ >(({ children, className, ...props }, ref) => {
196
+ const indexedChildren = React.Children.map(children, (child, index) => {
197
+ if (!React.isValidElement(child)) return child;
198
+ const el = child as React.ReactElement<BottomNavigationItemProps>;
199
+ if (
200
+ (el.type as { displayName?: string }).displayName ===
201
+ "BottomNavigationItem"
202
+ ) {
203
+ return React.cloneElement(el, { _index: index });
204
+ }
205
+ return child;
206
+ });
207
+
208
+ return (
209
+ <div
210
+ ref={ref}
211
+ role="tablist"
212
+ className={cn("bottom-nav-group", className)}
213
+ {...props}
214
+ >
215
+ {indexedChildren}
216
+ </div>
217
+ );
218
+ });
219
+ BottomNavigationGroup.displayName = "BottomNavigationGroup";
220
+
221
+ // ─── Exports ─────────────────────────────────────────────────────────────────
222
+
223
+ export { BottomNavigation, BottomNavigationItem, BottomNavigationGroup };
@@ -0,0 +1,170 @@
1
+ import * as React from "react";
2
+ import { Slot } from "@radix-ui/react-slot";
3
+
4
+ import { cn } from "@/lib/utils";
5
+ import {
6
+ CodeBadge,
7
+ type CodeBadgeProps,
8
+ type CodeBadgeVariant,
9
+ type CodeBadgeConcept,
10
+ } from "@/components/ui/code-badge";
11
+ import iconSeparator from "@/assets/breadcrumb/icon-separator.svg";
12
+ import iconHome from "@/assets/breadcrumb/icon-home.svg";
13
+ import iconEllipsis from "@/assets/breadcrumb/icon-ellipsis.svg";
14
+
15
+ const BreadcrumbTrail = React.forwardRef<
16
+ HTMLElement,
17
+ React.ComponentPropsWithoutRef<"nav">
18
+ >(({ className, ...props }, ref) => (
19
+ <nav
20
+ ref={ref}
21
+ aria-label="breadcrumb"
22
+ className={cn("breadcrumb-trail", className)}
23
+ {...props}
24
+ />
25
+ ));
26
+ BreadcrumbTrail.displayName = "BreadcrumbTrail";
27
+
28
+ const BreadcrumbTrailList = React.forwardRef<
29
+ HTMLOListElement,
30
+ React.ComponentPropsWithoutRef<"ol">
31
+ >(({ className, ...props }, ref) => (
32
+ <ol
33
+ ref={ref}
34
+ className={cn("breadcrumb-trail-list font-mazzardSoft", className)}
35
+ {...props}
36
+ />
37
+ ));
38
+ BreadcrumbTrailList.displayName = "BreadcrumbTrailList";
39
+
40
+ const BreadcrumbTrailItem = React.forwardRef<
41
+ HTMLLIElement,
42
+ React.ComponentPropsWithoutRef<"li">
43
+ >(({ className, ...props }, ref) => (
44
+ <li
45
+ ref={ref}
46
+ className={cn("breadcrumb-trail-item", className)}
47
+ {...props}
48
+ />
49
+ ));
50
+ BreadcrumbTrailItem.displayName = "BreadcrumbTrailItem";
51
+
52
+ const BreadcrumbTrailLink = React.forwardRef<
53
+ HTMLAnchorElement,
54
+ React.ComponentPropsWithoutRef<"a"> & {
55
+ asChild?: boolean;
56
+ }
57
+ >(({ asChild, className, ...props }, ref) => {
58
+ const Comp = asChild ? Slot : "a";
59
+
60
+ return (
61
+ <Comp
62
+ ref={ref}
63
+ className={cn("breadcrumb-trail-link", className)}
64
+ {...props}
65
+ />
66
+ );
67
+ });
68
+ BreadcrumbTrailLink.displayName = "BreadcrumbTrailLink";
69
+
70
+ const BreadcrumbTrailPage = React.forwardRef<
71
+ HTMLSpanElement,
72
+ React.ComponentPropsWithoutRef<"span">
73
+ >(({ className, ...props }, ref) => (
74
+ <span
75
+ ref={ref}
76
+ role="link"
77
+ aria-disabled="true"
78
+ aria-current="page"
79
+ className={cn("breadcrumb-trail-page", className)}
80
+ {...props}
81
+ />
82
+ ));
83
+ BreadcrumbTrailPage.displayName = "BreadcrumbTrailPage";
84
+
85
+ const BreadcrumbTrailSeparator = ({
86
+ children,
87
+ className,
88
+ ...props
89
+ }: React.ComponentProps<"li">) => (
90
+ <li
91
+ role="presentation"
92
+ aria-hidden="true"
93
+ className={cn("breadcrumb-trail-separator", className)}
94
+ {...props}
95
+ >
96
+ {children ?? (
97
+ <img
98
+ src={iconSeparator}
99
+ alt=""
100
+ className="breadcrumb-trail-separator-icon"
101
+ width={4.5}
102
+ height={7.5}
103
+ />
104
+ )}
105
+ </li>
106
+ );
107
+ BreadcrumbTrailSeparator.displayName = "BreadcrumbTrailSeparator";
108
+
109
+ const BreadcrumbTrailHome = ({
110
+ className,
111
+ ...props
112
+ }: React.ComponentProps<"span">) => (
113
+ <span className={cn("breadcrumb-trail-home", className)} {...props}>
114
+ <img
115
+ src={iconHome}
116
+ alt="Home"
117
+ className="breadcrumb-trail-home-icon"
118
+ width={14}
119
+ height={13.5475}
120
+ />
121
+ </span>
122
+ );
123
+ BreadcrumbTrailHome.displayName = "BreadcrumbTrailHome";
124
+
125
+ const BreadcrumbTrailEllipsis = ({
126
+ className,
127
+ ...props
128
+ }: React.ComponentProps<"span">) => (
129
+ <span
130
+ role="presentation"
131
+ aria-hidden="true"
132
+ className={cn("breadcrumb-trail-ellipsis", className)}
133
+ {...props}
134
+ >
135
+ <img
136
+ src={iconEllipsis}
137
+ alt=""
138
+ className="breadcrumb-trail-ellipsis-icon"
139
+ width={16}
140
+ height={16}
141
+ />
142
+ <span className="sr-only">More</span>
143
+ </span>
144
+ );
145
+ BreadcrumbTrailEllipsis.displayName = "BreadcrumbTrailEllipsis";
146
+
147
+ /**
148
+ * Thin alias over the shared CodeBadge, which the table also uses.
149
+ * Kept so existing breadcrumb call sites keep working.
150
+ */
151
+ export type BreadcrumbTrailCodeVariant = CodeBadgeVariant;
152
+ export type BreadcrumbTrailCodeConcept = CodeBadgeConcept;
153
+
154
+ const BreadcrumbTrailCode = React.forwardRef<
155
+ HTMLSpanElement,
156
+ Omit<CodeBadgeProps, "tone">
157
+ >((props, ref) => <CodeBadge ref={ref} tone="trail" {...props} />);
158
+ BreadcrumbTrailCode.displayName = "BreadcrumbTrailCode";
159
+
160
+ export {
161
+ BreadcrumbTrail,
162
+ BreadcrumbTrailList,
163
+ BreadcrumbTrailItem,
164
+ BreadcrumbTrailLink,
165
+ BreadcrumbTrailPage,
166
+ BreadcrumbTrailSeparator,
167
+ BreadcrumbTrailEllipsis,
168
+ BreadcrumbTrailHome,
169
+ BreadcrumbTrailCode,
170
+ };
@@ -0,0 +1,238 @@
1
+ import * as React from "react";
2
+ import { cva, type VariantProps } from "class-variance-authority";
3
+
4
+ import { cn } from "@/lib/utils";
5
+ import iconDevice from "@/assets/button-v01/icon-device.svg";
6
+ import iconClose from "@/assets/button-v01/icon-close.svg";
7
+ import iconCheck from "@/assets/button-v01/icon-check.svg";
8
+ import iconDelete from "@/assets/button-v01/icon-delete.svg";
9
+ import iconBack from "@/assets/button-v01/icon-back.svg";
10
+
11
+ export type ButtonV01Size = "large" | "medium" | "small";
12
+ export type ButtonV01Type = "cta" | "utility" | "icon";
13
+ export type ButtonV01Hierarchy = "primary" | "secondary" | "tertiary";
14
+ export type ButtonV01Appearance = "solid" | "gradient" | "stroke";
15
+ export type ButtonV01Theme = "default" | "light" | "transparent" | "color";
16
+ export type ButtonV01Context =
17
+ | "default"
18
+ | "general"
19
+ | "tooltip"
20
+ | "confirm"
21
+ | "error"
22
+ | "cancel"
23
+ | "redirect"
24
+ | "done"
25
+ | "floating"
26
+ | "back";
27
+ export type ButtonV01Icon = "none" | "check" | "close" | "delete" | "back";
28
+
29
+ const buttonV01Variants = cva("button-v01 font-mazzardSoft", {
30
+ variants: {
31
+ size: {
32
+ large: "button-v01--large",
33
+ medium: "button-v01--medium",
34
+ small: "button-v01--small",
35
+ },
36
+ },
37
+ });
38
+
39
+ const ICON_SRC: Record<Exclude<ButtonV01Icon, "none">, string> = {
40
+ close: iconClose,
41
+ check: iconCheck,
42
+ delete: iconDelete,
43
+ back: iconBack,
44
+ };
45
+
46
+ function resolveLook({
47
+ type,
48
+ hierarchy,
49
+ appearance,
50
+ theme,
51
+ context,
52
+ icon,
53
+ }: {
54
+ type: ButtonV01Type;
55
+ hierarchy: ButtonV01Hierarchy;
56
+ appearance: ButtonV01Appearance;
57
+ theme: ButtonV01Theme;
58
+ context: ButtonV01Context;
59
+ icon: ButtonV01Icon;
60
+ }) {
61
+ if (type === "icon") {
62
+ if (theme === "transparent") return "button-v01--icon-transparent";
63
+ if (theme === "color") {
64
+ return icon === "check"
65
+ ? "button-v01--icon-success"
66
+ : "button-v01--icon-danger";
67
+ }
68
+ return "button-v01--icon-light";
69
+ }
70
+
71
+ // `general`, `cancel`, `done` and `back` deliberately fall through: in Figma
72
+ // they carry no look of their own. `general` resolves via Type/Hierarchy
73
+ // below, and the other three only ever appear on Icon buttons, handled above.
74
+ if (context === "floating") return "button-v01--floating";
75
+ if (context === "confirm") return "button-v01--confirm";
76
+ if (context === "error") return "button-v01--error";
77
+ if (context === "redirect") return "button-v01--redirect";
78
+ if (context === "tooltip") return "button-v01--tooltip";
79
+
80
+ if (type === "cta" && appearance === "gradient") return "button-v01--gradient";
81
+ if (type === "cta" && hierarchy === "secondary") {
82
+ return "button-v01--cta-secondary";
83
+ }
84
+ if (type === "cta" && hierarchy === "tertiary") {
85
+ return "button-v01--cta-tertiary";
86
+ }
87
+ if (type === "utility" && hierarchy === "secondary") {
88
+ return "button-v01--utility-secondary";
89
+ }
90
+ if (type === "utility" && hierarchy === "tertiary") {
91
+ return "button-v01--utility-tertiary";
92
+ }
93
+ if (type === "utility") return "button-v01--utility-primary";
94
+
95
+ return "button-v01--gradient";
96
+ }
97
+
98
+ function MaskIcon({
99
+ src,
100
+ className,
101
+ }: {
102
+ src: string;
103
+ className?: string;
104
+ }) {
105
+ return (
106
+ <span
107
+ className={cn("button-v01-mask", className)}
108
+ style={{
109
+ WebkitMaskImage: `url(${src})`,
110
+ maskImage: `url(${src})`,
111
+ }}
112
+ aria-hidden
113
+ />
114
+ );
115
+ }
116
+
117
+ function defaultLabel({
118
+ type,
119
+ hierarchy,
120
+ context,
121
+ }: {
122
+ type: ButtonV01Type;
123
+ hierarchy: ButtonV01Hierarchy;
124
+ context: ButtonV01Context;
125
+ }) {
126
+ if (type === "icon") return null;
127
+ if (context === "confirm") return "Confirm";
128
+ if (context === "error" || context === "tooltip") return "Retract";
129
+ if (type === "utility" && hierarchy === "primary" && context === "default") {
130
+ return "Utility";
131
+ }
132
+ return "Call to action";
133
+ }
134
+
135
+ export interface ButtonV01Props
136
+ extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "style">,
137
+ VariantProps<typeof buttonV01Variants> {
138
+ /** Figma `Type`. */
139
+ typeVariant?: ButtonV01Type;
140
+ /** Figma `Hierarchy`. */
141
+ hierarchy?: ButtonV01Hierarchy;
142
+ /** Figma `Style`. */
143
+ appearance?: ButtonV01Appearance;
144
+ /** Figma `Theme`. */
145
+ theme?: ButtonV01Theme;
146
+ /** Figma `Context`. `toolti` in the file maps to `tooltip`. */
147
+ context?: ButtonV01Context;
148
+ /** Figma `Icon`. Used when `typeVariant="icon"`. */
149
+ icon?: ButtonV01Icon;
150
+ /** Replace the built-in leading/icon glyph. */
151
+ iconSlot?: React.ReactNode;
152
+ }
153
+
154
+ const ButtonV01 = React.forwardRef<HTMLButtonElement, ButtonV01Props>(
155
+ (
156
+ {
157
+ className,
158
+ size = "large",
159
+ typeVariant = "cta",
160
+ hierarchy = "primary",
161
+ appearance = "gradient",
162
+ theme = "default",
163
+ context = "default",
164
+ icon = "none",
165
+ iconSlot,
166
+ disabled,
167
+ children,
168
+ type = "button",
169
+ ...props
170
+ },
171
+ ref,
172
+ ) => {
173
+ const isIcon = typeVariant === "icon";
174
+ const isFloating = !isIcon && context === "floating";
175
+ const look = resolveLook({
176
+ type: typeVariant,
177
+ hierarchy,
178
+ appearance,
179
+ theme,
180
+ context,
181
+ icon,
182
+ });
183
+ const label = children ?? defaultLabel({ type: typeVariant, hierarchy, context });
184
+
185
+ const glyph = (() => {
186
+ if (iconSlot) return iconSlot;
187
+ if (isFloating) {
188
+ return (
189
+ <img
190
+ src={iconDevice}
191
+ alt=""
192
+ className="button-v01-device"
193
+ width={20}
194
+ height={19.0963}
195
+ />
196
+ );
197
+ }
198
+ if (isIcon && icon !== "none") {
199
+ return (
200
+ <MaskIcon
201
+ src={ICON_SRC[icon]}
202
+ className={cn(
203
+ "button-v01-glyph",
204
+ icon === "check" && "button-v01-glyph--check",
205
+ icon === "close" && "button-v01-glyph--close",
206
+ icon === "delete" && "button-v01-glyph--delete",
207
+ icon === "back" && "button-v01-glyph--back",
208
+ )}
209
+ />
210
+ );
211
+ }
212
+ return null;
213
+ })();
214
+
215
+ return (
216
+ <button
217
+ ref={ref}
218
+ type={type}
219
+ disabled={disabled}
220
+ className={cn(
221
+ buttonV01Variants({ size: isIcon ? undefined : (size ?? "large") }),
222
+ isIcon && "button-v01--icon",
223
+ look,
224
+ className,
225
+ )}
226
+ {...props}
227
+ >
228
+ {glyph}
229
+ {label != null && label !== "" ? (
230
+ <span className="button-v01-label">{label}</span>
231
+ ) : null}
232
+ </button>
233
+ );
234
+ },
235
+ );
236
+ ButtonV01.displayName = "ButtonV01";
237
+
238
+ export { ButtonV01, buttonV01Variants };
@@ -23,6 +23,8 @@ const buttonVariants = cva(
23
23
  "transparent-tertiary": "button--transparent-tertiary",
24
24
  "tooltip-primary": "button--tooltip-primary",
25
25
  "tooltip-secondary": "button--tooltip-secondary",
26
+ "tooltip-primary-light": "button--tooltip-primary-light",
27
+ "tooltip-secondary-light": "button--tooltip-secondary-light",
26
28
 
27
29
  },
28
30
  size: {