@rinzai/zen 0.14.0 → 0.16.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/CHANGELOG.md +153 -0
- package/README.md +16 -1
- package/dist/animated-number/index.js +14 -14
- package/dist/breadcrumbs/index.js +14 -14
- package/dist/calendar/index.js +29 -29
- package/dist/calendar-heatmap/index.js +12 -12
- package/dist/canvas/index.js +19 -19
- package/dist/carousel/index.d.ts.map +1 -1
- package/dist/carousel/index.js +29 -29
- package/dist/chart/index.js +7 -7
- package/dist/code-block/index.js +5 -5
- package/dist/collapse/index.js +6 -6
- package/dist/color-picker/index.js +10 -10
- package/dist/combobox/index.js +61 -61
- package/dist/command-palette/index.js +5 -5
- package/dist/confirm-dialog/index.js +4 -4
- package/dist/date-picker/index.js +15 -15
- package/dist/dialog/index.d.ts.map +1 -1
- package/dist/dialog/index.js +45 -65
- package/dist/docs/README.md +205 -0
- package/dist/docs/accordion.md +122 -0
- package/dist/docs/alert.md +69 -0
- package/dist/docs/animated-number.md +105 -0
- package/dist/docs/avatar.md +91 -0
- package/dist/docs/backdrop.md +94 -0
- package/dist/docs/badge.md +37 -0
- package/dist/docs/breadcrumbs.md +44 -0
- package/dist/docs/button-group.md +76 -0
- package/dist/docs/button.md +131 -0
- package/dist/docs/calendar-heatmap.md +73 -0
- package/dist/docs/calendar.md +101 -0
- package/dist/docs/canvas.md +584 -0
- package/dist/docs/card.md +180 -0
- package/dist/docs/carousel.md +162 -0
- package/dist/docs/chart.md +325 -0
- package/dist/docs/checkbox.md +88 -0
- package/dist/docs/chip.md +52 -0
- package/dist/docs/code-block.md +116 -0
- package/dist/docs/code-input.md +52 -0
- package/dist/docs/collapse.md +46 -0
- package/dist/docs/color-picker.md +145 -0
- package/dist/docs/combobox.md +208 -0
- package/dist/docs/command-palette.md +65 -0
- package/dist/docs/date-picker.md +167 -0
- package/dist/docs/dialog.md +271 -0
- package/dist/docs/disclosure.md +88 -0
- package/dist/docs/editable-cell.md +90 -0
- package/dist/docs/empty-state.md +46 -0
- package/dist/docs/field.md +82 -0
- package/dist/docs/file-drop.md +44 -0
- package/dist/docs/filter-bar.md +66 -0
- package/dist/docs/header.md +51 -0
- package/dist/docs/hover-card.md +110 -0
- package/dist/docs/icon-picker.md +65 -0
- package/dist/docs/input-group.md +89 -0
- package/dist/docs/input.md +33 -0
- package/dist/docs/inset.md +57 -0
- package/dist/docs/kbd.md +34 -0
- package/dist/docs/keypad.md +51 -0
- package/dist/docs/load-more.md +66 -0
- package/dist/docs/locale-provider.md +99 -0
- package/dist/docs/marquee.md +86 -0
- package/dist/docs/menu.md +456 -0
- package/dist/docs/meter.md +102 -0
- package/dist/docs/money-input.md +169 -0
- package/dist/docs/month-picker.md +80 -0
- package/dist/docs/navigation-menu.md +157 -0
- package/dist/docs/number-field.md +129 -0
- package/dist/docs/page-header.md +44 -0
- package/dist/docs/page-transition.md +93 -0
- package/dist/docs/pagination.md +42 -0
- package/dist/docs/pills.md +85 -0
- package/dist/docs/player-bar.md +158 -0
- package/dist/docs/popover.md +200 -0
- package/dist/docs/progress-ring.md +70 -0
- package/dist/docs/radio-cards.md +82 -0
- package/dist/docs/radio-group.md +86 -0
- package/dist/docs/range-slider.md +64 -0
- package/dist/docs/repeat-picker.md +44 -0
- package/dist/docs/resizable.md +107 -0
- package/dist/docs/seek-bar.md +97 -0
- package/dist/docs/segmented.md +89 -0
- package/dist/docs/select.md +148 -0
- package/dist/docs/separator.md +76 -0
- package/dist/docs/side-nav.md +75 -0
- package/dist/docs/skeleton.md +89 -0
- package/dist/docs/slider.md +87 -0
- package/dist/docs/sortable.md +244 -0
- package/dist/docs/sparkline.md +56 -0
- package/dist/docs/spinner.md +69 -0
- package/dist/docs/status-pill.md +81 -0
- package/dist/docs/stepper.md +59 -0
- package/dist/docs/tab-bar.md +78 -0
- package/dist/docs/table-of-contents.md +43 -0
- package/dist/docs/table.md +714 -0
- package/dist/docs/tabs.md +140 -0
- package/dist/docs/tag-input.md +45 -0
- package/dist/docs/textarea.md +25 -0
- package/dist/docs/theme-toggle.md +85 -0
- package/dist/docs/theming.md +193 -0
- package/dist/docs/time-field.md +71 -0
- package/dist/docs/timeline.md +50 -0
- package/dist/docs/toast.md +53 -0
- package/dist/docs/toggle-group.md +83 -0
- package/dist/docs/toggle.md +38 -0
- package/dist/docs/toolbar.md +97 -0
- package/dist/docs/tooltip.md +102 -0
- package/dist/docs/tree.md +295 -0
- package/dist/docs/trend.md +50 -0
- package/dist/filter-bar/index.js +6 -6
- package/dist/header/index.js +6 -6
- package/dist/icon-picker/index.js +5 -5
- package/dist/icons/arrow-path.d.ts +3 -0
- package/dist/icons/arrow-path.d.ts.map +1 -0
- package/dist/icons/arrows-right-left.d.ts +3 -0
- package/dist/icons/arrows-right-left.d.ts.map +1 -0
- package/dist/icons/backward.d.ts +3 -0
- package/dist/icons/backward.d.ts.map +1 -0
- package/dist/icons/forward.d.ts +3 -0
- package/dist/icons/forward.d.ts.map +1 -0
- package/dist/icons/heart-solid.d.ts +3 -0
- package/dist/icons/heart-solid.d.ts.map +1 -0
- package/dist/icons/heart.d.ts +3 -0
- package/dist/icons/heart.d.ts.map +1 -0
- package/dist/icons/musical-note.d.ts +3 -0
- package/dist/icons/musical-note.d.ts.map +1 -0
- package/dist/icons/pause.d.ts +3 -0
- package/dist/icons/pause.d.ts.map +1 -0
- package/dist/icons/play.d.ts +3 -0
- package/dist/icons/play.d.ts.map +1 -0
- package/dist/icons/queue-list.d.ts +3 -0
- package/dist/icons/queue-list.d.ts.map +1 -0
- package/dist/icons/speaker-wave.d.ts +3 -0
- package/dist/icons/speaker-wave.d.ts.map +1 -0
- package/dist/icons/speaker-x-mark.d.ts +3 -0
- package/dist/icons/speaker-x-mark.d.ts.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/keypad/index.js +5 -5
- package/dist/lib.js +86 -80
- package/dist/load-more/index.js +5 -5
- package/dist/marquee/index.d.ts +15 -0
- package/dist/marquee/index.d.ts.map +1 -0
- package/dist/marquee/index.js +90 -0
- package/dist/menu/index.d.ts +7 -1
- package/dist/menu/index.d.ts.map +1 -1
- package/dist/menu/index.js +222 -178
- package/dist/money-input/index.js +54 -54
- package/dist/month-picker/index.js +11 -11
- package/dist/number-field/index.js +5 -5
- package/dist/pagination/index.js +12 -12
- package/dist/player-bar/index.d.ts +11 -0
- package/dist/player-bar/index.d.ts.map +1 -0
- package/dist/player-bar/index.js +17 -0
- package/dist/radio-cards/index.d.ts +2 -1
- package/dist/radio-cards/index.d.ts.map +1 -1
- package/dist/radio-cards/index.js +3 -3
- package/dist/range-slider/index.js +11 -11
- package/dist/repeat-picker/index.js +5 -5
- package/dist/resizable/index.js +22 -22
- package/dist/seek-bar/index.d.ts +26 -0
- package/dist/seek-bar/index.d.ts.map +1 -0
- package/dist/seek-bar/index.js +121 -0
- package/dist/segmented/index.d.ts +3 -2
- package/dist/segmented/index.d.ts.map +1 -1
- package/dist/segmented/index.js +19 -18
- package/dist/select/index.js +21 -21
- package/dist/slider/index.d.ts +2 -1
- package/dist/slider/index.d.ts.map +1 -1
- package/dist/sortable/index.d.ts +13 -1
- package/dist/sortable/index.d.ts.map +1 -1
- package/dist/sortable/index.js +74 -55
- package/dist/sortable/shared.js +14 -14
- package/dist/sortable/tree.js +17 -17
- package/dist/sparkline/index.js +4 -4
- package/dist/stepper/index.js +13 -13
- package/dist/tab-bar/index.d.ts +4 -2
- package/dist/tab-bar/index.d.ts.map +1 -1
- package/dist/tab-bar/index.js +24 -19
- package/dist/table/index.js +26 -26
- package/dist/table/selection.js +9 -9
- package/dist/table-of-contents/index.js +11 -11
- package/dist/tag-input/index.js +5 -5
- package/dist/textarea/index.d.ts +6 -2
- package/dist/textarea/index.d.ts.map +1 -1
- package/dist/textarea/index.js +2 -1
- package/dist/theme-toggle/index.js +4 -4
- package/dist/theme.css +143 -0
- package/dist/time-field/index.js +21 -21
- package/dist/toast/index.d.ts.map +1 -1
- package/dist/toast/index.js +41 -41
- package/dist/tooltip/index.d.ts +9 -3
- package/dist/tooltip/index.d.ts.map +1 -1
- package/dist/tooltip/index.js +19 -16
- package/dist/tree/index.js +34 -34
- package/dist/trend/index.js +5 -5
- package/dist/utils/duration.d.ts +3 -0
- package/dist/utils/duration.d.ts.map +1 -0
- package/dist/utils/duration.js +7 -0
- package/dist/utils/messages.d.ts +3 -0
- package/dist/utils/messages.d.ts.map +1 -1
- package/dist/utils/messages.js +4 -1
- package/dist/utils/slot.d.ts +5 -2
- package/dist/utils/slot.d.ts.map +1 -1
- package/dist/utils/slot.js +16 -11
- package/dist/utils/swipe.d.ts +7 -0
- package/dist/utils/swipe.d.ts.map +1 -1
- package/dist/utils/swipe.js +45 -25
- package/dist/utils/useAnchoredPopup.d.ts +2 -1
- package/dist/utils/useAnchoredPopup.d.ts.map +1 -1
- package/dist/utils/useAnchoredPopup.js +10 -5
- package/dist/utils/useHeightVar.d.ts +7 -0
- package/dist/utils/useHeightVar.d.ts.map +1 -0
- package/dist/utils/useHeightVar.js +17 -0
- package/dist/utils/useMediaPlayer.d.ts +38 -0
- package/dist/utils/useMediaPlayer.d.ts.map +1 -0
- package/dist/utils/useMediaPlayer.js +91 -0
- package/dist/utils/useMediaSession.d.ts +24 -0
- package/dist/utils/useMediaSession.d.ts.map +1 -0
- package/dist/utils/useMediaSession.js +76 -0
- package/dist/utils/useVirtualList.d.ts +6 -1
- package/dist/utils/useVirtualList.d.ts.map +1 -1
- package/dist/utils/useVirtualList.js +12 -12
- package/dist/zen.css +1 -1
- package/package.json +4 -2
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Alert
|
|
2
|
+
|
|
3
|
+
A notice, warning or failure beside what it is about: a tinted box, or a plain line in a form or card.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Alert } from '@rinzai/zen';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Examples
|
|
10
|
+
|
|
11
|
+
### Default
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { Alert } from '@rinzai/zen';
|
|
15
|
+
|
|
16
|
+
/** A tinted box per tone; a `title` leads, and the text under it is quieter. */
|
|
17
|
+
export default function Default() {
|
|
18
|
+
return (
|
|
19
|
+
<div className="flex w-full max-w-md flex-col gap-3">
|
|
20
|
+
<Alert>Changes to this item apply from next month.</Alert>
|
|
21
|
+
<Alert tone="positive">Your data is up to date on every device.</Alert>
|
|
22
|
+
<Alert tone="negative" title="Your data will be deleted for good.">
|
|
23
|
+
Without your password or recovery key, nobody can open it. Starting over gives you a new, empty vault.
|
|
24
|
+
</Alert>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Plain
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { Alert, Card, CardHeader, CardTitle, Spinner } from '@rinzai/zen';
|
|
34
|
+
|
|
35
|
+
/** `plain`: just the icon and text, for a line inside a card or a form. Any icon, or none. */
|
|
36
|
+
export default function Plain() {
|
|
37
|
+
return (
|
|
38
|
+
<Card className="flex w-full max-w-sm flex-col gap-3">
|
|
39
|
+
<CardHeader className="mb-0">
|
|
40
|
+
<CardTitle>Currency</CardTitle>
|
|
41
|
+
</CardHeader>
|
|
42
|
+
<Alert plain icon={<Spinner className="text-primary" />}>
|
|
43
|
+
Getting today’s rate…
|
|
44
|
+
</Alert>
|
|
45
|
+
<Alert plain tone="negative">
|
|
46
|
+
Couldn’t get today’s rate, so converting isn’t possible right now.
|
|
47
|
+
</Alert>
|
|
48
|
+
</Card>
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## API reference
|
|
54
|
+
|
|
55
|
+
### Alert
|
|
56
|
+
|
|
57
|
+
A message beside the content it's about: a notice, a warning, a failure.
|
|
58
|
+
A tinted box by default, or with `plain`, just the icon and text in the
|
|
59
|
+
tone's colour. With a `title`, the title takes the tone and the text under
|
|
60
|
+
it is quieter. Bad news is announced at once (role="alert"), anything else
|
|
61
|
+
politely (role="status"); pass `role` to change that, e.g. "note" for a
|
|
62
|
+
message that doesn't change.
|
|
63
|
+
|
|
64
|
+
| Prop | Type | Default | Description |
|
|
65
|
+
| --- | --- | --- | --- |
|
|
66
|
+
| `icon` | `ReactNode` | | Replaces the tone's icon (info, check, warning); `null` for none. |
|
|
67
|
+
| `plain` | `boolean` | `false` | Just the icon and text, no box: for a line inside a form or a card. |
|
|
68
|
+
| `title` | `ReactNode` | | A first line in the tone's colour; the text under it is quieter. |
|
|
69
|
+
| `tone` | `"default" \| "negative" \| "positive"` | `default` | `positive` in the accent colour, `negative` in red for bad news. |
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Animated Number
|
|
2
|
+
|
|
3
|
+
Numbers and amounts that count smoothly to each new value.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { AnimatedNumber, AnimatedMoney } from '@rinzai/zen';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Examples
|
|
10
|
+
|
|
11
|
+
### Default
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { AnimatedMoney, AnimatedNumber, Button, Stat, StatRow } from '@rinzai/zen';
|
|
15
|
+
import { useState } from 'react';
|
|
16
|
+
|
|
17
|
+
/** Numbers count to each new value (and up from zero when first shown). */
|
|
18
|
+
export default function Default() {
|
|
19
|
+
const [net, setNet] = useState(625400);
|
|
20
|
+
const [entries, setEntries] = useState(42);
|
|
21
|
+
return (
|
|
22
|
+
<div className="flex w-full max-w-lg flex-col gap-4">
|
|
23
|
+
<StatRow className="sm:grid-cols-2!">
|
|
24
|
+
<Stat
|
|
25
|
+
label="Net this month"
|
|
26
|
+
tone="positive"
|
|
27
|
+
value={<AnimatedMoney value={net} currency="USD" locale="en-US" />}
|
|
28
|
+
/>
|
|
29
|
+
<Stat label="Entries" value={<AnimatedNumber value={entries} />} />
|
|
30
|
+
</StatRow>
|
|
31
|
+
<Button
|
|
32
|
+
variant="outline"
|
|
33
|
+
className="self-start"
|
|
34
|
+
onClick={() => {
|
|
35
|
+
setNet((n) => n + Math.round((Math.random() - 0.4) * 300000));
|
|
36
|
+
setEntries((e) => e + 1);
|
|
37
|
+
}}
|
|
38
|
+
>
|
|
39
|
+
Add an entry
|
|
40
|
+
</Button>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Roll
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { AnimatedMoney, Button, Stat } from '@rinzai/zen';
|
|
50
|
+
import { useState } from 'react';
|
|
51
|
+
|
|
52
|
+
/** With `roll`, each digit rolls to its new one, like an odometer. */
|
|
53
|
+
export default function Roll() {
|
|
54
|
+
const [total, setTotal] = useState(184250);
|
|
55
|
+
return (
|
|
56
|
+
<div className="flex w-full max-w-xs flex-col gap-4">
|
|
57
|
+
<Stat
|
|
58
|
+
label="Spent this month"
|
|
59
|
+
value={<AnimatedMoney roll value={total} currency="USD" locale="en-US" showDecimals />}
|
|
60
|
+
/>
|
|
61
|
+
<Button
|
|
62
|
+
variant="outline"
|
|
63
|
+
className="self-start"
|
|
64
|
+
onClick={() => setTotal((t) => t + Math.round(Math.random() * 40000))}
|
|
65
|
+
>
|
|
66
|
+
Add a purchase
|
|
67
|
+
</Button>
|
|
68
|
+
</div>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## API reference
|
|
74
|
+
|
|
75
|
+
### AnimatedNumber
|
|
76
|
+
|
|
77
|
+
A number that counts smoothly to each new value (and up from zero when it
|
|
78
|
+
first appears). Rounds to whole numbers while moving, so money in minor
|
|
79
|
+
units never shows fractions. With `roll`, each digit rolls to its new one
|
|
80
|
+
instead, like an odometer: easier to read when a total changes. Instant
|
|
81
|
+
with reduced motion.
|
|
82
|
+
|
|
83
|
+
| Prop | Type | Default | Description |
|
|
84
|
+
| --- | --- | --- | --- |
|
|
85
|
+
| `value` (required) | `number` | | |
|
|
86
|
+
| `className` | `string` | | |
|
|
87
|
+
| `duration` | `number` | `650` | Milliseconds per change. |
|
|
88
|
+
| `format` | `((value: number) => string)` | | Turns the (whole) number into text, e.g. with Intl.NumberFormat. |
|
|
89
|
+
| `fromZero` | `boolean` | `true` | Count up from zero when first shown (default), or start at the value. |
|
|
90
|
+
| `roll` | `boolean` | `false` | Roll each digit to its new one, like an odometer, instead of counting. |
|
|
91
|
+
|
|
92
|
+
### AnimatedMoney
|
|
93
|
+
|
|
94
|
+
An amount of money (minor units) that counts to each new value.
|
|
95
|
+
|
|
96
|
+
| Prop | Type | Default | Description |
|
|
97
|
+
| --- | --- | --- | --- |
|
|
98
|
+
| `currency` (required) | `string` | | |
|
|
99
|
+
| `value` (required) | `number` | | |
|
|
100
|
+
| `className` | `string` | | |
|
|
101
|
+
| `duration` | `number` | | Milliseconds per change. |
|
|
102
|
+
| `fromZero` | `boolean` | | Count up from zero when first shown (default), or start at the value. |
|
|
103
|
+
| `locale` | `string` | | Defaults to the LocaleProvider's. |
|
|
104
|
+
| `roll` | `boolean` | | Roll each digit to its new one, like an odometer, instead of counting. |
|
|
105
|
+
| `showDecimals` | `boolean` | `false` | |
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# Avatar
|
|
2
|
+
|
|
3
|
+
A round picture, or the name's initial on the glowing accent. With an in-browser crop for uploads.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Avatar } from '@rinzai/zen';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Examples
|
|
10
|
+
|
|
11
|
+
### Default
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { Avatar } from '@rinzai/zen';
|
|
15
|
+
|
|
16
|
+
const PHOTO =
|
|
17
|
+
'data:image/svg+xml;utf8,' +
|
|
18
|
+
encodeURIComponent(
|
|
19
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" fill="#2f2943"/><circle cx="32" cy="26" r="12" fill="#ab93ed"/><rect x="12" y="42" width="40" height="30" rx="15" fill="#ab93ed"/></svg>',
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
/** An image, or the name's initial on the accent. Size it with className. */
|
|
23
|
+
export default function Default() {
|
|
24
|
+
return (
|
|
25
|
+
<div className="flex items-center gap-4">
|
|
26
|
+
<Avatar name="rin@example.com" />
|
|
27
|
+
<Avatar name="Sam" className="size-12 text-lg" />
|
|
28
|
+
<Avatar name="Alex" src={PHOTO} alt="Alex" className="size-12" />
|
|
29
|
+
<div className="flex items-center gap-2 text-sm">
|
|
30
|
+
<Avatar name="Priya" className="size-7 text-xs" />
|
|
31
|
+
Priya
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Upload
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { Avatar, Button, cropImageToSquare, ImageCropError } from '@rinzai/zen';
|
|
42
|
+
import { useRef, useState } from 'react';
|
|
43
|
+
|
|
44
|
+
/** cropImageToSquare centres, crops and shrinks a chosen image in the browser to a small data URL. */
|
|
45
|
+
export default function Upload() {
|
|
46
|
+
const [src, setSrc] = useState<string | null>(null);
|
|
47
|
+
const [error, setError] = useState<string | null>(null);
|
|
48
|
+
const input = useRef<HTMLInputElement>(null);
|
|
49
|
+
return (
|
|
50
|
+
<div className="flex items-center gap-4">
|
|
51
|
+
<Avatar name="You" src={src} className="size-16 text-xl" />
|
|
52
|
+
<div className="flex flex-col gap-1">
|
|
53
|
+
<Button variant="outline" size="sm" onClick={() => input.current?.click()}>
|
|
54
|
+
Choose a picture
|
|
55
|
+
</Button>
|
|
56
|
+
{error && <span className="text-destructive text-xs">{error}</span>}
|
|
57
|
+
</div>
|
|
58
|
+
<input
|
|
59
|
+
ref={input}
|
|
60
|
+
type="file"
|
|
61
|
+
accept="image/*"
|
|
62
|
+
hidden
|
|
63
|
+
onChange={async (e) => {
|
|
64
|
+
const file = e.target.files?.[0];
|
|
65
|
+
if (!file) return;
|
|
66
|
+
try {
|
|
67
|
+
setSrc(await cropImageToSquare(file, 160));
|
|
68
|
+
setError(null);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
setError(err instanceof ImageCropError ? err.message : 'Something went wrong.');
|
|
71
|
+
}
|
|
72
|
+
}}
|
|
73
|
+
/>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## API reference
|
|
80
|
+
|
|
81
|
+
### Avatar
|
|
82
|
+
|
|
83
|
+
Round profile picture; without an image, the name's initial on the glowing
|
|
84
|
+
accent. Decorative (hidden from screen readers) unless you give it `alt`.
|
|
85
|
+
|
|
86
|
+
| Prop | Type | Default | Description |
|
|
87
|
+
| --- | --- | --- | --- |
|
|
88
|
+
| `name` (required) | `string` | | Name or email, for the initial. |
|
|
89
|
+
| `alt` | `string` | | Accessible name; leave out when the name is shown next to the avatar. |
|
|
90
|
+
| `className` | `string` | | Size and more, e.g. "size-12 text-lg" (default size-9). |
|
|
91
|
+
| `src` | `string \| null` | | Image URL (or data URL); without it, or if it fails to load, the initial is shown. |
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Backdrop
|
|
2
|
+
|
|
3
|
+
The page background: aurora glows over contours or dots, and a light that follows the pointer and catches card edges, or drifts round them by itself on pages with no pointer.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Backdrop } from '@rinzai/zen';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Examples
|
|
10
|
+
|
|
11
|
+
### Contours
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { Backdrop, Button, Card, CardHeader, CardTitle } from '@rinzai/zen';
|
|
15
|
+
|
|
16
|
+
import topo from './topo.svg';
|
|
17
|
+
|
|
18
|
+
export default function Contours() {
|
|
19
|
+
return (
|
|
20
|
+
// Backdrop is fixed to the viewport; the transform keeps it inside this frame for the demo.
|
|
21
|
+
<div className="relative h-96 w-full [transform:translateZ(0)] overflow-hidden rounded-xl">
|
|
22
|
+
<Backdrop topoSrc={topo} />
|
|
23
|
+
<div className="flex h-full items-center justify-center p-8">
|
|
24
|
+
<Card className="w-72">
|
|
25
|
+
<CardHeader>
|
|
26
|
+
<CardTitle>Move the pointer</CardTitle>
|
|
27
|
+
</CardHeader>
|
|
28
|
+
<p className="text-muted-foreground mt-0! mb-4 text-sm">
|
|
29
|
+
Card edges and the contour lines light up near it.
|
|
30
|
+
</p>
|
|
31
|
+
<Button variant="outline">Outline buttons too</Button>
|
|
32
|
+
</Card>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Dots
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
import { Backdrop, Stat } from '@rinzai/zen';
|
|
43
|
+
|
|
44
|
+
export default function Dots() {
|
|
45
|
+
return (
|
|
46
|
+
// Backdrop is fixed to the viewport; the transform keeps it inside this frame for the demo.
|
|
47
|
+
<div className="relative h-96 w-full [transform:translateZ(0)] overflow-hidden rounded-xl">
|
|
48
|
+
<Backdrop pattern="dots" />
|
|
49
|
+
<div className="flex h-full items-center justify-center p-8">
|
|
50
|
+
<Stat label="Net this month" value="$3,330" tone="positive" className="w-60" />
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Ambient
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { Card, CardTitle, Input, Stat } from '@rinzai/zen';
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* For a page that never gets a pointer, such as an OBS browser source: `data-zen-glow="ambient"`
|
|
64
|
+
* sends the edge light slowly round each border by itself. Put it on `<html>` for the whole page, or
|
|
65
|
+
* on any element for what's inside it. `data-zen-page="transparent"` on `<html>` (with the base
|
|
66
|
+
* stylesheet) paints nothing behind the content, so the stream shows through.
|
|
67
|
+
*/
|
|
68
|
+
export default function Ambient() {
|
|
69
|
+
return (
|
|
70
|
+
<div data-zen-glow="ambient" className="flex flex-wrap items-start gap-4 p-4">
|
|
71
|
+
<Stat label="Now playing" value="Lo-fi beats" className="w-56" />
|
|
72
|
+
<Card className="w-64">
|
|
73
|
+
<CardTitle className="mb-3">Chat</CardTitle>
|
|
74
|
+
<Input placeholder="Say something" aria-label="Message" />
|
|
75
|
+
</Card>
|
|
76
|
+
</div>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API reference
|
|
82
|
+
|
|
83
|
+
### Backdrop
|
|
84
|
+
|
|
85
|
+
Fixed page background: slow aurora glows over a faint pattern (topographic
|
|
86
|
+
contours or a dot grid), with a light that follows the pointer. It also
|
|
87
|
+
lights `.glow-edge` and `.glow-border` borders (cards, outline buttons,
|
|
88
|
+
dialogs, fields) near the pointer. Render once, behind everything.
|
|
89
|
+
|
|
90
|
+
| Prop | Type | Default | Description |
|
|
91
|
+
| --- | --- | --- | --- |
|
|
92
|
+
| `className` | `string` | | Classes for the backdrop layer (e.g. a view-transition-name). |
|
|
93
|
+
| `pattern` | `"contours" \| "dots"` | | Background pattern. Defaults to contours when `topoSrc` is given, dots otherwise. |
|
|
94
|
+
| `topoSrc` | `string` | | URL of a contour-line image (strokes on transparent), used as a mask. |
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Badge
|
|
2
|
+
|
|
3
|
+
A small label for status, counts and tags.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Badge } from '@rinzai/zen';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Examples
|
|
10
|
+
|
|
11
|
+
### Variants
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { Badge } from '@rinzai/zen';
|
|
15
|
+
|
|
16
|
+
export default function Variants() {
|
|
17
|
+
return (
|
|
18
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
19
|
+
<Badge>New</Badge>
|
|
20
|
+
<Badge variant="secondary">Draft</Badge>
|
|
21
|
+
<Badge tone="negative">Overdue</Badge>
|
|
22
|
+
<Badge variant="outline">Archived</Badge>
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## API reference
|
|
29
|
+
|
|
30
|
+
### Badge
|
|
31
|
+
|
|
32
|
+
A small label for status, counts and tags; `tone="negative"` for bad news.
|
|
33
|
+
|
|
34
|
+
| Prop | Type | Default | Description |
|
|
35
|
+
| --- | --- | --- | --- |
|
|
36
|
+
| `tone` | `"default" \| "negative" \| null` | | |
|
|
37
|
+
| `variant` | `"default" \| "outline" \| "secondary" \| null` | | |
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Breadcrumbs
|
|
2
|
+
|
|
3
|
+
Where a page sits: its parents as links, then the page itself. Works with any router.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Breadcrumbs, Breadcrumb } from '@rinzai/zen';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Examples
|
|
10
|
+
|
|
11
|
+
### Default
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { Breadcrumb, Breadcrumbs } from '@rinzai/zen';
|
|
15
|
+
|
|
16
|
+
/** Parents as links (with a router, Breadcrumb asChild around its Link), then the current page. */
|
|
17
|
+
export default function Default() {
|
|
18
|
+
return (
|
|
19
|
+
<Breadcrumbs>
|
|
20
|
+
<Breadcrumb href="#">Budgets</Breadcrumb>
|
|
21
|
+
<Breadcrumb href="#">2026</Breadcrumb>
|
|
22
|
+
<Breadcrumb current>September</Breadcrumb>
|
|
23
|
+
</Breadcrumbs>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## API reference
|
|
29
|
+
|
|
30
|
+
### Breadcrumbs
|
|
31
|
+
|
|
32
|
+
Where a page sits: its parents as links, then the page itself. Give the last crumb `current`.
|
|
33
|
+
|
|
34
|
+
No props of its own; it accepts the props of the element it renders.
|
|
35
|
+
|
|
36
|
+
### Breadcrumb
|
|
37
|
+
|
|
38
|
+
One crumb: a link, or with `current`, the page you're on (not a link). With
|
|
39
|
+
`asChild`, the styles go onto your own link element (a router's Link).
|
|
40
|
+
|
|
41
|
+
| Prop | Type | Default | Description |
|
|
42
|
+
| --- | --- | --- | --- |
|
|
43
|
+
| `asChild` | `boolean` | | Style the single child element (e.g. a router link) instead of rendering an <a>. |
|
|
44
|
+
| `current` | `boolean` | | The page you're on: the last crumb. |
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Button Group
|
|
2
|
+
|
|
3
|
+
Buttons joined into one, rounded only at the ends: a split button, or Previous, Today and Next.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { ButtonGroup } from '@rinzai/zen';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Examples
|
|
10
|
+
|
|
11
|
+
### Default
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { Button, ButtonGroup } from '@rinzai/zen';
|
|
15
|
+
// Icons from Heroicons (https://heroicons.com): ChevronLeft (chevron-left), ChevronRight (chevron-right).
|
|
16
|
+
|
|
17
|
+
/** Previous, today and next, joined: one control for moving through months. */
|
|
18
|
+
export default function Default() {
|
|
19
|
+
return (
|
|
20
|
+
<ButtonGroup aria-label="Month">
|
|
21
|
+
<Button variant="outline" size="icon" aria-label="Previous month">
|
|
22
|
+
<ChevronLeft className="size-4 rtl:-scale-x-100" />
|
|
23
|
+
</Button>
|
|
24
|
+
<Button variant="outline">This month</Button>
|
|
25
|
+
<Button variant="outline" size="icon" aria-label="Next month">
|
|
26
|
+
<ChevronRight className="size-4 rtl:-scale-x-100" />
|
|
27
|
+
</Button>
|
|
28
|
+
</ButtonGroup>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### Split
|
|
34
|
+
|
|
35
|
+
```tsx
|
|
36
|
+
import { Button, ButtonGroup, buttonVariants, Menu, MenuContent, MenuItem, MenuTrigger, useToast } from '@rinzai/zen';
|
|
37
|
+
// Icons from Heroicons (https://heroicons.com): ChevronDown (chevron-down).
|
|
38
|
+
|
|
39
|
+
/** A split button: the usual action, and a menu of the others. */
|
|
40
|
+
export default function Split() {
|
|
41
|
+
const toast = useToast();
|
|
42
|
+
return (
|
|
43
|
+
<ButtonGroup>
|
|
44
|
+
<Button variant="outline" onClick={() => toast('Exported as CSV', { tone: 'positive' })}>
|
|
45
|
+
Export CSV
|
|
46
|
+
</Button>
|
|
47
|
+
<Menu>
|
|
48
|
+
<MenuTrigger
|
|
49
|
+
aria-label="More export formats"
|
|
50
|
+
className={buttonVariants({ variant: 'outline', size: 'icon' })}
|
|
51
|
+
>
|
|
52
|
+
<ChevronDown className="size-4" />
|
|
53
|
+
</MenuTrigger>
|
|
54
|
+
<MenuContent align="end">
|
|
55
|
+
<MenuItem onSelect={() => toast('Exported as Excel', { tone: 'positive' })}>Excel</MenuItem>
|
|
56
|
+
<MenuItem onSelect={() => toast('Exported as PDF', { tone: 'positive' })}>PDF</MenuItem>
|
|
57
|
+
</MenuContent>
|
|
58
|
+
</Menu>
|
|
59
|
+
</ButtonGroup>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## API reference
|
|
65
|
+
|
|
66
|
+
### ButtonGroup
|
|
67
|
+
|
|
68
|
+
Buttons joined into one: shared edges, rounded only at the ends, e.g. a
|
|
69
|
+
split button (an action and a menu of others) or Previous / Today / Next.
|
|
70
|
+
Best with `outline` buttons, whose hairlines meet. Upright with
|
|
71
|
+
`orientation="vertical"`. Give it an aria-label when the buttons' names
|
|
72
|
+
don't say what they're for together.
|
|
73
|
+
|
|
74
|
+
| Prop | Type | Default | Description |
|
|
75
|
+
| --- | --- | --- | --- |
|
|
76
|
+
| `orientation` | `"horizontal" \| "vertical"` | `horizontal` | |
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Button
|
|
2
|
+
|
|
3
|
+
Actions, from the one glowing primary action to quiet ghost buttons.
|
|
4
|
+
|
|
5
|
+
```tsx
|
|
6
|
+
import { Button } from '@rinzai/zen';
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## Examples
|
|
10
|
+
|
|
11
|
+
### Variants
|
|
12
|
+
|
|
13
|
+
```tsx
|
|
14
|
+
import { Button } from '@rinzai/zen';
|
|
15
|
+
|
|
16
|
+
export default function Variants() {
|
|
17
|
+
return (
|
|
18
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
19
|
+
<Button>Save changes</Button>
|
|
20
|
+
<Button variant="secondary">Export</Button>
|
|
21
|
+
<Button variant="outline">Cancel</Button>
|
|
22
|
+
<Button variant="ghost">Skip</Button>
|
|
23
|
+
<Button tone="negative">Delete</Button>
|
|
24
|
+
<Button variant="link">Learn more</Button>
|
|
25
|
+
</div>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Sizes
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
import { Button } from '@rinzai/zen';
|
|
34
|
+
|
|
35
|
+
export default function Sizes() {
|
|
36
|
+
return (
|
|
37
|
+
<div className="flex flex-wrap items-center gap-3">
|
|
38
|
+
<Button size="sm">Small</Button>
|
|
39
|
+
<Button>Default</Button>
|
|
40
|
+
<Button size="lg">Large</Button>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Tones
|
|
47
|
+
|
|
48
|
+
```tsx
|
|
49
|
+
import { Button } from '@rinzai/zen';
|
|
50
|
+
// Icons from Heroicons (https://heroicons.com): XMark (x-mark).
|
|
51
|
+
|
|
52
|
+
/** A link sits in the text and truncates; `tone` quiets it or makes it red. An icon button with a destructive tone turns red only when pointed at. */
|
|
53
|
+
export default function Tones() {
|
|
54
|
+
return (
|
|
55
|
+
<div className="flex flex-col gap-3 text-sm">
|
|
56
|
+
<p>
|
|
57
|
+
Saved. <Button variant="link">Undo</Button> or{' '}
|
|
58
|
+
<Button variant="link" tone="muted">
|
|
59
|
+
see history
|
|
60
|
+
</Button>
|
|
61
|
+
</p>
|
|
62
|
+
<div className="flex items-center gap-3">
|
|
63
|
+
<Button variant="link" tone="negative">
|
|
64
|
+
Delete account
|
|
65
|
+
</Button>
|
|
66
|
+
<Button variant="ghost" size="icon" tone="negative" aria-label="Remove">
|
|
67
|
+
<XMark className="size-4" />
|
|
68
|
+
</Button>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Loading
|
|
76
|
+
|
|
77
|
+
```tsx
|
|
78
|
+
import { Button } from '@rinzai/zen';
|
|
79
|
+
import { useState } from 'react';
|
|
80
|
+
|
|
81
|
+
export default function Loading() {
|
|
82
|
+
const [saving, setSaving] = useState(false);
|
|
83
|
+
return (
|
|
84
|
+
<Button
|
|
85
|
+
loading={saving}
|
|
86
|
+
onClick={() => {
|
|
87
|
+
setSaving(true);
|
|
88
|
+
setTimeout(() => setSaving(false), 1500);
|
|
89
|
+
}}
|
|
90
|
+
>
|
|
91
|
+
{saving ? 'Saving…' : 'Save'}
|
|
92
|
+
</Button>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### As link
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
import { Button } from '@rinzai/zen';
|
|
101
|
+
|
|
102
|
+
/** asChild puts the button's styles on your own element, here a link (a router's Link works the same way). */
|
|
103
|
+
export default function AsLink() {
|
|
104
|
+
return (
|
|
105
|
+
<div className="flex gap-3">
|
|
106
|
+
<Button asChild>
|
|
107
|
+
<a href="#get-started">Get started</a>
|
|
108
|
+
</Button>
|
|
109
|
+
<Button asChild variant="outline">
|
|
110
|
+
<a href="#docs">Read the docs</a>
|
|
111
|
+
</Button>
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## API reference
|
|
118
|
+
|
|
119
|
+
### Button
|
|
120
|
+
|
|
121
|
+
A button in one of Zen's styles. With `asChild`, the styles go onto your own
|
|
122
|
+
single child element instead, such as a router's Link (`loading` and `type`
|
|
123
|
+
then don't apply).
|
|
124
|
+
|
|
125
|
+
| Prop | Type | Default | Description |
|
|
126
|
+
| --- | --- | --- | --- |
|
|
127
|
+
| `asChild` | `boolean` | `false` | Put the button's styles on the single child element (e.g. a link) instead of a <button>. |
|
|
128
|
+
| `loading` | `boolean` | `false` | Shows a spinner and disables the button while an action runs. |
|
|
129
|
+
| `size` | `"default" \| "icon-lg" \| "icon-sm" \| "icon-xs" \| "icon" \| "lg" \| "sm" \| null` | | |
|
|
130
|
+
| `tone` | `"default" \| "inherit" \| "muted" \| "negative" \| null` | | |
|
|
131
|
+
| `variant` | `"default" \| "ghost" \| "link" \| "outline" \| "secondary" \| null` | | |
|