@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,71 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
|
3
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
4
|
+
|
|
5
|
+
import { cn } from "@/lib/utils";
|
|
6
|
+
|
|
7
|
+
const switchV01Variants = cva("switch-v01 font-mazzardSoft", {
|
|
8
|
+
variants: {
|
|
9
|
+
textDirection: {
|
|
10
|
+
right: "switch-v01--text-right",
|
|
11
|
+
left: "switch-v01--text-left",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
defaultVariants: {
|
|
15
|
+
textDirection: "right",
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export type SwitchV01TextDirection = NonNullable<
|
|
20
|
+
VariantProps<typeof switchV01Variants>["textDirection"]
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
export interface SwitchV01Props
|
|
24
|
+
extends Omit<
|
|
25
|
+
React.ComponentPropsWithoutRef<typeof SwitchPrimitives.Root>,
|
|
26
|
+
"children"
|
|
27
|
+
>,
|
|
28
|
+
VariantProps<typeof switchV01Variants> {
|
|
29
|
+
label?: React.ReactNode;
|
|
30
|
+
showText?: boolean;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const SwitchV01 = React.forwardRef<
|
|
34
|
+
React.ElementRef<typeof SwitchPrimitives.Root>,
|
|
35
|
+
SwitchV01Props
|
|
36
|
+
>(
|
|
37
|
+
(
|
|
38
|
+
{
|
|
39
|
+
className,
|
|
40
|
+
textDirection = "right",
|
|
41
|
+
label = "Label",
|
|
42
|
+
showText = true,
|
|
43
|
+
...props
|
|
44
|
+
},
|
|
45
|
+
ref,
|
|
46
|
+
) => {
|
|
47
|
+
const toggle = (
|
|
48
|
+
<SwitchPrimitives.Root ref={ref} className="switch-v01-toggle" {...props}>
|
|
49
|
+
<SwitchPrimitives.Thumb className="switch-v01-thumb" />
|
|
50
|
+
</SwitchPrimitives.Root>
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
if (!showText || label == null || label === "") {
|
|
54
|
+
return (
|
|
55
|
+
<span className={cn(switchV01Variants({ textDirection }), className)}>
|
|
56
|
+
{toggle}
|
|
57
|
+
</span>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<label className={cn(switchV01Variants({ textDirection }), className)}>
|
|
63
|
+
{toggle}
|
|
64
|
+
<span className="switch-v01-label overline-medium">{label}</span>
|
|
65
|
+
</label>
|
|
66
|
+
);
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
SwitchV01.displayName = "SwitchV01";
|
|
70
|
+
|
|
71
|
+
export { SwitchV01, switchV01Variants };
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
// ─── SystemBar Root ───────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export interface SystemBarProps extends React.HTMLAttributes<HTMLElement> {
|
|
7
|
+
/**
|
|
8
|
+
* Background color — any CSS color or var() token.
|
|
9
|
+
* When set, text and icons automatically switch to white for contrast.
|
|
10
|
+
*/
|
|
11
|
+
color?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Window mode: renders a desktop-style title bar with
|
|
14
|
+
* message indicator on the left and window controls on the right.
|
|
15
|
+
*/
|
|
16
|
+
window?: boolean;
|
|
17
|
+
/** Height of the bar in px (default 24 for status bar, 32 for window) */
|
|
18
|
+
height?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const SystemBar = React.forwardRef<HTMLElement, SystemBarProps>(
|
|
22
|
+
(
|
|
23
|
+
{
|
|
24
|
+
className,
|
|
25
|
+
children,
|
|
26
|
+
color,
|
|
27
|
+
window: isWindow = false,
|
|
28
|
+
height,
|
|
29
|
+
style,
|
|
30
|
+
...props
|
|
31
|
+
},
|
|
32
|
+
ref
|
|
33
|
+
) => {
|
|
34
|
+
const barHeight = height ?? (isWindow ? 32 : 24);
|
|
35
|
+
|
|
36
|
+
return (
|
|
37
|
+
<header
|
|
38
|
+
ref={ref}
|
|
39
|
+
role="banner"
|
|
40
|
+
style={{
|
|
41
|
+
height: barHeight,
|
|
42
|
+
backgroundColor: color,
|
|
43
|
+
...style,
|
|
44
|
+
}}
|
|
45
|
+
className={cn(
|
|
46
|
+
"system-bar",
|
|
47
|
+
isWindow && "system-bar--window",
|
|
48
|
+
color && "system-bar--colored",
|
|
49
|
+
className
|
|
50
|
+
)}
|
|
51
|
+
{...props}
|
|
52
|
+
>
|
|
53
|
+
{children}
|
|
54
|
+
</header>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
);
|
|
58
|
+
SystemBar.displayName = "SystemBar";
|
|
59
|
+
|
|
60
|
+
// ─── SystemBarSpacer ──────────────────────────────────────────────────────────
|
|
61
|
+
// Pushes content to the right (flex: 1).
|
|
62
|
+
|
|
63
|
+
export interface SystemBarSpacerProps
|
|
64
|
+
extends React.HTMLAttributes<HTMLSpanElement> {}
|
|
65
|
+
|
|
66
|
+
const SystemBarSpacer = React.forwardRef<HTMLSpanElement, SystemBarSpacerProps>(
|
|
67
|
+
({ className, ...props }, ref) => (
|
|
68
|
+
<span
|
|
69
|
+
ref={ref}
|
|
70
|
+
className={cn("system-bar-spacer", className)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
SystemBarSpacer.displayName = "SystemBarSpacer";
|
|
76
|
+
|
|
77
|
+
// ─── SystemBarIcon ────────────────────────────────────────────────────────────
|
|
78
|
+
// Thin wrapper so icon children pick up the right size + color.
|
|
79
|
+
|
|
80
|
+
export interface SystemBarIconProps
|
|
81
|
+
extends React.HTMLAttributes<HTMLSpanElement> {}
|
|
82
|
+
|
|
83
|
+
const SystemBarIcon = React.forwardRef<HTMLSpanElement, SystemBarIconProps>(
|
|
84
|
+
({ className, ...props }, ref) => (
|
|
85
|
+
<span
|
|
86
|
+
ref={ref}
|
|
87
|
+
className={cn("system-bar-icon", className)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
91
|
+
);
|
|
92
|
+
SystemBarIcon.displayName = "SystemBarIcon";
|
|
93
|
+
|
|
94
|
+
// ─── SystemBarAction ──────────────────────────────────────────────────────────
|
|
95
|
+
// Icon button used in window mode (minimise / maximise / close).
|
|
96
|
+
|
|
97
|
+
export interface SystemBarActionProps
|
|
98
|
+
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
99
|
+
"aria-label": string;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const SystemBarAction = React.forwardRef<
|
|
103
|
+
HTMLButtonElement,
|
|
104
|
+
SystemBarActionProps
|
|
105
|
+
>(({ className, children, ...props }, ref) => (
|
|
106
|
+
<button
|
|
107
|
+
ref={ref}
|
|
108
|
+
type="button"
|
|
109
|
+
className={cn("system-bar-action", className)}
|
|
110
|
+
{...props}
|
|
111
|
+
>
|
|
112
|
+
{children}
|
|
113
|
+
</button>
|
|
114
|
+
));
|
|
115
|
+
SystemBarAction.displayName = "SystemBarAction";
|
|
116
|
+
|
|
117
|
+
// ─── Exports ─────────────────────────────────────────────────────────────────
|
|
118
|
+
|
|
119
|
+
export { SystemBar, SystemBarSpacer, SystemBarIcon, SystemBarAction };
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Table — built from the Figma component set `Table component` (node 12176:414).
|
|
7
|
+
*
|
|
8
|
+
* Figma models a single *cell* with four properties and ~135 variants. Two of
|
|
9
|
+
* them are structural rather than authored:
|
|
10
|
+
*
|
|
11
|
+
* - `Alignment` (Left / Center / Right) is really the cell's **column
|
|
12
|
+
* position**: it selects which borders and which corner radius the cell
|
|
13
|
+
* carries, not how the text sits. `Center` carries all four, so every column
|
|
14
|
+
* is divided; that grid is derived here from the cell order.
|
|
15
|
+
* - `Placement` (Header / Intermediate / Bottom) is the **row position**.
|
|
16
|
+
* Header is its own element; Intermediate vs Bottom is derived from
|
|
17
|
+
* `tr:last-child`.
|
|
18
|
+
*
|
|
19
|
+
* `Content type` (Text / Text + Tag / Text + Action / Tag / Action) collapses
|
|
20
|
+
* into composition — a cell renders whatever children it is given, with the
|
|
21
|
+
* 10px gap Figma specifies.
|
|
22
|
+
*
|
|
23
|
+
* That leaves `density` as the one real variant, and it is the cell's *vertical*
|
|
24
|
+
* padding only — 16 / 20 / 24, all on the `--space-*` scale. Horizontal padding
|
|
25
|
+
* stays 16 at every density. Cell heights then follow from the content, matching
|
|
26
|
+
* Figma's 40/48/56 for headers, 43/51/59 for text rows and 64/72/80 for tag and
|
|
27
|
+
* action rows.
|
|
28
|
+
*/
|
|
29
|
+
type TableV01Density = "compact" | "balanced" | "spacious";
|
|
30
|
+
type TableV01Align = "left" | "center" | "right";
|
|
31
|
+
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Root
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
interface TableV01Props extends React.TableHTMLAttributes<HTMLTableElement> {
|
|
37
|
+
/** Figma `Density`. Sets the cell's vertical padding; heights follow. Default `compact`. */
|
|
38
|
+
density?: TableV01Density;
|
|
39
|
+
/** Wrapper class, for width or overflow handling. */
|
|
40
|
+
containerClassName?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const TableV01 = React.forwardRef<HTMLTableElement, TableV01Props>(
|
|
44
|
+
({ className, containerClassName, density = "compact", ...props }, ref) => (
|
|
45
|
+
<div className={cn("table-v01-wrapper", containerClassName)}>
|
|
46
|
+
<table
|
|
47
|
+
ref={ref}
|
|
48
|
+
data-density={density}
|
|
49
|
+
className={cn("table-v01", className)}
|
|
50
|
+
{...props}
|
|
51
|
+
/>
|
|
52
|
+
</div>
|
|
53
|
+
),
|
|
54
|
+
);
|
|
55
|
+
TableV01.displayName = "TableV01";
|
|
56
|
+
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
// Sections and rows
|
|
59
|
+
// ---------------------------------------------------------------------------
|
|
60
|
+
|
|
61
|
+
const TableV01Header = React.forwardRef<
|
|
62
|
+
HTMLTableSectionElement,
|
|
63
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
64
|
+
>(({ className, ...props }, ref) => (
|
|
65
|
+
<thead ref={ref} className={cn("table-v01-header", className)} {...props} />
|
|
66
|
+
));
|
|
67
|
+
TableV01Header.displayName = "TableV01Header";
|
|
68
|
+
|
|
69
|
+
const TableV01Body = React.forwardRef<
|
|
70
|
+
HTMLTableSectionElement,
|
|
71
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
72
|
+
>(({ className, ...props }, ref) => (
|
|
73
|
+
<tbody ref={ref} className={cn("table-v01-body", className)} {...props} />
|
|
74
|
+
));
|
|
75
|
+
TableV01Body.displayName = "TableV01Body";
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Figma's `Placement=Bottom` is the last row of the table. As a section it also
|
|
79
|
+
* takes over the bottom corner radii from the body.
|
|
80
|
+
*/
|
|
81
|
+
const TableV01Footer = React.forwardRef<
|
|
82
|
+
HTMLTableSectionElement,
|
|
83
|
+
React.HTMLAttributes<HTMLTableSectionElement>
|
|
84
|
+
>(({ className, ...props }, ref) => (
|
|
85
|
+
<tfoot ref={ref} className={cn("table-v01-footer", className)} {...props} />
|
|
86
|
+
));
|
|
87
|
+
TableV01Footer.displayName = "TableV01Footer";
|
|
88
|
+
|
|
89
|
+
const TableV01Row = React.forwardRef<
|
|
90
|
+
HTMLTableRowElement,
|
|
91
|
+
React.HTMLAttributes<HTMLTableRowElement>
|
|
92
|
+
>(({ className, ...props }, ref) => (
|
|
93
|
+
<tr ref={ref} className={cn("table-v01-row", className)} {...props} />
|
|
94
|
+
));
|
|
95
|
+
TableV01Row.displayName = "TableV01Row";
|
|
96
|
+
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Cells
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Figma's code/strategy tag (instance `code_strategy_*`): emerald tint with a
|
|
103
|
+
* 1.5px tinted border and tabular figures.
|
|
104
|
+
*/
|
|
105
|
+
interface TableV01TagProps extends React.HTMLAttributes<HTMLSpanElement> {}
|
|
106
|
+
|
|
107
|
+
const TableV01Tag = React.forwardRef<HTMLSpanElement, TableV01TagProps>(
|
|
108
|
+
({ className, ...props }, ref) => (
|
|
109
|
+
<span ref={ref} className={cn("table-v01-tag", className)} {...props} />
|
|
110
|
+
),
|
|
111
|
+
);
|
|
112
|
+
TableV01Tag.displayName = "TableV01Tag";
|
|
113
|
+
|
|
114
|
+
interface TableV01CellProps
|
|
115
|
+
extends React.ThHTMLAttributes<HTMLTableCellElement> {
|
|
116
|
+
/**
|
|
117
|
+
* Text alignment within the cell. Figma's `Alignment` property is column
|
|
118
|
+
* position, not text alignment, so this is additive — tables need it for
|
|
119
|
+
* numeric columns.
|
|
120
|
+
*/
|
|
121
|
+
align?: TableV01Align;
|
|
122
|
+
/** The column label. Figma sets it uppercase in the overline ramp. */
|
|
123
|
+
label?: React.ReactNode;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Figma `Placement=Header`: cyan/50 fill, uppercase overline label in cyan/800. */
|
|
127
|
+
const TableV01HeaderCell = React.forwardRef<
|
|
128
|
+
HTMLTableCellElement,
|
|
129
|
+
TableV01CellProps
|
|
130
|
+
>(({ className, align = "left", label, children, ...props }, ref) => (
|
|
131
|
+
<th
|
|
132
|
+
ref={ref}
|
|
133
|
+
scope="col"
|
|
134
|
+
data-align={align}
|
|
135
|
+
className={cn("table-v01-header-cell", className)}
|
|
136
|
+
{...props}
|
|
137
|
+
>
|
|
138
|
+
<span className="table-v01-cell-inner overline-medium">
|
|
139
|
+
{label}
|
|
140
|
+
{children}
|
|
141
|
+
</span>
|
|
142
|
+
</th>
|
|
143
|
+
));
|
|
144
|
+
TableV01HeaderCell.displayName = "TableV01HeaderCell";
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Figma `Placement=Intermediates | Bottom`: white fill, Mazzard Medium 16/21.
|
|
148
|
+
*
|
|
149
|
+
* Figma's `Content type` axis is expressed as content props rather than an
|
|
150
|
+
* enum: pass any of `text`, `tag` and `action` and the cell renders them in
|
|
151
|
+
* Figma's order with the 10px gap. `children` still works for anything the
|
|
152
|
+
* props cannot express (a link, two lines, a custom control).
|
|
153
|
+
*/
|
|
154
|
+
interface TableV01BodyCellProps
|
|
155
|
+
extends React.TdHTMLAttributes<HTMLTableCellElement> {
|
|
156
|
+
/** Text alignment within the cell. Additive to Figma. */
|
|
157
|
+
align?: TableV01Align;
|
|
158
|
+
/** Figma `Content type=Text …`: Mazzard Medium 16/21 in gray/800. */
|
|
159
|
+
text?: React.ReactNode;
|
|
160
|
+
/** Figma `Content type=… + Tag`: wrapped in the code/strategy tag. */
|
|
161
|
+
tag?: React.ReactNode;
|
|
162
|
+
/** Figma `Content type=… + Action`: pass one or more `TableV01Action`. */
|
|
163
|
+
action?: React.ReactNode;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const TableV01Cell = React.forwardRef<
|
|
167
|
+
HTMLTableCellElement,
|
|
168
|
+
TableV01BodyCellProps
|
|
169
|
+
>(
|
|
170
|
+
(
|
|
171
|
+
{ className, align = "left", text, tag, action, children, ...props },
|
|
172
|
+
ref,
|
|
173
|
+
) => (
|
|
174
|
+
<td
|
|
175
|
+
ref={ref}
|
|
176
|
+
data-align={align}
|
|
177
|
+
className={cn("table-v01-cell", className)}
|
|
178
|
+
{...props}
|
|
179
|
+
>
|
|
180
|
+
<span className="table-v01-cell-inner">
|
|
181
|
+
{text}
|
|
182
|
+
{tag != null ? <TableV01Tag>{tag}</TableV01Tag> : null}
|
|
183
|
+
{action}
|
|
184
|
+
{children}
|
|
185
|
+
</span>
|
|
186
|
+
</td>
|
|
187
|
+
),
|
|
188
|
+
);
|
|
189
|
+
TableV01Cell.displayName = "TableV01Cell";
|
|
190
|
+
|
|
191
|
+
// ---------------------------------------------------------------------------
|
|
192
|
+
// Cell content — the pieces Figma's `Content type` composes
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Figma's `button_icon_*`: a 32px white circle with a gray/100 border holding a
|
|
197
|
+
* 14px icon. Pass the icon as children.
|
|
198
|
+
*/
|
|
199
|
+
const TableV01Action = React.forwardRef<
|
|
200
|
+
HTMLButtonElement,
|
|
201
|
+
React.ButtonHTMLAttributes<HTMLButtonElement>
|
|
202
|
+
>(({ className, type = "button", ...props }, ref) => (
|
|
203
|
+
<button
|
|
204
|
+
ref={ref}
|
|
205
|
+
type={type}
|
|
206
|
+
className={cn("table-v01-action", className)}
|
|
207
|
+
{...props}
|
|
208
|
+
/>
|
|
209
|
+
));
|
|
210
|
+
TableV01Action.displayName = "TableV01Action";
|
|
211
|
+
|
|
212
|
+
export {
|
|
213
|
+
TableV01,
|
|
214
|
+
TableV01Header,
|
|
215
|
+
TableV01Body,
|
|
216
|
+
TableV01Footer,
|
|
217
|
+
TableV01Row,
|
|
218
|
+
TableV01HeaderCell,
|
|
219
|
+
TableV01Cell,
|
|
220
|
+
TableV01Tag,
|
|
221
|
+
TableV01Action,
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
export type {
|
|
225
|
+
TableV01Props,
|
|
226
|
+
TableV01CellProps,
|
|
227
|
+
TableV01BodyCellProps,
|
|
228
|
+
TableV01Density,
|
|
229
|
+
TableV01Align,
|
|
230
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
export type TabsV01Item = {
|
|
6
|
+
value: string;
|
|
7
|
+
label: React.ReactNode;
|
|
8
|
+
disabled?: boolean;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export interface TabsV01Props
|
|
12
|
+
extends Omit<React.HTMLAttributes<HTMLDivElement>, "onChange"> {
|
|
13
|
+
items?: TabsV01Item[];
|
|
14
|
+
label?: React.ReactNode;
|
|
15
|
+
value?: string;
|
|
16
|
+
defaultValue?: string;
|
|
17
|
+
onValueChange?: (value: string) => void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const TabsV01 = React.forwardRef<HTMLDivElement, TabsV01Props>(
|
|
21
|
+
(
|
|
22
|
+
{
|
|
23
|
+
className,
|
|
24
|
+
items = [],
|
|
25
|
+
label,
|
|
26
|
+
value: controlledValue,
|
|
27
|
+
defaultValue,
|
|
28
|
+
onValueChange,
|
|
29
|
+
...props
|
|
30
|
+
},
|
|
31
|
+
ref,
|
|
32
|
+
) => {
|
|
33
|
+
const isControlled = controlledValue !== undefined;
|
|
34
|
+
const [uncontrolled, setUncontrolled] = React.useState(
|
|
35
|
+
() => defaultValue ?? items[0]?.value,
|
|
36
|
+
);
|
|
37
|
+
const active = isControlled ? controlledValue : uncontrolled;
|
|
38
|
+
|
|
39
|
+
const select = (next: string) => {
|
|
40
|
+
if (!isControlled) setUncontrolled(next);
|
|
41
|
+
onValueChange?.(next);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const refs = React.useRef<(HTMLButtonElement | null)[]>([]);
|
|
45
|
+
|
|
46
|
+
const onKeyDown = (event: React.KeyboardEvent, index: number) => {
|
|
47
|
+
const step =
|
|
48
|
+
event.key === "ArrowRight" ? 1 : event.key === "ArrowLeft" ? -1 : 0;
|
|
49
|
+
if (!step) return;
|
|
50
|
+
event.preventDefault();
|
|
51
|
+
const enabled = items
|
|
52
|
+
.map((item, i) => ({ item, i }))
|
|
53
|
+
.filter(({ item }) => !item.disabled);
|
|
54
|
+
const at = enabled.findIndex(({ i }) => i === index);
|
|
55
|
+
const next = enabled[(at + step + enabled.length) % enabled.length];
|
|
56
|
+
if (!next) return;
|
|
57
|
+
select(next.item.value);
|
|
58
|
+
refs.current[next.i]?.focus();
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
<div
|
|
63
|
+
ref={ref}
|
|
64
|
+
className={cn("tabs-v01 font-mazzardSoft", className)}
|
|
65
|
+
{...props}
|
|
66
|
+
>
|
|
67
|
+
{label != null && label !== "" ? (
|
|
68
|
+
<div className="tabs-v01-label body-large">{label}</div>
|
|
69
|
+
) : null}
|
|
70
|
+
<div className="tabs-v01-list" role="tablist">
|
|
71
|
+
{items.map((item, index) => (
|
|
72
|
+
<button
|
|
73
|
+
key={item.value}
|
|
74
|
+
ref={(node) => {
|
|
75
|
+
refs.current[index] = node;
|
|
76
|
+
}}
|
|
77
|
+
type="button"
|
|
78
|
+
role="tab"
|
|
79
|
+
aria-selected={item.value === active}
|
|
80
|
+
tabIndex={item.value === active ? 0 : -1}
|
|
81
|
+
disabled={item.disabled}
|
|
82
|
+
data-state={item.value === active ? "active" : "inactive"}
|
|
83
|
+
className="tabs-v01-tab"
|
|
84
|
+
onClick={() => select(item.value)}
|
|
85
|
+
onKeyDown={(event) => onKeyDown(event, index)}
|
|
86
|
+
>
|
|
87
|
+
<span className="tabs-v01-tab-label overline-large">
|
|
88
|
+
{item.label}
|
|
89
|
+
</span>
|
|
90
|
+
</button>
|
|
91
|
+
))}
|
|
92
|
+
</div>
|
|
93
|
+
</div>
|
|
94
|
+
);
|
|
95
|
+
},
|
|
96
|
+
);
|
|
97
|
+
TabsV01.displayName = "TabsV01";
|
|
98
|
+
|
|
99
|
+
export { TabsV01 };
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
import { UtilityChangeType } from "@/components/icons/UtilityChangeType";
|
|
6
|
+
|
|
7
|
+
const tagV02Variants = cva("tag-v02 font-mazzardSoft", {
|
|
8
|
+
variants: {
|
|
9
|
+
state: {
|
|
10
|
+
active: "tag-v02--active",
|
|
11
|
+
inactive: "tag-v02--inactive",
|
|
12
|
+
},
|
|
13
|
+
},
|
|
14
|
+
defaultVariants: {
|
|
15
|
+
state: "inactive",
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
export type TagV02State = NonNullable<
|
|
20
|
+
VariantProps<typeof tagV02Variants>["state"]
|
|
21
|
+
>;
|
|
22
|
+
export type TagV02ChangeType = "mild" | "medium" | "hot";
|
|
23
|
+
|
|
24
|
+
export interface TagV02Props
|
|
25
|
+
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, "type">,
|
|
26
|
+
VariantProps<typeof tagV02Variants> {
|
|
27
|
+
dot?: boolean;
|
|
28
|
+
change?: boolean;
|
|
29
|
+
changeType?: TagV02ChangeType;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const TagV02 = React.forwardRef<HTMLButtonElement, TagV02Props>(
|
|
33
|
+
(
|
|
34
|
+
{
|
|
35
|
+
className,
|
|
36
|
+
state = "inactive",
|
|
37
|
+
dot = false,
|
|
38
|
+
change = false,
|
|
39
|
+
changeType = "mild",
|
|
40
|
+
children,
|
|
41
|
+
...props
|
|
42
|
+
},
|
|
43
|
+
ref,
|
|
44
|
+
) => (
|
|
45
|
+
<button
|
|
46
|
+
ref={ref}
|
|
47
|
+
type="button"
|
|
48
|
+
aria-pressed={state === "active"}
|
|
49
|
+
className={cn(tagV02Variants({ state }), className)}
|
|
50
|
+
{...props}
|
|
51
|
+
>
|
|
52
|
+
<span className="tag-v02-label">{children}</span>
|
|
53
|
+
{dot ? <span className="tag-v02-dot" aria-hidden /> : null}
|
|
54
|
+
{change ? (
|
|
55
|
+
<span className="tag-v02-change-frame">
|
|
56
|
+
<UtilityChangeType
|
|
57
|
+
className={`tag-v02-change tag-v02-change--${changeType}`}
|
|
58
|
+
/>
|
|
59
|
+
</span>
|
|
60
|
+
) : null}
|
|
61
|
+
</button>
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
TagV02.displayName = "TagV02";
|
|
65
|
+
|
|
66
|
+
const tagStatusV02Variants = cva("tag-status-v02 font-mazzardSoft", {
|
|
67
|
+
variants: {
|
|
68
|
+
status: {
|
|
69
|
+
todo: "tag-status-v02--todo",
|
|
70
|
+
low: "tag-status-v02--low",
|
|
71
|
+
medium: "tag-status-v02--medium",
|
|
72
|
+
high: "tag-status-v02--high",
|
|
73
|
+
done: "tag-status-v02--done",
|
|
74
|
+
new: "tag-status-v02--new",
|
|
75
|
+
},
|
|
76
|
+
tone: {
|
|
77
|
+
light: "tag-status-v02--light",
|
|
78
|
+
dark: "tag-status-v02--dark",
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
defaultVariants: {
|
|
82
|
+
status: "todo",
|
|
83
|
+
tone: "light",
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
export type TagStatusV02Status = NonNullable<
|
|
88
|
+
VariantProps<typeof tagStatusV02Variants>["status"]
|
|
89
|
+
>;
|
|
90
|
+
export type TagStatusV02Tone = NonNullable<
|
|
91
|
+
VariantProps<typeof tagStatusV02Variants>["tone"]
|
|
92
|
+
>;
|
|
93
|
+
|
|
94
|
+
export interface TagStatusV02Props
|
|
95
|
+
extends React.HTMLAttributes<HTMLSpanElement>,
|
|
96
|
+
VariantProps<typeof tagStatusV02Variants> {}
|
|
97
|
+
|
|
98
|
+
const TagStatusV02 = React.forwardRef<HTMLSpanElement, TagStatusV02Props>(
|
|
99
|
+
({ className, status = "todo", tone = "light", children, ...props }, ref) => (
|
|
100
|
+
<span
|
|
101
|
+
ref={ref}
|
|
102
|
+
className={cn(tagStatusV02Variants({ status, tone }), className)}
|
|
103
|
+
{...props}
|
|
104
|
+
>
|
|
105
|
+
<span className="tag-status-v02-label overline-medium">
|
|
106
|
+
{children ?? status}
|
|
107
|
+
</span>
|
|
108
|
+
</span>
|
|
109
|
+
),
|
|
110
|
+
);
|
|
111
|
+
TagStatusV02.displayName = "TagStatusV02";
|
|
112
|
+
|
|
113
|
+
export { TagV02, tagV02Variants, TagStatusV02, tagStatusV02Variants };
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
export interface TextAreaV01Props
|
|
6
|
+
extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
7
|
+
focused?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const TextAreaV01 = React.forwardRef<HTMLTextAreaElement, TextAreaV01Props>(
|
|
11
|
+
(
|
|
12
|
+
{
|
|
13
|
+
className,
|
|
14
|
+
focused,
|
|
15
|
+
disabled,
|
|
16
|
+
placeholder = "Tell us what you think",
|
|
17
|
+
...props
|
|
18
|
+
},
|
|
19
|
+
ref,
|
|
20
|
+
) => (
|
|
21
|
+
<textarea
|
|
22
|
+
ref={ref}
|
|
23
|
+
className={cn("text-area-v01 font-mazzardSoft", className)}
|
|
24
|
+
data-focus={focused ? "true" : undefined}
|
|
25
|
+
disabled={disabled}
|
|
26
|
+
placeholder={placeholder}
|
|
27
|
+
{...props}
|
|
28
|
+
/>
|
|
29
|
+
),
|
|
30
|
+
);
|
|
31
|
+
TextAreaV01.displayName = "TextAreaV01";
|
|
32
|
+
|
|
33
|
+
export { TextAreaV01 };
|