@syscore/ui-library 1.27.3 → 2.0.0-beta-publish-workflow-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/App.tsx +0 -2
- package/client/assets/alert/icon-clear.svg +4 -0
- package/client/assets/alert/icon-done.svg +3 -0
- package/client/assets/avatar-identity/photo.png +0 -0
- package/client/assets/avatar-identity/placeholder-icon.svg +8 -0
- package/client/assets/breadcrumb/icon-ellipsis.svg +5 -0
- package/client/assets/breadcrumb/icon-home.svg +3 -0
- package/client/assets/breadcrumb/icon-separator.svg +3 -0
- package/client/assets/button-v01/icon-back.svg +4 -0
- package/client/assets/button-v01/icon-check.svg +3 -0
- package/client/assets/button-v01/icon-close.svg +4 -0
- package/client/assets/button-v01/icon-delete.svg +3 -0
- package/client/assets/button-v01/icon-device.svg +3 -0
- package/client/assets/dropdown/icon-carrot-contract.svg +3 -0
- package/client/assets/dropdown/icon-carrot-expand.svg +3 -0
- package/client/assets/dropdown/icon-clear.svg +4 -0
- package/client/assets/dropdown/icon-enter.svg +3 -0
- package/client/components/icons/UtilityChangeType.tsx +21 -0
- package/client/components/ui/alert-dialog-panel.tsx +246 -0
- package/client/components/ui/alert-v02.tsx +214 -0
- package/client/components/ui/app-bar.tsx +299 -0
- package/client/components/ui/avatar-identity.tsx +122 -0
- package/client/components/ui/banner.tsx +108 -0
- package/client/components/ui/bottom-navigation.tsx +223 -0
- package/client/components/ui/breadcrumb-trail.tsx +170 -0
- package/client/components/ui/button-v01.tsx +238 -0
- package/client/components/ui/button.tsx +2 -0
- package/client/components/ui/card.tsx +108 -26
- package/client/components/ui/code-badge.tsx +62 -0
- package/client/components/ui/date-picker.tsx +188 -0
- package/client/components/ui/dropdown.tsx +448 -0
- package/client/components/ui/empty-state.tsx +197 -0
- package/client/components/ui/file-upload.tsx +214 -0
- package/client/components/ui/footer.tsx +179 -0
- package/client/components/ui/layout.tsx +1182 -0
- package/client/components/ui/pagination-v01.tsx +157 -0
- package/client/components/ui/password-field-v01.tsx +67 -0
- package/client/components/ui/search-field-v01.tsx +192 -0
- package/client/components/ui/section-heading.tsx +49 -0
- package/client/components/ui/sidebar.tsx +2 -3
- package/client/components/ui/standard-navigation.tsx +8 -2
- package/client/components/ui/stepper.tsx +148 -0
- package/client/components/ui/switch-v01.tsx +71 -0
- package/client/components/ui/system-bar.tsx +119 -0
- package/client/components/ui/table-v01.tsx +230 -0
- package/client/components/ui/tabs-v01.tsx +99 -0
- package/client/components/ui/tag-v02.tsx +113 -0
- package/client/components/ui/text-area-v01.tsx +33 -0
- package/client/components/ui/text-field-v01.tsx +108 -0
- package/client/components/ui/timeline.tsx +181 -0
- package/client/components/ui/toast-v01.tsx +206 -0
- package/client/components/ui/tooltip.tsx +264 -71
- package/client/global.css +9565 -4378
- package/client/storybook.css +125 -0
- package/client/ui/Accordion.stories.tsx +1 -1
- package/client/ui/AlertDialogPanel/AlertDialogPanel.stories.tsx +150 -0
- package/client/ui/AlertV02/AlertV02.stories.tsx +188 -0
- package/client/ui/AppBar/app-bar.stories.tsx +280 -0
- package/client/ui/AspectRatio.stories.tsx +1 -1
- package/client/ui/AvatarIdentity/AvatarIdentity.stories.tsx +77 -0
- package/client/ui/Banner/banner.stories.tsx +238 -0
- package/client/ui/BottomNavigation/bottom-navigation.stories.tsx +285 -0
- package/client/ui/BreadcrumbTrail/BreadcrumbTrail.stories.tsx +232 -0
- package/client/ui/Button.stories.tsx +1 -1
- package/client/ui/ButtonV01/ButtonV01.stories.tsx +417 -0
- package/client/ui/Calendar/Calendar.stories.tsx +1 -1
- package/client/ui/Card.stories.tsx +285 -168
- package/client/ui/Carousel/Carousel.stories.tsx +1 -1
- package/client/ui/Chart/Chart.stories.tsx +1 -1
- package/client/ui/Checkbox.stories.tsx +1 -1
- package/client/ui/Collapsible/Collapsible.stories.tsx +1 -1
- package/client/ui/Colors.stories.tsx +1 -1
- package/client/ui/Command/Command.stories.tsx +1 -1
- package/client/ui/ContextMenu/ContextMenu.stories.tsx +1 -1
- package/client/ui/DatePicker/DatePicker.stories.tsx +293 -0
- package/client/ui/Dialog.stories.tsx +1 -1
- package/client/ui/Drawer/Drawer.stories.tsx +1 -1
- package/client/ui/Dropdown/Dropdown.stories.tsx +205 -0
- package/client/ui/EmptyState/empty-state.stories.tsx +251 -0
- package/client/ui/FileUpload/FileUpload.stories.tsx +218 -0
- package/client/ui/Footer/footer.stories.tsx +194 -0
- package/client/ui/Form/Form.stories.tsx +1 -1
- package/client/ui/Hero.stories.tsx +1 -1
- package/client/ui/HoverCard/HoverCard.stories.tsx +1 -1
- package/client/ui/Icons.stories.tsx +1 -1
- package/client/ui/Input.stories.tsx +1 -1
- package/client/ui/InputOTP/InputOTP.stories.tsx +1 -1
- package/client/ui/Label.stories.tsx +1 -1
- package/client/ui/Layout/Center.stories.tsx +100 -0
- package/client/ui/Layout/Columns.stories.tsx +136 -0
- package/client/ui/Layout/Container.stories.tsx +104 -0
- package/client/ui/Layout/FormLayout.stories.tsx +144 -0
- package/client/ui/Layout/Grid.stories.tsx +158 -0
- package/client/ui/Layout/GridSpan.stories.tsx +75 -0
- package/client/ui/Layout/HStack.stories.tsx +128 -0
- package/client/ui/Layout/Layout.stories.tsx +287 -0
- package/client/ui/Layout/LayoutContent.stories.tsx +91 -0
- package/client/ui/Layout/LayoutFooter.stories.tsx +77 -0
- package/client/ui/Layout/LayoutHeader.stories.tsx +104 -0
- package/client/ui/Layout/LayoutPanel.stories.tsx +143 -0
- package/client/ui/Layout/Positioned.stories.tsx +113 -0
- package/client/ui/Layout/Section.stories.tsx +84 -0
- package/client/ui/Layout/ShowHide.stories.tsx +99 -0
- package/client/ui/Layout/Spacer.stories.tsx +149 -0
- package/client/ui/Layout/Stack.stories.tsx +69 -0
- package/client/ui/Layout/StackItem.stories.tsx +124 -0
- package/client/ui/Layout/VStack.stories.tsx +101 -0
- package/client/ui/Layout/controls.ts +33 -0
- package/client/ui/Layout/demo.tsx +37 -0
- package/client/ui/Menubar/Menubar.stories.tsx +1 -1
- package/client/ui/MobileNav.stories.tsx +1 -1
- package/client/ui/Navigation.stories.tsx +1 -1
- package/client/ui/NavigationMenu/NavigationMenu.stories.tsx +1 -1
- package/client/ui/PageHeader.stories.tsx +13 -13
- package/client/ui/Pagination/Pagination.stories.tsx +178 -98
- package/client/ui/Pagination/PaginationLegacy.stories.tsx +121 -0
- package/client/ui/Panel.stories.tsx +1 -1
- package/client/ui/Popover/Popover.stories.tsx +1 -1
- package/client/ui/Progress/Progress.stories.tsx +1 -1
- package/client/ui/RadioGroup/RadioGroup.stories.tsx +1 -1
- package/client/ui/Resizable/Resizable.stories.tsx +1 -1
- package/client/ui/ScrollArea/ScrollArea.stories.tsx +1 -1
- package/client/ui/SearchField/SearchField.stories.tsx +153 -0
- package/client/ui/{SearchField.stories.tsx → SearchField/SearchFieldLegacy.stories.tsx} +2 -2
- package/client/ui/SectionBadge.stories.tsx +1 -1
- package/client/ui/Select.stories.tsx +1 -1
- package/client/ui/Separator/Separator.stories.tsx +1 -1
- package/client/ui/Sheet/Sheet.stories.tsx +1 -1
- package/client/ui/Sidebar/Sidebar.stories.tsx +57 -8
- package/client/ui/Skeleton/Skeleton.stories.tsx +1 -1
- package/client/ui/Slider/Slider.stories.tsx +1 -1
- package/client/ui/StandardNavigation.stories.tsx +1 -1
- package/client/ui/Stepper/Stepper.stories.tsx +344 -0
- package/client/ui/Switch.stories.tsx +1 -1
- package/client/ui/SwitchV01/SwitchV01.stories.tsx +152 -0
- package/client/ui/SystemBar/system-bar.stories.tsx +179 -0
- package/client/ui/Table/TableV01.stories.tsx +256 -0
- package/client/ui/Tabs.stories.tsx +1 -1
- package/client/ui/TabsV01/TabsV01.stories.tsx +144 -0
- package/client/ui/Tag.stories.tsx +1 -1
- package/client/ui/TagV02/TagStatusV02.stories.tsx +132 -0
- package/client/ui/TagV02/TagV02.stories.tsx +142 -0
- package/client/ui/TextAreaV01/TextAreaV01.stories.tsx +146 -0
- package/client/ui/TextFieldV01/PasswordFieldV01.stories.tsx +161 -0
- package/client/ui/TextFieldV01/TextFieldV01.stories.tsx +160 -0
- package/client/ui/Textarea.stories.tsx +1 -1
- package/client/ui/Timeline/timeline.stories.tsx +404 -0
- package/client/ui/Toast/Toast.stories.tsx +1 -1
- package/client/ui/ToastV01/ToastV01.stories.tsx +201 -0
- package/client/ui/Toaster/Toaster.stories.tsx +1 -1
- package/client/ui/Toggle.stories.tsx +1 -1
- package/client/ui/Tooltip.stories.tsx +287 -114
- package/client/ui/Typography.stories.tsx +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.d.ts +649 -13
- package/dist/index.es.js +4405 -653
- package/package.json +1 -1
- package/client/components/ui/badge.tsx +0 -33
- package/client/components/ui/sonner.tsx +0 -26
- package/client/components/ui/toggle-group.tsx +0 -76
- package/client/ui/Alert/Alert.stories.tsx +0 -82
- package/client/ui/AlertDialog/AlertDialog.stories.tsx +0 -106
- package/client/ui/Avatar/Avatar.stories.tsx +0 -94
- package/client/ui/Badge/Badge.stories.tsx +0 -60
- package/client/ui/Breadcrumb/Breadcrumb.stories.tsx +0 -97
- package/client/ui/DropdownMenu/DropdownMenu.stories.tsx +0 -139
- package/client/ui/Sonner/Sonner.stories.tsx +0 -48
- package/client/ui/Table/Table.stories.tsx +0 -135
- 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: {
|