@blakesteve/roster 5.0.1 → 5.2.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.
Files changed (73) hide show
  1. package/README.md +110 -9
  2. package/dist/components/atoms/Chip/Chip.cjs +1 -1
  3. package/dist/components/atoms/Chip/Chip.js +27 -27
  4. package/dist/components/atoms/Disclosure/Disclosure.cjs +1 -1
  5. package/dist/components/atoms/Disclosure/Disclosure.js +42 -44
  6. package/dist/components/atoms/Input/Input.cjs +1 -1
  7. package/dist/components/atoms/Input/Input.js +46 -41
  8. package/dist/components/atoms/Spinner/Spinner.cjs +1 -1
  9. package/dist/components/atoms/Spinner/Spinner.d.ts +6 -1
  10. package/dist/components/atoms/Spinner/Spinner.js +9 -8
  11. package/dist/components/atoms/Stat/Stat.cjs +1 -1
  12. package/dist/components/atoms/Stat/Stat.d.ts +27 -2
  13. package/dist/components/atoms/Stat/Stat.js +38 -13
  14. package/dist/components/atoms/Switch/Switch.cjs +1 -1
  15. package/dist/components/atoms/Switch/Switch.d.ts +11 -1
  16. package/dist/components/atoms/Switch/Switch.js +38 -36
  17. package/dist/components/atoms/Switch/switch-variants.cjs +1 -1
  18. package/dist/components/atoms/Switch/switch-variants.js +1 -1
  19. package/dist/components/molecules/Carousel/Carousel.cjs +1 -0
  20. package/dist/components/molecules/Carousel/Carousel.d.ts +146 -0
  21. package/dist/components/molecules/Carousel/Carousel.js +363 -0
  22. package/dist/components/molecules/Carousel/carousel-layout.cjs +1 -0
  23. package/dist/components/molecules/Carousel/carousel-layout.d.ts +23 -0
  24. package/dist/components/molecules/Carousel/carousel-layout.js +22 -0
  25. package/dist/components/molecules/Carousel/carousel-math.cjs +1 -0
  26. package/dist/components/molecules/Carousel/carousel-math.d.ts +60 -0
  27. package/dist/components/molecules/Carousel/carousel-math.js +58 -0
  28. package/dist/components/molecules/EmptyState/EmptyState.cjs +1 -1
  29. package/dist/components/molecules/EmptyState/EmptyState.d.ts +7 -1
  30. package/dist/components/molecules/EmptyState/EmptyState.js +18 -14
  31. package/dist/components/molecules/LiquidTabs/LiquidNav.cjs +1 -0
  32. package/dist/components/molecules/LiquidTabs/LiquidNav.d.ts +62 -0
  33. package/dist/components/molecules/LiquidTabs/LiquidNav.js +78 -0
  34. package/dist/components/molecules/LiquidTabs/LiquidTabs.cjs +1 -0
  35. package/dist/components/molecules/LiquidTabs/LiquidTabs.d.ts +69 -0
  36. package/dist/components/molecules/LiquidTabs/LiquidTabs.js +92 -0
  37. package/dist/components/molecules/LiquidTabs/liquid-pill.cjs +1 -0
  38. package/dist/components/molecules/LiquidTabs/liquid-pill.d.ts +56 -0
  39. package/dist/components/molecules/LiquidTabs/liquid-pill.js +129 -0
  40. package/dist/components/molecules/LiquidTabs/liquid-tab-id.cjs +1 -0
  41. package/dist/components/molecules/LiquidTabs/liquid-tab-id.d.ts +12 -0
  42. package/dist/components/molecules/LiquidTabs/liquid-tab-id.js +6 -0
  43. package/dist/components/molecules/Toast/Toaster.cjs +1 -1
  44. package/dist/components/molecules/Toast/Toaster.d.ts +7 -0
  45. package/dist/components/molecules/Toast/Toaster.js +67 -45
  46. package/dist/components/organisms/Dialog/Dialog.cjs +1 -1
  47. package/dist/components/organisms/Dialog/Dialog.js +102 -79
  48. package/dist/components/organisms/Navbar/Navbar.cjs +1 -1
  49. package/dist/components/organisms/Navbar/Navbar.js +157 -157
  50. package/dist/components/organisms/Sheet/Sheet.cjs +1 -0
  51. package/dist/components/organisms/Sheet/Sheet.d.ts +85 -0
  52. package/dist/components/organisms/Sheet/Sheet.js +304 -0
  53. package/dist/components/organisms/Sheet/sheet-drag.cjs +1 -0
  54. package/dist/components/organisms/Sheet/sheet-drag.d.ts +40 -0
  55. package/dist/components/organisms/Sheet/sheet-drag.js +22 -0
  56. package/dist/components/organisms/Sheet/sheet-title.cjs +1 -0
  57. package/dist/components/organisms/Sheet/sheet-title.d.ts +10 -0
  58. package/dist/components/organisms/Sheet/sheet-title.js +17 -0
  59. package/dist/index.d.ts +5 -1
  60. package/dist/internal/motion.cjs +1 -0
  61. package/dist/internal/motion.d.ts +12 -0
  62. package/dist/internal/motion.js +21 -0
  63. package/dist/internal/page-inert.cjs +1 -0
  64. package/dist/internal/page-inert.d.ts +51 -0
  65. package/dist/internal/page-inert.js +36 -0
  66. package/dist/meta.json +3 -3
  67. package/dist/roster.cjs +1 -1
  68. package/dist/roster.css +1 -1
  69. package/dist/roster.js +130 -122
  70. package/package.json +1 -1
  71. package/dist/components/atoms/LiquidTabs/LiquidTabs.cjs +0 -1
  72. package/dist/components/atoms/LiquidTabs/LiquidTabs.d.ts +0 -39
  73. package/dist/components/atoms/LiquidTabs/LiquidTabs.js +0 -120
