@syscore/ui-library 1.27.3 → 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.
- 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/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 +4401 -651
- 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,299 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
// ─── AppBar Root ────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export interface AppBarProps extends React.HTMLAttributes<HTMLElement> {
|
|
7
|
+
/** Collapses the bar to show only the prepend slot */
|
|
8
|
+
collapse?: boolean;
|
|
9
|
+
/** Rounds the corners of the bar */
|
|
10
|
+
rounded?: boolean;
|
|
11
|
+
/** Elevates the bar with a shadow */
|
|
12
|
+
elevated?: boolean;
|
|
13
|
+
/** Makes the bar flat (no shadow, no border) */
|
|
14
|
+
flat?: boolean;
|
|
15
|
+
/** Renders bar as fixed (default) or sticky */
|
|
16
|
+
position?: "fixed" | "sticky" | "relative" | "static";
|
|
17
|
+
/** Color / surface variant */
|
|
18
|
+
color?: "default" | "primary" | "transparent";
|
|
19
|
+
/**
|
|
20
|
+
* Density of the bar — controls height.
|
|
21
|
+
* - "default" → 64px
|
|
22
|
+
* - "comfortable" → 56px
|
|
23
|
+
* - "compact" → 48px
|
|
24
|
+
* - "prominent" → 128px (title sits at the bottom)
|
|
25
|
+
*/
|
|
26
|
+
density?: "default" | "comfortable" | "compact" | "prominent";
|
|
27
|
+
/**
|
|
28
|
+
* URL for a background image on the app bar.
|
|
29
|
+
* Pair with `imageOverlay` for a gradient on top.
|
|
30
|
+
*/
|
|
31
|
+
image?: string;
|
|
32
|
+
/**
|
|
33
|
+
* CSS gradient string applied as an overlay on top of the `image`.
|
|
34
|
+
* Example: "to top right, rgba(19,84,122,.8), rgba(128,208,199,.8)"
|
|
35
|
+
* Renders as: linear-gradient(<value>)
|
|
36
|
+
*/
|
|
37
|
+
imageOverlay?: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const DENSITY_HEIGHT: Record<
|
|
41
|
+
NonNullable<AppBarProps["density"]>,
|
|
42
|
+
number
|
|
43
|
+
> = {
|
|
44
|
+
default: 64,
|
|
45
|
+
comfortable: 56,
|
|
46
|
+
compact: 48,
|
|
47
|
+
prominent: 128,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const AppBar = React.forwardRef<HTMLElement, AppBarProps>(
|
|
51
|
+
(
|
|
52
|
+
{
|
|
53
|
+
className,
|
|
54
|
+
children,
|
|
55
|
+
collapse = false,
|
|
56
|
+
rounded = false,
|
|
57
|
+
elevated = false,
|
|
58
|
+
flat = false,
|
|
59
|
+
position = "fixed",
|
|
60
|
+
color = "default",
|
|
61
|
+
density = "default",
|
|
62
|
+
image,
|
|
63
|
+
imageOverlay,
|
|
64
|
+
style,
|
|
65
|
+
...props
|
|
66
|
+
},
|
|
67
|
+
ref
|
|
68
|
+
) => {
|
|
69
|
+
const height = DENSITY_HEIGHT[density];
|
|
70
|
+
const isProminent = density === "prominent";
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
<header
|
|
74
|
+
ref={ref}
|
|
75
|
+
role="banner"
|
|
76
|
+
style={{
|
|
77
|
+
height: collapse ? undefined : height,
|
|
78
|
+
...style,
|
|
79
|
+
}}
|
|
80
|
+
className={cn(
|
|
81
|
+
"app-bar",
|
|
82
|
+
// position
|
|
83
|
+
position === "fixed" && "app-bar--fixed",
|
|
84
|
+
position === "sticky" && "app-bar--sticky",
|
|
85
|
+
position === "relative" && "app-bar--relative",
|
|
86
|
+
position === "static" && "app-bar--static",
|
|
87
|
+
// color variants
|
|
88
|
+
color === "primary" && "app-bar--primary",
|
|
89
|
+
color === "transparent" && "app-bar--transparent",
|
|
90
|
+
// density
|
|
91
|
+
density === "comfortable" && "app-bar--comfortable",
|
|
92
|
+
density === "compact" && "app-bar--compact",
|
|
93
|
+
isProminent && "app-bar--prominent",
|
|
94
|
+
// modifiers
|
|
95
|
+
rounded && "app-bar--rounded",
|
|
96
|
+
elevated && "app-bar--elevated",
|
|
97
|
+
flat && "app-bar--flat",
|
|
98
|
+
collapse && "app-bar--collapse",
|
|
99
|
+
// image
|
|
100
|
+
image && "app-bar--image",
|
|
101
|
+
className
|
|
102
|
+
)}
|
|
103
|
+
{...props}
|
|
104
|
+
>
|
|
105
|
+
{/* Background image layer */}
|
|
106
|
+
{image && (
|
|
107
|
+
<span
|
|
108
|
+
className="app-bar-image-layer"
|
|
109
|
+
style={{ backgroundImage: `url(${image})` }}
|
|
110
|
+
aria-hidden="true"
|
|
111
|
+
>
|
|
112
|
+
{imageOverlay && (
|
|
113
|
+
<span
|
|
114
|
+
className="app-bar-image-overlay"
|
|
115
|
+
style={{
|
|
116
|
+
background: `linear-gradient(${imageOverlay})`,
|
|
117
|
+
}}
|
|
118
|
+
/>
|
|
119
|
+
)}
|
|
120
|
+
</span>
|
|
121
|
+
)}
|
|
122
|
+
|
|
123
|
+
{/* Prominent layout: split children into top-row (prepend/append)
|
|
124
|
+
and bottom-row (title) so the title sits at the bottom */}
|
|
125
|
+
{isProminent ? (
|
|
126
|
+
<ProminentLayout>{children}</ProminentLayout>
|
|
127
|
+
) : (
|
|
128
|
+
children
|
|
129
|
+
)}
|
|
130
|
+
</header>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
AppBar.displayName = "AppBar";
|
|
135
|
+
|
|
136
|
+
// ─── AppBarNavIcon ───────────────────────────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
export interface AppBarNavIconProps
|
|
139
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
140
|
+
/** Custom icon; defaults to hamburger menu */
|
|
141
|
+
icon?: React.ReactNode;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const AppBarNavIcon = React.forwardRef<HTMLButtonElement, AppBarNavIconProps>(
|
|
145
|
+
({ className, icon, children, ...props }, ref) => (
|
|
146
|
+
<button
|
|
147
|
+
ref={ref}
|
|
148
|
+
type="button"
|
|
149
|
+
aria-label="Open navigation menu"
|
|
150
|
+
className={cn("app-bar-nav-icon", className)}
|
|
151
|
+
{...props}
|
|
152
|
+
>
|
|
153
|
+
{children ?? icon ?? <HamburgerIcon />}
|
|
154
|
+
</button>
|
|
155
|
+
)
|
|
156
|
+
);
|
|
157
|
+
AppBarNavIcon.displayName = "AppBarNavIcon";
|
|
158
|
+
|
|
159
|
+
// ─── AppBarTitle ─────────────────────────────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
export interface AppBarTitleProps extends React.HTMLAttributes<HTMLDivElement> {}
|
|
162
|
+
|
|
163
|
+
const AppBarTitle = React.forwardRef<HTMLDivElement, AppBarTitleProps>(
|
|
164
|
+
({ className, ...props }, ref) => (
|
|
165
|
+
<div
|
|
166
|
+
ref={ref}
|
|
167
|
+
className={cn("app-bar-title", className)}
|
|
168
|
+
{...props}
|
|
169
|
+
/>
|
|
170
|
+
)
|
|
171
|
+
);
|
|
172
|
+
AppBarTitle.displayName = "AppBarTitle";
|
|
173
|
+
|
|
174
|
+
// ─── AppBarPrepend ───────────────────────────────────────────────────────────
|
|
175
|
+
|
|
176
|
+
export interface AppBarPrependProps
|
|
177
|
+
extends React.HTMLAttributes<HTMLDivElement> {}
|
|
178
|
+
|
|
179
|
+
const AppBarPrepend = React.forwardRef<HTMLDivElement, AppBarPrependProps>(
|
|
180
|
+
({ className, ...props }, ref) => (
|
|
181
|
+
<div
|
|
182
|
+
ref={ref}
|
|
183
|
+
className={cn("app-bar-prepend", className)}
|
|
184
|
+
{...props}
|
|
185
|
+
/>
|
|
186
|
+
)
|
|
187
|
+
);
|
|
188
|
+
AppBarPrepend.displayName = "AppBarPrepend";
|
|
189
|
+
|
|
190
|
+
// ─── AppBarAppend ─────────────────────────────────────────────────────────────
|
|
191
|
+
|
|
192
|
+
export interface AppBarAppendProps
|
|
193
|
+
extends React.HTMLAttributes<HTMLDivElement> {}
|
|
194
|
+
|
|
195
|
+
const AppBarAppend = React.forwardRef<HTMLDivElement, AppBarAppendProps>(
|
|
196
|
+
({ className, ...props }, ref) => (
|
|
197
|
+
<div
|
|
198
|
+
ref={ref}
|
|
199
|
+
className={cn("app-bar-append", className)}
|
|
200
|
+
{...props}
|
|
201
|
+
/>
|
|
202
|
+
)
|
|
203
|
+
);
|
|
204
|
+
AppBarAppend.displayName = "AppBarAppend";
|
|
205
|
+
|
|
206
|
+
// ─── AppBarContent (fills remaining horizontal space) ────────────────────────
|
|
207
|
+
|
|
208
|
+
export interface AppBarContentProps
|
|
209
|
+
extends React.HTMLAttributes<HTMLDivElement> {}
|
|
210
|
+
|
|
211
|
+
const AppBarContent = React.forwardRef<HTMLDivElement, AppBarContentProps>(
|
|
212
|
+
({ className, ...props }, ref) => (
|
|
213
|
+
<div
|
|
214
|
+
ref={ref}
|
|
215
|
+
className={cn("app-bar-content", className)}
|
|
216
|
+
{...props}
|
|
217
|
+
/>
|
|
218
|
+
)
|
|
219
|
+
);
|
|
220
|
+
AppBarContent.displayName = "AppBarContent";
|
|
221
|
+
|
|
222
|
+
// ─── ProminentLayout helper ───────────────────────────────────────────────
|
|
223
|
+
// Splits children into two rows: prepend+append at top, title at bottom.
|
|
224
|
+
// Matches Vuetify's prominent bar layout.
|
|
225
|
+
|
|
226
|
+
function ProminentLayout({ children }: { children: React.ReactNode }) {
|
|
227
|
+
const kids = React.Children.toArray(children);
|
|
228
|
+
|
|
229
|
+
const topKids = kids.filter((child) => {
|
|
230
|
+
if (!React.isValidElement(child)) return false;
|
|
231
|
+
const name =
|
|
232
|
+
(child.type as { displayName?: string }).displayName ?? "";
|
|
233
|
+
return name === "AppBarPrepend" || name === "AppBarAppend";
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
const bottomKids = kids.filter((child) => {
|
|
237
|
+
if (!React.isValidElement(child)) return false;
|
|
238
|
+
const name =
|
|
239
|
+
(child.type as { displayName?: string }).displayName ?? "";
|
|
240
|
+
return name === "AppBarTitle" || name === "AppBarContent";
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// Anything not matched falls into the top row
|
|
244
|
+
const otherKids = kids.filter((child) => {
|
|
245
|
+
if (!React.isValidElement(child)) return true;
|
|
246
|
+
const name =
|
|
247
|
+
(child.type as { displayName?: string }).displayName ?? "";
|
|
248
|
+
return ![
|
|
249
|
+
"AppBarPrepend",
|
|
250
|
+
"AppBarAppend",
|
|
251
|
+
"AppBarTitle",
|
|
252
|
+
"AppBarContent",
|
|
253
|
+
].includes(name);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
<span className="app-bar-prominent-inner">
|
|
258
|
+
<span className="app-bar-prominent-top">
|
|
259
|
+
{topKids}
|
|
260
|
+
{otherKids}
|
|
261
|
+
</span>
|
|
262
|
+
<span className="app-bar-prominent-bottom">{bottomKids}</span>
|
|
263
|
+
</span>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ─── Internal helpers ────────────────────────────────────────────────────────
|
|
268
|
+
|
|
269
|
+
function HamburgerIcon() {
|
|
270
|
+
return (
|
|
271
|
+
<svg
|
|
272
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
273
|
+
width="24"
|
|
274
|
+
height="24"
|
|
275
|
+
viewBox="0 0 24 24"
|
|
276
|
+
fill="none"
|
|
277
|
+
stroke="currentColor"
|
|
278
|
+
strokeWidth="2"
|
|
279
|
+
strokeLinecap="round"
|
|
280
|
+
strokeLinejoin="round"
|
|
281
|
+
aria-hidden="true"
|
|
282
|
+
>
|
|
283
|
+
<line x1="4" y1="6" x2="20" y2="6" />
|
|
284
|
+
<line x1="4" y1="12" x2="20" y2="12" />
|
|
285
|
+
<line x1="4" y1="18" x2="20" y2="18" />
|
|
286
|
+
</svg>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ─── Exports ─────────────────────────────────────────────────────────────────
|
|
291
|
+
|
|
292
|
+
export {
|
|
293
|
+
AppBar,
|
|
294
|
+
AppBarNavIcon,
|
|
295
|
+
AppBarTitle,
|
|
296
|
+
AppBarPrepend,
|
|
297
|
+
AppBarAppend,
|
|
298
|
+
AppBarContent,
|
|
299
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import placeholderIcon from "@/assets/avatar-identity/placeholder-icon.svg";
|
|
5
|
+
|
|
6
|
+
const avatarIdentityVariants = cva("avatar-identity", {
|
|
7
|
+
variants: {
|
|
8
|
+
size: {
|
|
9
|
+
xlarge: "avatar-identity--xlarge",
|
|
10
|
+
large: "avatar-identity--large",
|
|
11
|
+
medium: "avatar-identity--medium",
|
|
12
|
+
},
|
|
13
|
+
textDirection: {
|
|
14
|
+
right: "avatar-identity--right",
|
|
15
|
+
left: "avatar-identity--left",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: {
|
|
19
|
+
size: "xlarge",
|
|
20
|
+
textDirection: "right",
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
export type AvatarIdentitySize = NonNullable<
|
|
25
|
+
VariantProps<typeof avatarIdentityVariants>["size"]
|
|
26
|
+
>;
|
|
27
|
+
export type AvatarIdentityTextDirection = NonNullable<
|
|
28
|
+
VariantProps<typeof avatarIdentityVariants>["textDirection"]
|
|
29
|
+
>;
|
|
30
|
+
export type AvatarIdentityVariant = "placeholder" | "initials" | "image";
|
|
31
|
+
|
|
32
|
+
export interface AvatarIdentityProps
|
|
33
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "color">,
|
|
34
|
+
VariantProps<typeof avatarIdentityVariants> {
|
|
35
|
+
/** Face treatment from Figma `style`. */
|
|
36
|
+
variant?: AvatarIdentityVariant;
|
|
37
|
+
name?: string;
|
|
38
|
+
email?: string;
|
|
39
|
+
/** Override initials. Defaults to the first letters of `name`. */
|
|
40
|
+
initials?: string;
|
|
41
|
+
src?: string;
|
|
42
|
+
alt?: string;
|
|
43
|
+
showText?: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function initialsFromName(name: string) {
|
|
47
|
+
return name
|
|
48
|
+
.split(/\s+/)
|
|
49
|
+
.filter(Boolean)
|
|
50
|
+
.slice(0, 2)
|
|
51
|
+
.map((part) => part[0])
|
|
52
|
+
.join("")
|
|
53
|
+
.toUpperCase();
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const AvatarIdentity = React.forwardRef<HTMLDivElement, AvatarIdentityProps>(
|
|
57
|
+
(
|
|
58
|
+
{
|
|
59
|
+
className,
|
|
60
|
+
size,
|
|
61
|
+
textDirection,
|
|
62
|
+
variant = "placeholder",
|
|
63
|
+
name = "",
|
|
64
|
+
email = "",
|
|
65
|
+
initials,
|
|
66
|
+
src,
|
|
67
|
+
alt,
|
|
68
|
+
showText = true,
|
|
69
|
+
...props
|
|
70
|
+
},
|
|
71
|
+
ref,
|
|
72
|
+
) => {
|
|
73
|
+
const face = variant === "image" && !src ? "placeholder" : variant;
|
|
74
|
+
const letters = (initials || initialsFromName(name) || "JD").slice(0, 2);
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<div
|
|
78
|
+
ref={ref}
|
|
79
|
+
className={cn(avatarIdentityVariants({ size, textDirection }), className)}
|
|
80
|
+
{...props}
|
|
81
|
+
>
|
|
82
|
+
<div className="avatar-identity__face" data-face={face}>
|
|
83
|
+
{face === "image" && src ? (
|
|
84
|
+
<img
|
|
85
|
+
className="avatar-identity__photo"
|
|
86
|
+
src={src}
|
|
87
|
+
alt={alt || name}
|
|
88
|
+
/>
|
|
89
|
+
) : null}
|
|
90
|
+
|
|
91
|
+
{face === "initials" ? (
|
|
92
|
+
<span className="avatar-identity__initials" aria-hidden={showText}>
|
|
93
|
+
{letters}
|
|
94
|
+
</span>
|
|
95
|
+
) : null}
|
|
96
|
+
|
|
97
|
+
{face === "placeholder" ? (
|
|
98
|
+
<img
|
|
99
|
+
className="avatar-identity__placeholder-icon"
|
|
100
|
+
src={placeholderIcon}
|
|
101
|
+
alt=""
|
|
102
|
+
/>
|
|
103
|
+
) : null}
|
|
104
|
+
|
|
105
|
+
{face !== "placeholder" ? (
|
|
106
|
+
<span className="avatar-identity__border" aria-hidden="true" />
|
|
107
|
+
) : null}
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
{showText ? (
|
|
111
|
+
<div className="avatar-identity__text">
|
|
112
|
+
<p className="avatar-identity__name">{name}</p>
|
|
113
|
+
<p className="avatar-identity__email">{email}</p>
|
|
114
|
+
</div>
|
|
115
|
+
) : null}
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
AvatarIdentity.displayName = "AvatarIdentity";
|
|
121
|
+
|
|
122
|
+
export { AvatarIdentity, avatarIdentityVariants };
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
// ─── Variants ─────────────────────────────────────────────────────────────────
|
|
6
|
+
|
|
7
|
+
const bannerVariants = cva("banner", {
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "banner--default",
|
|
11
|
+
info: "banner--info",
|
|
12
|
+
success: "banner--success",
|
|
13
|
+
warning: "banner--warning",
|
|
14
|
+
destructive: "banner--destructive",
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
defaultVariants: {
|
|
18
|
+
variant: "default",
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// ─── Banner Root ──────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
export interface BannerProps
|
|
25
|
+
extends React.HTMLAttributes<HTMLDivElement>,
|
|
26
|
+
VariantProps<typeof bannerVariants> {
|
|
27
|
+
/** Renders the banner fixed to the top of the viewport */
|
|
28
|
+
sticky?: boolean;
|
|
29
|
+
/** Called when the close button is clicked — also renders the close button */
|
|
30
|
+
onClose?: () => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const Banner = React.forwardRef<HTMLDivElement, BannerProps>(
|
|
34
|
+
({ className, variant, sticky = false, onClose, children, ...props }, ref) => (
|
|
35
|
+
<div
|
|
36
|
+
ref={ref}
|
|
37
|
+
role="banner"
|
|
38
|
+
className={cn(bannerVariants({ variant }), sticky && "banner--sticky", className)}
|
|
39
|
+
{...props}
|
|
40
|
+
>
|
|
41
|
+
<div className="banner-content">{children}</div>
|
|
42
|
+
{onClose && (
|
|
43
|
+
<button
|
|
44
|
+
type="button"
|
|
45
|
+
className="banner-close"
|
|
46
|
+
aria-label="Dismiss banner"
|
|
47
|
+
onClick={onClose}
|
|
48
|
+
>
|
|
49
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
|
|
50
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
51
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
52
|
+
</svg>
|
|
53
|
+
</button>
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
)
|
|
57
|
+
);
|
|
58
|
+
Banner.displayName = "Banner";
|
|
59
|
+
|
|
60
|
+
// ─── BannerIcon ───────────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
const BannerIcon = React.forwardRef<HTMLSpanElement, React.HTMLAttributes<HTMLSpanElement>>(
|
|
63
|
+
({ className, ...props }, ref) => (
|
|
64
|
+
<span ref={ref} className={cn("banner-icon", className)} aria-hidden="true" {...props} />
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
BannerIcon.displayName = "BannerIcon";
|
|
68
|
+
|
|
69
|
+
// ─── BannerTitle ──────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
const BannerTitle = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLHeadingElement>>(
|
|
72
|
+
({ className, ...props }, ref) => (
|
|
73
|
+
<p ref={ref} className={cn("banner-title", className)} {...props} />
|
|
74
|
+
)
|
|
75
|
+
);
|
|
76
|
+
BannerTitle.displayName = "BannerTitle";
|
|
77
|
+
|
|
78
|
+
// ─── BannerDescription ────────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
const BannerDescription = React.forwardRef<HTMLParagraphElement, React.HTMLAttributes<HTMLParagraphElement>>(
|
|
81
|
+
({ className, ...props }, ref) => (
|
|
82
|
+
<p ref={ref} className={cn("banner-description", className)} {...props} />
|
|
83
|
+
)
|
|
84
|
+
);
|
|
85
|
+
BannerDescription.displayName = "BannerDescription";
|
|
86
|
+
|
|
87
|
+
// ─── BannerText ───────────────────────────────────────────────────────────────
|
|
88
|
+
// Wraps BannerTitle + BannerDescription so they stack vertically (2-line / 3-line layouts).
|
|
89
|
+
|
|
90
|
+
const BannerText = React.forwardRef<HTMLDivElement, React.HTMLAttributes<HTMLDivElement>>(
|
|
91
|
+
({ className, ...props }, ref) => (
|
|
92
|
+
<div ref={ref} className={cn("banner-text", className)} {...props} />
|
|
93
|
+
)
|
|
94
|
+
);
|
|
95
|
+
BannerText.displayName = "BannerText";
|
|
96
|
+
|
|
97
|
+
// ─── BannerAction ─────────────────────────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
const BannerAction = React.forwardRef<HTMLButtonElement, React.ButtonHTMLAttributes<HTMLButtonElement>>(
|
|
100
|
+
({ className, ...props }, ref) => (
|
|
101
|
+
<button ref={ref} type="button" className={cn("banner-action", className)} {...props} />
|
|
102
|
+
)
|
|
103
|
+
);
|
|
104
|
+
BannerAction.displayName = "BannerAction";
|
|
105
|
+
|
|
106
|
+
// ─── Exports ──────────────────────────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
export { Banner, BannerIcon, BannerTitle, BannerText, BannerDescription, BannerAction, bannerVariants };
|