@rinzai/zen 0.15.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 +15 -0
- 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.js +24 -24
- 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 +4 -4
- package/dist/docs/locale-provider.md +1 -1
- package/dist/docs/menu.md +54 -1
- package/dist/docs/seek-bar.md +30 -1
- package/dist/docs/sortable.md +63 -1
- package/dist/docs/tooltip.md +45 -3
- 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/keypad/index.js +5 -5
- package/dist/lib.js +7 -7
- package/dist/load-more/index.js +5 -5
- package/dist/menu/index.d.ts +7 -1
- package/dist/menu/index.d.ts.map +1 -1
- package/dist/menu/index.js +217 -174
- 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/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 +3 -1
- package/dist/seek-bar/index.d.ts.map +1 -1
- package/dist/seek-bar/index.js +41 -40
- package/dist/select/index.js +21 -21
- 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/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/theme-toggle/index.js +4 -4
- package/dist/theme.css +103 -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/messages.d.ts +1 -0
- package/dist/utils/messages.d.ts.map +1 -1
- package/dist/utils/messages.js +1 -0
- 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 +9 -5
- 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 +1 -1
package/dist/dialog/index.js
CHANGED
|
@@ -1,115 +1,95 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import i from "../
|
|
7
|
-
import a from "../
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
2
|
+
import { useMessages as e } from "../locale-provider/index.js";
|
|
3
|
+
import { cx as t } from "../utils/cx.js";
|
|
4
|
+
import { useGraphicsMode as n } from "../utils/graphics.js";
|
|
5
|
+
import { useMergedRefs as r } from "../utils/refs.js";
|
|
6
|
+
import { useSheetSwipe as i } from "../utils/swipe.js";
|
|
7
|
+
import a from "../button/index.js";
|
|
8
|
+
import o from "../icons/micro/x-mark.js";
|
|
9
|
+
import { useToastHost as s } from "../toast/index.js";
|
|
10
10
|
import { createContext as c, useContext as l, useEffect as u, useId as d, useRef as f } from "react";
|
|
11
11
|
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
12
12
|
//#region packages/dialog/index.tsx
|
|
13
13
|
var h = c(() => {});
|
|
14
|
-
function g({ open:
|
|
15
|
-
let T =
|
|
16
|
-
return
|
|
14
|
+
function g({ open: n, onOpenChange: a, title: s, description: c, children: l, dismissible: u = !0, initialFocus: g, size: v = "md", side: y, className: b, role: x, closeLabel: S, ref: C, ...w }) {
|
|
15
|
+
let T = e(), E = S ?? T.close, D = f(null), O = r(D, C), k = d(), A = d();
|
|
16
|
+
return i(D, u && n ? y : void 0, () => a?.(!1)), _(D, n, g), /* @__PURE__ */ m("dialog", {
|
|
17
17
|
...w,
|
|
18
18
|
ref: O,
|
|
19
19
|
role: x,
|
|
20
20
|
"aria-labelledby": k,
|
|
21
|
-
"aria-describedby":
|
|
22
|
-
"data-side":
|
|
23
|
-
className:
|
|
21
|
+
"aria-describedby": c ? A : void 0,
|
|
22
|
+
"data-side": y,
|
|
23
|
+
className: t("zen__dialog glass glass-blur glow-edge text-card-foreground bg-card/90! overflow-visible p-0", !y && "m-auto max-h-[calc(100dvh-2rem)] max-w-[calc(100vw-2rem)] rounded-xl", !y && (v === "lg" ? "w-[60rem]" : "w-[28rem]"), y === "bottom" && "mx-auto mt-auto mb-0 max-h-[85dvh] w-full max-w-2xl rounded-t-2xl", (y === "left" || y === "right") && "my-0 h-dvh max-h-dvh w-[24rem] max-w-[calc(100vw-2.5rem)]", y === "right" && "mr-0 ml-auto rounded-l-2xl", y === "left" && "mr-auto ml-0 rounded-r-2xl", "backdrop:bg-[color:var(--backdrop-scrim,oklch(0_0_0/0.5))] backdrop:backdrop-blur-sm pointer-coarse:backdrop:backdrop-blur-none", b),
|
|
24
24
|
onCancel: (e) => {
|
|
25
|
-
e.preventDefault(),
|
|
25
|
+
e.preventDefault(), u && a?.(!1);
|
|
26
26
|
},
|
|
27
27
|
onClick: (e) => {
|
|
28
|
-
e.target === e.currentTarget &&
|
|
28
|
+
e.target === e.currentTarget && u && a?.(!1);
|
|
29
29
|
},
|
|
30
30
|
children: [/* @__PURE__ */ m("div", {
|
|
31
|
-
className:
|
|
31
|
+
className: t("flex flex-col gap-4 overflow-x-hidden overflow-y-auto p-6", y ? y === "bottom" ? "max-h-[85dvh] pt-3" : "h-full" : "max-h-[calc(100dvh-2rem)]"),
|
|
32
32
|
children: [
|
|
33
|
-
|
|
33
|
+
y === "bottom" && /* @__PURE__ */ p("div", {
|
|
34
34
|
"aria-hidden": !0,
|
|
35
35
|
className: "bg-tint/20 mx-auto h-1 w-10 shrink-0 rounded-full"
|
|
36
36
|
}),
|
|
37
37
|
/* @__PURE__ */ m("div", {
|
|
38
|
-
className:
|
|
38
|
+
className: t("flex flex-col gap-2", u && "pe-8"),
|
|
39
39
|
children: [/* @__PURE__ */ p("h2", {
|
|
40
40
|
id: k,
|
|
41
41
|
className: "text-lg font-semibold tracking-tight text-balance",
|
|
42
|
-
children:
|
|
43
|
-
}),
|
|
42
|
+
children: s
|
|
43
|
+
}), c && /* @__PURE__ */ p("p", {
|
|
44
44
|
id: A,
|
|
45
45
|
className: "text-muted-foreground mt-0! text-sm leading-5",
|
|
46
|
-
children:
|
|
46
|
+
children: c
|
|
47
47
|
})]
|
|
48
48
|
}),
|
|
49
49
|
/* @__PURE__ */ p(h.Provider, {
|
|
50
|
-
value: () =>
|
|
51
|
-
children:
|
|
50
|
+
value: () => a?.(!1),
|
|
51
|
+
children: l
|
|
52
52
|
})
|
|
53
53
|
]
|
|
54
|
-
}),
|
|
54
|
+
}), u && /* @__PURE__ */ p("button", {
|
|
55
55
|
type: "button",
|
|
56
56
|
"aria-label": E,
|
|
57
|
-
onClick: () =>
|
|
58
|
-
className:
|
|
59
|
-
children: /* @__PURE__ */ p(
|
|
57
|
+
onClick: () => a?.(!1),
|
|
58
|
+
className: t("text-muted-foreground hover:bg-tint/[0.07] hover:text-foreground absolute end-4 grid size-8 cursor-pointer place-items-center rounded-lg outline-hidden transition-colors focus-ring", y === "bottom" ? "top-7.5" : "top-5.5"),
|
|
59
|
+
children: /* @__PURE__ */ p(o, { className: "size-4" })
|
|
60
60
|
})]
|
|
61
61
|
});
|
|
62
62
|
}
|
|
63
|
-
function _(e,
|
|
64
|
-
|
|
65
|
-
let
|
|
66
|
-
if (
|
|
67
|
-
if (
|
|
63
|
+
function _(e, t, r) {
|
|
64
|
+
n(), u(() => {
|
|
65
|
+
let n = e.current;
|
|
66
|
+
if (n) {
|
|
67
|
+
if (t && !n.open) {
|
|
68
68
|
let e = document.documentElement;
|
|
69
69
|
e.toggleAttribute("data-zen-scrollbar", window.innerWidth > e.clientWidth);
|
|
70
|
-
let
|
|
71
|
-
if (
|
|
72
|
-
let e =
|
|
73
|
-
|
|
70
|
+
let t = document.activeElement === document.body ? null : document.activeElement?.getBoundingClientRect(), i = !!t && !n.dataset.side;
|
|
71
|
+
if (n.toggleAttribute("data-zen-from", i), n.showModal(), i) {
|
|
72
|
+
let e = t.left + t.width / 2 - n.offsetLeft, r = t.top + t.height / 2 - n.offsetTop;
|
|
73
|
+
n.style.transformOrigin = `${Math.round(e)}px ${Math.round(r)}px`;
|
|
74
74
|
}
|
|
75
75
|
r?.current?.focus();
|
|
76
|
-
} else !
|
|
76
|
+
} else !t && n.open && n.close();
|
|
77
77
|
}
|
|
78
78
|
}, [
|
|
79
79
|
e,
|
|
80
|
-
|
|
80
|
+
t,
|
|
81
81
|
r
|
|
82
|
-
]),
|
|
83
|
-
}
|
|
84
|
-
var v = .3;
|
|
85
|
-
function y(e, t, n) {
|
|
86
|
-
let r = t === "bottom" ? "y" : "x", i = t === "left" ? -1 : 1, a = () => r === "y" ? e.current.offsetHeight : e.current.offsetWidth, o = () => {
|
|
87
|
-
e.current?.style.removeProperty("--zen-swipe"), e.current?.style.removeProperty("--zen-swipe-progress");
|
|
88
|
-
};
|
|
89
|
-
u(o, [e, t]), s(e, t ? {
|
|
90
|
-
axis: r,
|
|
91
|
-
sign: i
|
|
92
|
-
} : void 0, {
|
|
93
|
-
move: (t) => {
|
|
94
|
-
let n = t > 0 ? t : t / 6;
|
|
95
|
-
e.current.style.setProperty("--zen-swipe", r === "y" ? `0 ${n}px` : `${n * i}px 0`), e.current.style.setProperty("--zen-swipe-progress", String(Math.max(0, Math.min(1, t / a()))));
|
|
96
|
-
},
|
|
97
|
-
release: (e, t) => {
|
|
98
|
-
e > a() * v || e > 0 && t > .5 ? n() : o();
|
|
99
|
-
},
|
|
100
|
-
cancel: o,
|
|
101
|
-
threshold: () => a() * v
|
|
102
|
-
});
|
|
82
|
+
]), s(e, t);
|
|
103
83
|
}
|
|
104
|
-
function
|
|
84
|
+
function v({ className: e, ...n }) {
|
|
105
85
|
return /* @__PURE__ */ p("div", {
|
|
106
|
-
className:
|
|
86
|
+
className: t("flex justify-end gap-2", e),
|
|
107
87
|
...n
|
|
108
88
|
});
|
|
109
89
|
}
|
|
110
|
-
function
|
|
90
|
+
function y({ onClick: e, ...t }) {
|
|
111
91
|
let n = l(h);
|
|
112
|
-
return /* @__PURE__ */ p(
|
|
92
|
+
return /* @__PURE__ */ p(a, {
|
|
113
93
|
...t,
|
|
114
94
|
onClick: (t) => {
|
|
115
95
|
e?.(t), t.defaultPrevented || n();
|
|
@@ -117,4 +97,4 @@ function x({ onClick: e, ...t }) {
|
|
|
117
97
|
});
|
|
118
98
|
}
|
|
119
99
|
//#endregion
|
|
120
|
-
export {
|
|
100
|
+
export { y as DialogClose, v as DialogFooter, g as default, _ as useModal };
|
package/dist/docs/README.md
CHANGED
|
@@ -156,7 +156,7 @@ Breaking changes and additions by version are in ../../CHANGELOG.md.
|
|
|
156
156
|
- [Load More](./load-more.md): The end of a long list that has more: loads the next page when it scrolls into view, or on a press.
|
|
157
157
|
- [Locale Provider](./locale-provider.md): The language for everything inside: how dates, numbers and money read, and every word components show or announce. Without it, "en-US" and English.
|
|
158
158
|
- [Marquee](./marquee.md): One line of text cut off with an ellipsis, that scrolls to show the rest on hover or focus (of itself or of its row), or all the time. Only text that doesn't fit moves.
|
|
159
|
-
- [Menu](./menu.md): A button that opens a menu, or a context menu from a right-click or long press, with your own trigger, a header, icons, groups, destructive items, checkbox and radio items, submenus, and a menubar of menus.
|
|
159
|
+
- [Menu](./menu.md): A button that opens a menu, or a context menu from a right-click or long press, with your own trigger, a header, icons, groups, destructive items, checkbox and radio items, submenus, a bottom sheet on phones, and a menubar of menus.
|
|
160
160
|
- [Meter](./meter.md): A measure against a limit, like spend against a budget: warm near it, red past it.
|
|
161
161
|
- [Money Input](./money-input.md): Amounts in any currency and locale, with shorthand like 1.5L or 10k and sums like 120+45.50, exact to the paisa, and typed in other currencies, converted at your rates. Built on Input Group.
|
|
162
162
|
- [Month Picker](./month-picker.md): A field showing a month that opens a year and a 12-month grid, with optional limits and clearing.
|
|
@@ -174,14 +174,14 @@ Breaking changes and additions by version are in ../../CHANGELOG.md.
|
|
|
174
174
|
- [Range Slider](./range-slider.md): Two thumbs on one track, for a span like an amount from $50 to $500.
|
|
175
175
|
- [Repeat Picker](./repeat-picker.md): How often something recurs, like a bill, said in words, with the next dates it falls on.
|
|
176
176
|
- [Resizable](./resizable.md): Panels with handles between them to drag, or to move with the arrow keys, within limits you set; groups nest, and the layout is reported to keep.
|
|
177
|
-
- [Seek Bar](./seek-bar.md): Where playback is in a track, and the way to move it: played and loaded on one track,
|
|
177
|
+
- [Seek Bar](./seek-bar.md): Where playback is in a track, and the way to move it: played and loaded on one track, the time played and the length beside it or under its ends.
|
|
178
178
|
- [Segmented](./segmented.md): A pill row for a few mutually exclusive options.
|
|
179
179
|
- [Select](./select.md): Pick one value from a short list, fully keyboard-driven. For long or searchable lists, use Combobox.
|
|
180
180
|
- [Separator](./separator.md): A hairline between groups of content, across or upright.
|
|
181
181
|
- [Side Nav](./side-nav.md): Vertical navigation in titled groups, for docs and settings sidebars. Works with any router.
|
|
182
182
|
- [Skeleton & Spinner](./skeleton.md): Placeholders for content on its way.
|
|
183
183
|
- [Slider](./slider.md): A native range input with a glowing thumb and any track you like.
|
|
184
|
-
- [Sortable List](./sortable.md): Put a list in order, or a tree of groups (a table’s rows, say), by dragging items by a grip or with the keyboard, each step announced: into other groups, open sub-items, or under another item.
|
|
184
|
+
- [Sortable List](./sortable.md): Put a list in order, or a tree of groups (a table’s rows, say), by dragging items by a grip or with the keyboard, each step announced: into other groups, open sub-items, or under another item. Also a virtualized list of hundreds.
|
|
185
185
|
- [Sparkline](./sparkline.md): A small line of how a number has moved, with no axes, for beside a figure. It draws itself in.
|
|
186
186
|
- [Spinner](./spinner.md): A small loading spinner in the current text colour, announced to screen readers when labelled.
|
|
187
187
|
- [Status Pill](./status-pill.md): A small status that changes in place, such as Saving…, Saved or Not saved with a Retry, and fades back once the news is old.
|
|
@@ -200,6 +200,6 @@ Breaking changes and additions by version are in ../../CHANGELOG.md.
|
|
|
200
200
|
- [Toggle](./toggle.md): An on/off switch.
|
|
201
201
|
- [Toggle Group](./toggle-group.md): Buttons that stay pressed, in a row: any number of them, like filters, or at most one. One stop in the tab order; the arrow keys go through them.
|
|
202
202
|
- [Toolbar](./toolbar.md): A row of controls for one thing, like a table’s: one stop for Tab, and the arrow keys through its buttons, toggles and fields.
|
|
203
|
-
- [Tooltip](./tooltip.md): A short glass hint on hover or keyboard focus, describing its trigger to screen readers.
|
|
203
|
+
- [Tooltip](./tooltip.md): A short glass hint on hover or keyboard focus, describing its trigger to screen readers; below its trigger or beside it.
|
|
204
204
|
- [Tree Table](./tree.md): Rows that open into child rows, animated, built from useTree and the Table parts. With virtual rows and editable cells, enough for a budget planner.
|
|
205
205
|
- [Trend](./trend.md): A change, like +12% with an arrow, coloured by whether it is good news.
|
|
@@ -96,4 +96,4 @@ part of the page.
|
|
|
96
96
|
| Prop | Type | Default | Description |
|
|
97
97
|
| --- | --- | --- | --- |
|
|
98
98
|
| `locale` | `string` | | e.g. "en-IN", "fr-FR". Defaults to the enclosing provider's, or "en-US". |
|
|
99
|
-
| `messages` | `Partial<{ clear: string; cancel: string; close: string; dismiss: string; delete: string; remove: (item: string) => string; noResults: string; search: string; expand: string; collapse: string; minimum: string; ... 109 more ...; timeOf: (time: string, total: string) => string; }>` | | Any of the words to replace; the others are kept. |
|
|
99
|
+
| `messages` | `Partial<{ clear: string; cancel: string; close: string; back: string; dismiss: string; delete: string; remove: (item: string) => string; noResults: string; search: string; expand: string; collapse: string; minimum: string; ... 109 more ...; timeOf: (time: string, total: string) => string; }>` | | Any of the words to replace; the others are kept. |
|
package/dist/docs/menu.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Menu
|
|
2
2
|
|
|
3
|
-
A button that opens a menu, or a context menu from a right-click or long press, with your own trigger, a header, icons, groups, destructive items, checkbox and radio items, submenus, and a menubar of menus.
|
|
3
|
+
A button that opens a menu, or a context menu from a right-click or long press, with your own trigger, a header, icons, groups, destructive items, checkbox and radio items, submenus, a bottom sheet on phones, and a menubar of menus.
|
|
4
4
|
|
|
5
5
|
```tsx
|
|
6
6
|
import { Menu, MenuTrigger, MenuContextTrigger, MenuContent, MenuItem, MenuCheckboxItem, MenuRadioGroup, MenuRadioItem, MenuSub, MenuSubTrigger, MenuSubContent, MenuSeparator, MenuHeader, Menubar } from '@rinzai/zen';
|
|
@@ -187,6 +187,58 @@ export default function Options() {
|
|
|
187
187
|
}
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
+
### Sheet
|
|
191
|
+
|
|
192
|
+
```tsx
|
|
193
|
+
import {
|
|
194
|
+
buttonVariants,
|
|
195
|
+
Menu,
|
|
196
|
+
MenuContent,
|
|
197
|
+
MenuItem,
|
|
198
|
+
MenuSeparator,
|
|
199
|
+
MenuSub,
|
|
200
|
+
MenuSubContent,
|
|
201
|
+
MenuSubTrigger,
|
|
202
|
+
MenuTrigger,
|
|
203
|
+
useToast,
|
|
204
|
+
} from '@rinzai/zen';
|
|
205
|
+
// Icons from Heroicons (https://heroicons.com): Ellipsis (ellipsis-horizontal), Heart (heart), MusicalNote (musical-note), QueueList (queue-list).
|
|
206
|
+
|
|
207
|
+
/** A song's menu: on a phone (narrow the window), a bottom sheet, its playlists sliding in over it. */
|
|
208
|
+
export default function Sheet() {
|
|
209
|
+
const toast = useToast();
|
|
210
|
+
return (
|
|
211
|
+
<Menu>
|
|
212
|
+
<MenuTrigger aria-label="Song actions" className={buttonVariants({ variant: 'ghost', size: 'icon' })}>
|
|
213
|
+
<Ellipsis />
|
|
214
|
+
</MenuTrigger>
|
|
215
|
+
<MenuContent sheet>
|
|
216
|
+
<MenuItem icon={<QueueList className="size-4" />} onSelect={() => toast('Playing next')}>
|
|
217
|
+
Play next
|
|
218
|
+
</MenuItem>
|
|
219
|
+
<MenuItem icon={<Heart className="size-4" />} onSelect={() => toast('Liked', { tone: 'positive' })}>
|
|
220
|
+
Like
|
|
221
|
+
</MenuItem>
|
|
222
|
+
<MenuSub>
|
|
223
|
+
<MenuSubTrigger icon={<MusicalNote className="size-4" />}>Add to playlist</MenuSubTrigger>
|
|
224
|
+
<MenuSubContent>
|
|
225
|
+
{['Late night', 'Focus', 'Running', 'Sunday morning'].map((name) => (
|
|
226
|
+
<MenuItem key={name} onSelect={() => toast(`Added to ${name}`, { tone: 'positive' })}>
|
|
227
|
+
{name}
|
|
228
|
+
</MenuItem>
|
|
229
|
+
))}
|
|
230
|
+
</MenuSubContent>
|
|
231
|
+
</MenuSub>
|
|
232
|
+
<MenuSeparator />
|
|
233
|
+
<MenuItem tone="negative" onSelect={() => toast('Removed from queue', { tone: 'negative' })}>
|
|
234
|
+
Remove from queue
|
|
235
|
+
</MenuItem>
|
|
236
|
+
</MenuContent>
|
|
237
|
+
</Menu>
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
```
|
|
241
|
+
|
|
190
242
|
### Bar
|
|
191
243
|
|
|
192
244
|
```tsx
|
|
@@ -308,6 +360,7 @@ instead; name it with `aria-label`.
|
|
|
308
360
|
| --- | --- | --- | --- |
|
|
309
361
|
| `align` | `"center" \| "end" \| "start"` | | Which edge of the button the menu lines up with (default end; a context menu starts at the pointer), or centred on it. |
|
|
310
362
|
| `offset` | `number` | | Gap between the button and the menu, in px. |
|
|
363
|
+
| `sheet` | `boolean` | | On phones (below `md`), a bottom sheet instead, as music apps show a song's menu: across the bottom of the screen over a scrim, swiped down to close; its submenus slide in over it, with a way back at the top. |
|
|
311
364
|
|
|
312
365
|
### MenuItem
|
|
313
366
|
|
package/dist/docs/seek-bar.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Seek Bar
|
|
2
2
|
|
|
3
|
-
Where playback is in a track, and the way to move it: played and loaded on one track,
|
|
3
|
+
Where playback is in a track, and the way to move it: played and loaded on one track, the time played and the length beside it or under its ends.
|
|
4
4
|
|
|
5
5
|
```tsx
|
|
6
6
|
import { SeekBar, formatDuration } from '@rinzai/zen';
|
|
@@ -46,6 +46,34 @@ export default function Default() {
|
|
|
46
46
|
}
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
+
### Times below
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
import { SeekBar, useMediaPlayer } from '@rinzai/zen';
|
|
53
|
+
|
|
54
|
+
import { useDemoTrack } from '../../player-bar/examples/demo-track';
|
|
55
|
+
|
|
56
|
+
/** The times under the bar's ends, as on a phone's Now Playing screen. */
|
|
57
|
+
export default function TimesBelow() {
|
|
58
|
+
const src = useDemoTrack();
|
|
59
|
+
const [audioRef, player] = useMediaPlayer();
|
|
60
|
+
return (
|
|
61
|
+
<div className="w-80">
|
|
62
|
+
<audio ref={audioRef} src={src} preload="metadata" />
|
|
63
|
+
<SeekBar
|
|
64
|
+
times="below"
|
|
65
|
+
currentTime={player.currentTime}
|
|
66
|
+
playing={player.playing && !player.waiting}
|
|
67
|
+
playbackRate={player.playbackRate}
|
|
68
|
+
duration={player.duration}
|
|
69
|
+
buffered={player.buffered}
|
|
70
|
+
onSeek={player.seek}
|
|
71
|
+
/>
|
|
72
|
+
</div>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
49
77
|
## API reference
|
|
50
78
|
|
|
51
79
|
### SeekBar
|
|
@@ -65,4 +93,5 @@ the track keeps playing; letting go seeks. The arrow keys seek 5 seconds.
|
|
|
65
93
|
| `playbackRate` | `number` | `1` | How fast it moves on while playing (default 1). |
|
|
66
94
|
| `playing` | `boolean` | `false` | While the time is running (playing, not waiting for data): the bar glides on between time updates. |
|
|
67
95
|
| `thumbColor` | `string` | | CSS colour for the thumb (default: the accent). |
|
|
96
|
+
| `times` | `"below" \| "beside"` | `beside` | Where the time played and the length go: either side of the bar (default), or under its ends, as on a phone's Now Playing screen. |
|
|
68
97
|
| `valueText` | `((value: number) => string)` | | Spoken value, e.g. (v) => `Hue ${v} degrees`. |
|
package/dist/docs/sortable.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Sortable List
|
|
2
2
|
|
|
3
|
-
Put a list in order, or a tree of groups (a table’s rows, say), by dragging items by a grip or with the keyboard, each step announced: into other groups, open sub-items, or under another item.
|
|
3
|
+
Put a list in order, or a tree of groups (a table’s rows, say), by dragging items by a grip or with the keyboard, each step announced: into other groups, open sub-items, or under another item. Also a virtualized list of hundreds.
|
|
4
4
|
|
|
5
5
|
```tsx
|
|
6
6
|
import { SortableList, SortableTree, SortableItem, SortableHandle, flattenTree } from '@rinzai/zen';
|
|
@@ -116,6 +116,66 @@ export default function Groups() {
|
|
|
116
116
|
}
|
|
117
117
|
```
|
|
118
118
|
|
|
119
|
+
### Virtual
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
import { SortableHandle, SortableItem, SortableList, useVirtualList } from '@rinzai/zen';
|
|
123
|
+
import { useRef, useState } from 'react';
|
|
124
|
+
|
|
125
|
+
const ROW = 48;
|
|
126
|
+
const ARTISTS = ['Nils Frahm', 'Ólafur Arnalds', 'Hania Rani', 'Max Richter', 'Bonobo', 'Tycho', 'Jon Hopkins'];
|
|
127
|
+
const SONGS = Object.fromEntries(
|
|
128
|
+
Array.from({ length: 500 }, (_, i) => [`s${i}`, { title: `Track ${i + 1}`, artist: ARTISTS[(i * 3) % 7] }]),
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* A queue of 500, rendering only the rows in view: `itemHeight` on the list,
|
|
133
|
+
* and the dragged row kept rendered (`keep`) as it scrolls the list along.
|
|
134
|
+
*/
|
|
135
|
+
export default function Virtual() {
|
|
136
|
+
const [order, setOrder] = useState(Object.keys(SONGS));
|
|
137
|
+
const [lifted, setLifted] = useState<string | null>(null);
|
|
138
|
+
const scrollRef = useRef<HTMLDivElement>(null);
|
|
139
|
+
const rows = useVirtualList({
|
|
140
|
+
count: order.length,
|
|
141
|
+
itemHeight: ROW,
|
|
142
|
+
scrollRef,
|
|
143
|
+
keep: lifted ? order.indexOf(lifted) : undefined,
|
|
144
|
+
});
|
|
145
|
+
return (
|
|
146
|
+
<div ref={scrollRef} className="glass h-96 w-full max-w-md overflow-y-auto rounded-xl">
|
|
147
|
+
<SortableList
|
|
148
|
+
value={order}
|
|
149
|
+
onValueChange={setOrder}
|
|
150
|
+
itemHeight={ROW}
|
|
151
|
+
onLiftedChange={setLifted}
|
|
152
|
+
aria-label="Up next"
|
|
153
|
+
style={{ height: rows.totalSize }}
|
|
154
|
+
>
|
|
155
|
+
{rows.items.map(({ index, start }) => {
|
|
156
|
+
const id = order[index];
|
|
157
|
+
const song = SONGS[id];
|
|
158
|
+
return (
|
|
159
|
+
<SortableItem
|
|
160
|
+
key={id}
|
|
161
|
+
id={id}
|
|
162
|
+
className="absolute! inset-x-0 flex items-center gap-2 px-1.5"
|
|
163
|
+
style={{ top: start, height: ROW }}
|
|
164
|
+
>
|
|
165
|
+
<SortableHandle id={id} label={song.title} />
|
|
166
|
+
<div className="min-w-0">
|
|
167
|
+
<div className="truncate text-sm font-medium">{song.title}</div>
|
|
168
|
+
<div className="text-muted-foreground truncate text-xs">{song.artist}</div>
|
|
169
|
+
</div>
|
|
170
|
+
</SortableItem>
|
|
171
|
+
);
|
|
172
|
+
})}
|
|
173
|
+
</SortableList>
|
|
174
|
+
</div>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
```
|
|
178
|
+
|
|
119
179
|
## API reference
|
|
120
180
|
|
|
121
181
|
### SortableList
|
|
@@ -134,6 +194,8 @@ TableBody of TableRows, say, one per group, each sorted on its own.
|
|
|
134
194
|
| `onValueChange` (required) | `(value: string[]) => void` | | |
|
|
135
195
|
| `value` (required) | `readonly string[]` | | The items' ids, in order. |
|
|
136
196
|
| `asChild` | `boolean` | | Put the list's props on your own element (the single child, e.g. a TableBody) instead of a ul. |
|
|
197
|
+
| `itemHeight` | `number` | | For a virtualized list (useVirtualList), its rows' height, gap included: render just the items in view, each placed at its place in `value` times this, and the drag works from `value` instead of the rows on screen. |
|
|
198
|
+
| `onLiftedChange` | `((id: string \| null) => void)` | | When an item is picked up (by its handle, or from the keyboard), and when it's put down (null). A virtualized list keeps that row rendered meanwhile (useVirtualList's `keep`), so it stays under the pointer as the list scrolls. |
|
|
137
199
|
|
|
138
200
|
### SortableTree
|
|
139
201
|
|
package/dist/docs/tooltip.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Tooltip
|
|
2
2
|
|
|
3
|
-
A short glass hint on hover or keyboard focus, describing its trigger to screen readers.
|
|
3
|
+
A short glass hint on hover or keyboard focus, describing its trigger to screen readers; below its trigger or beside it.
|
|
4
4
|
|
|
5
5
|
```tsx
|
|
6
6
|
import { Tooltip, TooltipTrigger, TooltipContent } from '@rinzai/zen';
|
|
@@ -31,6 +31,45 @@ export default function Default() {
|
|
|
31
31
|
}
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
### Beside
|
|
35
|
+
|
|
36
|
+
```tsx
|
|
37
|
+
import { Tooltip, TooltipContent, TooltipTrigger } from '@rinzai/zen';
|
|
38
|
+
// Icons from Heroicons (https://heroicons.com): MusicalNote (musical-note), QueueList (queue-list), Search (magnifying-glass), Squares (squares-2x2).
|
|
39
|
+
|
|
40
|
+
const ITEMS = [
|
|
41
|
+
{ label: 'Home', icon: Squares },
|
|
42
|
+
{ label: 'Search', icon: Search },
|
|
43
|
+
{ label: 'Library', icon: MusicalNote },
|
|
44
|
+
{ label: 'Up next', icon: QueueList },
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* An icon rail, each icon's name in a tooltip beside it (`side="beside"`), clear
|
|
49
|
+
* of the icons below. The name is the button's own label, so it isn't also its description.
|
|
50
|
+
*/
|
|
51
|
+
export default function Beside() {
|
|
52
|
+
return (
|
|
53
|
+
<nav aria-label="Main" className="glass flex flex-col gap-1 rounded-xl p-1.5">
|
|
54
|
+
{ITEMS.map(({ label, icon: Icon }) => (
|
|
55
|
+
<Tooltip key={label}>
|
|
56
|
+
<TooltipTrigger
|
|
57
|
+
aria-label={label}
|
|
58
|
+
aria-describedby={undefined}
|
|
59
|
+
className="text-muted-foreground hover:text-foreground hover:bg-tint/[0.07] focus-ring grid size-10 cursor-pointer place-items-center rounded-lg transition-colors"
|
|
60
|
+
>
|
|
61
|
+
<Icon className="size-5" />
|
|
62
|
+
</TooltipTrigger>
|
|
63
|
+
<TooltipContent side="beside" offset={8}>
|
|
64
|
+
<p className="px-2.5 py-1.5 text-sm">{label}</p>
|
|
65
|
+
</TooltipContent>
|
|
66
|
+
</Tooltip>
|
|
67
|
+
))}
|
|
68
|
+
</nav>
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
34
73
|
## API reference
|
|
35
74
|
|
|
36
75
|
### Tooltip
|
|
@@ -52,9 +91,12 @@ What the tooltip describes: a button by default. With `asChild`, your own focusa
|
|
|
52
91
|
|
|
53
92
|
### TooltipContent
|
|
54
93
|
|
|
55
|
-
The hint: glass, below the trigger (above it if there's no room), centred on
|
|
94
|
+
The hint: glass, below the trigger (above it if there's no room), centred on
|
|
95
|
+
it by default. `side="beside"` puts it after the trigger instead (before it
|
|
96
|
+
if there's no room), as for the icons of a collapsed sidebar.
|
|
56
97
|
|
|
57
98
|
| Prop | Type | Default | Description |
|
|
58
99
|
| --- | --- | --- | --- |
|
|
59
|
-
| `align` | `"center" \| "end" \| "start"` | `center` | Centred on the trigger (default), or lined up with its start or end edge. |
|
|
100
|
+
| `align` | `"center" \| "end" \| "start"` | `center` | Centred on the trigger (default), or lined up with its start or end edge (beside: its top or bottom). |
|
|
60
101
|
| `offset` | `number` | `5` | Gap from the trigger, in px. |
|
|
102
|
+
| `side` | `"below" \| "beside"` | `below` | Below the trigger (default; above if there's no room), or beside it (after it; before if there's no room). |
|
package/dist/filter-bar/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useMessages as e } from "../locale-provider/index.js";
|
|
3
|
+
import { cx as t } from "../utils/cx.js";
|
|
4
4
|
import { reducedMotion as n } from "../utils/motion.js";
|
|
5
5
|
import r from "../icons/micro/x-mark.js";
|
|
6
6
|
import { Children as i, useEffect as a, useLayoutEffect as o, useRef as s } from "react";
|
|
@@ -14,7 +14,7 @@ var u = typeof window > "u" ? a : o, d = {
|
|
|
14
14
|
easing: "ease-in"
|
|
15
15
|
};
|
|
16
16
|
function p({ onClear: n, clearLabel: r, "aria-label": a, className: o, children: u, ref: d, ...f }) {
|
|
17
|
-
let p =
|
|
17
|
+
let p = e(), h = r ?? p.clearAll, g = a ?? p.activeFilters, _ = i.toArray(u).length, v = s(null);
|
|
18
18
|
return m(v), _ === 0 ? null : /* @__PURE__ */ l("div", {
|
|
19
19
|
ref: (e) => {
|
|
20
20
|
if (v.current = e, typeof d == "function") return d(e);
|
|
@@ -22,7 +22,7 @@ function p({ onClear: n, clearLabel: r, "aria-label": a, className: o, children:
|
|
|
22
22
|
},
|
|
23
23
|
role: "group",
|
|
24
24
|
"aria-label": g,
|
|
25
|
-
className:
|
|
25
|
+
className: t("zen__filter-bar relative flex flex-wrap items-center gap-1.5", o),
|
|
26
26
|
...f,
|
|
27
27
|
children: [u, _ > 1 && n && /* @__PURE__ */ c("button", {
|
|
28
28
|
type: "button",
|
|
@@ -76,9 +76,9 @@ function m(e) {
|
|
|
76
76
|
});
|
|
77
77
|
}
|
|
78
78
|
function h({ onRemove: n, removeLabel: i, className: a, children: o }) {
|
|
79
|
-
let s =
|
|
79
|
+
let s = e(), u = i ?? s.removeFilter;
|
|
80
80
|
return /* @__PURE__ */ l("span", {
|
|
81
|
-
className:
|
|
81
|
+
className: t("zen__filter-chip bg-primary/12 text-foreground inline-flex items-center gap-1 rounded-full py-1 ps-3 pe-1 text-xs shadow-[inset_0_0_0_1px_oklch(var(--primary)/0.25)]", a),
|
|
82
82
|
children: [o, /* @__PURE__ */ c("button", {
|
|
83
83
|
type: "button",
|
|
84
84
|
"aria-label": u,
|
package/dist/header/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { useMessages as e } from "../locale-provider/index.js";
|
|
3
|
+
import { cx as t } from "../utils/cx.js";
|
|
4
|
+
import { useGraphicsMode as n } from "../utils/graphics.js";
|
|
5
5
|
import { jsx as r, jsxs as i } from "react/jsx-runtime";
|
|
6
6
|
//#region packages/header/index.tsx
|
|
7
7
|
function a({ skipLabel: a, className: o, children: s, ...c }) {
|
|
8
|
-
let l =
|
|
9
|
-
return
|
|
10
|
-
className:
|
|
8
|
+
let l = e(), u = a === void 0 ? l.skipToContent : a;
|
|
9
|
+
return n(), /* @__PURE__ */ i("header", {
|
|
10
|
+
className: t("zen__header glow-edge border-tint/5 bg-background/60 sticky top-0 z-40 border-b pt-[env(safe-area-inset-top)] backdrop-blur-xl", "pointer-coarse:bg-background/95 pointer-coarse:backdrop-blur-none", o),
|
|
11
11
|
...c,
|
|
12
12
|
children: [u !== null && /* @__PURE__ */ r("a", {
|
|
13
13
|
href: "#main",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { useMessages as e } from "../locale-provider/index.js";
|
|
3
|
+
import { cx as t } from "../utils/cx.js";
|
|
4
|
+
import { useControllable as n } from "../utils/useControllable.js";
|
|
5
5
|
import r, { PopoverContent as i, PopoverTrigger as a } from "../popover/index.js";
|
|
6
6
|
import { useId as o } from "react";
|
|
7
7
|
import { jsx as s, jsxs as c } from "react/jsx-runtime";
|
|
@@ -41,12 +41,12 @@ var l = [
|
|
|
41
41
|
}
|
|
42
42
|
];
|
|
43
43
|
function u({ icons: u, colors: d = l, value: f, defaultValue: p = { icon: "" }, onValueChange: m, label: h, name: g, className: _, ...v }) {
|
|
44
|
-
let [y, b] =
|
|
44
|
+
let [y, b] = n(f, p, m), x = e(), S = (e) => e.label ?? x.colourNames[e.value] ?? e.value, C = o(), w = g ?? C, T = u.find((e) => e.value === y.icon), E = d.find((e) => e.value === y.color), D = (e) => ({ "--icon-color": e ?? "var(--color-muted-foreground)" });
|
|
45
45
|
return /* @__PURE__ */ c(r, { children: [/* @__PURE__ */ s(a, {
|
|
46
46
|
...v,
|
|
47
47
|
"aria-label": `${h}: ${[T?.label, E && S(E)].filter(Boolean).join(", ") || x.none}`,
|
|
48
48
|
style: D(E?.color),
|
|
49
|
-
className:
|
|
49
|
+
className: t("zen__icon-picker grid size-10 shrink-0 place-items-center rounded-xl bg-[color-mix(in_oklch,var(--icon-color)_18%,transparent)] text-lg text-(--icon-color) outline-hidden transition-colors focus-ring", _),
|
|
50
50
|
children: T?.icon
|
|
51
51
|
}), /* @__PURE__ */ c(i, {
|
|
52
52
|
"aria-label": h,
|
package/dist/keypad/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
2
|
+
import { useMessages as e } from "../locale-provider/index.js";
|
|
3
|
+
import { cx as t } from "../utils/cx.js";
|
|
4
|
+
import { useControllable as n } from "../utils/useControllable.js";
|
|
5
5
|
import r from "../icons/arrow-left.js";
|
|
6
6
|
import { jsx as i } from "react/jsx-runtime";
|
|
7
7
|
//#region packages/keypad/index.tsx
|
|
@@ -20,7 +20,7 @@ var a = [
|
|
|
20
20
|
"back"
|
|
21
21
|
];
|
|
22
22
|
function o({ value: o, defaultValue: s = "", onValueChange: c, decimals: l = 2, maxLength: u = 12, deleteLabel: d, "aria-label": f, className: p, ...m }) {
|
|
23
|
-
let h =
|
|
23
|
+
let h = e(), g = d ?? h.delete, _ = f ?? h.keypad, [v, y] = n(o, s, c), b = (e) => {
|
|
24
24
|
if (e === "back") return y(v.slice(0, -1));
|
|
25
25
|
let t = v.indexOf(".");
|
|
26
26
|
if (e === ".") return l === 0 || t !== -1 ? void 0 : y((v || "0") + ".");
|
|
@@ -29,7 +29,7 @@ function o({ value: o, defaultValue: s = "", onValueChange: c, decimals: l = 2,
|
|
|
29
29
|
return /* @__PURE__ */ i("div", {
|
|
30
30
|
role: "group",
|
|
31
31
|
"aria-label": _,
|
|
32
|
-
className:
|
|
32
|
+
className: t("zen__keypad grid grid-cols-3 gap-2", p),
|
|
33
33
|
...m,
|
|
34
34
|
children: a.map((e) => /* @__PURE__ */ i("button", {
|
|
35
35
|
type: "button",
|