@@ -0,0 +1,85 @@
1
+ import * as React from "react";
2
+ export interface SheetProps {
3
+ isOpen: boolean;
4
+ /** Called by Escape, a backdrop click, the close button and a dismissing drag. */
5
+ onClose: () => void;
6
+ /**
7
+ * Called once the leave transition has finished and the sheet is gone.
8
+ *
9
+ * Clear the sheet's content here rather than in `onClose`. Cleared in
10
+ * `onClose`, it empties while it is still sliding out.
11
+ */
12
+ onAfterClose?: () => void;
13
+ /**
14
+ * The sheet's accessible name, rendered as an `h2`. Required, because a
15
+ * dialog without a name is announced as "dialog" and nothing else.
16
+ *
17
+ * A node rather than only a string, so a title can carry markup (a date in
18
+ * a `<time>`, a word in another style); the name is its text either way.
19
+ *
20
+ * Changing its text while the sheet is open counts as a content swap: focus
21
+ * moves to the new title so a screen reader announces it. Not while `busy`:
22
+ * a title that changes during loading is a provisional one being refined,
23
+ * and the reader keeps their place.
24
+ */
25
+ title: React.ReactNode;
26
+ /** Hides the title visually. It stays the accessible name. */
27
+ hideTitle?: boolean;
28
+ /** Wired to the dialog's `aria-describedby`. */
29
+ description?: React.ReactNode;
30
+ /** A slot in the header, before the close button: previous and next, say. */
31
+ actions?: React.ReactNode;
32
+ /**
33
+ * The sheet's width from 768px up, where it centers. A number is px. Below
34
+ * 768px the sheet is always full width.
35
+ */
36
+ maxWidth?: number | string;
37
+ /** What receives focus on open. Defaults to the close button. */
38
+ initialFocus?: React.RefObject<HTMLElement | null>;
39
+ /**
40
+ * Where focus goes on close, over whatever opened the sheet.
41
+ *
42
+ * A sheet opened on page load from a deep link has no opener, so without
43
+ * this focus falls to `<body>`.
44
+ */
45
+ returnFocus?: React.RefObject<HTMLElement | null> | HTMLElement | null;
46
+ /** Sets `aria-busy` on the content region while its content loads. */
47
+ busy?: boolean;
48
+ /** Drag the handle or header down to dismiss. The close button stays either way. */
49
+ dragToDismiss?: boolean;
50
+ /**
51
+ * Counts as a content swap when it changes, like `title` does. For content
52
+ * that changes under an unchanged title.
53
+ */
54
+ contentKey?: React.Key;
55
+ /** Applied to the panel. */
56
+ className?: string;
57
+ children: React.ReactNode;
58
+ }
59
+ /**
60
+ * A panel anchored to the bottom of the screen, for detail that should not
61
+ * take the reader off the page: full width on a phone, centered at `maxWidth`
62
+ * from 768px.
63
+ *
64
+ * Three stacked parts, and only the last scrolls: a drag handle, a header with
65
+ * the title, the `actions` slot and a close button, and the content. The header
66
+ * is a sibling of the scroller rather than sticky inside it, so it can never
67
+ * cover the element that has focus.
68
+ *
69
+ * Built on Headless UI's `Dialog`, so the focus trap, Escape and the scroll
70
+ * lock are its. The rest of the page is made `inert` and `aria-hidden` by
71
+ * Headless UI and Roster together, every child of `<body>` but the dialog
72
+ * layer and the toast host. The scroll lock pads `<html>`
73
+ * for the scrollbar it hides; a fixed element elsewhere on the page can still
74
+ * shift by that width.
75
+ *
76
+ * Renders an empty `<span hidden>` where it is placed, to find out whether it
77
+ * sits inside a scoped `.dark` subtree and which part of the page it was
78
+ * opened from. The sheet itself is portaled to `<body>`, so it could not tell
79
+ * otherwise.
80
+ *
81
+ * The bottom padding includes `env(safe-area-inset-bottom)`, which is 0 unless
82
+ * the page's viewport meta sets `viewport-fit=cover`.
83
+ */
84
+ declare const Sheet: ({ isOpen, onClose, onAfterClose, title, hideTitle, description, actions, maxWidth, initialFocus, returnFocus, busy, dragToDismiss, contentKey, className, children, }: SheetProps) => import("react/jsx-runtime").JSX.Element;
85
+ export { Sheet };
@@ -0,0 +1,304 @@
1
+ "use client";
2
+ import { jsxs as l, Fragment as K, jsx as n } from "react/jsx-runtime";
3
+ import * as s from "react";
4
+ import { Description as Q } from "../../../node_modules/@headlessui/react/dist/components/description/description.js";
5
+ import { Dialog as H, DialogBackdrop as J, DialogPanel as Z, DialogTitle as W } from "../../../node_modules/@headlessui/react/dist/components/dialog/dialog.js";
6
+ import { TransitionChild as O } from "../../../node_modules/@headlessui/react/dist/components/transition/transition.js";
7
+ import { cn as d } from "../../../lib/utils.js";
8
+ import { useDarkScope as tt } from "../../../internal/popup.js";
9
+ import { useReducedMotion as et } from "../../../internal/motion.js";
10
+ import { lockPage as rt } from "../../../internal/page-inert.js";
11
+ import { sheetDragOutcome as st, sheetReleaseVelocity as nt, exceedsSheetTapSlop as ot } from "./sheet-drag.js";
12
+ import { titleSignature as at } from "./sheet-title.js";
13
+ const it = "rst:transition-transform rst:duration-(--roster-sheet-enter-duration) rst:ease-(--roster-sheet-enter-easing) rst:data-leave:duration-(--roster-sheet-leave-duration) rst:data-leave:ease-(--roster-sheet-leave-easing) rst:data-closed:translate-y-full", ct = "rst:transition-opacity rst:duration-(--roster-sheet-fade-duration) rst:ease-out rst:data-closed:opacity-0", ut = "rst:transition-opacity rst:duration-(--roster-sheet-enter-duration) rst:ease-out rst:data-leave:duration-(--roster-sheet-leave-duration) rst:data-closed:opacity-0", lt = "rst:transition-opacity rst:duration-(--roster-sheet-fade-duration) rst:ease-out rst:data-closed:opacity-0", dt = [
14
+ "input:not([type=checkbox]):not([type=radio]):not([type=button]):not([type=submit]):not([type=reset]):not([type=range]):not([type=color]):not([type=file]):not([type=image])",
15
+ "textarea",
16
+ "select",
17
+ "[contenteditable]:not([contenteditable=false])"
18
+ ].join(",");
19
+ function ft(r) {
20
+ return r ? "current" in r ? r.current : r : null;
21
+ }
22
+ function pt({ target: r }) {
23
+ const u = s.useRef(r), f = s.useRef(!1);
24
+ return s.useEffect(() => {
25
+ u.current = r;
26
+ }), s.useEffect(() => (f.current = !1, () => {
27
+ f.current = !0, queueMicrotask(() => {
28
+ if (!f.current) return;
29
+ const p = ft(u.current);
30
+ p?.isConnected && p.focus();
31
+ });
32
+ }), []), null;
33
+ }
34
+ function ht() {
35
+ return /* @__PURE__ */ n(
36
+ "svg",
37
+ {
38
+ "aria-hidden": "true",
39
+ focusable: "false",
40
+ viewBox: "0 0 20 20",
41
+ className: "rst:h-5 rst:w-5",
42
+ fill: "none",
43
+ stroke: "currentColor",
44
+ strokeWidth: "2",
45
+ strokeLinecap: "round",
46
+ children: /* @__PURE__ */ n("path", { d: "M5 5l10 10M15 5L5 15" })
47
+ }
48
+ );
49
+ }
50
+ const Lt = ({
51
+ isOpen: r,
52
+ onClose: u,
53
+ onAfterClose: f,
54
+ title: p,
55
+ hideTitle: z = !1,
56
+ description: L,
57
+ actions: N,
58
+ maxWidth: v = 560,
59
+ initialFocus: x,
60
+ returnFocus: Y,
61
+ busy: h = !1,
62
+ dragToDismiss: E = !0,
63
+ contentKey: m,
64
+ className: B,
65
+ children: T
66
+ }) => {
67
+ const { ref: R, inDarkScope: _ } = tt(), D = et();
68
+ s.useLayoutEffect(() => {
69
+ if (r)
70
+ return rt(R.current);
71
+ }, [r, R]);
72
+ const i = s.useRef(null), I = s.useRef(null), w = s.useRef(null), k = s.useRef(null), C = s.useRef(null), S = s.useId(), g = at(p), P = s.useRef({ signature: g, contentKey: m, open: r, busy: h });
73
+ s.useEffect(() => {
74
+ const t = P.current;
75
+ if (P.current = { signature: g, contentKey: m, open: r, busy: h }, !t.open || !r) return;
76
+ const e = t.contentKey !== m, o = t.signature !== g && !t.busy;
77
+ !e && !o || (w.current?.focus(), C.current && (C.current.scrollTop = 0));
78
+ }, [g, m, r, h]), s.useEffect(() => {
79
+ if (!r) return;
80
+ const t = requestAnimationFrame(() => {
81
+ const e = i.current, o = document.activeElement;
82
+ !e || o && e.contains(o) || (x?.current ?? k.current)?.focus();
83
+ });
84
+ return () => cancelAnimationFrame(t);
85
+ }, [r, x]);
86
+ const [M, A] = s.useState(null);
87
+ s.useEffect(() => {
88
+ if (!r) return;
89
+ const t = window.visualViewport;
90
+ if (!t) return;
91
+ const e = () => {
92
+ const o = document.activeElement, c = !!o && !!i.current?.contains(o) && o.matches(dt);
93
+ A(c ? Math.round(t.height) : null);
94
+ };
95
+ return t.addEventListener("resize", e), document.addEventListener("focusin", e), document.addEventListener("focusout", e), () => {
96
+ t.removeEventListener("resize", e), document.removeEventListener("focusin", e), document.removeEventListener("focusout", e), A(null);
97
+ };
98
+ }, [r]);
99
+ const a = s.useRef(null), y = s.useRef(!1), F = s.useRef(r);
100
+ s.useEffect(() => {
101
+ F.current = r;
102
+ }, [r]);
103
+ const b = (t) => {
104
+ i.current?.style.setProperty("--rst-sheet-drag", `${t}px`);
105
+ }, j = (t) => {
106
+ const e = i.current;
107
+ a.current = null, e && (delete e.dataset.dragging, t === "close" ? (u(), requestAnimationFrame(() => {
108
+ F.current && !a.current && b(0);
109
+ })) : b(0));
110
+ };
111
+ s.useEffect(() => {
112
+ !r && a.current && (a.current = null, i.current && delete i.current.dataset.dragging), r && !a.current && b(0);
113
+ }, [r]);
114
+ const q = (t) => {
115
+ a.current || (y.current = !1, !(!E || !r) && (t.pointerType === "mouse" && (t.button !== 0 || !I.current?.contains(t.target)) || (a.current = {
116
+ pointerId: t.pointerId,
117
+ x0: t.clientX,
118
+ y0: t.clientY,
119
+ dy: 0,
120
+ phase: "pending",
121
+ samples: [{ t: t.timeStamp, y: t.clientY }]
122
+ })));
123
+ }, $ = (t) => {
124
+ const e = a.current;
125
+ if (!e || t.pointerId !== e.pointerId) return;
126
+ const o = t.clientX - e.x0, c = t.clientY - e.y0;
127
+ if (e.phase === "pending") {
128
+ if (!ot(o, c)) return;
129
+ if (Math.abs(o) > Math.abs(c)) {
130
+ e.phase = "ignored";
131
+ return;
132
+ }
133
+ e.phase = "dragging", y.current = !0;
134
+ try {
135
+ t.currentTarget.setPointerCapture(t.pointerId);
136
+ } catch {
137
+ }
138
+ i.current && (i.current.dataset.dragging = "");
139
+ }
140
+ e.phase === "dragging" && (e.dy = c, e.samples.push({ t: t.timeStamp, y: t.clientY }), e.samples.length > 32 && e.samples.shift(), b(Math.max(0, c)));
141
+ }, U = (t) => {
142
+ const e = a.current;
143
+ if (!e || t.pointerId !== e.pointerId) return;
144
+ if (e.phase !== "dragging") {
145
+ a.current = null;
146
+ return;
147
+ }
148
+ e.samples.push({ t: t.timeStamp, y: t.clientY });
149
+ const o = t.clientY - e.y0, c = i.current?.getBoundingClientRect().height ?? 0;
150
+ j(st(o, c, nt(e.samples)));
151
+ }, V = (t) => {
152
+ const e = a.current;
153
+ !e || t.pointerId !== e.pointerId || (e.phase === "dragging" ? j("stay") : a.current = null);
154
+ }, X = (t) => {
155
+ !y.current || t.detail === 0 || (y.current = !1, t.preventDefault(), t.stopPropagation());
156
+ }, G = {
157
+ "--rst-sheet-max-width": typeof v == "number" ? `${v}px` : v,
158
+ ...M !== null && { "--rst-sheet-keyboard-cap": `${M}px` }
159
+ };
160
+ return /* @__PURE__ */ l(K, { children: [
161
+ /* @__PURE__ */ n("span", { ref: R, hidden: !0 }),
162
+ /* @__PURE__ */ l(
163
+ H,
164
+ {
165
+ open: r,
166
+ as: "div",
167
+ onClose: u,
168
+ initialFocus: x ?? k,
169
+ className: d(
170
+ "rst:font-ui rst:relative rst:z-50 rst:focus:outline-hidden",
171
+ _ && "dark"
172
+ ),
173
+ children: [
174
+ /* @__PURE__ */ n(
175
+ J,
176
+ {
177
+ transition: !0,
178
+ "data-testid": "sheet-backdrop",
179
+ className: d(
180
+ "rst:fixed rst:inset-0 rst:bg-(--roster-sheet-backdrop)",
181
+ D ? lt : ut
182
+ )
183
+ }
184
+ ),
185
+ /* @__PURE__ */ n("div", { className: "rst:fixed rst:inset-0 rst:flex rst:items-end rst:justify-center", children: /* @__PURE__ */ n(O, { afterLeave: f, children: /* @__PURE__ */ l(
186
+ Z,
187
+ {
188
+ ref: i,
189
+ style: G,
190
+ "data-testid": "sheet-panel",
191
+ className: d(
192
+ /* 92dvh, or the visual viewport while the keyboard is up,
193
+ whichever is shorter. */
194
+ "rst:relative rst:flex rst:max-h-[min(92dvh,var(--rst-sheet-keyboard-cap,92dvh))] rst:w-full rst:flex-col",
195
+ "rst:md:max-w-(--rst-sheet-max-width)",
196
+ "rst:rounded-t-2xl rst:border rst:border-b-0 rst:elevation-overlay",
197
+ "rst:bg-[var(--roster-popover-bg)] rst:border-[var(--roster-popover-border)] rst:text-[var(--roster-popover-text)]",
198
+ /* The ring offset paints the gap between a focused control and
199
+ its ring, so it has to be this surface, not the page. */
200
+ "rst:[--roster-ring-offset:var(--roster-popover-bg)]",
201
+ "rst:pb-[env(safe-area-inset-bottom)]",
202
+ "rst:translate-y-[var(--rst-sheet-drag,0px)]",
203
+ D ? ct : it,
204
+ "rst:data-dragging:transition-none",
205
+ B
206
+ ),
207
+ children: [
208
+ /* @__PURE__ */ l(
209
+ "div",
210
+ {
211
+ "data-testid": "sheet-drag-zone",
212
+ className: d("rst:shrink-0", E && "rst:touch-none"),
213
+ onPointerDown: q,
214
+ onPointerMove: $,
215
+ onPointerUp: U,
216
+ onPointerCancel: V,
217
+ onClickCapture: X,
218
+ children: [
219
+ /* @__PURE__ */ n(
220
+ "div",
221
+ {
222
+ ref: I,
223
+ "aria-hidden": "true",
224
+ "data-testid": "sheet-handle",
225
+ className: d(
226
+ /* The grip sits near the top edge, where a sheet's grabber
227
+ is expected, inside a 44px strip. The header below pulls
228
+ up over the strip's lower part, so a touch there drags
229
+ too (the header is a drag zone) and a mouse drags from
230
+ the 32px above it, the full width of the sheet. */
231
+ "rst:flex rst:h-11 rst:items-start rst:justify-center rst:pt-2",
232
+ E && "rst:cursor-grab rst:active:cursor-grabbing"
233
+ ),
234
+ children: /* @__PURE__ */ n("span", { className: "rst:h-1 rst:w-10 rst:rounded-full rst:bg-current rst:opacity-30" })
235
+ }
236
+ ),
237
+ /* @__PURE__ */ l("div", { className: "rst:-mt-3 rst:flex rst:items-start rst:gap-2 rst:px-5 rst:pb-4", children: [
238
+ /* @__PURE__ */ l("div", { className: "rst:min-w-0 rst:flex-1 rst:pt-2", children: [
239
+ /* @__PURE__ */ n(
240
+ W,
241
+ {
242
+ as: "h2",
243
+ id: S,
244
+ ref: w,
245
+ tabIndex: -1,
246
+ className: d(
247
+ "rst:text-lg rst:font-semibold rst:text-inherit rst:focus:outline-hidden",
248
+ z && "rst:sr-only"
249
+ ),
250
+ children: p
251
+ }
252
+ ),
253
+ L && /* @__PURE__ */ n(
254
+ Q,
255
+ {
256
+ as: "p",
257
+ className: "rst:mt-1 rst:text-sm rst:text-inherit rst:opacity-75",
258
+ children: L
259
+ }
260
+ )
261
+ ] }),
262
+ N && /* 44px tall like the close button, so actions of any
263
+ height center on the same line as it. */
264
+ /* @__PURE__ */ n("div", { className: "rst:flex rst:h-11 rst:shrink-0 rst:items-center rst:gap-1", children: N }),
265
+ /* @__PURE__ */ n(
266
+ "button",
267
+ {
268
+ ref: k,
269
+ type: "button",
270
+ onClick: u,
271
+ "aria-label": "Close",
272
+ className: "rst:inline-flex rst:size-11 rst:shrink-0 rst:-mr-2 rst:cursor-pointer rst:items-center rst:justify-center rst:rounded-md rst:bg-transparent rst:text-inherit rst:opacity-60 rst:transition-opacity rst:hover:opacity-100 rst:focus:outline-hidden rst:focus-visible:opacity-100 rst:focus-visible:ring-2 rst:focus-visible:ring-ring rst:focus-visible:ring-offset-2 rst:ring-offset-background",
273
+ children: /* @__PURE__ */ n(ht, {})
274
+ }
275
+ )
276
+ ] })
277
+ ]
278
+ }
279
+ ),
280
+ /* @__PURE__ */ n(
281
+ "div",
282
+ {
283
+ ref: C,
284
+ role: "region",
285
+ "aria-labelledby": S,
286
+ tabIndex: 0,
287
+ "data-testid": "sheet-content",
288
+ "aria-busy": h || void 0,
289
+ className: "rst:min-h-0 rst:flex-1 rst:overflow-y-auto rst:overscroll-contain rst:px-5 rst:pb-4 rst:focus:outline-hidden rst:ring-inset rst:focus-visible:ring-2 rst:focus-visible:ring-ring",
290
+ children: T
291
+ }
292
+ ),
293
+ /* @__PURE__ */ n(pt, { target: Y })
294
+ ]
295
+ }
296
+ ) }) })
297
+ ]
298
+ }
299
+ )
300
+ ] });
301
+ };
302
+ export {
303
+ Lt as Sheet
304
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const S=8,r=.3,n=.5,T=16,_=100;function s(t,e,E){return E<=-n||t<=S?"stay":t>e*r||E>=n&&t>=T?"close":"stay"}function u(t,e){return Math.hypot(t,e)>S}function I(t){if(t.length<2)return 0;const e=t[t.length-1],E=t.find(c=>e.t-c.t<=_)??e,o=e.t-E.t;return o>0?(e.y-E.y)/o:0}exports.SHEET_CLOSE_DISTANCE=r;exports.SHEET_FLICK_MIN_DISTANCE=T;exports.SHEET_FLICK_VELOCITY=n;exports.SHEET_TAP_SLOP=S;exports.SHEET_VELOCITY_WINDOW=_;exports.exceedsSheetTapSlop=u;exports.sheetDragOutcome=s;exports.sheetReleaseVelocity=I;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * When a drag on a sheet's handle or header dismisses it.
3
+ *
4
+ * Pure, and kept out of the component so the thresholds can be tested with
5
+ * literal numbers. jsdom has no layout, so a test that read the height from the
6
+ * DOM would be testing zero.
7
+ */
8
+ /** Movement at or under this, in px, is a tap rather than a drag. */
9
+ export declare const SHEET_TAP_SLOP = 8;
10
+ /** A drag further than this share of the sheet's height closes it. */
11
+ export declare const SHEET_CLOSE_DISTANCE = 0.3;
12
+ /** A downward flick at or above this speed, in px/ms, closes the sheet... */
13
+ export declare const SHEET_FLICK_VELOCITY = 0.5;
14
+ /** ...as long as it traveled at least this far, in px. */
15
+ export declare const SHEET_FLICK_MIN_DISTANCE = 16;
16
+ /** Release velocity is averaged over this much of the end of the drag, in ms. */
17
+ export declare const SHEET_VELOCITY_WINDOW = 100;
18
+ export type SheetDragOutcome = "close" | "stay";
19
+ /**
20
+ * @param dy downward travel in px; negative is upward
21
+ * @param sheetHeight the sheet's rendered height in px
22
+ * @param velocity px/ms at release, downward positive, from `sheetReleaseVelocity`
23
+ */
24
+ export declare function sheetDragOutcome(dy: number, sheetHeight: number, velocity: number): SheetDragOutcome;
25
+ /** Whether a pointer that moved this far has stopped being a tap. */
26
+ export declare function exceedsSheetTapSlop(dx: number, dy: number): boolean;
27
+ export interface SheetDragSample {
28
+ /** A timestamp in ms, from the pointer event. */
29
+ t: number;
30
+ /** The pointer's clientY in px. */
31
+ y: number;
32
+ }
33
+ /**
34
+ * Downward speed in px/ms over the last `SHEET_VELOCITY_WINDOW` ms of samples.
35
+ *
36
+ * The window is measured back from the LAST sample, not from now: a finger
37
+ * that stops and then lifts has a last sample from when it stopped, and the
38
+ * pause belongs in the average. Zero when the window holds a single instant.
39
+ */
40
+ export declare function sheetReleaseVelocity(samples: readonly SheetDragSample[]): number;
@@ -0,0 +1,22 @@
1
+ const e = 8, S = 0.3, r = 0.5, I = 16, o = 100;
2
+ function C(t, E, _) {
3
+ return _ <= -0.5 || t <= 8 ? "stay" : t > E * 0.3 || _ >= 0.5 && t >= 16 ? "close" : "stay";
4
+ }
5
+ function c(t, E) {
6
+ return Math.hypot(t, E) > 8;
7
+ }
8
+ function L(t) {
9
+ if (t.length < 2) return 0;
10
+ const E = t[t.length - 1], _ = t.find((T) => E.t - T.t <= 100) ?? E, n = E.t - _.t;
11
+ return n > 0 ? (E.y - _.y) / n : 0;
12
+ }
13
+ export {
14
+ S as SHEET_CLOSE_DISTANCE,
15
+ I as SHEET_FLICK_MIN_DISTANCE,
16
+ r as SHEET_FLICK_VELOCITY,
17
+ e as SHEET_TAP_SLOP,
18
+ o as SHEET_VELOCITY_WINDOW,
19
+ c as exceedsSheetTapSlop,
20
+ C as sheetDragOutcome,
21
+ L as sheetReleaseVelocity
22
+ };
@@ -0,0 +1 @@
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("react");function l(t){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(t){for(const r in t)if(r!=="default"){const n=Object.getOwnPropertyDescriptor(t,r);Object.defineProperty(e,r,n.get?n:{enumerable:!0,get:()=>t[r]})}}return e.default=t,Object.freeze(e)}const s=l(c);function a(t){if(t==null||typeof t=="boolean")return"";if(typeof t=="string"||typeof t=="number"||typeof t=="bigint")return String(t);if(Array.isArray(t))return t.map(a).join("");if(s.isValidElement(t)){const{children:e,...r}=t.props;if(typeof t.type=="string")return a(e);const n=t.type,u=Object.entries(r).filter(([i])=>i!=="className"&&i!=="style").map(([i,o])=>o instanceof Date?`${i}=${o.toISOString()}`:["string","number","boolean","bigint"].includes(typeof o)?`${i}=${String(o)}`:null).filter(Boolean).join(",");return`<${n.displayName??n.name??"?"}${u?` ${u}`:""}>`+a(e)}return""}exports.titleSignature=a;
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ /**
3
+ * What a title says, for telling whether it changed: its text, and for a
4
+ * component inside it, the component's name and its plain props (a date
5
+ * included), so `<NightDate date={d} />` says something even with no
6
+ * children. Styling is not content: `className` and `style` never count, on a
7
+ * component or an element. A component whose words change with nothing in its
8
+ * props (it reads a context, say) is invisible here; pass `contentKey`.
9
+ */
10
+ export declare function titleSignature(node: React.ReactNode): string;
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import * as a from "react";
3
+ function n(t) {
4
+ if (t == null || typeof t == "boolean") return "";
5
+ if (typeof t == "string" || typeof t == "number" || typeof t == "bigint") return String(t);
6
+ if (Array.isArray(t)) return t.map(n).join("");
7
+ if (a.isValidElement(t)) {
8
+ const { children: e, ...p } = t.props;
9
+ if (typeof t.type == "string") return n(e);
10
+ const s = t.type, f = Object.entries(p).filter(([r]) => r !== "className" && r !== "style").map(([r, i]) => i instanceof Date ? `${r}=${i.toISOString()}` : ["string", "number", "boolean", "bigint"].includes(typeof i) ? `${r}=${String(i)}` : null).filter(Boolean).join(",");
11
+ return `<${s.displayName ?? s.name ?? "?"}${f ? ` ${f}` : ""}>` + n(e);
12
+ }
13
+ return "";
14
+ }
15
+ export {
16
+ n as titleSignature
17
+ };
package/dist/index.d.ts CHANGED
@@ -37,7 +37,6 @@ export * from './components/atoms/Switch/switch-variants';
37
37
  export * from './components/atoms/Textarea/Textarea';
38
38
  export * from './components/atoms/Textarea/textarea-variants';
39
39
  export * from './components/atoms/AvatarStrip/AvatarStrip';
40
- export * from './components/atoms/LiquidTabs/LiquidTabs';
41
40
  export * from './components/atoms/Tooltip/Tooltip';
42
41
  export * from './components/atoms/CollapsibleSection/CollapsibleSection';
43
42
  export * from './components/atoms/SegmentBar/SegmentBar';
@@ -58,6 +57,10 @@ export * from './components/molecules/RadioGroup/RadioGroup';
58
57
  export * from './components/molecules/RadioGroup/radio-group-variants';
59
58
  export * from './components/molecules/Breadcrumbs/Breadcrumbs';
60
59
  export * from './components/molecules/Breadcrumbs/breadcrumb-variants';
60
+ export * from './components/molecules/LiquidTabs/LiquidTabs';
61
+ export * from './components/molecules/LiquidTabs/LiquidNav';
62
+ export * from './components/molecules/LiquidTabs/liquid-tab-id';
63
+ export * from './components/molecules/Carousel/Carousel';
61
64
  export * from './components/molecules/CallToAction/CallToAction';
62
65
  export * from './components/molecules/CallToAction/call-to-action-variants';
63
66
  export * from './components/molecules/EmptyState/EmptyState';
@@ -72,6 +75,7 @@ export * from './components/organisms/Dialog/Dialog';
72
75
  export * from './components/organisms/Footer/Footer';
73
76
  export * from './components/organisms/Navbar/Navbar';
74
77
  export * from './components/organisms/Navbar/navbar-variants';
78
+ export * from './components/organisms/Sheet/Sheet';
75
79
  export * from './components/organisms/Table/Table';
76
80
  export * from './components/organisms/Table/table-variants';
77
81
  export * from './hooks/useCountdown';
@@ -0,0 +1 @@
1
+ "use client";"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=require("react");function i(e){const t=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(e){for(const n in e)if(n!=="default"){const r=Object.getOwnPropertyDescriptor(e,n);Object.defineProperty(t,n,r.get?r:{enumerable:!0,get:()=>e[n]})}}return t.default=e,Object.freeze(t)}const d=i(c),a="(prefers-reduced-motion: reduce)";function o(){return typeof window>"u"||typeof window.matchMedia!="function"?null:window.matchMedia(a)}function u(){return o()?.matches??!1}function s(e){const t=o();return t?(t.addEventListener("change",e),()=>t.removeEventListener("change",e)):()=>{}}function f(){return d.useSyncExternalStore(s,u,()=>!1)}exports.prefersReducedMotion=u;exports.useReducedMotion=f;
@@ -0,0 +1,12 @@
1
+ /** Read at the moment of asking. For code that animates once, then forgets. */
2
+ export declare function prefersReducedMotion(): boolean;
3
+ /**
4
+ * The same preference as React state, so a component re-renders when the
5
+ * setting changes while it is on screen.
6
+ *
7
+ * `false` on the server, and during hydration, which renders what the server
8
+ * did before correcting itself. A component rendered on the client after that
9
+ * reads the real value on its first render. A portaled overlay does not render
10
+ * on the server anyway, and opens after hydration, so it animates correctly.
11
+ */
12
+ export declare function useReducedMotion(): boolean;
@@ -0,0 +1,21 @@
1
+ "use client";
2
+ import * as r from "react";
3
+ const u = "(prefers-reduced-motion: reduce)";
4
+ function t() {
5
+ return typeof window > "u" || typeof window.matchMedia != "function" ? null : window.matchMedia(u);
6
+ }
7
+ function i() {
8
+ return t()?.matches ?? !1;
9
+ }
10
+ function o(n) {
11
+ const e = t();
12
+ return e ? (e.addEventListener("change", n), () => e.removeEventListener("change", n)) : () => {
13
+ };
14
+ }
15
+ function c() {
16
+ return r.useSyncExternalStore(o, i, () => !1);
17
+ }
18
+ export {
19
+ i as prefersReducedMotion,
20
+ c as useReducedMotion
21
+ };
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u="data-roster-toaster",d="headlessui-portal-root",f={[u]:""},n=[],i=new Map,o=new Set;let r=null;function c(e){return e.id===d||e.hasAttribute(u)||n.includes(e)}function l(e){i.set(e,e.getAttribute("aria-hidden")),e.inert=!0,e.setAttribute("aria-hidden","true")}function h(e){if(!i.has(e))return;const t=i.get(e);i.delete(e),e.inert=!1,t===null?e.removeAttribute("aria-hidden"):e.setAttribute("aria-hidden",t)}function s(){const e=document.body;for(const t of Array.from(i.keys()))(!n.length||t.parentElement!==e||c(t))&&h(t);if(n.length)for(const t of Array.from(e.children))!(t instanceof HTMLElement)||i.has(t)||c(t)||(t.inert?o.add(t):o.has(t)||l(t));n.length||o.clear(),n.length&&!r?(r=new MutationObserver(s),r.observe(e,{childList:!0})):!n.length&&r&&(r.disconnect(),r=null)}function A(e){if(typeof document>"u")return()=>{};const t=e?.closest("body > *")??null;n.push(t),s();let a=!1;return()=>{a||(a=!0,n.splice(n.indexOf(t),1),s())}}exports.TOASTER_MARKER=f;exports.lockPage=A;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Makes the page behind an open `Dialog` or `Sheet` inert and hidden from
3
+ * assistive technology, all of it.
4
+ *
5
+ * Internal, and not exported from the package.
6
+ *
7
+ * Headless UI does part of this and not the rest. It marks exactly one child
8
+ * of `<body>`: the one the dialog is rendered from, found with
9
+ * `closest("body > *:not(#headlessui-portal-root)")`, plus the dialog's
10
+ * sibling portals. A page laid out as `body > header, main, footer` gets only
11
+ * its `<main>` marked, and the header and footer stay reachable by a screen
12
+ * reader's virtual cursor. A toast host mounted inside that one child goes the
13
+ * other way: it is marked, and a toast shown over the dialog is not announced.
14
+ *
15
+ * So while any lock is held, every direct child of `<body>` is `inert` and
16
+ * `aria-hidden`, except three:
17
+ *
18
+ * - Headless UI's portal root, which holds the dialog itself and any dialog
19
+ * stacked on it. Headless UI handles what is inside it.
20
+ * - Anything carrying `data-roster-toaster`, which `Toaster` renders into.
21
+ * - The child each lock was taken from. That one is Headless UI's, and it is
22
+ * left to Headless UI because two owners of one element can not both
23
+ * restore it: each records what it found, and whichever restores second
24
+ * writes back the other's `inert`, leaving the page stuck. Which one goes
25
+ * second depends on render timing, so skipping the element is the only
26
+ * order that is always right.
27
+ *
28
+ * The same reasoning covers an element that is already `inert` when a lock
29
+ * reaches it: someone else marked it (a dialog built on Headless UI directly,
30
+ * say, with a Roster one opened inside it) and will restore it. It stays
31
+ * theirs until the last lock is released, even if they let go of it in the
32
+ * meantime. Headless UI does let go and re-mark when the dialog on top
33
+ * changes, and marking it in that gap would make a second owner after all.
34
+ * The cost is that such an element can be live while a lock is held; the
35
+ * alternative is a page left inert after every dialog has closed.
36
+ *
37
+ * Locks are counted. The page is marked on the first and restored on the
38
+ * last. Each element the lock marked gets back the `aria-hidden` it had and
39
+ * is made not inert, which is what it was, so a page that marked something
40
+ * itself keeps it.
41
+ */
42
+ /** Put on the element `Toaster` renders into, so a toast stays announced. */
43
+ export declare const TOASTER_MARKER: {
44
+ readonly "data-roster-toaster": "";
45
+ };
46
+ /**
47
+ * Takes a lock for a dialog rendered at `anchor`: any element in the tree
48
+ * where the dialog is written, not inside its portal. Returns the release,
49
+ * which is safe to call more than once.
50
+ */
51
+ export declare function lockPage(anchor: Element | null): () => void;
@@ -0,0 +1,36 @@
1
+ const u = "data-roster-toaster", d = "headlessui-portal-root", h = { [u]: "" }, n = [], i = /* @__PURE__ */ new Map(), o = /* @__PURE__ */ new Set();
2
+ let r = null;
3
+ function c(e) {
4
+ return e.id === d || e.hasAttribute(u) || n.includes(e);
5
+ }
6
+ function f(e) {
7
+ i.set(e, e.getAttribute("aria-hidden")), e.inert = !0, e.setAttribute("aria-hidden", "true");
8
+ }
9
+ function l(e) {
10
+ if (!i.has(e)) return;
11
+ const t = i.get(e);
12
+ i.delete(e), e.inert = !1, t === null ? e.removeAttribute("aria-hidden") : e.setAttribute("aria-hidden", t);
13
+ }
14
+ function s() {
15
+ const e = document.body;
16
+ for (const t of Array.from(i.keys()))
17
+ (!n.length || t.parentElement !== e || c(t)) && l(t);
18
+ if (n.length)
19
+ for (const t of Array.from(e.children))
20
+ !(t instanceof HTMLElement) || i.has(t) || c(t) || (t.inert ? o.add(t) : o.has(t) || f(t));
21
+ n.length || o.clear(), n.length && !r ? (r = new MutationObserver(s), r.observe(e, { childList: !0 })) : !n.length && r && (r.disconnect(), r = null);
22
+ }
23
+ function A(e) {
24
+ if (typeof document > "u") return () => {
25
+ };
26
+ const t = e?.closest("body > *") ?? null;
27
+ n.push(t), s();
28
+ let a = !1;
29
+ return () => {
30
+ a || (a = !0, n.splice(n.indexOf(t), 1), s());
31
+ };
32
+ }
33
+ export {
34
+ h as TOASTER_MARKER,
35
+ A as lockPage
36
+ };