@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,179 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import {
|
|
3
|
+
SystemBar,
|
|
4
|
+
SystemBarSpacer,
|
|
5
|
+
SystemBarIcon,
|
|
6
|
+
SystemBarAction,
|
|
7
|
+
} from "../../components/ui/system-bar";
|
|
8
|
+
|
|
9
|
+
// ─── Inline SVG icons ─────────────────────────────────────────────────────────
|
|
10
|
+
|
|
11
|
+
function IconWifi({ strength = 4 }: { strength?: 1 | 2 | 3 | 4 }) {
|
|
12
|
+
// Simple wifi icon — bars filled based on strength
|
|
13
|
+
const bars = [1, 2, 3, 4];
|
|
14
|
+
return (
|
|
15
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
16
|
+
{strength >= 1 && <path d="M1 9l2 2c5-5 13-5 18 0l2-2C16.93 2.93 7.08 2.93 1 9z" opacity={strength === 1 ? 1 : 0.4} />}
|
|
17
|
+
{strength >= 2 && <path d="M5 13l2 2a7 7 0 0 1 10 0l2-2C15.14 9.14 8.87 9.14 5 13z" opacity={strength === 2 ? 1 : 0.6} />}
|
|
18
|
+
{strength >= 3 && <path d="M9 17l3 3 3-3a4.237 4.237 0 0 0-6 0z" />}
|
|
19
|
+
<circle cx="12" cy="20" r="1" />
|
|
20
|
+
</svg>
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function IconSignal() {
|
|
25
|
+
return (
|
|
26
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
27
|
+
<rect x="2" y="16" width="3" height="6" rx="0.5" />
|
|
28
|
+
<rect x="7" y="12" width="3" height="10" rx="0.5" />
|
|
29
|
+
<rect x="12" y="8" width="3" height="14" rx="0.5" />
|
|
30
|
+
<rect x="17" y="4" width="3" height="18" rx="0.5" opacity="0.3" />
|
|
31
|
+
</svg>
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function IconBattery() {
|
|
36
|
+
return (
|
|
37
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
38
|
+
<rect x="2" y="7" width="18" height="11" rx="2" stroke="currentColor" strokeWidth="1.5" fill="none" />
|
|
39
|
+
<rect x="3.5" y="8.5" width="12" height="8" rx="1" />
|
|
40
|
+
<path d="M20 10h2v5h-2z" />
|
|
41
|
+
</svg>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function IconMessage() {
|
|
46
|
+
return (
|
|
47
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
48
|
+
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
|
|
49
|
+
</svg>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function IconMinus() {
|
|
54
|
+
return (
|
|
55
|
+
<svg width="12" height="12" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" fill="none" aria-hidden="true">
|
|
56
|
+
<line x1="5" y1="12" x2="19" y2="12" />
|
|
57
|
+
</svg>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function IconMaximize() {
|
|
62
|
+
return (
|
|
63
|
+
<svg width="12" height="12" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" fill="none" aria-hidden="true">
|
|
64
|
+
<rect x="3" y="3" width="18" height="18" rx="1" />
|
|
65
|
+
</svg>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function IconClose() {
|
|
70
|
+
return (
|
|
71
|
+
<svg width="12" height="12" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2" fill="none" aria-hidden="true">
|
|
72
|
+
<line x1="18" y1="6" x2="6" y2="18" />
|
|
73
|
+
<line x1="6" y1="6" x2="18" y2="18" />
|
|
74
|
+
</svg>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
const meta = {
|
|
81
|
+
title: "Draft/SystemBar",
|
|
82
|
+
component: SystemBar,
|
|
83
|
+
tags: ["autodocs"],
|
|
84
|
+
parameters: {
|
|
85
|
+
layout: "fullscreen",
|
|
86
|
+
},
|
|
87
|
+
argTypes: {
|
|
88
|
+
color: { control: "color" },
|
|
89
|
+
window: { control: "boolean" },
|
|
90
|
+
},
|
|
91
|
+
} satisfies Meta<typeof SystemBar>;
|
|
92
|
+
|
|
93
|
+
export default meta;
|
|
94
|
+
|
|
95
|
+
type Story = StoryObj<typeof meta>;
|
|
96
|
+
|
|
97
|
+
// ─── Default ─────────────────────────────────────────────────────────────────
|
|
98
|
+
// Android-style status bar with wifi, signal, battery and a clock.
|
|
99
|
+
|
|
100
|
+
export const Default: Story = {
|
|
101
|
+
render: () => (
|
|
102
|
+
<SystemBar>
|
|
103
|
+
<SystemBarIcon>
|
|
104
|
+
<IconWifi strength={4} />
|
|
105
|
+
</SystemBarIcon>
|
|
106
|
+
<SystemBarIcon>
|
|
107
|
+
<IconSignal />
|
|
108
|
+
</SystemBarIcon>
|
|
109
|
+
<SystemBarIcon>
|
|
110
|
+
<IconBattery />
|
|
111
|
+
</SystemBarIcon>
|
|
112
|
+
<SystemBarSpacer />
|
|
113
|
+
<span className="system-bar-time">3:13 PM</span>
|
|
114
|
+
</SystemBar>
|
|
115
|
+
),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// ─── Colors ───────────────────────────────────────────────────────────────────
|
|
119
|
+
// Three bars with different background colors — text/icons auto-switch to white.
|
|
120
|
+
|
|
121
|
+
export const Colors: Story = {
|
|
122
|
+
render: () => (
|
|
123
|
+
<div style={{ display: "flex", flexDirection: "column", gap: "0.5rem" }}>
|
|
124
|
+
{/* Primary blue */}
|
|
125
|
+
<SystemBar color="#0284c7">
|
|
126
|
+
<SystemBarIcon><IconWifi strength={4} /></SystemBarIcon>
|
|
127
|
+
<SystemBarIcon><IconSignal /></SystemBarIcon>
|
|
128
|
+
<SystemBarIcon><IconBattery /></SystemBarIcon>
|
|
129
|
+
<SystemBarSpacer />
|
|
130
|
+
<span className="system-bar-time">08:30</span>
|
|
131
|
+
</SystemBar>
|
|
132
|
+
|
|
133
|
+
{/* Red */}
|
|
134
|
+
<SystemBar color="#f87171">
|
|
135
|
+
<SystemBarIcon><IconWifi strength={2} /></SystemBarIcon>
|
|
136
|
+
<SystemBarIcon><IconSignal /></SystemBarIcon>
|
|
137
|
+
<SystemBarIcon><IconBattery /></SystemBarIcon>
|
|
138
|
+
<SystemBarSpacer />
|
|
139
|
+
<span className="system-bar-time">18:30</span>
|
|
140
|
+
</SystemBar>
|
|
141
|
+
|
|
142
|
+
{/* Indigo */}
|
|
143
|
+
<SystemBar color="#3730a3">
|
|
144
|
+
<SystemBarIcon><IconWifi strength={3} /></SystemBarIcon>
|
|
145
|
+
<SystemBarIcon><IconSignal /></SystemBarIcon>
|
|
146
|
+
<SystemBarIcon><IconBattery /></SystemBarIcon>
|
|
147
|
+
<SystemBarSpacer />
|
|
148
|
+
<span className="system-bar-time">13:24</span>
|
|
149
|
+
</SystemBar>
|
|
150
|
+
</div>
|
|
151
|
+
),
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// ─── Window ───────────────────────────────────────────────────────────────────
|
|
155
|
+
// Desktop-style title bar: message on the left, window controls on the right.
|
|
156
|
+
|
|
157
|
+
export const Window: Story = {
|
|
158
|
+
render: () => (
|
|
159
|
+
<SystemBar window>
|
|
160
|
+
<SystemBarIcon>
|
|
161
|
+
<IconMessage />
|
|
162
|
+
</SystemBarIcon>
|
|
163
|
+
<span className="system-bar-label">10 unread messages</span>
|
|
164
|
+
<SystemBarSpacer />
|
|
165
|
+
<SystemBarAction aria-label="Minimise window">
|
|
166
|
+
<IconMinus />
|
|
167
|
+
</SystemBarAction>
|
|
168
|
+
<SystemBarAction aria-label="Maximise window">
|
|
169
|
+
<IconMaximize />
|
|
170
|
+
</SystemBarAction>
|
|
171
|
+
<SystemBarAction
|
|
172
|
+
aria-label="Close window"
|
|
173
|
+
className="system-bar-action--close"
|
|
174
|
+
>
|
|
175
|
+
<IconClose />
|
|
176
|
+
</SystemBarAction>
|
|
177
|
+
</SystemBar>
|
|
178
|
+
),
|
|
179
|
+
};
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { Pencil } from "lucide-react";
|
|
4
|
+
import {
|
|
5
|
+
TableV01,
|
|
6
|
+
TableV01Action,
|
|
7
|
+
TableV01Body,
|
|
8
|
+
TableV01Cell,
|
|
9
|
+
TableV01Header,
|
|
10
|
+
TableV01HeaderCell,
|
|
11
|
+
TableV01Row,
|
|
12
|
+
type TableV01Align,
|
|
13
|
+
type TableV01Density,
|
|
14
|
+
} from "../../components/ui/table-v01";
|
|
15
|
+
|
|
16
|
+
const meta = {
|
|
17
|
+
title: "Components/Table",
|
|
18
|
+
component: TableV01,
|
|
19
|
+
tags: ["autodocs"],
|
|
20
|
+
parameters: {
|
|
21
|
+
layout: "padded",
|
|
22
|
+
docs: {
|
|
23
|
+
description: {
|
|
24
|
+
component: [
|
|
25
|
+
"Data table with a header, body and optional footer.",
|
|
26
|
+
"",
|
|
27
|
+
"```tsx",
|
|
28
|
+
`import {`,
|
|
29
|
+
` TableV01, TableV01Header, TableV01Body, TableV01Footer, TableV01Row,`,
|
|
30
|
+
` TableV01HeaderCell, TableV01Cell, TableV01Tag, TableV01Action,`,
|
|
31
|
+
`} from "@syscore/ui-library"`,
|
|
32
|
+
"",
|
|
33
|
+
'<TableV01 density="compact">',
|
|
34
|
+
" <TableV01Header>",
|
|
35
|
+
" <TableV01Row>",
|
|
36
|
+
' <TableV01HeaderCell label="Concept" />',
|
|
37
|
+
' <TableV01HeaderCell label="Actions" align="right" />',
|
|
38
|
+
" </TableV01Row>",
|
|
39
|
+
" </TableV01Header>",
|
|
40
|
+
" <TableV01Body>",
|
|
41
|
+
" <TableV01Row>",
|
|
42
|
+
' <TableV01Cell text="Thermal comfort" tag="T4.1" />',
|
|
43
|
+
' <TableV01Cell align="right" action={<TableV01Action>…</TableV01Action>} />',
|
|
44
|
+
" </TableV01Row>",
|
|
45
|
+
" </TableV01Body>",
|
|
46
|
+
"</TableV01>",
|
|
47
|
+
"```",
|
|
48
|
+
"",
|
|
49
|
+
"### Props",
|
|
50
|
+
"",
|
|
51
|
+
"| Component | Prop | Values | Default |",
|
|
52
|
+
"|-----------|------|--------|---------|",
|
|
53
|
+
"| `TableV01` | `density` | `compact` \\| `balanced` \\| `spacious` | `compact` |",
|
|
54
|
+
"| `TableV01HeaderCell` | `label` | column label | — |",
|
|
55
|
+
"| | `align` | `left` \\| `center` \\| `right` | `left` |",
|
|
56
|
+
"| `TableV01Cell` | `text` | cell text | — |",
|
|
57
|
+
"| | `tag` | wrapped in the code tag automatically | — |",
|
|
58
|
+
"| | `action` | one or more `TableV01Action` | — |",
|
|
59
|
+
"| | `align` | `left` \\| `center` \\| `right` | `left` |",
|
|
60
|
+
"",
|
|
61
|
+
"Mix `text`, `tag` and `action` freely — they render in that order. `children`",
|
|
62
|
+
"still works for anything the props cannot express, such as a link or two lines.",
|
|
63
|
+
"",
|
|
64
|
+
"### Density",
|
|
65
|
+
"",
|
|
66
|
+
"`density` sets the cell's vertical padding; row heights follow from the content.",
|
|
67
|
+
"",
|
|
68
|
+
"| | Padding | Header | Text row | Tag / action row |",
|
|
69
|
+
"|---|---------|--------|----------|------------------|",
|
|
70
|
+
"| `compact` | 16 | 40 | 43 | 64 |",
|
|
71
|
+
"| `balanced` | 20 | 48 | 51 | 72 |",
|
|
72
|
+
"| `spacious` | 24 | 56 | 59 | 80 |",
|
|
73
|
+
"",
|
|
74
|
+
"Column dividers, outer borders and the rounded corners are handled",
|
|
75
|
+
"automatically from cell and row position — nothing to pass.",
|
|
76
|
+
].join("\n"),
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
} satisfies Meta<typeof TableV01>;
|
|
81
|
+
|
|
82
|
+
export default meta;
|
|
83
|
+
|
|
84
|
+
/** Density and alignment as controls; the three columns show one layout each. */
|
|
85
|
+
type StoryArgs = {
|
|
86
|
+
density: TableV01Density;
|
|
87
|
+
align: TableV01Align;
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
type Story = StoryObj<StoryArgs>;
|
|
91
|
+
|
|
92
|
+
const CONTROLS = {
|
|
93
|
+
density: {
|
|
94
|
+
control: "inline-radio" as const,
|
|
95
|
+
options: ["compact", "balanced", "spacious"],
|
|
96
|
+
description: "The cell's vertical padding.",
|
|
97
|
+
},
|
|
98
|
+
align: {
|
|
99
|
+
control: "inline-radio" as const,
|
|
100
|
+
options: ["left", "center", "right"],
|
|
101
|
+
description: "Text alignment, applied to every cell.",
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const ROWS = [
|
|
106
|
+
{ text: "Thermal comfort", tag: "T4.1" },
|
|
107
|
+
{ text: "Water quality", tag: "W2.2" },
|
|
108
|
+
{ text: "Nourishment", tag: "N1.2" },
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
/** A cell holds a single icon action; pass a fragment for more. */
|
|
112
|
+
const action = (label: string) => (
|
|
113
|
+
<TableV01Action aria-label={`Edit ${label}`}>
|
|
114
|
+
<Pencil strokeWidth={1.5} />
|
|
115
|
+
</TableV01Action>
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* One table per content type, three columns wide, with density and alignment
|
|
120
|
+
* as controls.
|
|
121
|
+
*/
|
|
122
|
+
function contentStory(
|
|
123
|
+
headers: [string, string, string],
|
|
124
|
+
cell: (row: (typeof ROWS)[number], align: TableV01Align) => React.ReactNode,
|
|
125
|
+
story: string,
|
|
126
|
+
): Story {
|
|
127
|
+
return {
|
|
128
|
+
argTypes: CONTROLS,
|
|
129
|
+
args: { density: "compact", align: "left" },
|
|
130
|
+
parameters: { docs: { description: { story } } },
|
|
131
|
+
render: ({ density, align }) => (
|
|
132
|
+
<TableV01 density={density} containerClassName="sb-table-triple">
|
|
133
|
+
<TableV01Header>
|
|
134
|
+
<TableV01Row>
|
|
135
|
+
{headers.map((label) => (
|
|
136
|
+
<TableV01HeaderCell key={label} label={label} align={align} />
|
|
137
|
+
))}
|
|
138
|
+
</TableV01Row>
|
|
139
|
+
</TableV01Header>
|
|
140
|
+
<TableV01Body>
|
|
141
|
+
{ROWS.map((row) => (
|
|
142
|
+
<TableV01Row key={row.tag}>
|
|
143
|
+
{headers.map((label) => (
|
|
144
|
+
<React.Fragment key={label}>{cell(row, align)}</React.Fragment>
|
|
145
|
+
))}
|
|
146
|
+
</TableV01Row>
|
|
147
|
+
))}
|
|
148
|
+
</TableV01Body>
|
|
149
|
+
</TableV01>
|
|
150
|
+
),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export const WithText: Story = {
|
|
155
|
+
...contentStory(
|
|
156
|
+
["Concept", "Strategy", "Status"],
|
|
157
|
+
(row, align) => <TableV01Cell text={row.text} align={align} />,
|
|
158
|
+
"Text cells — pass the `text` prop.",
|
|
159
|
+
),
|
|
160
|
+
name: "With text",
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
export const WithTag: Story = {
|
|
164
|
+
...contentStory(
|
|
165
|
+
["Code", "Strategy", "Reference"],
|
|
166
|
+
(row, align) => <TableV01Cell tag={row.tag} align={align} />,
|
|
167
|
+
"Tag cells — pass `tag` and it is wrapped in the code tag automatically.",
|
|
168
|
+
),
|
|
169
|
+
name: "With tag",
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
export const WithAction: Story = {
|
|
173
|
+
...contentStory(
|
|
174
|
+
["Actions", "Review", "Archive"],
|
|
175
|
+
(row, align) => <TableV01Cell action={action(row.text)} align={align} />,
|
|
176
|
+
"Action cells — pass one or more `TableV01Action` to `action`.",
|
|
177
|
+
),
|
|
178
|
+
name: "With action",
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
export const TextAndTag: Story = {
|
|
182
|
+
...contentStory(
|
|
183
|
+
["Concept", "Strategy", "Feature"],
|
|
184
|
+
(row, align) => (
|
|
185
|
+
<TableV01Cell text={row.text} tag={row.tag} align={align} />
|
|
186
|
+
),
|
|
187
|
+
"Text and tag on the same cell.",
|
|
188
|
+
),
|
|
189
|
+
name: "Text + tag",
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
export const TextAndAction: Story = {
|
|
193
|
+
...contentStory(
|
|
194
|
+
["Concept", "Strategy", "Actions"],
|
|
195
|
+
(row, align) => (
|
|
196
|
+
<TableV01Cell text={row.text} action={action(row.text)} align={align} />
|
|
197
|
+
),
|
|
198
|
+
"Text and an action on the same cell.",
|
|
199
|
+
),
|
|
200
|
+
name: "Text + action",
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export const AllVariants: Story = {
|
|
204
|
+
name: "All variants",
|
|
205
|
+
parameters: {
|
|
206
|
+
controls: { disable: true },
|
|
207
|
+
docs: {
|
|
208
|
+
description: {
|
|
209
|
+
story:
|
|
210
|
+
"Every content combination across all three densities, one row per alignment.",
|
|
211
|
+
},
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
render: () => (
|
|
215
|
+
<div style={{ display: "grid", gap: "var(--space-3xl)" }}>
|
|
216
|
+
{(["compact", "balanced", "spacious"] as const).map((density) => (
|
|
217
|
+
<div key={density} style={{ display: "grid", gap: "var(--space-xs)" }}>
|
|
218
|
+
<span className="sb-table-density-label font-mazzardSoft">
|
|
219
|
+
{density}
|
|
220
|
+
</span>
|
|
221
|
+
<TableV01 density={density}>
|
|
222
|
+
<TableV01Header>
|
|
223
|
+
<TableV01Row>
|
|
224
|
+
<TableV01HeaderCell label="Align" />
|
|
225
|
+
<TableV01HeaderCell label="Text only" />
|
|
226
|
+
<TableV01HeaderCell label="Tag only" />
|
|
227
|
+
<TableV01HeaderCell label="Text + tag" />
|
|
228
|
+
<TableV01HeaderCell label="Text + action" />
|
|
229
|
+
<TableV01HeaderCell label="Action only" />
|
|
230
|
+
</TableV01Row>
|
|
231
|
+
</TableV01Header>
|
|
232
|
+
<TableV01Body>
|
|
233
|
+
{(["left", "center", "right"] as const).map((align, index) => {
|
|
234
|
+
const row = ROWS[index];
|
|
235
|
+
return (
|
|
236
|
+
<TableV01Row key={align}>
|
|
237
|
+
<TableV01Cell text={align} />
|
|
238
|
+
<TableV01Cell text={row.text} align={align} />
|
|
239
|
+
<TableV01Cell tag={row.tag} align={align} />
|
|
240
|
+
<TableV01Cell text={row.text} tag={row.tag} align={align} />
|
|
241
|
+
<TableV01Cell
|
|
242
|
+
text={row.text}
|
|
243
|
+
action={action(row.text)}
|
|
244
|
+
align={align}
|
|
245
|
+
/>
|
|
246
|
+
<TableV01Cell action={action(row.text)} align={align} />
|
|
247
|
+
</TableV01Row>
|
|
248
|
+
);
|
|
249
|
+
})}
|
|
250
|
+
</TableV01Body>
|
|
251
|
+
</TableV01>
|
|
252
|
+
</div>
|
|
253
|
+
))}
|
|
254
|
+
</div>
|
|
255
|
+
),
|
|
256
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
import { TabsV01 } from "../../components/ui/tabs-v01";
|
|
4
|
+
|
|
5
|
+
const counts = [2, 3, 4, 5] as const;
|
|
6
|
+
|
|
7
|
+
const sectionHeading: React.CSSProperties = {
|
|
8
|
+
margin: "0 0 20px",
|
|
9
|
+
fontSize: 14,
|
|
10
|
+
fontWeight: 600,
|
|
11
|
+
textTransform: "uppercase",
|
|
12
|
+
letterSpacing: 0.5,
|
|
13
|
+
color: "#52545d",
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
function StatefulTabs({
|
|
17
|
+
count,
|
|
18
|
+
label,
|
|
19
|
+
}: {
|
|
20
|
+
count: number;
|
|
21
|
+
label?: React.ReactNode;
|
|
22
|
+
}) {
|
|
23
|
+
const [value, setValue] = React.useState("tab-0");
|
|
24
|
+
const items = Array.from({ length: count }, (_, i) => ({
|
|
25
|
+
value: `tab-${i}`,
|
|
26
|
+
label: value === `tab-${i}` ? "Active" : "Inactive",
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<TabsV01
|
|
31
|
+
label={label}
|
|
32
|
+
items={items}
|
|
33
|
+
value={value}
|
|
34
|
+
onValueChange={setValue}
|
|
35
|
+
/>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type StoryArgs = React.ComponentProps<typeof TabsV01> & { count?: number };
|
|
40
|
+
|
|
41
|
+
const meta = {
|
|
42
|
+
title: "Components/Tabs",
|
|
43
|
+
component: TabsV01,
|
|
44
|
+
tags: ["autodocs"],
|
|
45
|
+
parameters: {
|
|
46
|
+
layout: "centered",
|
|
47
|
+
docs: {
|
|
48
|
+
description: {
|
|
49
|
+
component: [
|
|
50
|
+
"Tab bar. Existing `Components/TabsLegacy` (`Tabs`) is unchanged.",
|
|
51
|
+
"",
|
|
52
|
+
"Use **Controls** for the tab count and the heading. Each label reports its",
|
|
53
|
+
"own state, so the selected tab reads Active and the rest read Inactive.",
|
|
54
|
+
"All Variants shows the four counts side by side.",
|
|
55
|
+
"",
|
|
56
|
+
"---",
|
|
57
|
+
"",
|
|
58
|
+
"## Import",
|
|
59
|
+
"",
|
|
60
|
+
"```tsx",
|
|
61
|
+
`import { TabsV01 } from "@syscore/ui-library"`,
|
|
62
|
+
"```",
|
|
63
|
+
"",
|
|
64
|
+
"## How to use",
|
|
65
|
+
"",
|
|
66
|
+
"This is the bar only — wire your own content to `onValueChange`. Leave it",
|
|
67
|
+
"uncontrolled to select the first item, or drive it with `value`.",
|
|
68
|
+
"",
|
|
69
|
+
"```tsx",
|
|
70
|
+
'const [tab, setTab] = useState("overview")',
|
|
71
|
+
"",
|
|
72
|
+
"<TabsV01",
|
|
73
|
+
` label="Section"`,
|
|
74
|
+
" items={[",
|
|
75
|
+
` { value: "overview", label: "Overview" },`,
|
|
76
|
+
` { value: "details", label: "Details" },`,
|
|
77
|
+
" ]}",
|
|
78
|
+
" value={tab}",
|
|
79
|
+
" onValueChange={setTab}",
|
|
80
|
+
"/>",
|
|
81
|
+
"```",
|
|
82
|
+
"",
|
|
83
|
+
'Tabs expose `role="tab"` inside a `role="tablist"`, with arrow-key movement',
|
|
84
|
+
"and a roving tab index. Both states share one text colour, so the underline",
|
|
85
|
+
"is the only visual cue for the selected tab.",
|
|
86
|
+
"",
|
|
87
|
+
"## Props",
|
|
88
|
+
"",
|
|
89
|
+
"| Prop | Values | Default | Effect |",
|
|
90
|
+
"|------|--------|---------|--------|",
|
|
91
|
+
"| `items` | `{ value, label, disabled? }[]` | `[]` | The tabs |",
|
|
92
|
+
"| `label` | `ReactNode` | — | Heading above the bar (18px/25.2) |",
|
|
93
|
+
"| `value` | `string` | — | Controlled selection |",
|
|
94
|
+
"| `defaultValue` | `string` | first item | Uncontrolled selection |",
|
|
95
|
+
"| `onValueChange` | `(value: string) => void` | — | Fires on select |",
|
|
96
|
+
].join("\n"),
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
argTypes: {
|
|
101
|
+
count: {
|
|
102
|
+
control: "inline-radio",
|
|
103
|
+
options: [...counts],
|
|
104
|
+
description: "How many tabs to render.",
|
|
105
|
+
table: { category: "Story only", defaultValue: { summary: "3" } },
|
|
106
|
+
},
|
|
107
|
+
label: { control: "text" },
|
|
108
|
+
items: { table: { disable: true } },
|
|
109
|
+
value: { table: { disable: true } },
|
|
110
|
+
},
|
|
111
|
+
args: {
|
|
112
|
+
label: "Label",
|
|
113
|
+
count: 3,
|
|
114
|
+
},
|
|
115
|
+
render: (args) => <StatefulTabs count={args.count ?? 3} label={args.label} />,
|
|
116
|
+
} satisfies Meta<StoryArgs>;
|
|
117
|
+
|
|
118
|
+
export default meta;
|
|
119
|
+
type Story = StoryObj<typeof meta>;
|
|
120
|
+
|
|
121
|
+
export const Default: Story = {};
|
|
122
|
+
|
|
123
|
+
export const AllVariants: Story = {
|
|
124
|
+
name: "All Variants",
|
|
125
|
+
parameters: { layout: "padded" },
|
|
126
|
+
argTypes: { count: { table: { disable: true } } },
|
|
127
|
+
render: (args) => (
|
|
128
|
+
<div
|
|
129
|
+
style={{
|
|
130
|
+
display: "flex",
|
|
131
|
+
flexDirection: "column",
|
|
132
|
+
gap: 72,
|
|
133
|
+
padding: "32px 24px 48px",
|
|
134
|
+
}}
|
|
135
|
+
>
|
|
136
|
+
{counts.map((count) => (
|
|
137
|
+
<section key={count}>
|
|
138
|
+
<h3 style={sectionHeading}>{count} tabs</h3>
|
|
139
|
+
<StatefulTabs count={count} label={args.label} />
|
|
140
|
+
</section>
|
|
141
|
+
))}
|
|
142
|
+
</div>
|
|
143
|
+
),
|
|
144
|
+
};
|