@zscloud/design 0.2.0 → 0.3.0
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/dist/components/primitive/card.d.ts +4 -1
- package/dist/guidelines/Guidelines.md +28 -0
- package/dist/guidelines/components/button.md +73 -0
- package/dist/guidelines/components/date-picker.md +49 -0
- package/dist/guidelines/components/dialog.md +93 -0
- package/dist/guidelines/components/drawer.md +48 -0
- package/dist/guidelines/components/form.md +100 -0
- package/dist/guidelines/components/select.md +81 -0
- package/dist/guidelines/components/tabs.md +73 -0
- package/dist/guidelines/components/toast.md +60 -0
- package/dist/guidelines/components/tooltip.md +40 -0
- package/dist/guidelines/design-tokens/colors.md +74 -0
- package/dist/guidelines/design-tokens/shadows.md +28 -0
- package/dist/guidelines/design-tokens/spacing.md +46 -0
- package/dist/guidelines/design-tokens/typography.md +62 -0
- package/dist/guidelines/overview-components.md +195 -0
- package/dist/guidelines/overview-icons.md +62 -0
- package/dist/zscloud-design.es.js +30 -22
- package/package.json +3 -2
|
@@ -6,12 +6,15 @@ declare const cardVariants: (props?: ({
|
|
|
6
6
|
declare const cardContentVariants: (props?: ({
|
|
7
7
|
variant?: "default" | "create" | "detail" | "plain" | null | undefined;
|
|
8
8
|
} & import('class-variance-authority/types').ClassProp) | undefined) => string;
|
|
9
|
+
type CardVariant = "default" | "create" | "detail" | "plain" | null | undefined;
|
|
9
10
|
export declare function useCardContext(): {
|
|
10
11
|
isCollapsed: boolean;
|
|
11
12
|
toggleCollapse: () => void;
|
|
13
|
+
variant: CardVariant;
|
|
12
14
|
};
|
|
13
|
-
export declare function CardProvider({ children }: {
|
|
15
|
+
export declare function CardProvider({ children, variant, }: {
|
|
14
16
|
children: React.ReactNode;
|
|
17
|
+
variant?: CardVariant;
|
|
15
18
|
}): import("react/jsx-runtime").JSX.Element;
|
|
16
19
|
export interface CardProps extends React.HTMLAttributes<HTMLDivElement>, VariantProps<typeof cardVariants> {
|
|
17
20
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# @zscloud/design - Design System Guidelines
|
|
2
|
+
|
|
3
|
+
This is a React component library built on Radix UI primitives and Tailwind CSS v4. It provides 70+ production-ready components for building cloud management interfaces.
|
|
4
|
+
|
|
5
|
+
## How to use these guidelines
|
|
6
|
+
|
|
7
|
+
1. Always read all files whose name starts with `overview-` to understand available components and icons.
|
|
8
|
+
2. When generating code for a specific component, read the corresponding file in `components/` for detailed API documentation.
|
|
9
|
+
3. When applying colors, typography, spacing, or shadows, read the corresponding file in `design-tokens/`.
|
|
10
|
+
|
|
11
|
+
## Quick start
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { Button, Dialog, Select, Input } from "@zscloud/design"
|
|
15
|
+
import "@zscloud/design/dist/style.css"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Key rules
|
|
19
|
+
|
|
20
|
+
- Always import components from `@zscloud/design`. Do not use raw HTML elements when a component exists.
|
|
21
|
+
- Always import the CSS file `@zscloud/design/dist/style.css` once at the application root.
|
|
22
|
+
- Use Tailwind CSS v4 utility classes for layout and custom styling. Do not write inline styles.
|
|
23
|
+
- Use semantic color tokens (e.g., `text-danger-500`, `bg-theme-500`) instead of raw hex values.
|
|
24
|
+
- The default control height is `h-8` (32px). Do not override unless specifically required.
|
|
25
|
+
- The default border radius is `rounded-xs` (2px).
|
|
26
|
+
- Spacing follows a 4px grid. Use multiples of 4px (e.g., `gap-2` = 8px, `p-4` = 16px).
|
|
27
|
+
- Dark mode is supported via the `.dark` class on `<html>`. All semantic tokens automatically adapt.
|
|
28
|
+
- The component library includes bilingual i18n (zh-CN / en-US). Wrap your app with `LocaleContainer` to enable translations.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Button
|
|
2
|
+
|
|
3
|
+
Use for all clickable actions. Do not use raw `<button>` elements.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Button } from "@zscloud/design"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Variants
|
|
12
|
+
|
|
13
|
+
| Variant | Use for |
|
|
14
|
+
|---------|---------|
|
|
15
|
+
| `primary` | Main action (submit, confirm). One per section. |
|
|
16
|
+
| `subtle` | Secondary actions (cancel, back) |
|
|
17
|
+
| `outline` | Alternative secondary, bordered |
|
|
18
|
+
| `danger` | Destructive actions (delete, remove) |
|
|
19
|
+
| `ghost` | Minimal emphasis, toolbar actions |
|
|
20
|
+
| `link` | Navigation-style action |
|
|
21
|
+
|
|
22
|
+
## Sizes
|
|
23
|
+
|
|
24
|
+
| Size | Height | Use for |
|
|
25
|
+
|------|--------|---------|
|
|
26
|
+
| `default` | 32px | Standard controls |
|
|
27
|
+
| `sm` | 28px | Compact areas, tables |
|
|
28
|
+
| `lg` | 40px | Prominent actions |
|
|
29
|
+
| `icon` | 32x32px | Icon-only buttons |
|
|
30
|
+
|
|
31
|
+
## Props
|
|
32
|
+
|
|
33
|
+
| Prop | Type | Default | Description |
|
|
34
|
+
|------|------|---------|-------------|
|
|
35
|
+
| `variant` | `"primary" \| "subtle" \| "outline" \| "secondary" \| "danger" \| "ghost" \| "ghost2" \| "link"` | `"primary"` | Visual style |
|
|
36
|
+
| `size` | `"default" \| "sm" \| "lg" \| "icon"` | `"default"` | Button size |
|
|
37
|
+
| `icon` | `ReactNode` | - | Icon displayed before text |
|
|
38
|
+
| `loading` | `boolean` | `false` | Shows spinner, disables button |
|
|
39
|
+
| `disabled` | `boolean` | `false` | Disables interaction |
|
|
40
|
+
| `asChild` | `boolean` | `false` | Render as child element (Slot) |
|
|
41
|
+
|
|
42
|
+
## Examples
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
// Primary action
|
|
46
|
+
<Button>Submit</Button>
|
|
47
|
+
|
|
48
|
+
// With icon
|
|
49
|
+
<Button icon={<Icon type="add" />}>Create Resource</Button>
|
|
50
|
+
|
|
51
|
+
// Loading state
|
|
52
|
+
<Button loading>Saving...</Button>
|
|
53
|
+
|
|
54
|
+
// Danger action
|
|
55
|
+
<Button variant="danger">Delete</Button>
|
|
56
|
+
|
|
57
|
+
// Icon-only
|
|
58
|
+
<Button variant="ghost" size="icon"><Icon type="settings" /></Button>
|
|
59
|
+
|
|
60
|
+
// Button group
|
|
61
|
+
<div className="flex gap-2">
|
|
62
|
+
<Button variant="subtle">Cancel</Button>
|
|
63
|
+
<Button>Confirm</Button>
|
|
64
|
+
</div>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Rules
|
|
68
|
+
|
|
69
|
+
- Use only one `primary` button per logical section.
|
|
70
|
+
- Always pair `primary` with `subtle` for confirm/cancel patterns.
|
|
71
|
+
- Use `danger` variant for destructive actions. Do not use `primary` with red styling.
|
|
72
|
+
- Always show a loading state during async operations. Do not leave the button enabled.
|
|
73
|
+
- Place the primary action on the right side in button groups.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# DatePicker
|
|
2
|
+
|
|
3
|
+
Use for date and date-time selection.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { DatePicker, RangePicker, TimePicker } from "@zscloud/design"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
// Date only
|
|
15
|
+
<DatePicker value={date} onChange={setDate} />
|
|
16
|
+
|
|
17
|
+
// Date + time
|
|
18
|
+
<DatePicker value={date} onChange={setDate} showTime />
|
|
19
|
+
|
|
20
|
+
// Date range
|
|
21
|
+
<RangePicker value={range} onChange={setRange} />
|
|
22
|
+
|
|
23
|
+
// Time only
|
|
24
|
+
<TimePicker value={time} onChange={setTime} />
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Props (DatePicker)
|
|
28
|
+
|
|
29
|
+
| Prop | Type | Default | Description |
|
|
30
|
+
|------|------|---------|-------------|
|
|
31
|
+
| `value` | `Date \| Moment \| undefined` | - | Selected date |
|
|
32
|
+
| `onChange` | `(val) => void` | - | Change handler |
|
|
33
|
+
| `showTime` | `boolean` | `false` | Include time selection |
|
|
34
|
+
| `showNow` | `boolean` | `true` | Show "Now" button |
|
|
35
|
+
| `disabledDate` | `(date: Date) => boolean` | - | Disable specific dates |
|
|
36
|
+
| `disabledTime` | `(date: Date) => DisabledTimeConfig` | - | Disable specific times |
|
|
37
|
+
| `placeholder` | `string` | - | Placeholder text |
|
|
38
|
+
| `format` | `string` | - | Display format |
|
|
39
|
+
| `disabled` | `boolean` | `false` | Disable interaction |
|
|
40
|
+
| `allowClear` | `boolean` | `true` | Show clear button |
|
|
41
|
+
| `width` | `number \| string` | - | Custom width |
|
|
42
|
+
|
|
43
|
+
## Rules
|
|
44
|
+
|
|
45
|
+
- Use `DatePicker` for single date selection.
|
|
46
|
+
- Use `RangePicker` for date range selection.
|
|
47
|
+
- Use `TimePicker` for time-only selection.
|
|
48
|
+
- Use `disabledDate` to prevent selecting past dates or future dates.
|
|
49
|
+
- Set `showTime` only when time precision is required.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Dialog
|
|
2
|
+
|
|
3
|
+
Use for modal interactions: confirmations, forms, and detailed content that requires focus.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import {
|
|
9
|
+
Dialog, DialogContent, DialogHeader, DialogFooter,
|
|
10
|
+
DialogTitle, DialogBody, DialogBanner, DialogScrollArea
|
|
11
|
+
} from "@zscloud/design"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Structure
|
|
15
|
+
|
|
16
|
+
```tsx
|
|
17
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
18
|
+
<DialogContent>
|
|
19
|
+
<DialogHeader>
|
|
20
|
+
<DialogTitle>Title</DialogTitle>
|
|
21
|
+
</DialogHeader>
|
|
22
|
+
<DialogBody>
|
|
23
|
+
{/* Content */}
|
|
24
|
+
</DialogBody>
|
|
25
|
+
<DialogFooter>
|
|
26
|
+
<Button variant="subtle" onClick={() => setOpen(false)}>Cancel</Button>
|
|
27
|
+
<Button>Confirm</Button>
|
|
28
|
+
</DialogFooter>
|
|
29
|
+
</DialogContent>
|
|
30
|
+
</Dialog>
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Sub-components
|
|
34
|
+
|
|
35
|
+
| Component | Purpose |
|
|
36
|
+
|-----------|---------|
|
|
37
|
+
| `DialogContent` | Container with overlay, animation, close button |
|
|
38
|
+
| `DialogHeader` | Top section. Variants: primary, secondary |
|
|
39
|
+
| `DialogTitle` | Dialog heading. Variants: normal, weak |
|
|
40
|
+
| `DialogBody` | Main content area. Variants: primary, secondary |
|
|
41
|
+
| `DialogFooter` | Action buttons area |
|
|
42
|
+
| `DialogBanner` | Alert banner below header. Variants: danger, warning, info |
|
|
43
|
+
| `DialogScrollArea` | Scrollable content area within body |
|
|
44
|
+
| `DialogDivider` | Horizontal separator line |
|
|
45
|
+
|
|
46
|
+
## Props (DialogContent)
|
|
47
|
+
|
|
48
|
+
| Prop | Type | Default | Description |
|
|
49
|
+
|------|------|---------|-------------|
|
|
50
|
+
| `zIndex` | `number` | auto | Override z-index |
|
|
51
|
+
| `disableAutoZIndex` | `boolean` | `false` | Disable automatic z-index |
|
|
52
|
+
|
|
53
|
+
## Examples
|
|
54
|
+
|
|
55
|
+
### With danger banner
|
|
56
|
+
|
|
57
|
+
```tsx
|
|
58
|
+
<Dialog open={open} onOpenChange={setOpen}>
|
|
59
|
+
<DialogContent>
|
|
60
|
+
<DialogHeader>
|
|
61
|
+
<DialogTitle>Delete Resource</DialogTitle>
|
|
62
|
+
</DialogHeader>
|
|
63
|
+
<DialogBanner variant="danger">
|
|
64
|
+
This action cannot be undone.
|
|
65
|
+
</DialogBanner>
|
|
66
|
+
<DialogBody>
|
|
67
|
+
Are you sure you want to delete this resource?
|
|
68
|
+
</DialogBody>
|
|
69
|
+
<DialogFooter>
|
|
70
|
+
<Button variant="subtle" onClick={() => setOpen(false)}>Cancel</Button>
|
|
71
|
+
<Button variant="danger">Delete</Button>
|
|
72
|
+
</DialogFooter>
|
|
73
|
+
</DialogContent>
|
|
74
|
+
</Dialog>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### With scrollable content
|
|
78
|
+
|
|
79
|
+
```tsx
|
|
80
|
+
<DialogBody>
|
|
81
|
+
<DialogScrollArea>
|
|
82
|
+
{/* Long content */}
|
|
83
|
+
</DialogScrollArea>
|
|
84
|
+
</DialogBody>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Rules
|
|
88
|
+
|
|
89
|
+
- Always provide a `DialogTitle` for accessibility.
|
|
90
|
+
- Always include a cancel/close mechanism.
|
|
91
|
+
- Use `DialogBanner` with `variant="danger"` for destructive confirmations.
|
|
92
|
+
- The z-index is managed automatically. Do not set manual z-index unless dealing with stacking issues.
|
|
93
|
+
- Use `AlertDialog` instead of `Dialog` for simple confirm/cancel interactions without form content.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Drawer
|
|
2
|
+
|
|
3
|
+
Use for side panel overlays displaying detail views, forms, or secondary content.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Drawer, DrawerHeader, DrawerBody, DrawerFooter } from "@zscloud/design"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Drawer open={open} setOpen={setOpen} placement="right">
|
|
15
|
+
<DrawerHeader>Detail View</DrawerHeader>
|
|
16
|
+
<DrawerBody>
|
|
17
|
+
{/* Content */}
|
|
18
|
+
</DrawerBody>
|
|
19
|
+
<DrawerFooter>
|
|
20
|
+
<Button variant="subtle" onClick={() => setOpen(false)}>Close</Button>
|
|
21
|
+
<Button>Save</Button>
|
|
22
|
+
</DrawerFooter>
|
|
23
|
+
</Drawer>
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Props (Drawer)
|
|
27
|
+
|
|
28
|
+
| Prop | Type | Default | Description |
|
|
29
|
+
|------|------|---------|-------------|
|
|
30
|
+
| `open` | `boolean` | required | Visibility state |
|
|
31
|
+
| `setOpen` | `(open: boolean) => void` | required | State setter |
|
|
32
|
+
| `placement` | `"right" \| "left" \| "top" \| "bottom"` | `"right"` | Slide direction |
|
|
33
|
+
| `zIndex` | `number` | auto | Override z-index |
|
|
34
|
+
|
|
35
|
+
## Props (DrawerHeader)
|
|
36
|
+
|
|
37
|
+
| Prop | Type | Default | Description |
|
|
38
|
+
|------|------|---------|-------------|
|
|
39
|
+
| `closable` | `boolean` | `true` | Show close button |
|
|
40
|
+
| `onClose` | `() => void` | - | Close handler |
|
|
41
|
+
|
|
42
|
+
## Rules
|
|
43
|
+
|
|
44
|
+
- Use `placement="right"` (default) for detail panels and forms.
|
|
45
|
+
- Use `placement="bottom"` for mobile-style bottom sheets.
|
|
46
|
+
- Always include a `DrawerHeader` with a title.
|
|
47
|
+
- For forms inside drawers, put action buttons in `DrawerFooter`.
|
|
48
|
+
- Do not nest drawers within drawers. Use a dialog instead.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Form
|
|
2
|
+
|
|
3
|
+
Form system built on react-hook-form + zod for validation.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import {
|
|
9
|
+
Form, FormField, FormItem, FormLabel,
|
|
10
|
+
FormControl, FormMessage, FormDescription,
|
|
11
|
+
FormRowContainer, FormHint, FormNestedContainer
|
|
12
|
+
} from "@zscloud/design"
|
|
13
|
+
import { useForm } from "react-hook-form"
|
|
14
|
+
import { zodResolver } from "@hookform/resolvers/zod"
|
|
15
|
+
import { z } from "zod"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Standard pattern
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
const schema = z.object({
|
|
22
|
+
name: z.string().min(1, "Required"),
|
|
23
|
+
email: z.string().email("Invalid email"),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
type FormValues = z.infer<typeof schema>
|
|
27
|
+
|
|
28
|
+
function MyForm() {
|
|
29
|
+
const form = useForm<FormValues>({
|
|
30
|
+
resolver: zodResolver(schema),
|
|
31
|
+
defaultValues: { name: "", email: "" },
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Form {...form}>
|
|
36
|
+
<form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-3">
|
|
37
|
+
<FormField
|
|
38
|
+
control={form.control}
|
|
39
|
+
name="name"
|
|
40
|
+
render={({ field }) => (
|
|
41
|
+
<FormItem>
|
|
42
|
+
<FormLabel required>Name</FormLabel>
|
|
43
|
+
<FormControl>
|
|
44
|
+
<Input {...field} />
|
|
45
|
+
</FormControl>
|
|
46
|
+
<FormMessage />
|
|
47
|
+
</FormItem>
|
|
48
|
+
)}
|
|
49
|
+
/>
|
|
50
|
+
<FormField
|
|
51
|
+
control={form.control}
|
|
52
|
+
name="email"
|
|
53
|
+
render={({ field }) => (
|
|
54
|
+
<FormItem>
|
|
55
|
+
<FormLabel required>Email</FormLabel>
|
|
56
|
+
<FormControl>
|
|
57
|
+
<Input {...field} />
|
|
58
|
+
</FormControl>
|
|
59
|
+
<FormDescription>We will not share your email.</FormDescription>
|
|
60
|
+
<FormMessage />
|
|
61
|
+
</FormItem>
|
|
62
|
+
)}
|
|
63
|
+
/>
|
|
64
|
+
</form>
|
|
65
|
+
</Form>
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Sub-components
|
|
71
|
+
|
|
72
|
+
| Component | Purpose |
|
|
73
|
+
|-----------|---------|
|
|
74
|
+
| `Form` | Provider wrapper (react-hook-form FormProvider) |
|
|
75
|
+
| `FormField` | Controller for a single field |
|
|
76
|
+
| `FormItem` | Container for label + control + message |
|
|
77
|
+
| `FormLabel` | Field label. Set `required` prop for asterisk |
|
|
78
|
+
| `FormControl` | Wraps the input, connects aria attributes |
|
|
79
|
+
| `FormMessage` | Validation error message |
|
|
80
|
+
| `FormDescription` | Helper text below the input |
|
|
81
|
+
| `FormHint` | Small hint text (text-xs) |
|
|
82
|
+
| `FormRowContainer` | Horizontal layout for inline fields |
|
|
83
|
+
| `FormNestedContainer` | Indented nested section with left border |
|
|
84
|
+
| `FormRequiredIndicator` | Red asterisk (*) |
|
|
85
|
+
|
|
86
|
+
## FormLabel props
|
|
87
|
+
|
|
88
|
+
| Prop | Type | Description |
|
|
89
|
+
|------|------|-------------|
|
|
90
|
+
| `required` | `boolean` | Shows red asterisk |
|
|
91
|
+
| `info` | `ReactNode` | Shows Info icon with tooltip |
|
|
92
|
+
|
|
93
|
+
## Rules
|
|
94
|
+
|
|
95
|
+
- Always use zod schemas for validation. Do not use manual validation logic.
|
|
96
|
+
- Always wrap forms with the `Form` component.
|
|
97
|
+
- Use `FormLabel` with `required` prop for mandatory fields. Do not manually add asterisks.
|
|
98
|
+
- Use `FormMessage` for validation errors. Do not render errors manually.
|
|
99
|
+
- Use `gap-3` (12px) between `FormItem` elements vertically.
|
|
100
|
+
- Use `FormRowContainer` for side-by-side fields, not custom flex layouts.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Select
|
|
2
|
+
|
|
3
|
+
Use for single-value selection from a list of options.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Select } from "@zscloud/design"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Simple usage (recommended)
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
const options = [
|
|
15
|
+
{ label: "Option A", value: "a" },
|
|
16
|
+
{ label: "Option B", value: "b", description: "With helper text" },
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
<Select
|
|
20
|
+
options={options}
|
|
21
|
+
value={value}
|
|
22
|
+
onValueChange={setValue}
|
|
23
|
+
placeholder="Select an option"
|
|
24
|
+
/>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Props
|
|
28
|
+
|
|
29
|
+
| Prop | Type | Default | Description |
|
|
30
|
+
|------|------|---------|-------------|
|
|
31
|
+
| `options` | `SelectOptions[]` | required | List of options |
|
|
32
|
+
| `value` | `string` | - | Controlled value |
|
|
33
|
+
| `defaultValue` | `string` | - | Uncontrolled default |
|
|
34
|
+
| `onValueChange` | `(value: string) => void` | required | Change handler |
|
|
35
|
+
| `placeholder` | `string` | - | Placeholder text |
|
|
36
|
+
| `disabled` | `boolean` | `false` | Disable interaction |
|
|
37
|
+
| `allowClear` | `boolean` | `false` | Show clear button |
|
|
38
|
+
| `onClear` | `() => void` | - | Clear handler |
|
|
39
|
+
| `emptyPlaceholderText` | `string` | - | Text when no options |
|
|
40
|
+
|
|
41
|
+
## Option shape
|
|
42
|
+
|
|
43
|
+
```typescript
|
|
44
|
+
interface SelectOptions {
|
|
45
|
+
label: ReactNode // Display text in dropdown
|
|
46
|
+
selectedLabel?: ReactNode // Display in trigger when selected (optional)
|
|
47
|
+
description?: string // Secondary text below label
|
|
48
|
+
value: string // Unique value
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Composable usage (advanced)
|
|
53
|
+
|
|
54
|
+
For custom layouts, use the primitive sub-components:
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import {
|
|
58
|
+
SelectRoot, SelectTrigger, SelectValue,
|
|
59
|
+
SelectContent, SelectGroup, SelectItem
|
|
60
|
+
} from "@zscloud/design"
|
|
61
|
+
|
|
62
|
+
<SelectRoot value={value} onValueChange={setValue}>
|
|
63
|
+
<SelectTrigger>
|
|
64
|
+
<SelectValue placeholder="Choose..." />
|
|
65
|
+
</SelectTrigger>
|
|
66
|
+
<SelectContent>
|
|
67
|
+
<SelectGroup>
|
|
68
|
+
<SelectItem value="a">Option A</SelectItem>
|
|
69
|
+
<SelectItem value="b">Option B</SelectItem>
|
|
70
|
+
</SelectGroup>
|
|
71
|
+
</SelectContent>
|
|
72
|
+
</SelectRoot>
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Rules
|
|
76
|
+
|
|
77
|
+
- Use the simple `Select` with `options` prop for standard cases.
|
|
78
|
+
- Use the composable pattern only when you need custom item rendering.
|
|
79
|
+
- For multi-selection, use `MultiSelect` instead.
|
|
80
|
+
- For searchable selection, use `AutoComplete` instead.
|
|
81
|
+
- For hierarchical selection, use `Cascader` instead.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Tabs
|
|
2
|
+
|
|
3
|
+
Use for switching between related content panels.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Tabs } from "@zscloud/design"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Simple usage
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
const tabsList = [
|
|
15
|
+
{ value: "overview", label: "Overview", content: <OverviewPanel /> },
|
|
16
|
+
{ value: "config", label: "Configuration", content: <ConfigPanel /> },
|
|
17
|
+
{ value: "logs", label: "Logs", content: <LogsPanel />, disabled: true },
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
<Tabs tabsList={tabsList} defaultValue="overview" />
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Props
|
|
24
|
+
|
|
25
|
+
| Prop | Type | Default | Description |
|
|
26
|
+
|------|------|---------|-------------|
|
|
27
|
+
| `tabsList` | `TabsListItem[]` | required | Tab definitions |
|
|
28
|
+
| `value` | `string` | - | Controlled active tab |
|
|
29
|
+
| `defaultValue` | `string` | - | Initial active tab |
|
|
30
|
+
| `onValueChange` | `(value: string) => void` | - | Tab change handler |
|
|
31
|
+
| `variant` | `"line" \| "card" \| "outline"` | `"line"` | Visual style |
|
|
32
|
+
| `forceMount` | `true` | - | Keep inactive tab DOM alive |
|
|
33
|
+
| `contentId` | `string` | - | Enable tab state persistence |
|
|
34
|
+
|
|
35
|
+
## TabsListItem
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
interface TabsListItem {
|
|
39
|
+
value: string // Unique identifier
|
|
40
|
+
label?: ReactNode // Tab trigger text
|
|
41
|
+
content?: ReactNode // Tab panel content
|
|
42
|
+
disabled?: boolean // Disable this tab
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Variants
|
|
47
|
+
|
|
48
|
+
| Variant | Use for |
|
|
49
|
+
|---------|---------|
|
|
50
|
+
| `line` | Default. Underline indicator. Use in page sections. |
|
|
51
|
+
| `card` | Card-style tabs. Use for container switching. |
|
|
52
|
+
| `outline` | Bordered tabs. Use for compact layouts. |
|
|
53
|
+
|
|
54
|
+
## Composable usage
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
import { TabsRoot, TabsList, TabsTrigger, TabsContent } from "@zscloud/design"
|
|
58
|
+
|
|
59
|
+
<TabsRoot defaultValue="tab1">
|
|
60
|
+
<TabsList>
|
|
61
|
+
<TabsTrigger value="tab1">Tab 1</TabsTrigger>
|
|
62
|
+
<TabsTrigger value="tab2">Tab 2</TabsTrigger>
|
|
63
|
+
</TabsList>
|
|
64
|
+
<TabsContent value="tab1">Content 1</TabsContent>
|
|
65
|
+
<TabsContent value="tab2">Content 2</TabsContent>
|
|
66
|
+
</TabsRoot>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Rules
|
|
70
|
+
|
|
71
|
+
- Use the simple `Tabs` with `tabsList` prop for standard cases.
|
|
72
|
+
- Use `forceMount` when tab panels contain stateful content that should not be destroyed.
|
|
73
|
+
- Do not nest tabs within tabs. Use a different navigation pattern for deeper hierarchy.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Toast
|
|
2
|
+
|
|
3
|
+
Use for temporary notification messages (success, error, info).
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { toast, Toaster } from "@zscloud/design"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Setup
|
|
12
|
+
|
|
13
|
+
Add `Toaster` once at your application root:
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
function App() {
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<Toaster />
|
|
20
|
+
{/* App content */}
|
|
21
|
+
</>
|
|
22
|
+
)
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
// Success
|
|
30
|
+
toast({ title: "Resource created successfully", indicator: "success" })
|
|
31
|
+
|
|
32
|
+
// Error
|
|
33
|
+
toast({ title: "Failed to create resource", variant: "destructive" })
|
|
34
|
+
|
|
35
|
+
// With description
|
|
36
|
+
toast({
|
|
37
|
+
title: "Operation complete",
|
|
38
|
+
description: "3 resources were updated.",
|
|
39
|
+
indicator: "success",
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
// With action
|
|
43
|
+
toast({
|
|
44
|
+
title: "Resource deleted",
|
|
45
|
+
action: <ToastAction altText="Undo">Undo</ToastAction>,
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// Programmatic dismiss
|
|
49
|
+
const { dismiss } = toast({ title: "Processing..." })
|
|
50
|
+
// Later:
|
|
51
|
+
dismiss()
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Rules
|
|
55
|
+
|
|
56
|
+
- Always place `Toaster` at the application root, once.
|
|
57
|
+
- Use `indicator: "success"` for success messages. Use `variant: "destructive"` for errors.
|
|
58
|
+
- Keep toast messages concise (under 10 words for title).
|
|
59
|
+
- Do not use toasts for critical errors that require user action. Use `Dialog` or `Alert` instead.
|
|
60
|
+
- Toasts auto-dismiss. Do not set excessively long durations.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Tooltip
|
|
2
|
+
|
|
3
|
+
Use for hover-triggered explanatory text. Do not use for interactive content.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Tooltip } from "@zscloud/design"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
<Tooltip title="This is a helpful explanation">
|
|
15
|
+
<Button variant="ghost" size="icon">
|
|
16
|
+
<Icon type="info-circle" />
|
|
17
|
+
</Button>
|
|
18
|
+
</Tooltip>
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Props
|
|
22
|
+
|
|
23
|
+
| Prop | Type | Default | Description |
|
|
24
|
+
|------|------|---------|-------------|
|
|
25
|
+
| `title` | `ReactNode` | required | Tooltip content |
|
|
26
|
+
| `placement` | Position string | `"top"` | Where to show the tooltip |
|
|
27
|
+
| `disappearOnClick` | `boolean` | `true` | Hide on click |
|
|
28
|
+
| `delayDuration` | `number` | `0` | Delay before showing (ms) |
|
|
29
|
+
|
|
30
|
+
## Placement options
|
|
31
|
+
|
|
32
|
+
`topLeft`, `top`, `topRight`, `leftTop`, `left`, `leftBottom`, `rightTop`, `right`, `rightBottom`, `bottomLeft`, `bottom`, `bottomRight`
|
|
33
|
+
|
|
34
|
+
## Rules
|
|
35
|
+
|
|
36
|
+
- Use `Tooltip` for non-interactive explanatory text only.
|
|
37
|
+
- For interactive content (links, buttons), use `Popover` instead.
|
|
38
|
+
- Keep tooltip text concise (1-2 sentences max).
|
|
39
|
+
- Do not put tooltips on disabled elements. Use the `Info` component for persistent hints.
|
|
40
|
+
- Delay is 0ms by default. Do not add delay unless showing many tooltips in a dense area.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Color Tokens
|
|
2
|
+
|
|
3
|
+
## Semantic color system
|
|
4
|
+
|
|
5
|
+
Use semantic color names, not raw hex values. The color system adapts automatically in dark mode.
|
|
6
|
+
|
|
7
|
+
### Theme (brand) colors
|
|
8
|
+
|
|
9
|
+
The primary brand color. Customizable at runtime via CSS variables.
|
|
10
|
+
|
|
11
|
+
| Tailwind class | CSS variable | Default |
|
|
12
|
+
|---------------|-------------|---------|
|
|
13
|
+
| `bg-theme-500` | `--color-theme-500` | `#0076f7` (blue) |
|
|
14
|
+
| `text-theme-600` | `--color-theme-600` | Darker brand |
|
|
15
|
+
| `bg-theme-50` | `--color-theme-50` | Light brand tint |
|
|
16
|
+
|
|
17
|
+
Full scale: `theme-50` through `theme-950`.
|
|
18
|
+
|
|
19
|
+
### Status colors
|
|
20
|
+
|
|
21
|
+
| Role | Tailwind prefix | Use for | Default base |
|
|
22
|
+
|------|----------------|---------|-------------|
|
|
23
|
+
| **danger** | `text-danger-500` | Errors, destructive actions | `#f4454c` (red) |
|
|
24
|
+
| **positive** | `text-positive-500` | Success, healthy status | `#5aca49` (green) |
|
|
25
|
+
| **alert** | `text-alert-500` | Warnings, caution | `#ff9000` (orange) |
|
|
26
|
+
| **info** | `text-info-500` | Informational | `#0076f7` (blue) |
|
|
27
|
+
| **pending** | `text-pending-500` | In-progress, waiting | `#9a45e4` (purple) |
|
|
28
|
+
|
|
29
|
+
Each status color has a full scale from `50` to `950`.
|
|
30
|
+
|
|
31
|
+
### Neutral colors
|
|
32
|
+
|
|
33
|
+
For text, borders, backgrounds, and disabled states.
|
|
34
|
+
|
|
35
|
+
| Usage | Tailwind class | Description |
|
|
36
|
+
|-------|---------------|-------------|
|
|
37
|
+
| Default text | `text-neutral-700` | Primary text |
|
|
38
|
+
| Secondary text | `text-neutral-500` | Muted/helper text |
|
|
39
|
+
| Disabled text | `text-neutral-400` | Disabled state |
|
|
40
|
+
| Default border | `border-neutral-300` | Standard borders |
|
|
41
|
+
| Subtle border | `border-neutral-200` | Subtle separation |
|
|
42
|
+
| Hover background | `bg-neutral-100` | Hover state |
|
|
43
|
+
| Page background | `bg-neutral-50` | Light background |
|
|
44
|
+
|
|
45
|
+
Full scale: `neutral-0` (white) through `neutral-950` (near-black).
|
|
46
|
+
|
|
47
|
+
### On-brand color
|
|
48
|
+
|
|
49
|
+
Use `text-on-brand` for text on brand-colored backgrounds (buttons, badges). This is always white.
|
|
50
|
+
|
|
51
|
+
## Semantic variables (CSS)
|
|
52
|
+
|
|
53
|
+
These map to component default styles:
|
|
54
|
+
|
|
55
|
+
| Variable | Light mode | Purpose |
|
|
56
|
+
|----------|-----------|---------|
|
|
57
|
+
| `--foreground` | `neutral-700` | Default text |
|
|
58
|
+
| `--background` | `white` | Page background |
|
|
59
|
+
| `--primary` | `theme-600` | Primary actions |
|
|
60
|
+
| `--destructive` | `red-500` | Destructive actions |
|
|
61
|
+
| `--border` | `neutral-300` | Default border |
|
|
62
|
+
| `--input` | `neutral-400` | Input border |
|
|
63
|
+
| `--ring` | `theme-300` | Focus ring |
|
|
64
|
+
| `--muted` | `neutral-100` | Muted background |
|
|
65
|
+
| `--muted-foreground` | `neutral-500` | Muted text |
|
|
66
|
+
|
|
67
|
+
## Rules
|
|
68
|
+
|
|
69
|
+
- Always use semantic color names (`danger`, `positive`, `alert`) instead of raw color names (`red`, `green`, `orange`).
|
|
70
|
+
- Use `theme-*` for brand-related elements. Do not hardcode blue.
|
|
71
|
+
- Use `neutral-*` for grays. Do not use arbitrary gray values.
|
|
72
|
+
- For text on colored backgrounds, use `text-on-brand` (white).
|
|
73
|
+
- For hover states, go one shade darker (e.g., `bg-theme-500` hover `bg-theme-600`).
|
|
74
|
+
- For disabled states, reduce to `neutral-400` text and `neutral-200` background.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Shadow Tokens
|
|
2
|
+
|
|
3
|
+
## Elevation scale
|
|
4
|
+
|
|
5
|
+
| Tailwind class | Use for |
|
|
6
|
+
|---------------|---------|
|
|
7
|
+
| `shadow-2xs` | Subtle bottom border effect |
|
|
8
|
+
| `shadow-xs` | Cards at rest |
|
|
9
|
+
| `shadow-sm` | Raised cards, form controls on focus |
|
|
10
|
+
| `shadow-md` | Dropdowns, popovers |
|
|
11
|
+
| `shadow-lg` | Dialogs, drawers |
|
|
12
|
+
| `shadow-xl` | Top-level modals |
|
|
13
|
+
| `shadow-2xl` | Maximum elevation |
|
|
14
|
+
|
|
15
|
+
## Focus rings
|
|
16
|
+
|
|
17
|
+
| Tailwind class | Use for |
|
|
18
|
+
|---------------|---------|
|
|
19
|
+
| `shadow-focus-ring` | Default focus indicator (green tint) |
|
|
20
|
+
| `shadow-focus-ring-error` | Error state focus indicator (red tint) |
|
|
21
|
+
| `shadow-focus-ring-sidebar` | Sidebar-specific focus indicator (gray) |
|
|
22
|
+
|
|
23
|
+
## Rules
|
|
24
|
+
|
|
25
|
+
- Use `shadow-md` for all floating elements (popovers, dropdowns, context menus).
|
|
26
|
+
- Use `shadow-lg` for dialogs and drawers.
|
|
27
|
+
- Do not apply shadows to flat, inline elements.
|
|
28
|
+
- Focus rings are applied automatically by components. Do not manually add focus ring styles.
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# Spacing Tokens
|
|
2
|
+
|
|
3
|
+
The spacing system follows a 4px base grid. All spacing values are multiples of 4px.
|
|
4
|
+
|
|
5
|
+
## Scale
|
|
6
|
+
|
|
7
|
+
| Tailwind class | Value | Common use |
|
|
8
|
+
|---------------|-------|------------|
|
|
9
|
+
| `gap-0.5` / `p-0.5` | 2px | Tight internal spacing |
|
|
10
|
+
| `gap-1` / `p-1` | 4px | Icon-to-text gap |
|
|
11
|
+
| `gap-1.5` / `p-1.5` | 6px | Compact padding |
|
|
12
|
+
| `gap-2` / `p-2` | 8px | Standard gap between related items |
|
|
13
|
+
| `gap-3` / `p-3` | 12px | Form field gap, card padding |
|
|
14
|
+
| `gap-4` / `p-4` | 16px | Section padding, standard margin |
|
|
15
|
+
| `gap-6` / `p-6` | 24px | Section separation |
|
|
16
|
+
| `gap-8` / `p-8` | 32px | Large section separation |
|
|
17
|
+
|
|
18
|
+
## Component-specific spacing
|
|
19
|
+
|
|
20
|
+
| Context | Spacing | Example |
|
|
21
|
+
|---------|---------|---------|
|
|
22
|
+
| Button icon gap | `gap-1` (4px) | Icon to label inside button |
|
|
23
|
+
| Form field spacing | `gap-3` (12px) | Between form items vertically |
|
|
24
|
+
| Dialog body padding | `p-4` (16px) | Content area padding |
|
|
25
|
+
| Card content padding | `p-4` (16px) | Card body padding |
|
|
26
|
+
| Page section gap | `gap-6` (24px) | Between major sections |
|
|
27
|
+
| Inline elements | `gap-2` (8px) | Between buttons in a toolbar |
|
|
28
|
+
|
|
29
|
+
## Border radius
|
|
30
|
+
|
|
31
|
+
| Tailwind class | Value | Use for |
|
|
32
|
+
|---------------|-------|---------|
|
|
33
|
+
| `rounded-none` | 0px | No rounding |
|
|
34
|
+
| `rounded-xs` | 2px | Inputs, buttons, cards (default) |
|
|
35
|
+
| `rounded-sm` | 4px | Tags, badges |
|
|
36
|
+
| `rounded-md` | 6px | Dialogs, dropdowns |
|
|
37
|
+
| `rounded-lg` | 8px | Large containers |
|
|
38
|
+
| `rounded-full` | 9999px | Circular elements, pills |
|
|
39
|
+
|
|
40
|
+
## Rules
|
|
41
|
+
|
|
42
|
+
- Always use the 4px grid. Do not use arbitrary spacing values like 5px or 7px.
|
|
43
|
+
- Use `gap-*` for flex/grid layouts instead of margin on individual children.
|
|
44
|
+
- The default border radius for interactive controls is `rounded-xs` (2px). Do not override unless specifically required.
|
|
45
|
+
- Use `rounded-md` (6px) for floating elements (dialogs, popovers, dropdowns).
|
|
46
|
+
- Use `rounded-full` only for avatars, status dots, and pill-shaped elements.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Typography Tokens
|
|
2
|
+
|
|
3
|
+
## Font families
|
|
4
|
+
|
|
5
|
+
| Token | Tailwind class | Value |
|
|
6
|
+
|-------|---------------|-------|
|
|
7
|
+
| Sans (default) | `font-sans` | `-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", Helvetica, Arial, sans-serif` |
|
|
8
|
+
| Monospace | `font-mono` | `"SFMono-Medium", "SF Mono", "Segoe UI Mono", Menlo, Consolas, monospace` |
|
|
9
|
+
|
|
10
|
+
Use `font-sans` for all UI text. Use `font-mono` only for code, UUIDs, and technical identifiers.
|
|
11
|
+
|
|
12
|
+
## Font sizes
|
|
13
|
+
|
|
14
|
+
| Token | Tailwind class | Size | Use for |
|
|
15
|
+
|-------|---------------|------|---------|
|
|
16
|
+
| xs | `text-xs` | 12px | Captions, helper text, badges |
|
|
17
|
+
| sm | `text-sm` | 14px | Default body text, form labels, table cells |
|
|
18
|
+
| base | `text-base` | 16px | Emphasized body text |
|
|
19
|
+
| lg | `text-lg` | 20px | Section headings |
|
|
20
|
+
| xl | `text-xl` | 24px | Page titles |
|
|
21
|
+
| 2xl | `text-2xl` | 30px | Large headings |
|
|
22
|
+
| 3xl | `text-3xl` | 38px | Hero headings |
|
|
23
|
+
|
|
24
|
+
## Line heights
|
|
25
|
+
|
|
26
|
+
| Token | Tailwind class | Value | Pair with |
|
|
27
|
+
|-------|---------------|-------|-----------|
|
|
28
|
+
| tight | `leading-tight` | 20px | `text-xs`, `text-sm` |
|
|
29
|
+
| snug | `leading-snug` | 22px | `text-sm` |
|
|
30
|
+
| normal | `leading-normal` | 24px | `text-base` |
|
|
31
|
+
| relaxed | `leading-relaxed` | 28px | `text-lg` |
|
|
32
|
+
| loose | `leading-loose` | 32px | `text-xl` |
|
|
33
|
+
|
|
34
|
+
## Font weights
|
|
35
|
+
|
|
36
|
+
| Tailwind class | Weight | Use for |
|
|
37
|
+
|---------------|--------|---------|
|
|
38
|
+
| `font-normal` | 400 | Body text |
|
|
39
|
+
| `font-medium` | 500 | Labels, emphasis |
|
|
40
|
+
| `font-semibold` | 600 | Headings, buttons |
|
|
41
|
+
| `font-bold` | 700 | Strong emphasis |
|
|
42
|
+
|
|
43
|
+
## Text component
|
|
44
|
+
|
|
45
|
+
Use the `Text` component for consistent typography:
|
|
46
|
+
|
|
47
|
+
```tsx
|
|
48
|
+
import { Text } from "@zscloud/design"
|
|
49
|
+
|
|
50
|
+
<Text variant="heading-lg">Page Title</Text>
|
|
51
|
+
<Text variant="body">Regular text content</Text>
|
|
52
|
+
<Text variant="caption" color="muted">Helper text</Text>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Rules
|
|
56
|
+
|
|
57
|
+
- The default font size for all UI controls is `text-sm` (14px). Do not use `text-xs` for form inputs or buttons.
|
|
58
|
+
- Use `text-xs` (12px) only for captions, timestamps, and secondary metadata.
|
|
59
|
+
- Do not use `text-base` (16px) or larger for table cell content.
|
|
60
|
+
- Always pair font sizes with appropriate line heights. Use `leading-tight` for compact layouts.
|
|
61
|
+
- Use `font-medium` (500) for form labels. Use `font-semibold` (600) for dialog titles and section headings.
|
|
62
|
+
- Do not use letter-spacing adjustments unless specifically required for headings.
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# Component Overview
|
|
2
|
+
|
|
3
|
+
## Import pattern
|
|
4
|
+
|
|
5
|
+
All components are named exports from the package root:
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Button, Dialog, Input, Select } from "@zscloud/design"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Component categories
|
|
12
|
+
|
|
13
|
+
### Form Controls
|
|
14
|
+
|
|
15
|
+
| Component | Use for |
|
|
16
|
+
|-----------|---------|
|
|
17
|
+
| `Button` | Actions and submissions. Variants: primary, subtle, outline, secondary, danger, ghost, link |
|
|
18
|
+
| `Input` | Single-line text input |
|
|
19
|
+
| `InputNumber` | Numeric input with optional increment/decrement controls |
|
|
20
|
+
| `InputPassword` | Password input with show/hide toggle |
|
|
21
|
+
| `Textarea` | Multi-line text input |
|
|
22
|
+
| `SearchInput` | Input with search icon, for filtering |
|
|
23
|
+
| `Checkbox` | Boolean toggle. Supports indeterminate state |
|
|
24
|
+
| `CheckboxGroup` | Multiple checkbox options |
|
|
25
|
+
| `RadioGroup` | Single selection from options. Variants: basic, button |
|
|
26
|
+
| `Select` | Single selection dropdown. Use `options` prop for simple usage |
|
|
27
|
+
| `MultiSelect` | Multiple selection dropdown with tags |
|
|
28
|
+
| `AutoComplete` | Input with suggestions dropdown |
|
|
29
|
+
| `Cascader` | Hierarchical selection (province/city/district) |
|
|
30
|
+
| `Switch` | Boolean toggle as a slider |
|
|
31
|
+
| `Slider` | Range value selection |
|
|
32
|
+
| `DatePicker` | Date selection with optional time picker |
|
|
33
|
+
| `RangePicker` | Date range selection |
|
|
34
|
+
| `TimePicker` | Time-only selection |
|
|
35
|
+
| `Calendar` | Inline calendar display |
|
|
36
|
+
| `Form` | Form container with validation. Built on react-hook-form + zod |
|
|
37
|
+
|
|
38
|
+
### Data Display
|
|
39
|
+
|
|
40
|
+
| Component | Use for |
|
|
41
|
+
|-----------|---------|
|
|
42
|
+
| `Tag` | Status labels and categories |
|
|
43
|
+
| `TagList` | Multiple tags with overflow handling |
|
|
44
|
+
| `Badge` | Notification count or dot indicator |
|
|
45
|
+
| `StatusBadge` | Status indicator with semantic colors |
|
|
46
|
+
| `Text` | Typography with variants (heading, body, caption) |
|
|
47
|
+
| `Card` | Content container with header/body/footer sections |
|
|
48
|
+
| `Collapse` | Expandable/collapsible content sections |
|
|
49
|
+
| `Steps` | Multi-step process indicator |
|
|
50
|
+
| `Progress` | Progress bar with percentage |
|
|
51
|
+
| `Tree` | Hierarchical data display with expand/collapse |
|
|
52
|
+
| `TreeSelect` | Tree-based selection dropdown |
|
|
53
|
+
| `JsonViewer` | JSON data viewer with syntax highlighting |
|
|
54
|
+
| `CodeEditor` | Code editing with syntax highlighting (CodeMirror) |
|
|
55
|
+
| `Markdown` | Markdown content renderer |
|
|
56
|
+
| `Divider` | Visual separator line |
|
|
57
|
+
|
|
58
|
+
### Feedback
|
|
59
|
+
|
|
60
|
+
| Component | Use for |
|
|
61
|
+
|-----------|---------|
|
|
62
|
+
| `Dialog` | Modal dialogs for confirmations, forms, and complex interactions |
|
|
63
|
+
| `AlertDialog` | Simple confirm/cancel dialogs |
|
|
64
|
+
| `Drawer` | Side panel overlay. Placement: right (default), left, top, bottom |
|
|
65
|
+
| `Toast` | Temporary notification messages |
|
|
66
|
+
| `Alert` | Inline alert messages |
|
|
67
|
+
| `Spin` | Loading spinner overlay |
|
|
68
|
+
| `Loader` | Inline loading indicator |
|
|
69
|
+
| `PopoverConfirm` | Confirmation popover attached to a trigger |
|
|
70
|
+
|
|
71
|
+
### Navigation
|
|
72
|
+
|
|
73
|
+
| Component | Use for |
|
|
74
|
+
|-----------|---------|
|
|
75
|
+
| `Tabs` | Tab-based content switching. Variants: line, card, outline |
|
|
76
|
+
| `Breadcrumb` | Navigation hierarchy |
|
|
77
|
+
| `Pagination` | Page navigation with size selector |
|
|
78
|
+
| `Link` | Styled anchor element |
|
|
79
|
+
|
|
80
|
+
### Overlay
|
|
81
|
+
|
|
82
|
+
| Component | Use for |
|
|
83
|
+
|-----------|---------|
|
|
84
|
+
| `Tooltip` | Hover information. 12 placement options |
|
|
85
|
+
| `Popover` | Click-triggered content overlay |
|
|
86
|
+
| `Dropdown` | Action menu triggered by button |
|
|
87
|
+
| `ContextMenu` | Right-click context menu |
|
|
88
|
+
|
|
89
|
+
### Layout
|
|
90
|
+
|
|
91
|
+
| Component | Use for |
|
|
92
|
+
|-----------|---------|
|
|
93
|
+
| `ResizablePanelGroup` | Resizable split panes |
|
|
94
|
+
| `ResizablePanel` | Individual panel within a group |
|
|
95
|
+
| `ResizableHandle` | Drag handle between panels |
|
|
96
|
+
|
|
97
|
+
### Business Components
|
|
98
|
+
|
|
99
|
+
These are higher-level components for specific use cases:
|
|
100
|
+
|
|
101
|
+
| Component | Use for |
|
|
102
|
+
|-----------|---------|
|
|
103
|
+
| `Field` | Labeled value display (label: value pairs) |
|
|
104
|
+
| `FieldOperations` | Field with action buttons |
|
|
105
|
+
| `State` | Resource state display with semantic colors |
|
|
106
|
+
| `Info` | Info icon with tooltip explanation |
|
|
107
|
+
| `InfoPopover` | Info icon with rich popover content |
|
|
108
|
+
| `SmartTip` | Adaptive content display (truncate with expand) |
|
|
109
|
+
| `Copy` | Copy-to-clipboard button |
|
|
110
|
+
| `NoData` | Empty state placeholder |
|
|
111
|
+
| `HeaderPage` | Page-level header with title and actions |
|
|
112
|
+
| `HeaderDetail` | Detail page header with breadcrumb |
|
|
113
|
+
| `HelperDoc` | Help documentation link |
|
|
114
|
+
| `DateValue` | Formatted date display |
|
|
115
|
+
| `ConfigProvider` | Global configuration provider |
|
|
116
|
+
| `LocaleContainer` | i18n locale provider |
|
|
117
|
+
| `AppBase` | Application root wrapper |
|
|
118
|
+
|
|
119
|
+
## Common patterns
|
|
120
|
+
|
|
121
|
+
### Form with validation
|
|
122
|
+
|
|
123
|
+
```tsx
|
|
124
|
+
import { Form, FormField, FormItem, FormLabel, FormControl, FormMessage, Input, Button } from "@zscloud/design"
|
|
125
|
+
import { useForm } from "react-hook-form"
|
|
126
|
+
import { zodResolver } from "@hookform/resolvers/zod"
|
|
127
|
+
import { z } from "zod"
|
|
128
|
+
|
|
129
|
+
const schema = z.object({
|
|
130
|
+
name: z.string().min(1, "Name is required"),
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
function MyForm() {
|
|
134
|
+
const form = useForm({ resolver: zodResolver(schema) })
|
|
135
|
+
|
|
136
|
+
return (
|
|
137
|
+
<Form {...form}>
|
|
138
|
+
<form onSubmit={form.handleSubmit(onSubmit)}>
|
|
139
|
+
<FormField
|
|
140
|
+
control={form.control}
|
|
141
|
+
name="name"
|
|
142
|
+
render={({ field }) => (
|
|
143
|
+
<FormItem>
|
|
144
|
+
<FormLabel required>Name</FormLabel>
|
|
145
|
+
<FormControl>
|
|
146
|
+
<Input {...field} />
|
|
147
|
+
</FormControl>
|
|
148
|
+
<FormMessage />
|
|
149
|
+
</FormItem>
|
|
150
|
+
)}
|
|
151
|
+
/>
|
|
152
|
+
<Button type="submit">Submit</Button>
|
|
153
|
+
</form>
|
|
154
|
+
</Form>
|
|
155
|
+
)
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Dialog with form
|
|
160
|
+
|
|
161
|
+
```tsx
|
|
162
|
+
import { Dialog, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogBody, Button } from "@zscloud/design"
|
|
163
|
+
|
|
164
|
+
function MyDialog({ open, onOpenChange }) {
|
|
165
|
+
return (
|
|
166
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
167
|
+
<DialogContent>
|
|
168
|
+
<DialogHeader>
|
|
169
|
+
<DialogTitle>Edit Resource</DialogTitle>
|
|
170
|
+
</DialogHeader>
|
|
171
|
+
<DialogBody>
|
|
172
|
+
{/* Form content here */}
|
|
173
|
+
</DialogBody>
|
|
174
|
+
<DialogFooter>
|
|
175
|
+
<Button variant="subtle" onClick={() => onOpenChange(false)}>Cancel</Button>
|
|
176
|
+
<Button>Confirm</Button>
|
|
177
|
+
</DialogFooter>
|
|
178
|
+
</DialogContent>
|
|
179
|
+
</Dialog>
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Select with options
|
|
185
|
+
|
|
186
|
+
```tsx
|
|
187
|
+
import { Select } from "@zscloud/design"
|
|
188
|
+
|
|
189
|
+
const options = [
|
|
190
|
+
{ label: "Option A", value: "a" },
|
|
191
|
+
{ label: "Option B", value: "b", description: "With description" },
|
|
192
|
+
]
|
|
193
|
+
|
|
194
|
+
<Select options={options} value={value} onValueChange={setValue} placeholder="Select..." />
|
|
195
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Icon System
|
|
2
|
+
|
|
3
|
+
The icon library provides 644 system icons and 38 app icons, all SVG-based.
|
|
4
|
+
|
|
5
|
+
## Import
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { Icon, AppIcon } from "@zscloud/design"
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## System Icons (`Icon`)
|
|
12
|
+
|
|
13
|
+
Use for inline UI icons (buttons, menus, status indicators).
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
<Icon type="search" />
|
|
17
|
+
<Icon type="arrow-down" />
|
|
18
|
+
<Icon type="checkmark-circle-fill" color="positive" />
|
|
19
|
+
<Icon type="close" color="danger" />
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Props
|
|
23
|
+
|
|
24
|
+
| Prop | Type | Default | Description |
|
|
25
|
+
|------|------|---------|-------------|
|
|
26
|
+
| `type` | `IconName` | required | Kebab-case icon name |
|
|
27
|
+
| `color` | `"neutral" \| "danger" \| "positive" \| "info" \| "alert" \| "pending"` | - | Semantic color |
|
|
28
|
+
| `colorNumber` | `number` | - | Color shade (50-950) |
|
|
29
|
+
|
|
30
|
+
Default size: 16x16px.
|
|
31
|
+
|
|
32
|
+
### Commonly used icons
|
|
33
|
+
|
|
34
|
+
**Navigation**: `arrow-left`, `arrow-right`, `arrow-up`, `arrow-down`, `chevron-left`, `chevron-right`, `chevron-down`, `chevron-up`
|
|
35
|
+
|
|
36
|
+
**Actions**: `add`, `close`, `delete`, `edit`, `search`, `refresh`, `download`, `upload`, `copy`, `more`, `settings`
|
|
37
|
+
|
|
38
|
+
**Status**: `checkmark-circle-fill`, `close-circle-fill`, `warning-fill`, `info-circle-fill`, `loading`
|
|
39
|
+
|
|
40
|
+
**Content**: `file`, `folder`, `image`, `link`, `calendar`, `clock`, `user`, `lock`, `eye`, `eye-off`
|
|
41
|
+
|
|
42
|
+
## App Icons (`AppIcon`)
|
|
43
|
+
|
|
44
|
+
Use for application/module navigation icons. Larger, more detailed than system icons.
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
<AppIcon type="settings" />
|
|
48
|
+
<AppIcon type="hardware" />
|
|
49
|
+
<AppIcon type="cloud-monitoring" />
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Default size: 40x40px.
|
|
53
|
+
|
|
54
|
+
### Available app icons
|
|
55
|
+
|
|
56
|
+
`backup`, `settings`, `hardware`, `cloud-monitoring`, `ai-store`, `network-resource`, `network-service`, `vcenter`, `dashboard`, `virtual-orchestration`, `storage`, `tenant-management`, `zwatch-alarm`, `zwatch-endpoint`, `zwatch-monitor-group`, `zwatch-monitor-template`, and more.
|
|
57
|
+
|
|
58
|
+
## Rules
|
|
59
|
+
|
|
60
|
+
- Always use the `Icon` component instead of raw SVG or icon fonts.
|
|
61
|
+
- Use semantic `color` prop for status-related icons. Do not apply Tailwind color classes directly to icons.
|
|
62
|
+
- Do not resize icons with `width`/`height` attributes. Use the Tailwind `size-*` class on a wrapper if needed.
|
|
@@ -19032,7 +19032,7 @@ var SelectEmptyPlaceholder = (e) => {
|
|
|
19032
19032
|
});
|
|
19033
19033
|
InputNumber.displayName = "InputNumber";
|
|
19034
19034
|
var Pagination = (e) => {
|
|
19035
|
-
let { pagination: t, setPagination: n, count: i } = e, o = useIntl(), l = Math.ceil(i / t.pageSize), [u, d] = useState(""), f =
|
|
19035
|
+
let { pagination: t, setPagination: n, count: i } = e, o = useIntl(), l = Math.ceil(i / t.pageSize), [u, d] = useState(""), f = useRef(null);
|
|
19036
19036
|
return /* @__PURE__ */ jsxs("div", {
|
|
19037
19037
|
className: "flex items-center gap-6 h-8",
|
|
19038
19038
|
children: [/* @__PURE__ */ jsx(PaginationRoot, {
|
|
@@ -19092,7 +19092,7 @@ var Pagination = (e) => {
|
|
|
19092
19092
|
})
|
|
19093
19093
|
}), /* @__PURE__ */ jsxs("div", {
|
|
19094
19094
|
className: "items-center text-sm flex gap-4 h-8",
|
|
19095
|
-
|
|
19095
|
+
ref: f,
|
|
19096
19096
|
children: [/* @__PURE__ */ jsxs(SelectRoot, {
|
|
19097
19097
|
value: t.pageSize + "",
|
|
19098
19098
|
onValueChange: (e) => {
|
|
@@ -19105,7 +19105,7 @@ var Pagination = (e) => {
|
|
|
19105
19105
|
className: "w-auto text-nowrap h-8",
|
|
19106
19106
|
children: /* @__PURE__ */ jsx(SelectValue, {})
|
|
19107
19107
|
}), /* @__PURE__ */ jsx(SelectContent, {
|
|
19108
|
-
selectPortalProps: { container:
|
|
19108
|
+
selectPortalProps: { container: f.current },
|
|
19109
19109
|
children: /* @__PURE__ */ jsxs(SelectGroup, { children: [
|
|
19110
19110
|
/* @__PURE__ */ jsx(SelectItem, {
|
|
19111
19111
|
value: "10",
|
|
@@ -23761,34 +23761,42 @@ var cardVariants = cva("rounded-xs w-full box-border h-min bg-neutral-0", {
|
|
|
23761
23761
|
defaultVariants: { variant: "default" }
|
|
23762
23762
|
}), CardContext = createContext({
|
|
23763
23763
|
isCollapsed: !1,
|
|
23764
|
-
toggleCollapse: () => {}
|
|
23764
|
+
toggleCollapse: () => {},
|
|
23765
|
+
variant: "default"
|
|
23765
23766
|
});
|
|
23766
23767
|
function useCardContext() {
|
|
23767
23768
|
return useContext(CardContext);
|
|
23768
23769
|
}
|
|
23769
|
-
function CardProvider({ children: e }) {
|
|
23770
|
-
let [
|
|
23770
|
+
function CardProvider({ children: e, variant: t }) {
|
|
23771
|
+
let [n, i] = useState(!1);
|
|
23771
23772
|
return /* @__PURE__ */ jsx(CardContext.Provider, {
|
|
23772
23773
|
value: {
|
|
23773
|
-
isCollapsed:
|
|
23774
|
+
isCollapsed: n,
|
|
23774
23775
|
toggleCollapse: () => {
|
|
23775
|
-
|
|
23776
|
-
}
|
|
23776
|
+
i(!n);
|
|
23777
|
+
},
|
|
23778
|
+
variant: t
|
|
23777
23779
|
},
|
|
23778
23780
|
children: e
|
|
23779
23781
|
});
|
|
23780
23782
|
}
|
|
23781
|
-
var Card = React$1.forwardRef(({ className: e, variant: t, ...n }, i) => /* @__PURE__ */ jsx(CardProvider, {
|
|
23782
|
-
|
|
23783
|
-
|
|
23784
|
-
|
|
23785
|
-
})
|
|
23786
|
-
|
|
23787
|
-
|
|
23788
|
-
ref: n,
|
|
23789
|
-
className: cn("flex bg-transparent text-neutral-700 w-full items-center px-6 py-3 box-border justify-between border-b border-solid border-neutral-300", e),
|
|
23790
|
-
...t
|
|
23783
|
+
var Card = React$1.forwardRef(({ className: e, variant: t, ...n }, i) => /* @__PURE__ */ jsx(CardProvider, {
|
|
23784
|
+
variant: t,
|
|
23785
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
23786
|
+
ref: i,
|
|
23787
|
+
className: cn(cardVariants({ variant: t }), e),
|
|
23788
|
+
...n
|
|
23789
|
+
})
|
|
23791
23790
|
}));
|
|
23791
|
+
Card.displayName = "Card";
|
|
23792
|
+
var CardHeader = React$1.forwardRef(({ className: e, ...t }, n) => {
|
|
23793
|
+
let { variant: i } = useCardContext();
|
|
23794
|
+
return /* @__PURE__ */ jsx("div", {
|
|
23795
|
+
ref: n,
|
|
23796
|
+
className: cn("flex text-neutral-700 w-full items-center box-border justify-between", i === "default" || i === "detail" || !i ? "bg-neutral-100 h-9.5 px-5" : "bg-transparent px-6 py-3", e),
|
|
23797
|
+
...t
|
|
23798
|
+
});
|
|
23799
|
+
});
|
|
23792
23800
|
CardHeader.displayName = "CardHeader";
|
|
23793
23801
|
var CardTitle = React$1.forwardRef(({ className: e, ...t }, n) => /* @__PURE__ */ jsx("div", {
|
|
23794
23802
|
ref: n,
|
|
@@ -32122,7 +32130,7 @@ var CollapseContent = React$1.forwardRef(({ className: t, forceRender: n = !1, .
|
|
|
32122
32130
|
});
|
|
32123
32131
|
});
|
|
32124
32132
|
CollapseContent.displayName = "CollapseContent";
|
|
32125
|
-
var alertVariants = cva("flex items-
|
|
32133
|
+
var alertVariants = cva("flex items-start relative flex-row px-4 py-2.5 rounded-xs", {
|
|
32126
32134
|
variants: {
|
|
32127
32135
|
variant: {
|
|
32128
32136
|
danger: "bg-danger-50",
|
|
@@ -32152,7 +32160,7 @@ var alertVariants = cva("flex items-center relative flex-row px-4 py-2.5 rounded
|
|
|
32152
32160
|
m(!1), u == null || u();
|
|
32153
32161
|
}, [u]);
|
|
32154
32162
|
if (!p) return null;
|
|
32155
|
-
let g = cn("mr-2 min-h-4 min-w-4 shrink-0", i === "weak"
|
|
32163
|
+
let g = cn("mr-2 min-h-4 min-w-4 shrink-0", i === "weak" ? "mr-1.5" : "mt-0.5");
|
|
32156
32164
|
return /* @__PURE__ */ jsxs("div", {
|
|
32157
32165
|
ref: f,
|
|
32158
32166
|
className: alertVariants({
|
|
@@ -32162,7 +32170,7 @@ var alertVariants = cva("flex items-center relative flex-row px-4 py-2.5 rounded
|
|
|
32162
32170
|
}),
|
|
32163
32171
|
...d,
|
|
32164
32172
|
children: [/* @__PURE__ */ jsxs("div", {
|
|
32165
|
-
className: cn("text-neutral-700 text-sm flex flex-row flex-1", i === "weak" && "text-xs text-neutral-500"),
|
|
32173
|
+
className: cn("text-neutral-700 text-sm flex flex-row items-start flex-1", i === "weak" && "text-xs text-neutral-500"),
|
|
32166
32174
|
children: [
|
|
32167
32175
|
n === "danger" && /* @__PURE__ */ jsx(alert_triangle_fillreact$1, { className: cn(g, "text-danger-500") }),
|
|
32168
32176
|
n === "warning" && /* @__PURE__ */ jsx(alert_triangle_fillreact$1, { className: cn(g, "text-alert-500") }),
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zscloud/design",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "A modern React component library built with Radix UI and Tailwind CSS V4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"files": [
|
|
8
|
-
"dist"
|
|
8
|
+
"dist",
|
|
9
|
+
"dist/guidelines"
|
|
9
10
|
],
|
|
10
11
|
"typings": "./dist/index.d.ts",
|
|
11
12
|
"module": "./dist/zscloud-design.es.js",
|