@botiverse/raft-ui 0.10.7 → 0.12.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.
@@ -1,988 +0,0 @@
1
- import { cn } from "./cn.mjs";
2
- import { Dialog } from "@base-ui/react/dialog";
3
- import { X } from "lucide-react";
4
- import { tv } from "tailwind-variants/lite";
5
- import { createContext, use, useContext, useMemo } from "react";
6
- import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
- import { Button } from "@base-ui/react/button";
8
- import { mergeProps } from "@base-ui/react/merge-props";
9
- import { useRender } from "@base-ui/react/use-render";
10
- import { Drawer } from "@base-ui/react/drawer";
11
- //#region src/lib/tv.ts
12
- const tv$1 = ((options) => {
13
- const recipe = tv(options);
14
- const wrappedRecipe = ((props) => {
15
- const result = recipe(props);
16
- if (result == null || typeof result === "string") return cn(result);
17
- return Object.fromEntries(Object.entries(result).map(([slotName, slot]) => [slotName, (slotProps) => cn(slot(slotProps))]));
18
- });
19
- return Object.assign(wrappedRecipe, recipe);
20
- });
21
- //#endregion
22
- //#region src/lib/theme/theme-context.ts
23
- const ThemeStateContext = createContext({
24
- mode: "light",
25
- resolvedMode: "light",
26
- setMode: () => void 0
27
- });
28
- //#endregion
29
- //#region src/lib/theme/use-theme.ts
30
- /** Read and update the colour mode. */
31
- function useTheme() {
32
- return use(ThemeStateContext);
33
- }
34
- //#endregion
35
- //#region src/lib/theme/theme-scope.ts
36
- /**
37
- * Carry the provider's resolved mode (`light` / `dark`) onto content that
38
- * renders outside its subtree (a portal), so a dark island's popup stays dark.
39
- */
40
- function resolveThemeScopeProps({ className, ...props }, { resolvedMode }) {
41
- const resolvedClassName = typeof className === "function" ? (state) => cn(className(state), resolvedMode) : typeof className === "string" ? cn(className, resolvedMode) : className ?? resolvedMode;
42
- return {
43
- ...props,
44
- className: resolvedClassName
45
- };
46
- }
47
- function useThemeScopeProps(props) {
48
- return resolveThemeScopeProps(props, useTheme());
49
- }
50
- //#endregion
51
- //#region src/lib/overlay-depth.tsx
52
- const OverlayStackContext = createContext([]);
53
- function OverlayLayerProvider({ kind, modal, children }) {
54
- const stack = useContext(OverlayStackContext);
55
- const value = useMemo(() => modal ? [...stack, kind] : stack, [
56
- stack,
57
- kind,
58
- modal
59
- ]);
60
- return /* @__PURE__ */ jsx(OverlayStackContext.Provider, {
61
- value,
62
- children
63
- });
64
- }
65
- /**
66
- * The modal surface this overlay's own surface was opened from, or null when
67
- * it is the first. Called inside a surface, so the last entry is the surface
68
- * itself and the one before it is its parent.
69
- */
70
- function useParentOverlayKind() {
71
- const stack = useContext(OverlayStackContext);
72
- return stack.length >= 2 ? stack[stack.length - 2] : null;
73
- }
74
- /**
75
- * Wraps a dialog or drawer root's children in its layer. Base UI lets those
76
- * children be a render function (`({ payload }) => …`), so keep that shape.
77
- */
78
- function withOverlayLayer(kind, modal, children) {
79
- if (typeof children === "function") return (...args) => /* @__PURE__ */ jsx(OverlayLayerProvider, {
80
- kind,
81
- modal,
82
- children: children(...args)
83
- });
84
- return /* @__PURE__ */ jsx(OverlayLayerProvider, {
85
- kind,
86
- modal,
87
- children
88
- });
89
- }
90
- //#endregion
91
- //#region src/lib/default-native-button.ts
92
- function defaultNativeButton(render) {
93
- if (!render || typeof render !== "object" || !("type" in render)) return;
94
- const element = render;
95
- if (typeof element.type === "string") return element.type === "button";
96
- }
97
- //#endregion
98
- //#region src/components/spinner/spinner.tsx
99
- const SPINNER_STYLE = `
100
- @keyframes raft-spinner-pixel {
101
- 0%,
102
- 24% {
103
- opacity: 1;
104
- }
105
- 25%,
106
- 100% {
107
- opacity: 0.2;
108
- }
109
- }
110
-
111
- [data-slot="spinner"] [data-spinner-pixel] {
112
- animation: raft-spinner-pixel 1s steps(1) infinite;
113
- }
114
-
115
- [data-slot="spinner"] [data-spinner-pixel]:nth-child(2) {
116
- animation-delay: 0.25s;
117
- }
118
-
119
- [data-slot="spinner"] [data-spinner-pixel]:nth-child(4) {
120
- animation-delay: 0.5s;
121
- }
122
-
123
- [data-slot="spinner"] [data-spinner-pixel]:nth-child(3) {
124
- animation-delay: 0.75s;
125
- }
126
-
127
- @media (prefers-reduced-motion: reduce) {
128
- [data-slot="spinner"] [data-spinner-pixel] {
129
- animation: none;
130
- }
131
-
132
- [data-slot="spinner"] [data-spinner-pixel]:first-child {
133
- opacity: 1;
134
- }
135
- }
136
- `;
137
- const spinner = tv$1({
138
- slots: {
139
- root: [
140
- "relative inline-flex shrink-0 items-center justify-center",
141
- "leading-none text-current",
142
- "[--spinner-pixel:5px] [--spinner-gap:2px]"
143
- ],
144
- ring: ["shrink-0", "grid size-auto grid-cols-[repeat(2,var(--spinner-pixel))] gap-(--spinner-gap)"],
145
- pixel: ["block size-(--spinner-pixel) rounded-[1px] bg-current opacity-20"]
146
- },
147
- variants: {
148
- size: {
149
- xs: { root: ["[--spinner-pixel:4px] [--spinner-gap:1.5px]"] },
150
- sm: {},
151
- md: { root: ["[--spinner-pixel:6px] [--spinner-gap:2px]"] },
152
- lg: { root: ["[--spinner-pixel:8px] [--spinner-gap:3px]"] }
153
- },
154
- variant: {
155
- default: {},
156
- inverse: { root: ["text-foreground-inverse"] }
157
- }
158
- },
159
- defaultVariants: { size: "sm" }
160
- });
161
- const spinnerStyle = /* @__PURE__ */ jsx("style", {
162
- href: "raft-ui-spinner",
163
- precedence: "default",
164
- children: SPINNER_STYLE
165
- });
166
- function Spinner({ size = "sm", variant = "default", className, render, ...props }) {
167
- const { root, ring, pixel } = spinner({
168
- size,
169
- variant
170
- });
171
- const children = /* @__PURE__ */ jsxs(Fragment$1, { children: [spinnerStyle, /* @__PURE__ */ jsxs("span", {
172
- "data-spinner-grid": "",
173
- "aria-hidden": "true",
174
- className: ring(),
175
- children: [
176
- /* @__PURE__ */ jsx("span", {
177
- "data-spinner-pixel": "",
178
- className: pixel()
179
- }),
180
- /* @__PURE__ */ jsx("span", {
181
- "data-spinner-pixel": "",
182
- className: pixel()
183
- }),
184
- /* @__PURE__ */ jsx("span", {
185
- "data-spinner-pixel": "",
186
- className: pixel()
187
- }),
188
- /* @__PURE__ */ jsx("span", {
189
- "data-spinner-pixel": "",
190
- className: pixel()
191
- })
192
- ]
193
- })] });
194
- const defaultProps = {
195
- role: "status",
196
- "aria-label": "Loading",
197
- className: root({ className }),
198
- children
199
- };
200
- return useRender({
201
- defaultTagName: "span",
202
- render,
203
- props: mergeProps(defaultProps, props),
204
- state: {
205
- slot: "spinner",
206
- size
207
- }
208
- });
209
- }
210
- //#endregion
211
- //#region src/components/button/button.tsx
212
- const sticker = (fill) => [`border-sticker-ink ${fill} text-sticker-ink ring-[1.5px] ring-sticker-edge`, "hover:press-overlay-sticker active:press-overlay-sticker"];
213
- const outline = (ink) => [`border-line-strong bg-layer-card ${ink}`, "hover:press-overlay active:press-overlay"];
214
- const textMd = [
215
- "h-8 gap-2 rounded-control px-3 py-0 text-control",
216
- "has-data-[icon=inline-start]:pl-2.5 has-data-[icon=inline-end]:pr-2.5",
217
- "[&_svg:not([class*='size-'])]:size-4 **:data-icon:size-[1em]",
218
- "after:absolute after:inset-x-0 after:-inset-y-1.5"
219
- ];
220
- const iconMd = ["size-8 rounded-control p-0 [&_svg:not([class*='size-'])]:size-[18px]", "after:absolute after:-inset-1.5"];
221
- const buttonVariants = tv$1({
222
- slots: {
223
- root: [
224
- "relative inline-flex shrink-0 items-center justify-center",
225
- "whitespace-nowrap select-none",
226
- "rounded-none border-[1.5px] font-sans font-bold shadow-none",
227
- "transition-colors duration-100",
228
- "outline-[1px] outline-offset-2 outline-transparent focus-visible:outline-transparent",
229
- "focus-visible:shadow-focus",
230
- "data-[loading=true]:cursor-wait data-[loading=true]:opacity-100",
231
- "[&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
232
- "disabled:pointer-events-none aria-disabled:cursor-not-allowed",
233
- "disabled:transform-none aria-disabled:transform-none",
234
- "aria-disabled:hover:translate-none",
235
- "aria-disabled:active:translate-none aria-disabled:active:scale-none",
236
- "disabled:opacity-100 aria-disabled:opacity-100",
237
- "disabled:border-line-hairline disabled:bg-fill-muted disabled:bg-none disabled:text-foreground-disabled disabled:ring-0",
238
- "aria-disabled:border-line-hairline aria-disabled:bg-fill-muted aria-disabled:bg-none aria-disabled:text-foreground-disabled aria-disabled:ring-0"
239
- ],
240
- content: "relative z-10 inline-flex items-center justify-center gap-[inherit] data-[loading=true]:invisible",
241
- loadingIndicator: "pointer-events-none absolute inset-0 z-10 flex items-center justify-center"
242
- },
243
- variants: {
244
- variant: {
245
- primary: { root: sticker("bg-attention-400") },
246
- accent: { root: sticker("bg-attention-400") },
247
- warning: { root: sticker("bg-warning") },
248
- danger: { root: sticker("bg-danger") },
249
- default: { root: outline("text-foreground") },
250
- outline: { root: outline("text-foreground") },
251
- information: { root: outline("text-foreground") },
252
- muted: { root: outline("text-foreground") },
253
- success: { root: outline("text-foreground") },
254
- "danger-secondary": { root: outline("text-danger-strong") },
255
- "danger-outline": { root: outline("text-danger-strong") },
256
- ghost: { root: ["border-transparent bg-transparent text-foreground", "hover:bg-fill-pressed active:bg-fill-pressed"] },
257
- "on-frame": { root: [
258
- "border-transparent bg-transparent text-foreground-on-frame",
259
- "hover:bg-fill-frame-pressed active:bg-fill-frame-pressed",
260
- "aria-expanded:bg-fill-frame-pressed data-popup-open:bg-fill-frame-pressed",
261
- "disabled:border-transparent disabled:bg-transparent",
262
- "aria-disabled:border-transparent aria-disabled:bg-transparent"
263
- ] }
264
- },
265
- size: {
266
- xs: { root: textMd },
267
- sm: { root: textMd },
268
- md: { root: textMd },
269
- lg: { root: [
270
- "h-control gap-2 rounded-control px-4 py-0 text-subhead",
271
- "has-data-[icon=inline-start]:pl-3.5 has-data-[icon=inline-end]:pr-3.5",
272
- "[&_svg:not([class*='size-'])]:size-[18px] **:data-icon:size-[1em]",
273
- "after:absolute after:inset-x-0 after:-inset-y-control-hit"
274
- ] },
275
- xl: { root: ["h-control-cta gap-2 rounded-control px-5 py-0 text-body", "[&_svg:not([class*='size-'])]:size-[18px] **:data-icon:size-[1em]"] },
276
- "icon-xs": { root: iconMd },
277
- "icon-sm": { root: iconMd },
278
- "icon-md": { root: iconMd },
279
- "icon-lg": { root: ["size-control rounded-control p-0 [&_svg:not([class*='size-'])]:size-control-icon", "after:absolute after:-inset-control-hit"] }
280
- }
281
- },
282
- defaultVariants: {
283
- variant: "default",
284
- size: "md"
285
- },
286
- compoundVariants: [
287
- {
288
- variant: "ghost",
289
- size: [
290
- "icon-xs",
291
- "icon-sm",
292
- "icon-md",
293
- "icon-lg"
294
- ],
295
- class: { root: ["disabled:border-transparent disabled:bg-transparent", "aria-disabled:border-transparent aria-disabled:bg-transparent"] }
296
- },
297
- {
298
- variant: "on-frame",
299
- size: [
300
- "xs",
301
- "sm",
302
- "md"
303
- ],
304
- class: { root: "h-9 px-2.5 text-subhead after:-inset-y-1" }
305
- },
306
- {
307
- variant: "on-frame",
308
- size: [
309
- "icon-xs",
310
- "icon-sm",
311
- "icon-md"
312
- ],
313
- class: { root: "size-9 after:-inset-1 [&_svg:not([class*='size-'])]:size-5" }
314
- }
315
- ]
316
- });
317
- function Button$1({ variant, size, loading = false, loadingLabel = "Loading", disabled, className, children, render, nativeButton, ...props }) {
318
- const { root, content, loadingIndicator } = buttonVariants({
319
- variant,
320
- size
321
- });
322
- return /* @__PURE__ */ jsxs(Button, {
323
- ...props,
324
- render,
325
- nativeButton: nativeButton ?? defaultNativeButton(render),
326
- "data-slot": "button",
327
- className: root({ className }),
328
- "data-loading": loading ? "true" : void 0,
329
- disabled: disabled || loading,
330
- ...loading ? { "aria-disabled": true } : {},
331
- children: [loading ? /* @__PURE__ */ jsx("span", {
332
- "data-slot": "button-loading-indicator",
333
- className: loadingIndicator(),
334
- children: /* @__PURE__ */ jsx(Spinner, { "aria-label": loadingLabel })
335
- }) : null, /* @__PURE__ */ jsx("span", {
336
- "data-slot": "button-content",
337
- "data-loading": loading ? "true" : void 0,
338
- className: content(),
339
- children
340
- })]
341
- });
342
- }
343
- //#endregion
344
- //#region src/components/dialog/dialog.tsx
345
- const dialog = tv$1({
346
- slots: {
347
- overlay: ["fixed inset-0 z-50 bg-layer-backdrop"],
348
- content: [
349
- "fixed top-1/2 left-1/2 z-50 flex w-[calc(100%-2rem)] -translate-1/2 flex-col",
350
- "max-h-[calc(100dvh-2rem)] outline-none",
351
- "overflow-clip rounded-dialog bg-layer-popover text-foreground",
352
- "shadow-float ring-1 ring-line-seam"
353
- ],
354
- header: ["grid shrink-0 grid-cols-[minmax(0,1fr)_max-content] items-center min-w-0", "gap-2 px-5 pt-5"],
355
- body: [
356
- "min-w-0 flex-1 overflow-y-auto",
357
- "px-5 pt-1.5 pb-5 first:pt-5",
358
- "font-sans text-subhead text-foreground-muted"
359
- ],
360
- footer: ["flex shrink-0 items-center justify-end", "gap-2 px-5 pb-5 first:pt-5 [[data-slot=dialog-header]+&]:pt-5"],
361
- title: ["m-0 min-w-0 text-foreground", "font-sans text-headline font-bold"],
362
- description: ["m-0", "font-sans text-subhead text-foreground-muted"]
363
- },
364
- variants: {
365
- size: {
366
- sm: { content: ["max-w-sm"] },
367
- md: { content: ["max-w-md"] },
368
- lg: { content: ["max-w-lg"] },
369
- wide: { content: ["max-w-4xl h-[min(760px,calc(100dvh-2rem))]"] }
370
- },
371
- bar: {
372
- false: {},
373
- true: {
374
- header: ["flex h-14 shrink-0 items-center gap-2 border-b px-5 py-0", "border-line-seam pt-0 [&>[data-slot=dialog-close]]:my-0"],
375
- body: ["flex min-h-0 flex-1 overflow-hidden p-0", "px-0 pt-0 pb-0 first:pt-0 text-foreground"],
376
- footer: ["shrink-0 border-t px-5 py-3", "border-line-seam pt-3 pb-3 first:pt-3 [[data-slot=dialog-header]+&]:pt-3"]
377
- }
378
- },
379
- layer: {
380
- 0: {
381
- overlay: [],
382
- content: []
383
- },
384
- 1: {
385
- overlay: ["z-60"],
386
- content: ["z-60"]
387
- }
388
- },
389
- nested: {
390
- false: {},
391
- true: { overlay: ["bg-layer-backdrop-nested"] }
392
- }
393
- },
394
- defaultVariants: {
395
- size: "md",
396
- bar: false
397
- }
398
- });
399
- function Dialog$1({ children, ...props }) {
400
- return /* @__PURE__ */ jsx(Dialog.Root, {
401
- "data-slot": "dialog",
402
- ...props,
403
- children: withOverlayLayer("dialog", props.modal !== false, children)
404
- });
405
- }
406
- function DialogTrigger(props) {
407
- return /* @__PURE__ */ jsx(Dialog.Trigger, {
408
- "data-slot": "dialog-trigger",
409
- ...props
410
- });
411
- }
412
- function DialogPortal(props) {
413
- return /* @__PURE__ */ jsx(Dialog.Portal, {
414
- "data-slot": "dialog-portal",
415
- ...useThemeScopeProps(props)
416
- });
417
- }
418
- function DialogOverlay({ className, layer, ...props }) {
419
- const nested = useParentOverlayKind() != null;
420
- const { overlay } = dialog({
421
- layer,
422
- nested
423
- });
424
- return /* @__PURE__ */ jsx(Dialog.Backdrop, {
425
- "data-slot": "dialog-overlay",
426
- "data-nested": nested ? "" : void 0,
427
- forceRender: true,
428
- className: overlay({ className }),
429
- ...props
430
- });
431
- }
432
- function DialogContent({ className, children, portalProps, overlay, size, layer, ...props }) {
433
- const { content } = dialog({
434
- size,
435
- layer
436
- });
437
- const overlayProps = typeof overlay === "object" ? overlay : {};
438
- return /* @__PURE__ */ jsxs(DialogPortal, {
439
- ...portalProps,
440
- children: [overlay === false ? null : /* @__PURE__ */ jsx(DialogOverlay, {
441
- layer,
442
- ...overlayProps
443
- }), /* @__PURE__ */ jsx(Dialog.Popup, {
444
- "data-slot": "dialog-content",
445
- className: content({ className }),
446
- ...props,
447
- children
448
- })]
449
- });
450
- }
451
- function DialogClose({ className, variant, size, children, render, "aria-label": ariaLabel, ...props }) {
452
- if (render) return /* @__PURE__ */ jsx(Dialog.Close, {
453
- "data-slot": "dialog-close",
454
- render,
455
- "aria-label": ariaLabel,
456
- ...props,
457
- children
458
- });
459
- return /* @__PURE__ */ jsx(Dialog.Close, {
460
- "data-slot": "dialog-close",
461
- render: /* @__PURE__ */ jsx(Button$1, {
462
- variant: variant ?? "ghost",
463
- size: size ?? "icon-md",
464
- "aria-label": ariaLabel ?? "Close",
465
- className: cn("-my-1.5 -mr-1.5", className)
466
- }),
467
- ...props,
468
- children: children ?? /* @__PURE__ */ jsx(X, { className: "size-4.5" })
469
- });
470
- }
471
- function DialogHeader({ className, bar = false, ...props }) {
472
- const { header } = dialog({ bar });
473
- return /* @__PURE__ */ jsx("div", {
474
- "data-slot": "dialog-header",
475
- className: header({ className }),
476
- ...props
477
- });
478
- }
479
- function DialogBody({ className, bar = false, ...props }) {
480
- const { body } = dialog({ bar });
481
- return /* @__PURE__ */ jsx("div", {
482
- "data-slot": "dialog-body",
483
- className: body({ className }),
484
- ...props
485
- });
486
- }
487
- function DialogFooter({ className, bar = false, ...props }) {
488
- const { footer } = dialog({ bar });
489
- return /* @__PURE__ */ jsx("div", {
490
- "data-slot": "dialog-footer",
491
- className: footer({ className }),
492
- ...props
493
- });
494
- }
495
- function DialogTitle({ className, ...props }) {
496
- const { title } = dialog();
497
- return /* @__PURE__ */ jsx(Dialog.Title, {
498
- "data-slot": "dialog-title",
499
- className: title({ className }),
500
- ...props
501
- });
502
- }
503
- function DialogDescription({ className, ...props }) {
504
- const { description } = dialog();
505
- return /* @__PURE__ */ jsx(Dialog.Description, {
506
- "data-slot": "dialog-description",
507
- className: description({ className }),
508
- ...props
509
- });
510
- }
511
- const dialogSurface = tv$1({ slots: { root: [
512
- "flex min-w-0 flex-col outline-none",
513
- "overflow-clip rounded-dialog bg-layer-popover text-foreground",
514
- "shadow-float ring-1 ring-line-seam"
515
- ] } });
516
- /**
517
- * The dialog's frame — the popover layer, the dialog radius, the seam ring
518
- * and the float shadow — for a centred modal whose lifecycle the app owns
519
- * (its own backdrop, Escape and focus handling). It neither positions nor
520
- * animates itself: the host centres it, and a centred modal has no motion.
521
- * DialogContent draws the same frame for a Base UI dialog.
522
- */
523
- function DialogSurface({ className, render, ...props }) {
524
- const { root } = dialogSurface();
525
- return useRender({
526
- defaultTagName: "div",
527
- render,
528
- props: mergeProps({
529
- "data-slot": "dialog-surface",
530
- className: root({ className })
531
- }, props)
532
- });
533
- }
534
- const dialogNav = tv$1({ slots: { root: ["flex shrink-0 flex-col overflow-y-auto", "w-52 gap-0.5 border-r border-line-seam p-2 max-md:hidden"] } });
535
- /** The section list of a wide (`size="wide"`) dialog, inside a `bar` body. */
536
- function DialogNav({ className, render, ...props }) {
537
- const { root } = dialogNav();
538
- return useRender({
539
- defaultTagName: "nav",
540
- render,
541
- props: mergeProps({
542
- "data-slot": "dialog-nav",
543
- className: root({ className })
544
- }, props)
545
- });
546
- }
547
- //#endregion
548
- //#region src/components/panel/panel-header.recipe.ts
549
- /**
550
- * Where a head's edge controls sit, shared by every head (panel, section,
551
- * drawer, thread). Phone: a filled thing (a sticker, a pill) puts its shape
552
- * on 16. A ghost button has no shape, so its empty 44px box overhangs, and
553
- * the two edge buttons mirror each other: the last action (6px padding,
554
- * `panelHeadActions`' -3px) and a leading Back (16px padding, -13px) both sit
555
- * 3px from their edge. Their 22px icon frames are then 14px in and every
556
- * glyph's centre 25px in, whatever the glyph (an arrow, a gear, a narrow ⋮);
557
- * aligning ink instead would move each button by its glyph's own side
558
- * bearing. A Button straight in the head (ghost ones are PanelActions) is a
559
- * filled control, so its shape ends on the content edge: 20 / 16.
560
- */
561
- const panelHeadEdges = [
562
- "pr-3 pl-5 max-md:pr-1.5 max-md:pl-4",
563
- "max-md:[&>[data-slot=panel-action]:first-child]:-ml-[13px]",
564
- "[&>[data-slot=button]:last-child]:mr-2 max-md:[&>[data-slot=button]:last-child]:mr-2.5"
565
- ];
566
- /**
567
- * A head's trailing actions. The last glyph ends on the head's edge: 20px on
568
- * desktop (12px of padding, 1px here, the 32px button's 7px glyph inset); on
569
- * a phone 14px in (44px buttons, 11px inset), mirroring a leading Back. The
570
- * margin and gap follow the button's size, not the layout: a touch screen
571
- * from 768px has the desktop head with 44px buttons, so -3px puts its last
572
- * glyph on 20px too.
573
- */
574
- const panelHeadActions = "flex shrink-0 items-center gap-0.5 mr-[-3px] md:pointer-fine:mr-px md:pointer-fine:gap-1";
575
- /**
576
- * `header` and `sectionHeader` share one bar, expressed through
577
- * `compoundSlots`: a 56px row (the shell header height) with a seam under it.
578
- * On a phone the section head sits on the frame.
579
- */
580
- const panelHeader = tv$1({
581
- slots: {
582
- header: [],
583
- sectionHeader: [],
584
- headerIcon: [
585
- "flex size-[27px] shrink-0 items-center justify-center",
586
- "rounded-sticker border-[1.5px] border-sticker-ink bg-brand text-sticker-ink ring-[1.5px] ring-sticker-edge",
587
- "not-data-[icon-kind=avatar]:[&_svg]:size-3.5 not-data-[icon-kind=avatar]:[&_svg]:stroke-2",
588
- "data-[icon-kind=avatar]:size-auto data-[icon-kind=avatar]:border-transparent data-[icon-kind=avatar]:bg-transparent"
589
- ],
590
- headerContent: "flex min-w-0 flex-1 flex-col justify-center",
591
- heading: "flex min-w-0 items-center gap-2",
592
- title: "truncate font-sans text-headline font-bold text-foreground",
593
- meta: "min-w-0 truncate font-sans text-caption text-foreground-hint",
594
- status: [
595
- "inline-flex shrink-0 items-center gap-1 border-0 bg-transparent p-0",
596
- "font-sans text-caption font-medium normal-case text-foreground-hint",
597
- "[&_svg]:size-3.5 [&_svg]:shrink-0"
598
- ],
599
- activity: [
600
- "inline-flex shrink-0 items-center gap-1.5",
601
- "font-sans text-caption text-foreground-hint",
602
- "[&_svg]:size-3.5 [&_svg]:shrink-0"
603
- ],
604
- actions: panelHeadActions
605
- },
606
- variants: { size: {
607
- sm: {},
608
- md: { meta: ["text-footnote"] }
609
- } },
610
- compoundSlots: [{
611
- slots: ["header", "sectionHeader"],
612
- class: [
613
- "flex h-(--shell-header-height,56px) shrink-0 items-center gap-3 max-md:gap-2",
614
- panelHeadEdges,
615
- "border-b border-line-seam bg-layer-canvas text-foreground",
616
- "max-md:sticky max-md:top-0 max-md:z-30"
617
- ]
618
- }, {
619
- slots: ["sectionHeader"],
620
- class: [
621
- "max-md:bg-layer-frame max-md:text-foreground-on-frame",
622
- "max-md:[&>[data-slot=panel-header-icon]]:hidden",
623
- "max-md:**:data-[slot=panel-title]:text-foreground-on-frame",
624
- "max-md:**:data-[slot=panel-meta]:text-foreground-on-frame-muted",
625
- "max-md:**:data-[slot=panel-action]:text-foreground-on-frame",
626
- "max-md:**:data-[slot=panel-action]:hover:bg-fill-frame-pressed max-md:**:data-[slot=panel-action]:active:bg-fill-frame-pressed"
627
- ]
628
- }]
629
- });
630
- const drawer = tv$1({
631
- extend: tv$1({
632
- slots: {
633
- overlay: [
634
- "opacity-[max(var(--drawer-overlay-min-opacity,0),calc(1-var(--drawer-swipe-progress)))]",
635
- "transition-opacity duration-200 ease-slide",
636
- "data-starting-style:opacity-0 data-ending-style:pointer-events-none data-ending-style:opacity-0 data-ending-style:duration-150",
637
- "motion-reduce:transition-none"
638
- ],
639
- popup: [
640
- "[--drawer-blur:2px] [--drawer-ease:var(--ease-slide)]",
641
- "[--closed-transform:translate3d(var(--drawer-exit-x,0px),var(--drawer-exit-y,0px),0)]",
642
- "[--drawer-stack-peek:16px] [--drawer-stack-step:0.05]",
643
- "[--drawer-stack-progress:clamp(0,var(--drawer-swipe-progress),1)]",
644
- "[--drawer-stack-offset:max(0px,calc((var(--nested-drawers)-var(--drawer-stack-progress))*var(--drawer-stack-peek)))]",
645
- "[--drawer-stack-scale-base:calc(max(0,1-(var(--nested-drawers)*var(--drawer-stack-step))))]",
646
- "[--drawer-stack-scale:clamp(0,calc(var(--drawer-stack-scale-base)+(var(--drawer-stack-step)*var(--drawer-stack-progress))),1)]",
647
- "[--drawer-stack-shrink:calc(1-var(--drawer-stack-scale))]",
648
- "[--drawer-stack-shrink-offset:calc(var(--drawer-stack-shrink)*var(--drawer-stack-size))]",
649
- "origin-(--drawer-transform-origin)",
650
- "transform-[translate3d(calc(var(--drawer-swipe-x,0px)+var(--drawer-stack-x,0px)),calc(var(--drawer-swipe-y,0px)+var(--drawer-stack-y,0px)),0)_scale(var(--drawer-stack-scale))]",
651
- "opacity-100 blur-0",
652
- "transition-[transform,height,opacity,filter] duration-240 ease-(--drawer-ease) will-change-transform",
653
- "data-ending-style:duration-[calc(var(--drawer-swipe-strength,1)*180ms)]",
654
- "data-starting-style:transform-(--closed-transform) data-starting-style:opacity-0 data-starting-style:blur-(--drawer-blur)",
655
- "data-ending-style:transform-(--closed-transform) data-ending-style:opacity-0 data-ending-style:blur-(--drawer-blur)",
656
- "data-swiping:duration-0 data-nested-drawer-swiping:duration-0 motion-reduce:transition-none"
657
- ],
658
- content: ["transition-opacity duration-150 ease-slide motion-reduce:transition-none", "group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100"],
659
- swipeHandle: ["transition-opacity duration-150 motion-reduce:transition-none", "group-data-nested-drawer-open/drawer-popup:opacity-0 group-data-nested-drawer-swiping/drawer-popup:opacity-100"]
660
- },
661
- variants: {
662
- side: {
663
- down: { popup: [
664
- "[--drawer-exit-y:calc(100%+var(--drawer-inset)+2px)]",
665
- "[--drawer-stack-y:calc(0px-var(--drawer-stack-offset)-var(--drawer-stack-shrink-offset))]",
666
- "[--drawer-transform-origin:50%_100%]"
667
- ] },
668
- up: { popup: [
669
- "[--drawer-exit-y:calc(-100%-var(--drawer-inset)-2px)]",
670
- "[--drawer-stack-y:calc(var(--drawer-stack-offset)+var(--drawer-stack-shrink-offset))]",
671
- "[--drawer-transform-origin:50%_0%]"
672
- ] },
673
- left: { popup: [
674
- "[--drawer-exit-x:calc(-100%-var(--drawer-inset)-2px)]",
675
- "[--drawer-stack-x:calc(var(--drawer-stack-offset)+var(--drawer-stack-shrink-offset))]",
676
- "[--drawer-transform-origin:0%_50%]"
677
- ] },
678
- right: { popup: [
679
- "[--drawer-exit-x:calc(100%+var(--drawer-inset)+2px)]",
680
- "[--drawer-stack-x:calc(0px-var(--drawer-stack-offset)-var(--drawer-stack-shrink-offset))]",
681
- "[--drawer-transform-origin:100%_50%]"
682
- ] }
683
- },
684
- axis: {
685
- x: { popup: ["[--drawer-stack-size:100%]", "[--drawer-swipe-x:var(--drawer-swipe-movement-x)]"] },
686
- y: { popup: ["[--drawer-stack-size:max(0px,var(--drawer-frontmost-height,var(--drawer-height)))]", "[--drawer-swipe-y:calc(var(--drawer-snap-point-offset,0px)+var(--drawer-swipe-movement-y))]"] }
687
- },
688
- snapPoints: { true: { overlay: ["[--drawer-overlay-min-opacity:0.5]"] } }
689
- }
690
- }),
691
- slots: {
692
- overlay: [
693
- "fixed inset-0 z-50 min-h-dvh select-none",
694
- "supports-[-webkit-touch-callout:none]:absolute",
695
- "bg-layer-backdrop"
696
- ],
697
- viewport: ["pointer-events-none fixed inset-0 z-50 select-none"],
698
- popup: [
699
- "group/drawer-popup pointer-events-auto fixed z-50 flex h-(--drawer-content-height) flex-col outline-none",
700
- "[--drawer-inset:12px] [--drawer-content-height:var(--drawer-height,auto)]",
701
- "data-nested-drawer-open:overflow-hidden",
702
- "bg-layer-popover text-foreground",
703
- "shadow-float ring-1 ring-line-seam"
704
- ],
705
- content: ["flex min-h-0 flex-1 flex-col rounded-[inherit] select-text", "group-data-swiping/drawer-popup:select-none"],
706
- swipeHandle: [
707
- "relative z-10 shrink-0 cursor-grab select-none active:cursor-grabbing",
708
- "before:absolute before:content-['']",
709
- "before:rounded-[1px] before:bg-line-seam"
710
- ],
711
- close: ["shrink-0"],
712
- closeIcon: ["size-5.5"],
713
- header: ["flex flex-col", "items-stretch gap-1 px-4 pt-4 pb-2 text-left"],
714
- body: ["flex flex-1 flex-col", "items-stretch px-4 pt-2 pb-4 text-left"],
715
- footer: ["mt-auto py-5", "px-4 pt-3 pb-4"],
716
- actions: [
717
- "flex w-full flex-col items-stretch",
718
- "**:data-[slot=button]:w-full",
719
- "gap-2"
720
- ],
721
- title: ["text-pretty text-foreground", "max-w-none text-left font-sans text-headline font-bold"],
722
- description: ["text-pretty", "max-w-none text-left font-sans text-callout text-foreground-muted"]
723
- },
724
- variants: {
725
- nested: {
726
- false: {},
727
- true: { overlay: ["bg-layer-backdrop-nested"] }
728
- },
729
- side: {
730
- down: {
731
- popup: [
732
- "bottom-(--drawer-inset) min-h-64 max-h-[82vh]",
733
- "rounded-t-dialog [--drawer-inset:0px]",
734
- "pb-[env(safe-area-inset-bottom,0px)]"
735
- ],
736
- swipeHandle: ["before:top-3"]
737
- },
738
- up: {
739
- popup: [
740
- "top-(--drawer-inset) max-h-[80vh]",
741
- "rounded-b-dialog [--drawer-inset:0px]",
742
- "pt-[env(safe-area-inset-top,0px)]"
743
- ],
744
- swipeHandle: ["order-last", "before:bottom-3"]
745
- },
746
- left: {
747
- popup: ["left-(--drawer-inset)"],
748
- swipeHandle: ["order-last", "before:right-3"]
749
- },
750
- right: {
751
- popup: ["right-(--drawer-inset)"],
752
- swipeHandle: ["before:left-3"]
753
- }
754
- },
755
- axis: {
756
- x: {
757
- popup: ["flex-row", "inset-y-0 w-full max-w-[420px] [--drawer-inset:0px]"],
758
- swipeHandle: [
759
- "my-auto h-16 w-11",
760
- "before:top-1/2 before:-translate-y-1/2",
761
- "before:w-1 before:h-9"
762
- ],
763
- header: [
764
- "min-h-14 shrink-0 flex-row items-center justify-between gap-2 py-0 text-left",
765
- "border-b border-line-seam",
766
- panelHeadEdges
767
- ],
768
- body: ["items-stretch text-left", "min-h-0 overflow-y-auto px-5 py-4"],
769
- footer: ["border-t border-line-seam px-5 py-3"],
770
- title: ["min-w-0 max-w-none flex-1 text-left"],
771
- description: ["max-w-none text-left"],
772
- actions: ["flex-row flex-wrap justify-end", "**:data-[slot=button]:w-auto"]
773
- },
774
- y: {
775
- popup: [
776
- "data-nested-drawer-open:h-(--drawer-frontmost-height)",
777
- "mx-auto md:max-w-md",
778
- "inset-x-0"
779
- ],
780
- swipeHandle: [
781
- "mx-auto w-16",
782
- "before:left-1/2 before:-translate-x-1/2",
783
- "h-4",
784
- "before:top-1.5 before:h-1 before:w-9"
785
- ]
786
- }
787
- }
788
- },
789
- compoundVariants: [{
790
- side: "down",
791
- snapPoints: true,
792
- class: { header: ["pt-2"] }
793
- }, {
794
- axis: "y",
795
- snapPoints: true,
796
- class: { popup: ["[--drawer-content-height:100dvh]"] }
797
- }]
798
- });
799
- //#endregion
800
- //#region src/components/drawer/drawer.tsx
801
- const DrawerConfigContext = createContext(null);
802
- function useDrawerConfig() {
803
- const context = useContext(DrawerConfigContext);
804
- if (!context) throw new Error("Drawer parts must be used within Drawer.");
805
- return context;
806
- }
807
- function Drawer$1({ modal = true, snapPoints, swipeDirection = "down", children, ...props }) {
808
- const hasSnapPoints = snapPoints != null && snapPoints.length > 0;
809
- const swipeAxis = swipeDirection === "down" || swipeDirection === "up" ? "y" : "x";
810
- const contextValue = useMemo(() => ({
811
- hasSnapPoints,
812
- modal,
813
- swipeAxis,
814
- swipeDirection
815
- }), [
816
- hasSnapPoints,
817
- modal,
818
- swipeAxis,
819
- swipeDirection
820
- ]);
821
- return /* @__PURE__ */ jsx(DrawerConfigContext.Provider, {
822
- value: contextValue,
823
- children: /* @__PURE__ */ jsx(Drawer.Root, {
824
- modal,
825
- snapPoints,
826
- swipeDirection,
827
- ...props,
828
- children: withOverlayLayer("drawer", modal === true, children)
829
- })
830
- });
831
- }
832
- function DrawerTrigger(props) {
833
- return /* @__PURE__ */ jsx(Drawer.Trigger, {
834
- "data-slot": "drawer-trigger",
835
- ...props
836
- });
837
- }
838
- function DrawerPortal(props) {
839
- return /* @__PURE__ */ jsx(Drawer.Portal, {
840
- "data-slot": "drawer-portal",
841
- ...useThemeScopeProps(props)
842
- });
843
- }
844
- function DrawerOverlay({ className, ...props }) {
845
- const { hasSnapPoints } = useDrawerConfig();
846
- const nested = useParentOverlayKind() === "dialog";
847
- const { overlay } = drawer({
848
- snapPoints: hasSnapPoints,
849
- nested
850
- });
851
- return /* @__PURE__ */ jsx(Drawer.Backdrop, {
852
- "data-slot": "drawer-overlay",
853
- "data-nested": nested ? "" : void 0,
854
- forceRender: nested,
855
- className: overlay({ className }),
856
- ...props
857
- });
858
- }
859
- function DrawerSwipeHandle() {
860
- const { swipeAxis, swipeDirection } = useDrawerConfig();
861
- const { swipeHandle } = drawer({
862
- side: swipeDirection,
863
- axis: swipeAxis
864
- });
865
- return /* @__PURE__ */ jsx("div", {
866
- "data-slot": "drawer-swipe-handle",
867
- "aria-hidden": "true",
868
- className: swipeHandle()
869
- });
870
- }
871
- function DrawerContent({ className, children, portalProps, ...props }) {
872
- const { hasSnapPoints, modal, swipeAxis, swipeDirection } = useDrawerConfig();
873
- const { content, popup, viewport } = drawer({
874
- side: swipeDirection,
875
- axis: swipeAxis,
876
- snapPoints: hasSnapPoints
877
- });
878
- return /* @__PURE__ */ jsxs(DrawerPortal, {
879
- ...portalProps,
880
- children: [modal === true ? /* @__PURE__ */ jsx(DrawerOverlay, {}) : null, /* @__PURE__ */ jsx(Drawer.Viewport, {
881
- "data-slot": "drawer-viewport",
882
- className: cn(viewport(), modal === true && "pointer-events-auto"),
883
- children: /* @__PURE__ */ jsxs(Drawer.Popup, {
884
- "data-slot": "drawer-popup",
885
- className: popup({ className }),
886
- ...props,
887
- children: [hasSnapPoints ? /* @__PURE__ */ jsx(DrawerSwipeHandle, {}) : null, /* @__PURE__ */ jsx(Drawer.Content, {
888
- "data-slot": "drawer-content",
889
- className: content(),
890
- children
891
- })]
892
- })
893
- })]
894
- });
895
- }
896
- function DrawerClose({ className, variant, size, children, render, "aria-label": ariaLabel, ...props }) {
897
- const { close, closeIcon } = drawer();
898
- if (render) return /* @__PURE__ */ jsx(Drawer.Close, {
899
- "data-slot": "drawer-close",
900
- className,
901
- render,
902
- "aria-label": ariaLabel,
903
- ...props,
904
- children
905
- });
906
- return /* @__PURE__ */ jsx(Drawer.Close, {
907
- "data-slot": "drawer-close",
908
- render: /* @__PURE__ */ jsx(Button$1, {
909
- variant: variant ?? "ghost",
910
- size: size ?? "icon-lg",
911
- "aria-label": ariaLabel ?? "Close"
912
- }),
913
- className: close({ className }),
914
- ...props,
915
- children: children ?? /* @__PURE__ */ jsx(X, { className: closeIcon() })
916
- });
917
- }
918
- function DrawerHeader({ className, render, ...props }) {
919
- const { hasSnapPoints, swipeAxis, swipeDirection } = useDrawerConfig();
920
- const { header } = drawer({
921
- side: swipeDirection,
922
- axis: swipeAxis,
923
- snapPoints: hasSnapPoints
924
- });
925
- const defaultProps = { className: header({ className }) };
926
- return useRender({
927
- defaultTagName: "div",
928
- render,
929
- props: mergeProps(defaultProps, props),
930
- state: { slot: "drawer-header" }
931
- });
932
- }
933
- function DrawerFooter({ className, render, ...props }) {
934
- const { swipeAxis } = useDrawerConfig();
935
- const { footer } = drawer({ axis: swipeAxis });
936
- const defaultProps = { className: footer({ className }) };
937
- return useRender({
938
- defaultTagName: "div",
939
- render,
940
- props: mergeProps(defaultProps, props),
941
- state: { slot: "drawer-footer" }
942
- });
943
- }
944
- function DrawerBody({ className, render, ...props }) {
945
- const { swipeAxis } = useDrawerConfig();
946
- const { body } = drawer({ axis: swipeAxis });
947
- const defaultProps = { className: body({ className }) };
948
- return useRender({
949
- defaultTagName: "div",
950
- render,
951
- props: mergeProps(defaultProps, props),
952
- state: { slot: "drawer-body" }
953
- });
954
- }
955
- function DrawerActions({ className, render, children, ...props }) {
956
- const swipeAxis = useContext(DrawerConfigContext)?.swipeAxis;
957
- const { actions } = drawer({ axis: swipeAxis });
958
- const defaultProps = {
959
- className: actions({ className }),
960
- children
961
- };
962
- return useRender({
963
- defaultTagName: "div",
964
- render,
965
- props: mergeProps(defaultProps, props),
966
- state: { slot: "drawer-actions" }
967
- });
968
- }
969
- function DrawerTitle({ className, ...props }) {
970
- const { swipeAxis } = useDrawerConfig();
971
- const { title } = drawer({ axis: swipeAxis });
972
- return /* @__PURE__ */ jsx(Drawer.Title, {
973
- "data-slot": "drawer-title",
974
- className: title({ className }),
975
- ...props
976
- });
977
- }
978
- function DrawerDescription({ className, ...props }) {
979
- const { swipeAxis } = useDrawerConfig();
980
- const { description } = drawer({ axis: swipeAxis });
981
- return /* @__PURE__ */ jsx(Drawer.Description, {
982
- "data-slot": "drawer-description",
983
- className: description({ className }),
984
- ...props
985
- });
986
- }
987
- //#endregion
988
- export { buttonVariants as A, DialogNav as C, DialogTitle as D, DialogSurface as E, resolveThemeScopeProps as F, useThemeScopeProps as I, useTheme as L, defaultNativeButton as M, useParentOverlayKind as N, DialogTrigger as O, withOverlayLayer as P, ThemeStateContext as R, DialogHeader as S, DialogPortal as T, DialogBody as _, DrawerContent as a, DialogDescription as b, DrawerHeader as c, DrawerTitle as d, DrawerTrigger as f, Dialog$1 as g, panelHeader as h, DrawerClose as i, Spinner as j, Button$1 as k, DrawerOverlay as l, panelHeadEdges as m, DrawerActions as n, DrawerDescription as o, panelHeadActions as p, DrawerBody as r, DrawerFooter as s, Drawer$1 as t, DrawerPortal as u, DialogClose as v, DialogOverlay as w, DialogFooter as x, DialogContent as y, tv$1 as z };