@botiverse/raft-ui 0.21.2 → 0.22.1
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 +90 -0
- package/dist/components/timeline/timeline.css +2 -2
- package/dist/foundation.css +3 -3
- package/dist/{inbox-item.recipe-CuJPcy-3.mjs → inbox-item-BtvK75T1.mjs} +2242 -390
- package/dist/{index-C1FHbVfH.d.mts → inbox-item-ChANmbeB.d.mts} +496 -11
- package/dist/index.d.mts +32 -320
- package/dist/index.mjs +110 -1006
- package/dist/style.css +1 -1
- package/dist/styles.css +82 -8
- package/dist/tokens.json +2 -2
- package/dist/wip.d.mts +107 -3
- package/dist/wip.mjs +139 -3
- package/package.json +1 -1
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { cn } from "./cn.mjs";
|
|
2
2
|
import { Dialog } from "@base-ui/react/dialog";
|
|
3
|
-
import { Check, Minus, X } from "lucide-react";
|
|
3
|
+
import { ArrowLeft, Check, ChevronRight, Minus, X } from "lucide-react";
|
|
4
|
+
import { Children, Fragment, createContext, createElement, isValidElement, use, useCallback, useContext, useEffect, useId, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
4
5
|
import { tv } from "tailwind-variants/lite";
|
|
5
|
-
import { Children, Fragment, createContext, isValidElement, use, useCallback, useContext, useEffect, useMemo, useRef, useSyncExternalStore } from "react";
|
|
6
6
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
import { Button } from "@base-ui/react/button";
|
|
8
8
|
import { mergeProps } from "@base-ui/react/merge-props";
|
|
9
9
|
import { useRender } from "@base-ui/react/use-render";
|
|
10
|
+
import { createPortal, flushSync } from "react-dom";
|
|
10
11
|
import { Tooltip } from "@base-ui/react/tooltip";
|
|
11
12
|
import { Checkbox } from "@base-ui/react/checkbox";
|
|
12
13
|
import { ScrollArea } from "@base-ui/react/scroll-area";
|
|
13
14
|
import { Avatar } from "@base-ui/react";
|
|
14
15
|
import { Menu } from "@base-ui/react/menu";
|
|
15
|
-
import {
|
|
16
|
+
import { Popover } from "@base-ui/react/popover";
|
|
17
|
+
import { PointerSensor, TouchSensor, useSensor } from "@dnd-kit/core";
|
|
18
|
+
import { PreviewCard } from "@base-ui/react/preview-card";
|
|
16
19
|
import { Toggle } from "@base-ui/react/toggle";
|
|
17
20
|
import { Toolbar } from "@base-ui/react/toolbar";
|
|
18
|
-
import { PointerSensor, TouchSensor, useSensor } from "@dnd-kit/core";
|
|
19
21
|
//#region src/lib/decision-width.ts
|
|
20
22
|
/**
|
|
21
23
|
* The width of every decision dialog (dialog.md#variants): AlertDialog and a
|
|
@@ -54,6 +56,103 @@ const headEdges = ["px-5 max-md:px-4", ...headEdgePull];
|
|
|
54
56
|
* as the arithmetic so it follows the control tokens wherever they switch.
|
|
55
57
|
*/
|
|
56
58
|
const controlIconInset = "[--icon-inset:calc((var(--control-height)-var(--control-icon-size))/2)]";
|
|
59
|
+
/**
|
|
60
|
+
* The row of buttons while `data-stacked` is set: a full-width column. A
|
|
61
|
+
* button alone in a wrapper (a tooltip's trigger) fills the width too.
|
|
62
|
+
*/
|
|
63
|
+
const stackedActions = [
|
|
64
|
+
"data-stacked:flex-col-reverse",
|
|
65
|
+
"data-stacked:items-stretch",
|
|
66
|
+
"data-stacked:*:data-[slot=button]:w-full data-stacked:[&>*>[data-slot=button]:only-child]:w-full",
|
|
67
|
+
...[].map((value) => `data-stacked:${value}`)
|
|
68
|
+
];
|
|
69
|
+
function assignRef$2(ref, value) {
|
|
70
|
+
if (typeof ref === "function") ref(value);
|
|
71
|
+
else if (ref) ref.current = value;
|
|
72
|
+
}
|
|
73
|
+
function contentWidth(element) {
|
|
74
|
+
const style = getComputedStyle(element);
|
|
75
|
+
return element.getBoundingClientRect().width - (Number.parseFloat(style.paddingLeft) || 0) - (Number.parseFloat(style.paddingRight) || 0) - (Number.parseFloat(style.borderLeftWidth) || 0) - (Number.parseFloat(style.borderRightWidth) || 0);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Whether a row of buttons must stack: laid out as a row (`data-stacked` off
|
|
79
|
+
* for the measure and put back at once, inside one frame, so nothing is
|
|
80
|
+
* painted in between and the layouts never flip back and forth), its children
|
|
81
|
+
* run past the content box of `frame`. Children that may shrink (a label that
|
|
82
|
+
* truncates or wraps) shrink first, so only content that can't, the buttons,
|
|
83
|
+
* counts as not fitting. Undefined while the frame is hidden (a sheet hides
|
|
84
|
+
* its footer), so nothing is decided then.
|
|
85
|
+
*/
|
|
86
|
+
function rowOverflows(row, frame) {
|
|
87
|
+
if (!(frame.getBoundingClientRect().width > 0)) return void 0;
|
|
88
|
+
const wasStacked = row.hasAttribute("data-stacked");
|
|
89
|
+
if (wasStacked) row.removeAttribute("data-stacked");
|
|
90
|
+
const available = contentWidth(frame);
|
|
91
|
+
let start = Number.POSITIVE_INFINITY;
|
|
92
|
+
let end = Number.NEGATIVE_INFINITY;
|
|
93
|
+
for (const child of row.children) {
|
|
94
|
+
const box = child.getBoundingClientRect();
|
|
95
|
+
if (box.width === 0 && box.height === 0) continue;
|
|
96
|
+
const style = getComputedStyle(child);
|
|
97
|
+
start = Math.min(start, box.left - (Number.parseFloat(style.marginLeft) || 0));
|
|
98
|
+
end = Math.max(end, box.right + (Number.parseFloat(style.marginRight) || 0));
|
|
99
|
+
}
|
|
100
|
+
if (wasStacked) row.setAttribute("data-stacked", "");
|
|
101
|
+
return end - start > available + .5;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Sets `data-stacked` on a footer's row of buttons when it doesn't fit on one
|
|
105
|
+
* line (rowOverflows). Pass the returned ref to the row. It must fit its own
|
|
106
|
+
* content box, or with `inParent` (the group of buttons beside a note) its
|
|
107
|
+
* parent's. As BannerAction's `data-wrap`, it measures again when the footer
|
|
108
|
+
* resizes, the buttons change or a web font arrives.
|
|
109
|
+
*/
|
|
110
|
+
function useStackedActions(ref, inParent = false) {
|
|
111
|
+
const [row, setRow] = useState(null);
|
|
112
|
+
const [stacked, setStacked] = useState(false);
|
|
113
|
+
const callbackRef = useCallback((node) => {
|
|
114
|
+
setRow(node);
|
|
115
|
+
assignRef$2(ref, node);
|
|
116
|
+
}, [ref]);
|
|
117
|
+
useLayoutEffect(() => {
|
|
118
|
+
const frame = inParent ? row?.parentElement : row;
|
|
119
|
+
if (!row || !frame || typeof ResizeObserver === "undefined") return;
|
|
120
|
+
const measure = () => {
|
|
121
|
+
const overflows = rowOverflows(row, frame);
|
|
122
|
+
if (overflows !== void 0) setStacked(overflows);
|
|
123
|
+
};
|
|
124
|
+
let raf = 0;
|
|
125
|
+
const schedule = () => {
|
|
126
|
+
cancelAnimationFrame(raf);
|
|
127
|
+
raf = requestAnimationFrame(measure);
|
|
128
|
+
};
|
|
129
|
+
measure();
|
|
130
|
+
const resize = new ResizeObserver(schedule);
|
|
131
|
+
resize.observe(frame);
|
|
132
|
+
resize.observe(row);
|
|
133
|
+
for (const child of row.children) resize.observe(child);
|
|
134
|
+
const mutation = new MutationObserver(schedule);
|
|
135
|
+
mutation.observe(row, {
|
|
136
|
+
childList: true,
|
|
137
|
+
characterData: true,
|
|
138
|
+
subtree: true
|
|
139
|
+
});
|
|
140
|
+
const fonts = typeof document.fonts === "undefined" ? void 0 : document.fonts;
|
|
141
|
+
let live = true;
|
|
142
|
+
fonts?.ready.then(() => {
|
|
143
|
+
if (live) schedule();
|
|
144
|
+
});
|
|
145
|
+
fonts?.addEventListener("loadingdone", schedule);
|
|
146
|
+
return () => {
|
|
147
|
+
live = false;
|
|
148
|
+
cancelAnimationFrame(raf);
|
|
149
|
+
resize.disconnect();
|
|
150
|
+
mutation.disconnect();
|
|
151
|
+
fonts?.removeEventListener("loadingdone", schedule);
|
|
152
|
+
};
|
|
153
|
+
}, [row, inParent]);
|
|
154
|
+
return [stacked, callbackRef];
|
|
155
|
+
}
|
|
57
156
|
//#endregion
|
|
58
157
|
//#region src/lib/tv.ts
|
|
59
158
|
const tv$1 = ((options) => {
|
|
@@ -97,6 +196,16 @@ function useThemeScopeProps(props) {
|
|
|
97
196
|
//#endregion
|
|
98
197
|
//#region src/lib/overlay-depth.tsx
|
|
99
198
|
const OverlayStackContext = createContext([]);
|
|
199
|
+
/**
|
|
200
|
+
* Tells everything under it that a modal surface is open, so a raft-ui
|
|
201
|
+
* Dialog, AlertDialog or Drawer opened from inside draws the lighter nested
|
|
202
|
+
* scrim (design/foundations/elevation.md#stacked-overlays). raft-ui's own
|
|
203
|
+
* roots add one by themselves; an app's own modal (its own backdrop, Esc and
|
|
204
|
+
* focus) wraps its scrim and its content in one to join the stack, and reads
|
|
205
|
+
* `useParentOverlayKind` inside it to pick its own scrim. Modal overlays only:
|
|
206
|
+
* a non-modal one passes `modal={false}`, adds nothing, and must not call
|
|
207
|
+
* `useParentOverlayKind` (it would read the layer below as its own).
|
|
208
|
+
*/
|
|
100
209
|
function OverlayLayerProvider({ kind, modal, children }) {
|
|
101
210
|
const stack = useContext(OverlayStackContext);
|
|
102
211
|
const value = useMemo(() => modal ? [...stack, kind] : stack, [
|
|
@@ -111,8 +220,14 @@ function OverlayLayerProvider({ kind, modal, children }) {
|
|
|
111
220
|
}
|
|
112
221
|
/**
|
|
113
222
|
* The modal surface this overlay's own surface was opened from, or null when
|
|
114
|
-
* it is the first.
|
|
115
|
-
*
|
|
223
|
+
* it is the first. Call it inside the surface's own `OverlayLayerProvider`
|
|
224
|
+
* (for raft-ui's overlays, inside their root): the last entry is the surface
|
|
225
|
+
* itself and the one before it is its parent. Not null means the surface is
|
|
226
|
+
* stacked over another and draws `layer-backdrop-nested` instead of
|
|
227
|
+
* `layer-backdrop`. Modal overlays only: under `modal={false}` nothing is
|
|
228
|
+
* added, so the last entry is the parent and this returns the wrong layer. It reads React context, so it sees the surfaces whose
|
|
229
|
+
* tree this one is rendered in, portals included; one opened from outside
|
|
230
|
+
* that tree is not stacked on it here.
|
|
116
231
|
*/
|
|
117
232
|
function useParentOverlayKind() {
|
|
118
233
|
const stack = useContext(OverlayStackContext);
|
|
@@ -410,6 +525,141 @@ function Button$1({ variant, size, loading = false, loadingLabel = "Loading", di
|
|
|
410
525
|
});
|
|
411
526
|
}
|
|
412
527
|
//#endregion
|
|
528
|
+
//#region src/components/dialog/dialog-sheet-context.ts
|
|
529
|
+
const NO_SHEET = {
|
|
530
|
+
sheet: false,
|
|
531
|
+
commitSlot: null,
|
|
532
|
+
setCommitSlot: () => void 0,
|
|
533
|
+
bodyEndSlot: null,
|
|
534
|
+
setBodyEndSlot: () => void 0
|
|
535
|
+
};
|
|
536
|
+
const DialogSheetContext = createContext(NO_SHEET);
|
|
537
|
+
function useDialogSheet() {
|
|
538
|
+
return useContext(DialogSheetContext);
|
|
539
|
+
}
|
|
540
|
+
/** Inside the sheet's head: its × is a pushed page's leading action. */
|
|
541
|
+
const DialogSheetHeadContext = createContext(false);
|
|
542
|
+
function useInDialogSheetHead() {
|
|
543
|
+
return useContext(DialogSheetHeadContext);
|
|
544
|
+
}
|
|
545
|
+
/**
|
|
546
|
+
* Every overlay surface starts outside any sheet: a confirm, a dialog or a
|
|
547
|
+
* drawer opened from inside a sheet keeps its own head, footer and commit
|
|
548
|
+
* button instead of taking the sheet's.
|
|
549
|
+
*/
|
|
550
|
+
function DialogSheetReset({ children }) {
|
|
551
|
+
return createElement(DialogSheetContext.Provider, { value: NO_SHEET }, createElement(DialogSheetHeadContext.Provider, { value: false }, children));
|
|
552
|
+
}
|
|
553
|
+
/**
|
|
554
|
+
* A dialog drawn as a phone sheet (design: dialog.md "On a phone"). The
|
|
555
|
+
* surface is DialogContent's frame docked to the bottom, 90% of the screen
|
|
556
|
+
* tall, rounded on its top corners, sliding up as a bottom sheet does; its
|
|
557
|
+
* head is a pushed page's head (× at the start, the title, the commit button
|
|
558
|
+
* at the end); its body keeps the phone's 16px margin and the home indicator's
|
|
559
|
+
* inset. Merged over DialogContent's classes, so these win.
|
|
560
|
+
*/
|
|
561
|
+
const dialogSheet = tv$1({ slots: {
|
|
562
|
+
content: [
|
|
563
|
+
"fixed inset-x-0 top-auto bottom-0 left-0 h-[90dvh] max-h-none w-full max-w-none translate-none",
|
|
564
|
+
"rounded-none rounded-t-dialog",
|
|
565
|
+
"pr-[env(safe-area-inset-right,0px)] pl-[env(safe-area-inset-left,0px)]",
|
|
566
|
+
"transition-transform duration-240 ease-slide data-ending-style:duration-180",
|
|
567
|
+
"data-starting-style:translate-y-full data-ending-style:translate-y-full",
|
|
568
|
+
"motion-reduce:transition-none"
|
|
569
|
+
],
|
|
570
|
+
overlay: [
|
|
571
|
+
"transition-opacity duration-240 ease-slide data-ending-style:duration-180",
|
|
572
|
+
"data-starting-style:opacity-0 data-ending-style:opacity-0",
|
|
573
|
+
"motion-reduce:transition-none"
|
|
574
|
+
],
|
|
575
|
+
header: ["flex h-14 shrink-0 items-center gap-2 px-4 pt-0", "border-b border-line-seam"],
|
|
576
|
+
title: ["min-w-0 flex-1 truncate"],
|
|
577
|
+
commitSlot: ["flex shrink-0 items-center empty:hidden"],
|
|
578
|
+
body: ["min-h-0 content-start", "px-4 pt-4 first:pt-4 pb-[max(16px,env(safe-area-inset-bottom,0px))]"],
|
|
579
|
+
barBody: ["block overflow-y-auto"],
|
|
580
|
+
bodyEnd: ["mt-4 empty:hidden", "text-start font-sans text-footnote text-foreground-muted *:data-[slot=text-link]:text-footnote"]
|
|
581
|
+
} });
|
|
582
|
+
//#endregion
|
|
583
|
+
//#region src/components/text-link/text-link.tsx
|
|
584
|
+
const textLinkVariants = tv$1({
|
|
585
|
+
base: [
|
|
586
|
+
"rounded-[2px]",
|
|
587
|
+
"decoration-[1.5px] underline-offset-[3px] not-disabled:hover:underline",
|
|
588
|
+
"outline-none focus-visible:shadow-focus",
|
|
589
|
+
"disabled:cursor-not-allowed disabled:text-foreground-disabled"
|
|
590
|
+
],
|
|
591
|
+
variants: { variant: {
|
|
592
|
+
muted: ["font-medium text-foreground-muted", "hover:text-foreground"],
|
|
593
|
+
strong: ["font-semibold text-foreground"]
|
|
594
|
+
} },
|
|
595
|
+
defaultVariants: { variant: "muted" }
|
|
596
|
+
});
|
|
597
|
+
function TextLink({ variant, className, render, ...props }) {
|
|
598
|
+
const defaultProps = {
|
|
599
|
+
className: textLinkVariants({
|
|
600
|
+
variant,
|
|
601
|
+
className
|
|
602
|
+
}),
|
|
603
|
+
...render ? {} : { type: "button" }
|
|
604
|
+
};
|
|
605
|
+
return useRender({
|
|
606
|
+
defaultTagName: "button",
|
|
607
|
+
render,
|
|
608
|
+
props: mergeProps(defaultProps, props),
|
|
609
|
+
state: { slot: "text-link" }
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
//#endregion
|
|
613
|
+
//#region src/components/panel/panel-action.recipe.ts
|
|
614
|
+
const panelActionBase = tv$1({ base: [
|
|
615
|
+
"relative inline-flex w-auto shrink-0 items-center justify-center",
|
|
616
|
+
"overflow-visible font-sans leading-none tabular-nums select-none outline-none",
|
|
617
|
+
"transition-all duration-100",
|
|
618
|
+
"[&>svg]:pointer-events-none [&>svg]:shrink-0",
|
|
619
|
+
"h-control min-w-control rounded-control border-[1.5px] border-transparent bg-transparent py-0",
|
|
620
|
+
"px-2 md:pointer-fine:px-[5px]",
|
|
621
|
+
"after:absolute after:-inset-control-hit",
|
|
622
|
+
"text-foreground",
|
|
623
|
+
"hover:bg-fill-medium active:bg-fill-medium",
|
|
624
|
+
"focus-visible:shadow-focus",
|
|
625
|
+
"[&>svg]:size-control-icon",
|
|
626
|
+
controlIconInset
|
|
627
|
+
] });
|
|
628
|
+
const panelAction = tv$1({
|
|
629
|
+
extend: panelActionBase,
|
|
630
|
+
base: [
|
|
631
|
+
"whitespace-nowrap",
|
|
632
|
+
"disabled:pointer-events-none disabled:transform-none",
|
|
633
|
+
"gap-1.5 text-control font-bold",
|
|
634
|
+
"disabled:bg-transparent disabled:text-foreground-disabled disabled:opacity-100",
|
|
635
|
+
"aria-pressed:border-sticker-ink aria-pressed:bg-brand aria-pressed:text-sticker-ink aria-pressed:ring-[1.5px] aria-pressed:ring-sticker-edge",
|
|
636
|
+
"aria-pressed:active:overlay-sticker-medium"
|
|
637
|
+
]
|
|
638
|
+
});
|
|
639
|
+
const panelToggleAction = tv$1({
|
|
640
|
+
extend: panelActionBase,
|
|
641
|
+
base: [
|
|
642
|
+
"data-disabled:pointer-events-none data-disabled:transform-none",
|
|
643
|
+
"gap-1.5 text-control font-bold",
|
|
644
|
+
"data-disabled:bg-transparent data-disabled:text-foreground-disabled data-disabled:opacity-100",
|
|
645
|
+
"data-pressed:border-sticker-ink data-pressed:bg-brand data-pressed:text-sticker-ink data-pressed:ring-[1.5px] data-pressed:ring-sticker-edge",
|
|
646
|
+
"data-pressed:active:overlay-sticker-medium"
|
|
647
|
+
]
|
|
648
|
+
});
|
|
649
|
+
//#endregion
|
|
650
|
+
//#region src/components/panel/panel-action.tsx
|
|
651
|
+
function PanelAction({ className, render, nativeButton, ...props }) {
|
|
652
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
653
|
+
...props,
|
|
654
|
+
render,
|
|
655
|
+
nativeButton: nativeButton ?? defaultNativeButton(render),
|
|
656
|
+
type: "button",
|
|
657
|
+
"data-slot": "panel-action",
|
|
658
|
+
"data-icon-inset": "",
|
|
659
|
+
className: panelAction({ className })
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
//#endregion
|
|
413
663
|
//#region src/components/dialog/dialog.tsx
|
|
414
664
|
const dialog = tv$1({
|
|
415
665
|
slots: {
|
|
@@ -430,7 +680,18 @@ const dialog = tv$1({
|
|
|
430
680
|
"px-5 pt-1.5 pb-5 first:pt-5",
|
|
431
681
|
"font-sans text-subhead text-foreground-muted"
|
|
432
682
|
],
|
|
433
|
-
footer: [
|
|
683
|
+
footer: [
|
|
684
|
+
"flex shrink-0 items-center justify-end",
|
|
685
|
+
...stackedActions,
|
|
686
|
+
"gap-2 px-5 pb-5 first:pt-5 [[data-slot=dialog-header]+&]:pt-5",
|
|
687
|
+
"*:data-[slot=text-link]:text-footnote"
|
|
688
|
+
],
|
|
689
|
+
footerNote: ["m-0 md:me-2 md:flex-1", "text-start font-sans text-footnote text-pretty text-foreground-muted md:whitespace-nowrap"],
|
|
690
|
+
footerActions: [
|
|
691
|
+
"ms-auto flex shrink-0 items-center justify-end gap-2",
|
|
692
|
+
...stackedActions,
|
|
693
|
+
"data-stacked:ms-0 data-stacked:w-full"
|
|
694
|
+
],
|
|
434
695
|
title: ["m-0 min-w-0 text-foreground", "font-sans text-headline font-bold"],
|
|
435
696
|
description: ["m-0", "font-sans text-subhead text-foreground-muted"]
|
|
436
697
|
},
|
|
@@ -462,6 +723,10 @@ const dialog = tv$1({
|
|
|
462
723
|
nested: {
|
|
463
724
|
false: {},
|
|
464
725
|
true: { overlay: ["bg-layer-backdrop-nested"] }
|
|
726
|
+
},
|
|
727
|
+
start: {
|
|
728
|
+
false: {},
|
|
729
|
+
true: { footer: ["flex-wrap justify-between"] }
|
|
465
730
|
}
|
|
466
731
|
},
|
|
467
732
|
defaultVariants: {
|
|
@@ -502,6 +767,7 @@ function DialogOverlay({ className, layer, ...props }) {
|
|
|
502
767
|
...props
|
|
503
768
|
});
|
|
504
769
|
}
|
|
770
|
+
const DialogSizeContext = createContext(void 0);
|
|
505
771
|
function DialogContent({ className, children, portalProps, overlay, size, layer, ...props }) {
|
|
506
772
|
const { content } = dialog({
|
|
507
773
|
size,
|
|
@@ -517,11 +783,15 @@ function DialogContent({ className, children, portalProps, overlay, size, layer,
|
|
|
517
783
|
"data-slot": "dialog-content",
|
|
518
784
|
className: content({ className }),
|
|
519
785
|
...props,
|
|
520
|
-
children
|
|
786
|
+
children: /* @__PURE__ */ jsx(DialogSizeContext.Provider, {
|
|
787
|
+
value: size ?? (/(^|\s)max-w-/.test(className ?? "") ? void 0 : "md"),
|
|
788
|
+
children: /* @__PURE__ */ jsx(DialogSheetReset, { children })
|
|
789
|
+
})
|
|
521
790
|
})]
|
|
522
791
|
});
|
|
523
792
|
}
|
|
524
793
|
function DialogClose({ className, variant, size, children, render, "aria-label": ariaLabel, ...props }) {
|
|
794
|
+
const inSheetHead = useInDialogSheetHead();
|
|
525
795
|
if (render) return /* @__PURE__ */ jsx(Dialog.Close, {
|
|
526
796
|
"data-slot": "dialog-close",
|
|
527
797
|
render,
|
|
@@ -529,6 +799,15 @@ function DialogClose({ className, variant, size, children, render, "aria-label":
|
|
|
529
799
|
...props,
|
|
530
800
|
children
|
|
531
801
|
});
|
|
802
|
+
if (inSheetHead) return /* @__PURE__ */ jsx(Dialog.Close, {
|
|
803
|
+
"data-slot": "dialog-close",
|
|
804
|
+
render: /* @__PURE__ */ jsx(PanelAction, {
|
|
805
|
+
"aria-label": ariaLabel ?? "Close",
|
|
806
|
+
className
|
|
807
|
+
}),
|
|
808
|
+
...props,
|
|
809
|
+
children: children ?? /* @__PURE__ */ jsx(X, {})
|
|
810
|
+
});
|
|
532
811
|
return /* @__PURE__ */ jsx(Dialog.Close, {
|
|
533
812
|
"data-slot": "dialog-close",
|
|
534
813
|
render: /* @__PURE__ */ jsx(Button$1, {
|
|
@@ -541,35 +820,149 @@ function DialogClose({ className, variant, size, children, render, "aria-label":
|
|
|
541
820
|
children: children ?? /* @__PURE__ */ jsx(X, { className: "size-4.5" })
|
|
542
821
|
});
|
|
543
822
|
}
|
|
544
|
-
|
|
823
|
+
/**
|
|
824
|
+
* Back, in a later step of a flow drawn as a phone sheet: it takes the head's
|
|
825
|
+
* start in place of × (stdrc, 2026-10-22). The sheet is a stack of steps, and
|
|
826
|
+
* Back undoes one, as on a pushed page; Esc still closes. Put it in
|
|
827
|
+
* `DialogHeader` beside the `DialogClose`, from the second step on; it draws
|
|
828
|
+
* nothing outside a sheet, where Back stays in the footer. Its icon is
|
|
829
|
+
* `arrow-left`, as every Back (Alignment, "The ends of a header").
|
|
830
|
+
*/
|
|
831
|
+
function DialogSheetBack({ className, children, "aria-label": ariaLabel, ...props }) {
|
|
832
|
+
if (!useInDialogSheetHead()) return null;
|
|
833
|
+
return /* @__PURE__ */ jsx(PanelAction, {
|
|
834
|
+
"data-slot": "dialog-sheet-back",
|
|
835
|
+
"aria-label": ariaLabel ?? "Back",
|
|
836
|
+
className,
|
|
837
|
+
...props,
|
|
838
|
+
children: children ?? /* @__PURE__ */ jsx(ArrowLeft, {})
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
function DialogHeader({ className, bar = false, children, ...props }) {
|
|
842
|
+
const { sheet, setCommitSlot } = useDialogSheet();
|
|
843
|
+
const items = sheet ? Children.toArray(children) : [];
|
|
844
|
+
const backs = items.filter((item) => isValidElement(item) && item.type === DialogSheetBack);
|
|
845
|
+
const closes = items.filter((item) => isValidElement(item) && item.type === DialogClose);
|
|
846
|
+
const missingLeading = sheet && backs.length === 0 && closes.length === 0;
|
|
847
|
+
useEffect(() => {
|
|
848
|
+
if (process.env.NODE_ENV === "production" || !missingLeading) return;
|
|
849
|
+
console.warn("raft-ui: a DialogHeader drawn as a phone sheet has no DialogClose (or DialogSheetBack) as a direct child, so the sheet has no × at the start of its head. Put DialogClose directly in DialogHeader, not inside a Tooltip, a Fragment or a component of your own.");
|
|
850
|
+
}, [missingLeading]);
|
|
851
|
+
if (sheet) {
|
|
852
|
+
const rest = items.filter((item) => !backs.includes(item) && !closes.includes(item));
|
|
853
|
+
const head = dialogSheet();
|
|
854
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
855
|
+
"data-slot": "dialog-header",
|
|
856
|
+
"data-sheet": "",
|
|
857
|
+
className: dialog({ bar: false }).header({ className: cn(head.header(), className) }),
|
|
858
|
+
...props,
|
|
859
|
+
children: [/* @__PURE__ */ jsxs(DialogSheetHeadContext.Provider, {
|
|
860
|
+
value: true,
|
|
861
|
+
children: [backs.length > 0 ? backs : closes, rest]
|
|
862
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
863
|
+
ref: setCommitSlot,
|
|
864
|
+
"data-slot": "dialog-commit-slot",
|
|
865
|
+
className: head.commitSlot()
|
|
866
|
+
})]
|
|
867
|
+
});
|
|
868
|
+
}
|
|
545
869
|
const { header } = dialog({ bar });
|
|
546
870
|
return /* @__PURE__ */ jsx("div", {
|
|
547
871
|
"data-slot": "dialog-header",
|
|
548
872
|
className: header({ className }),
|
|
549
|
-
...props
|
|
873
|
+
...props,
|
|
874
|
+
children
|
|
550
875
|
});
|
|
551
876
|
}
|
|
552
|
-
function DialogBody({ className, bar = false, ...props }) {
|
|
877
|
+
function DialogBody({ className, bar = false, children, ...props }) {
|
|
878
|
+
const { sheet, setBodyEndSlot } = useDialogSheet();
|
|
553
879
|
const { body } = dialog({ bar });
|
|
554
|
-
|
|
880
|
+
const sheetBody = sheet ? cn(bar && dialogSheet().barBody(), dialogSheet().body()) : void 0;
|
|
881
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
555
882
|
"data-slot": "dialog-body",
|
|
556
|
-
className: body({ className }),
|
|
557
|
-
...props
|
|
883
|
+
className: body({ className: sheetBody ? cn(sheetBody, className) : className }),
|
|
884
|
+
...props,
|
|
885
|
+
children: [children, sheet ? /* @__PURE__ */ jsx("div", {
|
|
886
|
+
ref: setBodyEndSlot,
|
|
887
|
+
"data-slot": "dialog-sheet-body-end",
|
|
888
|
+
className: dialogSheet().bodyEnd()
|
|
889
|
+
}) : null]
|
|
558
890
|
});
|
|
559
891
|
}
|
|
560
|
-
function DialogFooter({ className, bar = false, ...props }) {
|
|
561
|
-
const {
|
|
562
|
-
|
|
892
|
+
function DialogFooter({ className, bar = false, hidden, children, ref, ...props }) {
|
|
893
|
+
const { sheet, bodyEndSlot } = useDialogSheet();
|
|
894
|
+
const items = Children.toArray(children);
|
|
895
|
+
const start = items.find((item) => isValidElement(item) && item.type === DialogFooterNote && hasNoteContent(item.props.children)) ?? (isValidElement(items[0]) && items[0].type === TextLink ? items[0] : void 0);
|
|
896
|
+
const [stacked, rowRef] = useStackedActions(start == null ? ref : void 0, start != null);
|
|
897
|
+
const { footer, footerActions } = dialog({
|
|
898
|
+
bar,
|
|
899
|
+
start: start != null
|
|
900
|
+
});
|
|
901
|
+
const stackedProps = stacked ? { "data-stacked": "" } : {};
|
|
902
|
+
if (start == null) return /* @__PURE__ */ jsx("div", {
|
|
563
903
|
"data-slot": "dialog-footer",
|
|
904
|
+
ref: rowRef,
|
|
564
905
|
className: footer({ className }),
|
|
565
|
-
|
|
906
|
+
hidden: sheet || hidden,
|
|
907
|
+
...stackedProps,
|
|
908
|
+
...props,
|
|
909
|
+
children
|
|
910
|
+
});
|
|
911
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [sheet && bodyEndSlot ? createPortal(start, bodyEndSlot) : null, /* @__PURE__ */ jsxs("div", {
|
|
912
|
+
"data-slot": "dialog-footer",
|
|
913
|
+
ref,
|
|
914
|
+
className: footer({ className }),
|
|
915
|
+
hidden: sheet || hidden,
|
|
916
|
+
...props,
|
|
917
|
+
children: [sheet ? null : start, /* @__PURE__ */ jsx("div", {
|
|
918
|
+
"data-slot": "dialog-footer-actions",
|
|
919
|
+
ref: rowRef,
|
|
920
|
+
className: footerActions(),
|
|
921
|
+
...stackedProps,
|
|
922
|
+
children: items.filter((item) => item !== start)
|
|
923
|
+
})]
|
|
924
|
+
})] });
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* Whether a note has anything to show: not only null, false or "", but also
|
|
928
|
+
* whitespace and an empty array (`{items.map(...)}` of nothing). Numbers,
|
|
929
|
+
* 0 included, and elements count.
|
|
930
|
+
*/
|
|
931
|
+
function hasNoteContent(children) {
|
|
932
|
+
return Children.toArray(children).some((child) => typeof child !== "string" || child.trim() !== "");
|
|
933
|
+
}
|
|
934
|
+
/**
|
|
935
|
+
* One short line at the start of `DialogFooter` that says what the commit
|
|
936
|
+
* button will do ("You'll be billed $8 per member each month"). Muted footnote
|
|
937
|
+
* text, start-aligned and never cut. Only in a `lg` or `wide` dialog, and at
|
|
938
|
+
* most 40 characters: on a desktop it is one line beside the buttons and never
|
|
939
|
+
* wraps; on a phone it goes above the buttons when they don't fit beside it,
|
|
940
|
+
* and may wrap to 2 lines. In a phone sheet, which has no footer, it moves
|
|
941
|
+
* to the end of the body. Longer copy belongs in the body. A footer holds a note or a `TextLink`,
|
|
942
|
+
* never both.
|
|
943
|
+
*/
|
|
944
|
+
function DialogFooterNote({ className, children, ...props }) {
|
|
945
|
+
const { footerNote } = dialog();
|
|
946
|
+
const size = useContext(DialogSizeContext);
|
|
947
|
+
useEffect(() => {
|
|
948
|
+
if (process.env.NODE_ENV === "production") return;
|
|
949
|
+
if (size === "sm" || size === "md") console.warn(`raft-ui: a DialogFooterNote is in a size="${size}" dialog. A footer note fits beside the buttons only in a size="lg" or size="wide" dialog; put the sentence in DialogBody instead.`);
|
|
950
|
+
}, [size]);
|
|
951
|
+
if (!hasNoteContent(children)) return null;
|
|
952
|
+
return /* @__PURE__ */ jsx("p", {
|
|
953
|
+
"data-slot": "dialog-footer-note",
|
|
954
|
+
className: footerNote({ className }),
|
|
955
|
+
...props,
|
|
956
|
+
children
|
|
566
957
|
});
|
|
567
958
|
}
|
|
568
959
|
function DialogTitle({ className, ...props }) {
|
|
960
|
+
const inSheetHead = useInDialogSheetHead();
|
|
569
961
|
const { title } = dialog();
|
|
962
|
+
const classes = inSheetHead ? cn(dialogSheet().title(), className) : className;
|
|
570
963
|
return /* @__PURE__ */ jsx(Dialog.Title, {
|
|
571
964
|
"data-slot": "dialog-title",
|
|
572
|
-
className: title({ className }),
|
|
965
|
+
className: title({ className: classes }),
|
|
573
966
|
...props
|
|
574
967
|
});
|
|
575
968
|
}
|
|
@@ -593,7 +986,7 @@ const dialogSurface = tv$1({ slots: { root: [
|
|
|
593
986
|
* animates itself: the host centres it, and a centred modal has no motion.
|
|
594
987
|
* DialogContent draws the same frame for a Base UI dialog.
|
|
595
988
|
*/
|
|
596
|
-
function DialogSurface({ className, render, ...props }) {
|
|
989
|
+
function DialogSurface({ className, render, children, ...props }) {
|
|
597
990
|
const { root } = dialogSurface();
|
|
598
991
|
return useRender({
|
|
599
992
|
defaultTagName: "div",
|
|
@@ -601,7 +994,7 @@ function DialogSurface({ className, render, ...props }) {
|
|
|
601
994
|
props: mergeProps({
|
|
602
995
|
"data-slot": "dialog-surface",
|
|
603
996
|
className: root({ className })
|
|
604
|
-
}, props)
|
|
997
|
+
}, props, { children: /* @__PURE__ */ jsx(DialogSheetReset, { children }) })
|
|
605
998
|
});
|
|
606
999
|
}
|
|
607
1000
|
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"] } });
|
|
@@ -618,6 +1011,206 @@ function DialogNav({ className, render, ...props }) {
|
|
|
618
1011
|
});
|
|
619
1012
|
}
|
|
620
1013
|
//#endregion
|
|
1014
|
+
//#region src/lib/use-mobile-viewport.ts
|
|
1015
|
+
const MOBILE_QUERY = "(max-width: 767px)";
|
|
1016
|
+
function subscribeMobileViewport(onStoreChange) {
|
|
1017
|
+
if (typeof window === "undefined") return () => void 0;
|
|
1018
|
+
const media = window.matchMedia(MOBILE_QUERY);
|
|
1019
|
+
media.addEventListener("change", onStoreChange);
|
|
1020
|
+
return () => media.removeEventListener("change", onStoreChange);
|
|
1021
|
+
}
|
|
1022
|
+
function getMobileViewportSnapshot() {
|
|
1023
|
+
return typeof window !== "undefined" && window.matchMedia(MOBILE_QUERY).matches;
|
|
1024
|
+
}
|
|
1025
|
+
function useMobileViewport() {
|
|
1026
|
+
return useSyncExternalStore(subscribeMobileViewport, getMobileViewportSnapshot, () => false);
|
|
1027
|
+
}
|
|
1028
|
+
//#endregion
|
|
1029
|
+
//#region src/components/dialog/dialog-sheet.tsx
|
|
1030
|
+
/**
|
|
1031
|
+
* DialogContent for a dialog with several fields, a long list or steps. On a
|
|
1032
|
+
* desktop and a tablet it is DialogContent, the same DOM and classes. Below
|
|
1033
|
+
* 768px it is a sheet: docked to the bottom, 90% of the screen tall, sliding
|
|
1034
|
+
* up over its scrim; `DialogHeader` becomes a pushed page's head (× at the
|
|
1035
|
+
* start, the title, the `DialogCommit` button at the end), `DialogFooter` is
|
|
1036
|
+
* hidden (× is Cancel) and `DialogBody` scrolls on the phone's 16px margin
|
|
1037
|
+
* above the home indicator. Base UI keeps Esc, focus and close.
|
|
1038
|
+
*/
|
|
1039
|
+
function DialogSheetContent({ className, overlay, style, ref, children, ...props }) {
|
|
1040
|
+
const phone = useMobileViewport();
|
|
1041
|
+
const [commitSlot, setCommitSlot] = useState(null);
|
|
1042
|
+
const [bodyEndSlot, setBodyEndSlot] = useState(null);
|
|
1043
|
+
const [popup, setPopup] = useState(null);
|
|
1044
|
+
const keyboardBox = useSheetKeyboardBox(phone);
|
|
1045
|
+
useRevealNewProblems(phone ? popup : null);
|
|
1046
|
+
const state = useMemo(() => ({
|
|
1047
|
+
sheet: phone,
|
|
1048
|
+
commitSlot: phone ? commitSlot : null,
|
|
1049
|
+
setCommitSlot,
|
|
1050
|
+
bodyEndSlot: phone ? bodyEndSlot : null,
|
|
1051
|
+
setBodyEndSlot
|
|
1052
|
+
}), [
|
|
1053
|
+
phone,
|
|
1054
|
+
commitSlot,
|
|
1055
|
+
bodyEndSlot
|
|
1056
|
+
]);
|
|
1057
|
+
const setRef = useCallback((element) => {
|
|
1058
|
+
setPopup(element);
|
|
1059
|
+
assignRef$1(ref, element);
|
|
1060
|
+
}, [ref]);
|
|
1061
|
+
const sheet = dialogSheet();
|
|
1062
|
+
const sheetOverlay = useMemo(() => overlay === false ? false : {
|
|
1063
|
+
...typeof overlay === "object" ? overlay : null,
|
|
1064
|
+
className: cn(dialogSheet().overlay(), typeof overlay === "object" ? overlay.className : ""),
|
|
1065
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
1066
|
+
"aria-hidden": true,
|
|
1067
|
+
className: "absolute inset-0"
|
|
1068
|
+
})
|
|
1069
|
+
}, [overlay]);
|
|
1070
|
+
const sheetStyle = useMemo(() => keyboardBox ? {
|
|
1071
|
+
...style,
|
|
1072
|
+
...keyboardBox
|
|
1073
|
+
} : style, [style, keyboardBox]);
|
|
1074
|
+
return /* @__PURE__ */ jsx(DialogContent, {
|
|
1075
|
+
...props,
|
|
1076
|
+
ref: setRef,
|
|
1077
|
+
overlay: phone ? sheetOverlay : overlay,
|
|
1078
|
+
className: phone ? cn(className, sheet.content()) : className,
|
|
1079
|
+
style: phone ? sheetStyle : style,
|
|
1080
|
+
"data-sheet": phone ? "" : void 0,
|
|
1081
|
+
children: /* @__PURE__ */ jsx(DialogSheetContext.Provider, {
|
|
1082
|
+
value: state,
|
|
1083
|
+
children
|
|
1084
|
+
})
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
function assignRef$1(ref, value) {
|
|
1088
|
+
if (typeof ref === "function") ref(value);
|
|
1089
|
+
else if (ref) ref.current = value;
|
|
1090
|
+
}
|
|
1091
|
+
/**
|
|
1092
|
+
* The dialog's one commit button: a `Button`, written where it goes on a
|
|
1093
|
+
* desktop (the end of `DialogFooter`). In a `DialogSheetContent` below 768px
|
|
1094
|
+
* it is drawn at the end of the sheet's head instead, the same button (color,
|
|
1095
|
+
* words, disabled and loading state) at `size="md"`. In a flow of steps, write
|
|
1096
|
+
* each step's button as one (Next, then the last step's action).
|
|
1097
|
+
*
|
|
1098
|
+
* A submit button stays its form's default button in the head: it is tied to
|
|
1099
|
+
* the form it was written in by the form's `id` (the `form` attribute), so a
|
|
1100
|
+
* click and Enter in a field submit, and neither does while it is disabled.
|
|
1101
|
+
* Give that form an `id`, or pass `form`. Without one a click still submits
|
|
1102
|
+
* the form, but Enter in a field doesn't.
|
|
1103
|
+
*/
|
|
1104
|
+
function DialogCommit({ size, form, onClick, ...props }) {
|
|
1105
|
+
const { commitSlot } = useDialogSheet();
|
|
1106
|
+
const anchorRef = useRef(null);
|
|
1107
|
+
const [writtenIn, setWrittenIn] = useState(null);
|
|
1108
|
+
useLayoutEffect(() => {
|
|
1109
|
+
if (!commitSlot || form) return;
|
|
1110
|
+
const found = anchorRef.current?.closest("form") ?? null;
|
|
1111
|
+
setWrittenIn(found);
|
|
1112
|
+
if (process.env.NODE_ENV === "production" || props.type !== "submit" || found?.id) return;
|
|
1113
|
+
console.warn(found ? "raft-ui: a submit DialogCommit is drawn in a phone sheet's head, outside its form, and the form has no id, so Enter in a field doesn't submit it (a click still does). Give the form an id, or pass `form` to DialogCommit." : "raft-ui: a submit DialogCommit in a phone sheet isn't inside a form and has no `form`, so it submits nothing. Pass `form` with the form's id.");
|
|
1114
|
+
}, [
|
|
1115
|
+
commitSlot,
|
|
1116
|
+
form,
|
|
1117
|
+
props.type
|
|
1118
|
+
]);
|
|
1119
|
+
const handleClick = useCallback((event) => {
|
|
1120
|
+
onClick?.(event);
|
|
1121
|
+
if (!event.defaultPrevented && props.type === "submit" && writtenIn && !writtenIn.id) {
|
|
1122
|
+
event.preventDefault();
|
|
1123
|
+
writtenIn.requestSubmit();
|
|
1124
|
+
}
|
|
1125
|
+
}, [
|
|
1126
|
+
onClick,
|
|
1127
|
+
props.type,
|
|
1128
|
+
writtenIn
|
|
1129
|
+
]);
|
|
1130
|
+
if (!commitSlot) return /* @__PURE__ */ jsx(Button$1, {
|
|
1131
|
+
size,
|
|
1132
|
+
form,
|
|
1133
|
+
onClick,
|
|
1134
|
+
...props
|
|
1135
|
+
});
|
|
1136
|
+
const linkedForm = form ?? (writtenIn?.id || void 0);
|
|
1137
|
+
return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("span", {
|
|
1138
|
+
ref: anchorRef,
|
|
1139
|
+
hidden: true
|
|
1140
|
+
}), createPortal(/* @__PURE__ */ jsx(Button$1, {
|
|
1141
|
+
size: "md",
|
|
1142
|
+
form: linkedForm,
|
|
1143
|
+
onClick: handleClick,
|
|
1144
|
+
...props
|
|
1145
|
+
}), commitSlot)] });
|
|
1146
|
+
}
|
|
1147
|
+
/**
|
|
1148
|
+
* The sheet's box while the on-screen keyboard is up: docked to the top of the
|
|
1149
|
+
* keyboard, as tall as the visual viewport allows up to its usual 90% of the
|
|
1150
|
+
* window. With the keyboard up the visual viewport is shorter than that, so
|
|
1151
|
+
* the sheet fills it: the head stays in view and the body scrolls above the
|
|
1152
|
+
* keyboard. `undefined` otherwise (the CSS box).
|
|
1153
|
+
*/
|
|
1154
|
+
function useSheetKeyboardBox(active) {
|
|
1155
|
+
const [box, setBox] = useState(void 0);
|
|
1156
|
+
useEffect(() => {
|
|
1157
|
+
const viewport = typeof window === "undefined" ? void 0 : window.visualViewport;
|
|
1158
|
+
if (!active || !viewport) return;
|
|
1159
|
+
const update = () => {
|
|
1160
|
+
const focused = document.activeElement;
|
|
1161
|
+
const keyboardUp = (focused instanceof HTMLInputElement || focused instanceof HTMLTextAreaElement || focused instanceof HTMLElement && focused.isContentEditable) && window.innerHeight - viewport.height > 100;
|
|
1162
|
+
const height = Math.round(Math.min(viewport.height, window.innerHeight * .9));
|
|
1163
|
+
const top = Math.round(Math.max(viewport.offsetTop, 0) + viewport.height - height);
|
|
1164
|
+
setBox((previous) => {
|
|
1165
|
+
if (!keyboardUp) return void 0;
|
|
1166
|
+
return previous?.top === `${top}px` && previous?.height === `${height}px` ? previous : {
|
|
1167
|
+
top: `${top}px`,
|
|
1168
|
+
bottom: "auto",
|
|
1169
|
+
height: `${height}px`
|
|
1170
|
+
};
|
|
1171
|
+
});
|
|
1172
|
+
};
|
|
1173
|
+
viewport.addEventListener("resize", update);
|
|
1174
|
+
viewport.addEventListener("scroll", update, { passive: true });
|
|
1175
|
+
document.addEventListener("focusin", update);
|
|
1176
|
+
document.addEventListener("focusout", update);
|
|
1177
|
+
return () => {
|
|
1178
|
+
viewport.removeEventListener("resize", update);
|
|
1179
|
+
viewport.removeEventListener("scroll", update);
|
|
1180
|
+
document.removeEventListener("focusin", update);
|
|
1181
|
+
document.removeEventListener("focusout", update);
|
|
1182
|
+
};
|
|
1183
|
+
}, [active]);
|
|
1184
|
+
return active ? box : void 0;
|
|
1185
|
+
}
|
|
1186
|
+
/**
|
|
1187
|
+
* The commit is at the top of a sheet, so an error a failed commit adds near
|
|
1188
|
+
* the bottom of a long form would appear out of sight. A problem added after
|
|
1189
|
+
* the sheet opens (an alert, or a warning or destructive Banner) is scrolled
|
|
1190
|
+
* into view.
|
|
1191
|
+
*/
|
|
1192
|
+
const PROBLEM_SELECTOR = "[role=alert], [data-slot=banner][data-status=warning], [data-slot=banner][data-status=destructive]";
|
|
1193
|
+
function useRevealNewProblems(popup) {
|
|
1194
|
+
useEffect(() => {
|
|
1195
|
+
if (!popup || typeof MutationObserver === "undefined") return;
|
|
1196
|
+
const observer = new MutationObserver((records) => {
|
|
1197
|
+
for (const record of records) for (const node of record.addedNodes) {
|
|
1198
|
+
if (!(node instanceof Element)) continue;
|
|
1199
|
+
const problem = node.matches(PROBLEM_SELECTOR) ? node : node.querySelector(PROBLEM_SELECTOR);
|
|
1200
|
+
if (problem) {
|
|
1201
|
+
problem.scrollIntoView?.({ block: "nearest" });
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
observer.observe(popup, {
|
|
1207
|
+
childList: true,
|
|
1208
|
+
subtree: true
|
|
1209
|
+
});
|
|
1210
|
+
return () => observer.disconnect();
|
|
1211
|
+
}, [popup]);
|
|
1212
|
+
}
|
|
1213
|
+
//#endregion
|
|
621
1214
|
//#region src/components/badge/badge.tsx
|
|
622
1215
|
const badge = tv$1({
|
|
623
1216
|
slots: { root: [
|
|
@@ -799,36 +1392,6 @@ function Experimental(props) {
|
|
|
799
1392
|
}
|
|
800
1393
|
const Badge = Object.assign(BadgeRoot, { Experimental });
|
|
801
1394
|
//#endregion
|
|
802
|
-
//#region src/components/text-link/text-link.tsx
|
|
803
|
-
const textLinkVariants = tv$1({
|
|
804
|
-
base: [
|
|
805
|
-
"rounded-[2px]",
|
|
806
|
-
"decoration-[1.5px] underline-offset-[3px] not-disabled:hover:underline",
|
|
807
|
-
"outline-none focus-visible:shadow-focus",
|
|
808
|
-
"disabled:cursor-not-allowed disabled:text-foreground-disabled"
|
|
809
|
-
],
|
|
810
|
-
variants: { variant: {
|
|
811
|
-
muted: ["font-medium text-foreground-muted", "hover:text-foreground"],
|
|
812
|
-
strong: ["font-semibold text-foreground"]
|
|
813
|
-
} },
|
|
814
|
-
defaultVariants: { variant: "muted" }
|
|
815
|
-
});
|
|
816
|
-
function TextLink({ variant, className, render, ...props }) {
|
|
817
|
-
const defaultProps = {
|
|
818
|
-
className: textLinkVariants({
|
|
819
|
-
variant,
|
|
820
|
-
className
|
|
821
|
-
}),
|
|
822
|
-
...render ? {} : { type: "button" }
|
|
823
|
-
};
|
|
824
|
-
return useRender({
|
|
825
|
-
defaultTagName: "button",
|
|
826
|
-
render,
|
|
827
|
-
props: mergeProps(defaultProps, props),
|
|
828
|
-
state: { slot: "text-link" }
|
|
829
|
-
});
|
|
830
|
-
}
|
|
831
|
-
//#endregion
|
|
832
1395
|
//#region src/components/tooltip/tooltip.tsx
|
|
833
1396
|
const tooltip = tv$1({
|
|
834
1397
|
slots: {
|
|
@@ -1004,32 +1567,257 @@ function TooltipContent({ align = "center", alignOffset = 0, anchor, arrowPaddin
|
|
|
1004
1567
|
});
|
|
1005
1568
|
}
|
|
1006
1569
|
//#endregion
|
|
1007
|
-
//#region src/
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1570
|
+
//#region src/components/list-row/list-row.recipe.ts
|
|
1571
|
+
const hairline = [
|
|
1572
|
+
"before:pointer-events-none before:absolute before:top-0 before:right-0 before:hidden",
|
|
1573
|
+
"before:left-(--list-row-rule-start) before:border-t before:border-line-hairline",
|
|
1574
|
+
"[[data-slot=list-row]+&]:before:block [li+li>&]:before:block"
|
|
1575
|
+
];
|
|
1576
|
+
const listRow = tv$1({
|
|
1577
|
+
slots: {
|
|
1578
|
+
group: ["flex min-w-0 flex-col overflow-hidden", "rounded-container border border-line-hairline bg-layer-card text-foreground"],
|
|
1579
|
+
root: [
|
|
1580
|
+
"group/list-row relative flex w-full min-w-0 items-center gap-3 px-4 py-3 text-left",
|
|
1581
|
+
"font-sans no-underline",
|
|
1582
|
+
"transition-colors duration-100",
|
|
1583
|
+
"outline-none focus-visible:keyboard:inset-ring-2 focus-visible:keyboard:inset-ring-foreground",
|
|
1584
|
+
"data-selected:bg-info-soft",
|
|
1585
|
+
"[--list-row-ink:var(--foreground)] [--list-row-ink-2:var(--foreground-muted)] [--list-row-ink-3:var(--foreground-hint)]",
|
|
1586
|
+
"text-(--list-row-ink)",
|
|
1587
|
+
"not-disabled:not-aria-disabled:data-[tone=unread]:[--list-row-ink-2:var(--foreground)]",
|
|
1588
|
+
"not-disabled:not-aria-disabled:data-[tone=muted]:[--list-row-ink:var(--foreground-muted)]",
|
|
1589
|
+
"not-disabled:not-aria-disabled:data-[tone=away]:[--list-row-ink:var(--foreground-hint)]",
|
|
1590
|
+
"not-disabled:not-aria-disabled:data-[tone=away]:[--list-row-ink-2:var(--foreground-hint)]",
|
|
1591
|
+
"disabled:cursor-not-allowed disabled:[--list-row-ink:var(--foreground-disabled)]",
|
|
1592
|
+
"disabled:[--list-row-ink-2:var(--foreground-disabled)] disabled:[--list-row-ink-3:var(--foreground-disabled)]",
|
|
1593
|
+
"aria-disabled:cursor-not-allowed aria-disabled:[--list-row-ink:var(--foreground-disabled)]",
|
|
1594
|
+
"aria-disabled:[--list-row-ink-2:var(--foreground-disabled)] aria-disabled:[--list-row-ink-3:var(--foreground-disabled)]"
|
|
1595
|
+
],
|
|
1596
|
+
icon: ["flex shrink-0 items-center justify-center", "text-(--list-row-ink-3) [&>svg]:size-5 [&>svg]:shrink-0"],
|
|
1597
|
+
body: ["flex min-w-0 flex-1 flex-col gap-0.5"],
|
|
1598
|
+
line: ["flex min-w-0 items-center gap-2"],
|
|
1599
|
+
title: [
|
|
1600
|
+
"min-w-0 flex-1 truncate",
|
|
1601
|
+
"text-body font-semibold",
|
|
1602
|
+
"group-data-[tone=unread]/list-row:font-bold"
|
|
1603
|
+
],
|
|
1604
|
+
description: ["min-w-0 flex-1 truncate", "text-callout text-(--list-row-ink-2)"],
|
|
1605
|
+
meta: ["shrink-0", "font-mono text-mono-meta font-normal tabular-nums text-(--list-row-ink-3)"],
|
|
1606
|
+
value: ["max-w-[45%] shrink-0 truncate", "text-subhead text-(--list-row-ink-3)"],
|
|
1607
|
+
chevron: ["flex shrink-0 items-center justify-center", "text-(--list-row-ink-3) [&>svg]:size-5 [&>svg]:shrink-0"]
|
|
1608
|
+
},
|
|
1029
1609
|
variants: {
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1610
|
+
variant: {
|
|
1611
|
+
default: { root: ["min-h-16 [--list-row-rule-start:63px]", hairline] },
|
|
1612
|
+
plain: { root: ["[--list-row-rule-start:16px]", hairline] },
|
|
1613
|
+
entry: { root: "" }
|
|
1614
|
+
},
|
|
1615
|
+
interactive: {
|
|
1616
|
+
true: { root: [
|
|
1617
|
+
"not-disabled:not-aria-disabled:not-data-selected:hover:overlay-subtle",
|
|
1618
|
+
"not-disabled:not-aria-disabled:not-data-selected:active:overlay-medium",
|
|
1619
|
+
"not-data-selected:data-popup-open:overlay-subtle"
|
|
1620
|
+
] },
|
|
1621
|
+
false: {}
|
|
1622
|
+
},
|
|
1623
|
+
wrap: {
|
|
1624
|
+
true: { description: "whitespace-normal text-pretty" },
|
|
1625
|
+
false: {}
|
|
1626
|
+
}
|
|
1627
|
+
},
|
|
1628
|
+
defaultVariants: {
|
|
1629
|
+
variant: "default",
|
|
1630
|
+
interactive: true,
|
|
1631
|
+
wrap: false
|
|
1632
|
+
}
|
|
1633
|
+
});
|
|
1634
|
+
//#endregion
|
|
1635
|
+
//#region src/components/list-row/list-row.tsx
|
|
1636
|
+
/** An inset group: rows wrapped in a card (surface, hairline, radius-lg). */
|
|
1637
|
+
function ListRowGroup({ className, render, ...props }) {
|
|
1638
|
+
const { group } = listRow();
|
|
1639
|
+
const defaultProps = {
|
|
1640
|
+
"data-slot": "list-row-group",
|
|
1641
|
+
className: group({ className })
|
|
1642
|
+
};
|
|
1643
|
+
return useRender({
|
|
1644
|
+
defaultTagName: "div",
|
|
1645
|
+
render,
|
|
1646
|
+
props: mergeProps(defaultProps, props)
|
|
1647
|
+
});
|
|
1648
|
+
}
|
|
1649
|
+
/**
|
|
1650
|
+
* One row of a list. It renders a `button` by default; pass `render={<a />}`
|
|
1651
|
+
* (or a router link) for a row that goes somewhere. Children are the leading
|
|
1652
|
+
* block (an avatar, a channel or icon tile, `ListRowIcon`), `ListRowBody`, and
|
|
1653
|
+
* anything trailing (a value, a badge, a switch, `ListRowChevron`).
|
|
1654
|
+
*/
|
|
1655
|
+
function ListRow({ variant = "default", tone = "default", selected = false, interactive = true, className, render, ...props }) {
|
|
1656
|
+
const { root } = listRow({
|
|
1657
|
+
variant,
|
|
1658
|
+
interactive
|
|
1659
|
+
});
|
|
1660
|
+
const defaultProps = {
|
|
1661
|
+
"data-slot": "list-row",
|
|
1662
|
+
"data-variant": variant,
|
|
1663
|
+
"data-tone": tone === "default" ? void 0 : tone,
|
|
1664
|
+
"data-selected": selected ? "" : void 0,
|
|
1665
|
+
className: root({ className }),
|
|
1666
|
+
...interactive && !render ? { type: "button" } : {}
|
|
1667
|
+
};
|
|
1668
|
+
return useRender({
|
|
1669
|
+
defaultTagName: interactive ? "button" : "div",
|
|
1670
|
+
render,
|
|
1671
|
+
props: mergeProps(defaultProps, props)
|
|
1672
|
+
});
|
|
1673
|
+
}
|
|
1674
|
+
/** A 20px leading icon in ink-3: entry rows and settings. */
|
|
1675
|
+
function ListRowIcon({ className, render, ...props }) {
|
|
1676
|
+
const { icon } = listRow();
|
|
1677
|
+
const defaultProps = {
|
|
1678
|
+
"data-slot": "list-row-icon",
|
|
1679
|
+
"aria-hidden": true,
|
|
1680
|
+
className: icon({ className })
|
|
1681
|
+
};
|
|
1682
|
+
return useRender({
|
|
1683
|
+
defaultTagName: "span",
|
|
1684
|
+
render,
|
|
1685
|
+
props: mergeProps(defaultProps, props)
|
|
1686
|
+
});
|
|
1687
|
+
}
|
|
1688
|
+
/** The text column: the title and the preview, or a `ListRowTop` and a `ListRowBottom`. */
|
|
1689
|
+
function ListRowBody({ className, render, ...props }) {
|
|
1690
|
+
const { body } = listRow();
|
|
1691
|
+
const defaultProps = {
|
|
1692
|
+
"data-slot": "list-row-body",
|
|
1693
|
+
className: body({ className })
|
|
1694
|
+
};
|
|
1695
|
+
return useRender({
|
|
1696
|
+
defaultTagName: "span",
|
|
1697
|
+
render,
|
|
1698
|
+
props: mergeProps(defaultProps, props)
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
/** The first line: the title, then the time. */
|
|
1702
|
+
function ListRowTop({ className, render, ...props }) {
|
|
1703
|
+
const { line } = listRow();
|
|
1704
|
+
const defaultProps = {
|
|
1705
|
+
"data-slot": "list-row-top",
|
|
1706
|
+
className: line({ className })
|
|
1707
|
+
};
|
|
1708
|
+
return useRender({
|
|
1709
|
+
defaultTagName: "span",
|
|
1710
|
+
render,
|
|
1711
|
+
props: mergeProps(defaultProps, props)
|
|
1712
|
+
});
|
|
1713
|
+
}
|
|
1714
|
+
/** The second line: the preview, then at most one badge. */
|
|
1715
|
+
function ListRowBottom({ className, render, ...props }) {
|
|
1716
|
+
const { line } = listRow();
|
|
1717
|
+
const defaultProps = {
|
|
1718
|
+
"data-slot": "list-row-bottom",
|
|
1719
|
+
className: line({ className })
|
|
1720
|
+
};
|
|
1721
|
+
return useRender({
|
|
1722
|
+
defaultTagName: "span",
|
|
1723
|
+
render,
|
|
1724
|
+
props: mergeProps(defaultProps, props)
|
|
1725
|
+
});
|
|
1726
|
+
}
|
|
1727
|
+
/** 16/24 600 on one line; the row's tone sets its weight and ink. */
|
|
1728
|
+
function ListRowTitle({ className, render, ...props }) {
|
|
1729
|
+
const { title } = listRow();
|
|
1730
|
+
const defaultProps = {
|
|
1731
|
+
"data-slot": "list-row-title",
|
|
1732
|
+
className: title({ className })
|
|
1733
|
+
};
|
|
1734
|
+
return useRender({
|
|
1735
|
+
defaultTagName: "span",
|
|
1736
|
+
render,
|
|
1737
|
+
props: mergeProps(defaultProps, props)
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
/** The preview (who spoke first: "Wug: …") or a setting's consequence, 14/20 ink-2. */
|
|
1741
|
+
function ListRowDescription({ className, render, wrap = false, ...props }) {
|
|
1742
|
+
const { description } = listRow({ wrap });
|
|
1743
|
+
const defaultProps = {
|
|
1744
|
+
"data-slot": "list-row-description",
|
|
1745
|
+
className: description({ className })
|
|
1746
|
+
};
|
|
1747
|
+
return useRender({
|
|
1748
|
+
defaultTagName: "span",
|
|
1749
|
+
render,
|
|
1750
|
+
props: mergeProps(defaultProps, props)
|
|
1751
|
+
});
|
|
1752
|
+
}
|
|
1753
|
+
/** Machine-written facts in Space Mono 12, ink-3: the time, a gray count. */
|
|
1754
|
+
function ListRowMeta({ className, render, ...props }) {
|
|
1755
|
+
const { meta } = listRow();
|
|
1756
|
+
const defaultProps = {
|
|
1757
|
+
"data-slot": "list-row-meta",
|
|
1758
|
+
className: meta({ className })
|
|
1759
|
+
};
|
|
1760
|
+
return useRender({
|
|
1761
|
+
defaultTagName: "span",
|
|
1762
|
+
render,
|
|
1763
|
+
props: mergeProps(defaultProps, props)
|
|
1764
|
+
});
|
|
1765
|
+
}
|
|
1766
|
+
/** A setting's current value on the right, 15/20 ink-3, at most 45% of the row. */
|
|
1767
|
+
function ListRowValue({ className, render, ...props }) {
|
|
1768
|
+
const { value } = listRow();
|
|
1769
|
+
const defaultProps = {
|
|
1770
|
+
"data-slot": "list-row-value",
|
|
1771
|
+
className: value({ className })
|
|
1772
|
+
};
|
|
1773
|
+
return useRender({
|
|
1774
|
+
defaultTagName: "span",
|
|
1775
|
+
render,
|
|
1776
|
+
props: mergeProps(defaultProps, props)
|
|
1777
|
+
});
|
|
1778
|
+
}
|
|
1779
|
+
/** The ink-3 arrow of a row that opens another page. */
|
|
1780
|
+
function ListRowChevron({ className, render, ...props }) {
|
|
1781
|
+
const { chevron } = listRow();
|
|
1782
|
+
const defaultProps = {
|
|
1783
|
+
"data-slot": "list-row-chevron",
|
|
1784
|
+
"aria-hidden": true,
|
|
1785
|
+
className: chevron({ className }),
|
|
1786
|
+
children: /* @__PURE__ */ jsx(ChevronRight, {})
|
|
1787
|
+
};
|
|
1788
|
+
return useRender({
|
|
1789
|
+
defaultTagName: "span",
|
|
1790
|
+
render,
|
|
1791
|
+
props: mergeProps(defaultProps, props)
|
|
1792
|
+
});
|
|
1793
|
+
}
|
|
1794
|
+
//#endregion
|
|
1795
|
+
//#region src/lib/presence.ts
|
|
1796
|
+
/**
|
|
1797
|
+
* The five status lights of the design system. Each colour is a fixed
|
|
1798
|
+
* meaning: online green, busy (an agent at work) yellow, error orange,
|
|
1799
|
+
* offline stone, an external agent cyan. One map, read by Status and by the
|
|
1800
|
+
* avatar's dot through the same `--status-color`.
|
|
1801
|
+
*/
|
|
1802
|
+
const PRESENCE_COLOR = {
|
|
1803
|
+
online: "[--status-color:var(--presence-online)]",
|
|
1804
|
+
busy: "[--status-color:var(--presence-busy)]",
|
|
1805
|
+
error: "[--status-color:var(--presence-error)]",
|
|
1806
|
+
offline: "[--status-color:var(--presence-offline)]",
|
|
1807
|
+
external: "[--status-color:var(--presence-external)]"
|
|
1808
|
+
};
|
|
1809
|
+
//#endregion
|
|
1810
|
+
//#region src/components/checkbox/checkbox-indicator.tsx
|
|
1811
|
+
const checkboxIndicator = tv$1({
|
|
1812
|
+
slots: { root: [
|
|
1813
|
+
"inline-flex size-5 shrink-0 items-center justify-center",
|
|
1814
|
+
"rounded-sticker border-[1.5px] border-line-strong transition-colors",
|
|
1815
|
+
"[&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-3.5 [&_svg]:stroke-2"
|
|
1816
|
+
] },
|
|
1817
|
+
variants: {
|
|
1818
|
+
shape: {
|
|
1819
|
+
square: "",
|
|
1820
|
+
circle: { root: "rounded-full" }
|
|
1033
1821
|
},
|
|
1034
1822
|
checked: {
|
|
1035
1823
|
true: { root: "border-foreground bg-foreground text-foreground-inverse" },
|
|
@@ -1110,21 +1898,6 @@ function Checkbox$1({ shape = "square", className, ...props }) {
|
|
|
1110
1898
|
});
|
|
1111
1899
|
}
|
|
1112
1900
|
//#endregion
|
|
1113
|
-
//#region src/lib/use-mobile-viewport.ts
|
|
1114
|
-
const MOBILE_QUERY = "(max-width: 767px)";
|
|
1115
|
-
function subscribeMobileViewport(onStoreChange) {
|
|
1116
|
-
if (typeof window === "undefined") return () => void 0;
|
|
1117
|
-
const media = window.matchMedia(MOBILE_QUERY);
|
|
1118
|
-
media.addEventListener("change", onStoreChange);
|
|
1119
|
-
return () => media.removeEventListener("change", onStoreChange);
|
|
1120
|
-
}
|
|
1121
|
-
function getMobileViewportSnapshot() {
|
|
1122
|
-
return typeof window !== "undefined" && window.matchMedia(MOBILE_QUERY).matches;
|
|
1123
|
-
}
|
|
1124
|
-
function useMobileViewport() {
|
|
1125
|
-
return useSyncExternalStore(subscribeMobileViewport, getMobileViewportSnapshot, () => false);
|
|
1126
|
-
}
|
|
1127
|
-
//#endregion
|
|
1128
1901
|
//#region src/components/scroll-area/scroll-area.recipe.ts
|
|
1129
1902
|
const scrollArea = tv$1({ slots: {
|
|
1130
1903
|
root: ["relative min-h-0"],
|
|
@@ -1403,44 +2176,926 @@ function DropdownMenu(props) {
|
|
|
1403
2176
|
...props
|
|
1404
2177
|
});
|
|
1405
2178
|
}
|
|
1406
|
-
function DropdownMenuSubmenu(props) {
|
|
1407
|
-
return /* @__PURE__ */ jsx(Menu.SubmenuRoot, {
|
|
1408
|
-
"data-slot": "dropdown-menu-submenu",
|
|
2179
|
+
function DropdownMenuSubmenu(props) {
|
|
2180
|
+
return /* @__PURE__ */ jsx(Menu.SubmenuRoot, {
|
|
2181
|
+
"data-slot": "dropdown-menu-submenu",
|
|
2182
|
+
...props
|
|
2183
|
+
});
|
|
2184
|
+
}
|
|
2185
|
+
function DropdownMenuTrigger({ className, render = /* @__PURE__ */ jsx(Button$1, {
|
|
2186
|
+
variant: "outline",
|
|
2187
|
+
size: "md"
|
|
2188
|
+
}), ...props }) {
|
|
2189
|
+
const { trigger } = dropdownMenu();
|
|
2190
|
+
return /* @__PURE__ */ jsx(Menu.Trigger, {
|
|
2191
|
+
"data-slot": "dropdown-menu-trigger",
|
|
2192
|
+
render,
|
|
2193
|
+
className: trigger({ className }),
|
|
2194
|
+
...props
|
|
2195
|
+
});
|
|
2196
|
+
}
|
|
2197
|
+
function DropdownMenuPortal(props) {
|
|
2198
|
+
return /* @__PURE__ */ jsx(Menu.Portal, {
|
|
2199
|
+
"data-slot": "dropdown-menu-portal",
|
|
2200
|
+
...useThemeScopeProps(props)
|
|
2201
|
+
});
|
|
2202
|
+
}
|
|
2203
|
+
function DropdownMenuPopup({ className, ...props }) {
|
|
2204
|
+
const { content } = dropdownMenu();
|
|
2205
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2206
|
+
"data-slot": "dropdown-menu-content",
|
|
2207
|
+
className: content({ className }),
|
|
2208
|
+
...props
|
|
2209
|
+
});
|
|
2210
|
+
}
|
|
2211
|
+
function DropdownMenuContent({ align = "start", alignOffset = 0, anchor, arrowPadding, collisionAvoidance, collisionBoundary, collisionPadding, disableAnchorTracking, positionMethod = "fixed", side = "bottom", sideOffset, sticky, portalProps, className, ...props }) {
|
|
2212
|
+
const resolvedSideOffset = sideOffset ?? 6;
|
|
2213
|
+
const { positioner, content } = dropdownMenu();
|
|
2214
|
+
return /* @__PURE__ */ jsx(DropdownMenuPortal, {
|
|
2215
|
+
...portalProps,
|
|
2216
|
+
children: /* @__PURE__ */ jsx(Menu.Positioner, {
|
|
2217
|
+
align,
|
|
2218
|
+
alignOffset,
|
|
2219
|
+
anchor,
|
|
2220
|
+
arrowPadding,
|
|
2221
|
+
collisionAvoidance,
|
|
2222
|
+
collisionBoundary,
|
|
2223
|
+
collisionPadding,
|
|
2224
|
+
disableAnchorTracking,
|
|
2225
|
+
positionMethod,
|
|
2226
|
+
side,
|
|
2227
|
+
sideOffset: resolvedSideOffset,
|
|
2228
|
+
sticky,
|
|
2229
|
+
className: positioner(),
|
|
2230
|
+
children: /* @__PURE__ */ jsx(Menu.Popup, {
|
|
2231
|
+
"data-slot": "dropdown-menu-content",
|
|
2232
|
+
className: content({ className }),
|
|
2233
|
+
...props
|
|
2234
|
+
})
|
|
2235
|
+
})
|
|
2236
|
+
});
|
|
2237
|
+
}
|
|
2238
|
+
function DropdownMenuItem({ className, ...props }) {
|
|
2239
|
+
const { item } = dropdownMenu();
|
|
2240
|
+
return /* @__PURE__ */ jsx(Menu.Item, {
|
|
2241
|
+
"data-slot": "dropdown-menu-item",
|
|
2242
|
+
className: item({ className }),
|
|
2243
|
+
...props
|
|
2244
|
+
});
|
|
2245
|
+
}
|
|
2246
|
+
function DropdownMenuItemLabel({ className, ...props }) {
|
|
2247
|
+
const { itemLabel } = dropdownMenu();
|
|
2248
|
+
return /* @__PURE__ */ jsx("span", {
|
|
2249
|
+
"data-slot": "dropdown-menu-item-label",
|
|
2250
|
+
className: itemLabel({ className }),
|
|
2251
|
+
...props
|
|
2252
|
+
});
|
|
2253
|
+
}
|
|
2254
|
+
function DropdownMenuFooter({ className, ...props }) {
|
|
2255
|
+
const { footer } = dropdownMenu();
|
|
2256
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2257
|
+
"data-slot": "dropdown-menu-footer",
|
|
2258
|
+
className: footer({ className }),
|
|
2259
|
+
...props
|
|
2260
|
+
});
|
|
2261
|
+
}
|
|
2262
|
+
function DropdownMenuSubmenuTrigger({ children, className, ...props }) {
|
|
2263
|
+
const { submenuTrigger, indicator } = dropdownMenu();
|
|
2264
|
+
return /* @__PURE__ */ jsxs(Menu.SubmenuTrigger, {
|
|
2265
|
+
"data-slot": "dropdown-menu-submenu-trigger",
|
|
2266
|
+
className: submenuTrigger({ className }),
|
|
2267
|
+
...props,
|
|
2268
|
+
children: [children, /* @__PURE__ */ jsx("span", {
|
|
2269
|
+
className: indicator(),
|
|
2270
|
+
"data-slot": "dropdown-menu-submenu-trigger-indicator",
|
|
2271
|
+
"aria-hidden": "true",
|
|
2272
|
+
children: /* @__PURE__ */ jsx(DropdownMenuSubmenuChevronIcon, {})
|
|
2273
|
+
})]
|
|
2274
|
+
});
|
|
2275
|
+
}
|
|
2276
|
+
function DropdownMenuLabel({ className, ...props }) {
|
|
2277
|
+
const { label } = dropdownMenu();
|
|
2278
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2279
|
+
"data-slot": "dropdown-menu-label",
|
|
2280
|
+
className: label({ className }),
|
|
2281
|
+
...props
|
|
2282
|
+
});
|
|
2283
|
+
}
|
|
2284
|
+
function DropdownMenuShortcut({ className, ...props }) {
|
|
2285
|
+
const { shortcut } = dropdownMenu();
|
|
2286
|
+
return /* @__PURE__ */ jsx(Kbd, {
|
|
2287
|
+
"data-slot": "dropdown-menu-shortcut",
|
|
2288
|
+
className: shortcut({ className }),
|
|
2289
|
+
...props
|
|
2290
|
+
});
|
|
2291
|
+
}
|
|
2292
|
+
function DropdownMenuItemCount({ className, ...props }) {
|
|
2293
|
+
const { itemCount } = dropdownMenu();
|
|
2294
|
+
return /* @__PURE__ */ jsx(Badge, {
|
|
2295
|
+
"data-slot": "dropdown-menu-item-count",
|
|
2296
|
+
className: itemCount({ className }),
|
|
2297
|
+
...props
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2300
|
+
function DropdownMenuSeparator({ className, ...props }) {
|
|
2301
|
+
const { separator } = dropdownMenu();
|
|
2302
|
+
return /* @__PURE__ */ jsx(Menu.Separator, {
|
|
2303
|
+
"data-slot": "dropdown-menu-separator",
|
|
2304
|
+
className: separator({ className }),
|
|
2305
|
+
...props
|
|
2306
|
+
});
|
|
2307
|
+
}
|
|
2308
|
+
function DropdownMenuSubmenuChevronIcon() {
|
|
2309
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
2310
|
+
width: "12",
|
|
2311
|
+
height: "12",
|
|
2312
|
+
viewBox: "0 0 12 12",
|
|
2313
|
+
"aria-hidden": true,
|
|
2314
|
+
className: "size-3 shrink-0",
|
|
2315
|
+
children: /* @__PURE__ */ jsx("g", {
|
|
2316
|
+
fill: "none",
|
|
2317
|
+
strokeLinecap: "round",
|
|
2318
|
+
strokeLinejoin: "round",
|
|
2319
|
+
strokeWidth: "1",
|
|
2320
|
+
stroke: "currentColor",
|
|
2321
|
+
children: /* @__PURE__ */ jsx("path", { d: "M4.25 2.5L7.75 6l-3.5 3.5" })
|
|
2322
|
+
})
|
|
2323
|
+
});
|
|
2324
|
+
}
|
|
2325
|
+
function DropdownMenuCheckIcon() {
|
|
2326
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
2327
|
+
width: "12",
|
|
2328
|
+
height: "12",
|
|
2329
|
+
viewBox: "0 0 12 12",
|
|
2330
|
+
"aria-hidden": true,
|
|
2331
|
+
className: "size-3 shrink-0",
|
|
2332
|
+
children: /* @__PURE__ */ jsx("g", {
|
|
2333
|
+
fill: "none",
|
|
2334
|
+
strokeLinecap: "round",
|
|
2335
|
+
strokeLinejoin: "round",
|
|
2336
|
+
strokeWidth: "1",
|
|
2337
|
+
stroke: "currentColor",
|
|
2338
|
+
children: /* @__PURE__ */ jsx("path", { d: "M1.75 6.41L4.4 9.25L10.25 2.99" })
|
|
2339
|
+
})
|
|
2340
|
+
});
|
|
2341
|
+
}
|
|
2342
|
+
function DropdownMenuRadioGroup({ ...props }) {
|
|
2343
|
+
return /* @__PURE__ */ jsx(Menu.RadioGroup, {
|
|
2344
|
+
"data-slot": "dropdown-menu-radio-group",
|
|
2345
|
+
...props
|
|
2346
|
+
});
|
|
2347
|
+
}
|
|
2348
|
+
function DropdownMenuRadioItem({ className, children, ...props }) {
|
|
2349
|
+
const { radioItem, radioLabel, indicator } = dropdownMenu();
|
|
2350
|
+
return /* @__PURE__ */ jsxs(Menu.RadioItem, {
|
|
2351
|
+
"data-slot": "dropdown-menu-radio-item",
|
|
2352
|
+
className: radioItem({ className }),
|
|
2353
|
+
...props,
|
|
2354
|
+
children: [renderRadioItemChildren(children, radioLabel()), /* @__PURE__ */ jsx("span", {
|
|
2355
|
+
className: indicator(),
|
|
2356
|
+
children: /* @__PURE__ */ jsx(Menu.RadioItemIndicator, { children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {}) })
|
|
2357
|
+
})]
|
|
2358
|
+
});
|
|
2359
|
+
}
|
|
2360
|
+
function renderRadioItemChildren(children, className) {
|
|
2361
|
+
if (typeof children === "string" || typeof children === "number") return /* @__PURE__ */ jsx("span", {
|
|
2362
|
+
"data-slot": "dropdown-menu-radio-item-label",
|
|
2363
|
+
className,
|
|
2364
|
+
children
|
|
2365
|
+
});
|
|
2366
|
+
if (Array.isArray(children) && children.every((child) => typeof child === "string")) return /* @__PURE__ */ jsx("span", {
|
|
2367
|
+
"data-slot": "dropdown-menu-radio-item-label",
|
|
2368
|
+
className,
|
|
2369
|
+
children
|
|
2370
|
+
});
|
|
2371
|
+
return children;
|
|
2372
|
+
}
|
|
2373
|
+
function DropdownMenuRadioItemIndicator({ className, ...props }) {
|
|
2374
|
+
return /* @__PURE__ */ jsx(Menu.RadioItemIndicator, {
|
|
2375
|
+
"data-slot": "dropdown-menu-radio-item-indicator",
|
|
2376
|
+
className,
|
|
2377
|
+
...props
|
|
2378
|
+
});
|
|
2379
|
+
}
|
|
2380
|
+
function DropdownMenuCheckboxItem({ className, children, ...props }) {
|
|
2381
|
+
const { item, indicator } = dropdownMenu();
|
|
2382
|
+
return /* @__PURE__ */ jsxs(Menu.CheckboxItem, {
|
|
2383
|
+
"data-slot": "dropdown-menu-checkbox-item",
|
|
2384
|
+
className: item({ className }),
|
|
2385
|
+
...props,
|
|
2386
|
+
children: [children, /* @__PURE__ */ jsx("span", {
|
|
2387
|
+
className: indicator(),
|
|
2388
|
+
children: /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {}) })
|
|
2389
|
+
})]
|
|
2390
|
+
});
|
|
2391
|
+
}
|
|
2392
|
+
function DropdownMenuCheckboxItemIndicator({ className, ...props }) {
|
|
2393
|
+
return /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, {
|
|
2394
|
+
"data-slot": "dropdown-menu-checkbox-item-indicator",
|
|
2395
|
+
className,
|
|
2396
|
+
...props
|
|
2397
|
+
});
|
|
2398
|
+
}
|
|
2399
|
+
/**
|
|
2400
|
+
* One choice that is another page, such as the same page in another
|
|
2401
|
+
* language: a link, so it opens in a new tab and can be copied. Its
|
|
2402
|
+
* `href` goes on the item; `current` marks the page being shown.
|
|
2403
|
+
*/
|
|
2404
|
+
function DropdownMenuLinkItem({ current = false, className, children, ...props }) {
|
|
2405
|
+
const { radioItem, radioLabel, indicator } = dropdownMenu();
|
|
2406
|
+
return /* @__PURE__ */ jsxs(Menu.LinkItem, {
|
|
2407
|
+
"data-slot": "dropdown-menu-link-item",
|
|
2408
|
+
"aria-current": current ? "page" : void 0,
|
|
2409
|
+
"data-checked": current ? "" : void 0,
|
|
2410
|
+
className: radioItem({ className }),
|
|
2411
|
+
...props,
|
|
2412
|
+
children: [renderRadioItemChildren(children, radioLabel()), current ? /* @__PURE__ */ jsx("span", {
|
|
2413
|
+
className: indicator(),
|
|
2414
|
+
children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {})
|
|
2415
|
+
}) : null]
|
|
2416
|
+
});
|
|
2417
|
+
}
|
|
2418
|
+
//#endregion
|
|
2419
|
+
//#region src/components/mobile-nav/mobile-nav.tsx
|
|
2420
|
+
const mobileNav = tv$1({ slots: {
|
|
2421
|
+
root: [
|
|
2422
|
+
"grid w-full flex-none grid-flow-col auto-cols-fr items-start gap-0",
|
|
2423
|
+
"border-t border-line-seam bg-layer-card px-2 pt-1.5 text-foreground-hint",
|
|
2424
|
+
"pb-[max(8px,min(env(safe-area-inset-bottom,0px),34px))]"
|
|
2425
|
+
],
|
|
2426
|
+
item: [
|
|
2427
|
+
"relative inline-flex shrink-0 items-center outline-none",
|
|
2428
|
+
"disabled:pointer-events-none disabled:opacity-45",
|
|
2429
|
+
"[&_svg]:shrink-0",
|
|
2430
|
+
"isolate min-w-0 flex-col justify-start gap-[7px] border-0 px-0 pt-1 pb-0",
|
|
2431
|
+
"font-sans text-[10px]/3 font-semibold text-foreground-hint",
|
|
2432
|
+
"*:relative *:z-10 [&_svg]:size-[22px] [&_svg]:stroke-2",
|
|
2433
|
+
"before:pointer-events-none before:absolute before:top-0 before:left-1/2 before:z-0 before:-translate-x-1/2",
|
|
2434
|
+
"before:h-[30px] before:w-14 before:rounded-control before:border-[1.5px] before:border-transparent before:content-['']",
|
|
2435
|
+
"active:before:bg-fill-medium",
|
|
2436
|
+
"data-[selected=true]:font-bold data-[selected=true]:text-foreground",
|
|
2437
|
+
"data-[selected=true]:[&_svg]:text-sticker-ink",
|
|
2438
|
+
"data-[selected=true]:before:border-sticker-ink data-[selected=true]:before:bg-brand data-[selected=true]:before:ring-[1.5px] data-[selected=true]:before:ring-sticker-edge",
|
|
2439
|
+
"data-[selected=true]:active:before:overlay-sticker-medium",
|
|
2440
|
+
"rounded-control focus-visible:shadow-focus"
|
|
2441
|
+
],
|
|
2442
|
+
itemLabel: ["min-w-0 truncate", "max-w-full"]
|
|
2443
|
+
} });
|
|
2444
|
+
function MobileNavRoot({ className, render, ...props }) {
|
|
2445
|
+
const { root } = mobileNav();
|
|
2446
|
+
const defaultProps = {
|
|
2447
|
+
"data-slot": "mobile-nav-root",
|
|
2448
|
+
className: root({ className })
|
|
2449
|
+
};
|
|
2450
|
+
return useRender({
|
|
2451
|
+
defaultTagName: "nav",
|
|
2452
|
+
render,
|
|
2453
|
+
props: mergeProps(defaultProps, props)
|
|
2454
|
+
});
|
|
2455
|
+
}
|
|
2456
|
+
function MobileNavItem({ nativeButton = true, selected, className, render, ...props }) {
|
|
2457
|
+
const { item } = mobileNav();
|
|
2458
|
+
const defaultProps = {
|
|
2459
|
+
type: nativeButton ? "button" : void 0,
|
|
2460
|
+
"aria-pressed": nativeButton ? selected : void 0,
|
|
2461
|
+
"data-slot": "mobile-nav-item",
|
|
2462
|
+
"data-selected": selected ? "true" : void 0,
|
|
2463
|
+
className: item({ className })
|
|
2464
|
+
};
|
|
2465
|
+
return useRender({
|
|
2466
|
+
render: render ?? /* @__PURE__ */ jsx(Button, {}),
|
|
2467
|
+
props: mergeProps(defaultProps, props)
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
function MobileNavLabel({ className, render, ...props }) {
|
|
2471
|
+
const { itemLabel } = mobileNav();
|
|
2472
|
+
const defaultProps = {
|
|
2473
|
+
"data-slot": "mobile-nav-item-label",
|
|
2474
|
+
className: itemLabel({ className })
|
|
2475
|
+
};
|
|
2476
|
+
return useRender({
|
|
2477
|
+
defaultTagName: "span",
|
|
2478
|
+
render,
|
|
2479
|
+
props: mergeProps(defaultProps, props)
|
|
2480
|
+
});
|
|
2481
|
+
}
|
|
2482
|
+
const holds = /* @__PURE__ */ new WeakMap();
|
|
2483
|
+
function touchPoint(event) {
|
|
2484
|
+
const touch = event.touches?.[0] ?? event.changedTouches?.[0];
|
|
2485
|
+
return touch ? {
|
|
2486
|
+
x: touch.clientX,
|
|
2487
|
+
y: touch.clientY
|
|
2488
|
+
} : null;
|
|
2489
|
+
}
|
|
2490
|
+
function farther(from, to, limit) {
|
|
2491
|
+
return Math.hypot(to.x - from.x, to.y - from.y) > limit;
|
|
2492
|
+
}
|
|
2493
|
+
var HoldThenMoveTouchSensorImpl = class extends TouchSensor {
|
|
2494
|
+
static activators = TouchSensor.activators;
|
|
2495
|
+
static setup() {
|
|
2496
|
+
return TouchSensor.setup();
|
|
2497
|
+
}
|
|
2498
|
+
attach() {
|
|
2499
|
+
super.attach();
|
|
2500
|
+
const self = this;
|
|
2501
|
+
const { holdMs = 500 } = self.props.options;
|
|
2502
|
+
const state = {
|
|
2503
|
+
heldAt: null,
|
|
2504
|
+
last: null,
|
|
2505
|
+
timer: setTimeout(() => {
|
|
2506
|
+
state.heldAt = state.last ?? self.initialCoordinates;
|
|
2507
|
+
}, holdMs)
|
|
2508
|
+
};
|
|
2509
|
+
holds.set(this, state);
|
|
2510
|
+
}
|
|
2511
|
+
handleMove(event) {
|
|
2512
|
+
const self = this;
|
|
2513
|
+
const state = holds.get(this);
|
|
2514
|
+
if (self.activated || !state) {
|
|
2515
|
+
super.handleMove(event);
|
|
2516
|
+
return;
|
|
2517
|
+
}
|
|
2518
|
+
if ((event.touches?.length ?? 1) > 1) {
|
|
2519
|
+
self.handleCancel();
|
|
2520
|
+
return;
|
|
2521
|
+
}
|
|
2522
|
+
const point = touchPoint(event);
|
|
2523
|
+
const start = self.initialCoordinates;
|
|
2524
|
+
if (!point || !start) return;
|
|
2525
|
+
state.last = point;
|
|
2526
|
+
const { distance = 10, tolerance = 10 } = self.props.options;
|
|
2527
|
+
if (!state.heldAt) {
|
|
2528
|
+
if (farther(start, point, tolerance)) self.handleCancel();
|
|
2529
|
+
return;
|
|
2530
|
+
}
|
|
2531
|
+
if (event.cancelable) event.preventDefault();
|
|
2532
|
+
if (farther(state.heldAt, point, distance)) self.handleStart();
|
|
2533
|
+
}
|
|
2534
|
+
detach() {
|
|
2535
|
+
const state = holds.get(this);
|
|
2536
|
+
if (state) clearTimeout(state.timer);
|
|
2537
|
+
holds.delete(this);
|
|
2538
|
+
super.detach();
|
|
2539
|
+
}
|
|
2540
|
+
};
|
|
2541
|
+
/** A TouchSensor that starts a drag on the first move after a still hold (see above). */
|
|
2542
|
+
const HoldThenMoveTouchSensor = HoldThenMoveTouchSensorImpl;
|
|
2543
|
+
/**
|
|
2544
|
+
* dnd-kit's PointerSensor for a mouse and a pen only: a finger's pointerdown is
|
|
2545
|
+
* left to `HoldThenMoveTouchSensor`.
|
|
2546
|
+
*/
|
|
2547
|
+
var MouseOnlyPointerSensor = class extends PointerSensor {
|
|
2548
|
+
static activators = [{
|
|
2549
|
+
eventName: "onPointerDown",
|
|
2550
|
+
handler: (event, options) => event.nativeEvent.pointerType === "touch" ? false : PointerSensor.activators[0].handler(event, options)
|
|
2551
|
+
}];
|
|
2552
|
+
};
|
|
2553
|
+
/**
|
|
2554
|
+
* `useSensor(HoldThenMoveTouchSensor, …)` with the activation the sensor relies
|
|
2555
|
+
* on: no constraint dnd-kit would act on by itself, so only a move after the
|
|
2556
|
+
* hold starts the drag.
|
|
2557
|
+
*/
|
|
2558
|
+
function useHoldThenMoveTouchSensor({ holdMs = 500, distance = 10, tolerance = 10 } = {}) {
|
|
2559
|
+
const options = useMemo(() => ({
|
|
2560
|
+
holdMs,
|
|
2561
|
+
distance,
|
|
2562
|
+
tolerance,
|
|
2563
|
+
activationConstraint: { distance: Number.POSITIVE_INFINITY }
|
|
2564
|
+
}), [
|
|
2565
|
+
holdMs,
|
|
2566
|
+
distance,
|
|
2567
|
+
tolerance
|
|
2568
|
+
]);
|
|
2569
|
+
return useSensor(HoldThenMoveTouchSensor, options);
|
|
2570
|
+
}
|
|
2571
|
+
/** The mouse / pen sensor that goes with it: picks up after `distance` px. */
|
|
2572
|
+
function useMouseOnlyPointerSensor(distance) {
|
|
2573
|
+
const options = useMemo(() => ({ activationConstraint: { distance } }), [distance]);
|
|
2574
|
+
return useSensor(MouseOnlyPointerSensor, options);
|
|
2575
|
+
}
|
|
2576
|
+
//#endregion
|
|
2577
|
+
//#region src/components/popover/popover.tsx
|
|
2578
|
+
const popover = tv$1({ slots: {
|
|
2579
|
+
trigger: ["select-none"],
|
|
2580
|
+
positioner: ["isolate z-50 outline-none"],
|
|
2581
|
+
content: [
|
|
2582
|
+
"max-h-(--available-height) min-w-[220px] overflow-hidden",
|
|
2583
|
+
"outline-none",
|
|
2584
|
+
"rounded-container bg-layer-popover text-foreground",
|
|
2585
|
+
"shadow-float ring-1 ring-line-seam"
|
|
2586
|
+
],
|
|
2587
|
+
header: ["flex items-center justify-between px-3", "min-h-11 gap-2 py-1.5 pr-1.5 pl-4"],
|
|
2588
|
+
body: ["flex min-w-0 flex-col gap-1.5", "px-4 pt-4 pb-3 last:pb-4"],
|
|
2589
|
+
actions: ["flex flex-wrap items-center justify-end gap-2", "px-4 pb-3 last:pb-4"],
|
|
2590
|
+
separator: ["border-t", "border-line-hairline"],
|
|
2591
|
+
title: ["m-0", "font-sans text-headline font-bold text-foreground"],
|
|
2592
|
+
description: ["m-0", "font-sans text-footnote text-foreground-muted"],
|
|
2593
|
+
close: [
|
|
2594
|
+
"inline-flex items-center justify-center",
|
|
2595
|
+
"outline-none transition-colors",
|
|
2596
|
+
"h-8 min-w-8 rounded-chip px-1.5",
|
|
2597
|
+
"font-sans text-footnote font-bold text-foreground",
|
|
2598
|
+
"hover:bg-fill-medium active:bg-fill-medium",
|
|
2599
|
+
"focus-visible:shadow-focus",
|
|
2600
|
+
"[&_svg]:size-4.5"
|
|
2601
|
+
],
|
|
2602
|
+
arrow: ["size-2 rotate-45", "border-l border-t border-line-seam bg-layer-popover"],
|
|
2603
|
+
backdrop: ["fixed inset-0", "bg-transparent"],
|
|
2604
|
+
viewport: ["block"]
|
|
2605
|
+
} });
|
|
2606
|
+
function Popover$1(props) {
|
|
2607
|
+
return /* @__PURE__ */ jsx(Popover.Root, {
|
|
2608
|
+
"data-slot": "popover",
|
|
2609
|
+
...props
|
|
2610
|
+
});
|
|
2611
|
+
}
|
|
2612
|
+
function PopoverTrigger({ className, ...props }) {
|
|
2613
|
+
const { trigger } = popover();
|
|
2614
|
+
return /* @__PURE__ */ jsx(Popover.Trigger, {
|
|
2615
|
+
"data-slot": "popover-trigger",
|
|
2616
|
+
className: trigger({ className }),
|
|
2617
|
+
...props
|
|
2618
|
+
});
|
|
2619
|
+
}
|
|
2620
|
+
function PopoverPortal(props) {
|
|
2621
|
+
return /* @__PURE__ */ jsx(Popover.Portal, {
|
|
2622
|
+
"data-slot": "popover-portal",
|
|
2623
|
+
...useThemeScopeProps(props)
|
|
2624
|
+
});
|
|
2625
|
+
}
|
|
2626
|
+
function PopoverPopup({ className, ...props }) {
|
|
2627
|
+
const { content } = popover();
|
|
2628
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2629
|
+
"data-slot": "popover-content",
|
|
2630
|
+
className: content({ className }),
|
|
2631
|
+
...props
|
|
2632
|
+
});
|
|
2633
|
+
}
|
|
2634
|
+
function PopoverContent({ align = "start", alignOffset = 0, anchor, arrowPadding, collisionAvoidance, collisionBoundary, collisionPadding, disableAnchorTracking, positionMethod = "fixed", side = "bottom", sideOffset = 4, sticky, container, portalProps, className, ...props }) {
|
|
2635
|
+
const { positioner, content } = popover();
|
|
2636
|
+
const portalContainer = container !== void 0 ? container : portalProps?.container;
|
|
2637
|
+
return /* @__PURE__ */ jsx(PopoverPortal, {
|
|
2638
|
+
...portalProps,
|
|
2639
|
+
container: portalContainer,
|
|
2640
|
+
children: /* @__PURE__ */ jsx(Popover.Positioner, {
|
|
2641
|
+
align,
|
|
2642
|
+
alignOffset,
|
|
2643
|
+
anchor,
|
|
2644
|
+
arrowPadding,
|
|
2645
|
+
collisionAvoidance,
|
|
2646
|
+
collisionBoundary,
|
|
2647
|
+
collisionPadding,
|
|
2648
|
+
disableAnchorTracking,
|
|
2649
|
+
positionMethod,
|
|
2650
|
+
side,
|
|
2651
|
+
sideOffset,
|
|
2652
|
+
sticky,
|
|
2653
|
+
className: positioner(),
|
|
2654
|
+
children: /* @__PURE__ */ jsx(Popover.Popup, {
|
|
2655
|
+
"data-slot": "popover-content",
|
|
2656
|
+
className: content({ className }),
|
|
2657
|
+
...props
|
|
2658
|
+
})
|
|
2659
|
+
})
|
|
2660
|
+
});
|
|
2661
|
+
}
|
|
2662
|
+
function PopoverViewport({ className, ...props }) {
|
|
2663
|
+
const { viewport } = popover();
|
|
2664
|
+
return /* @__PURE__ */ jsx(Popover.Viewport, {
|
|
2665
|
+
"data-slot": "popover-viewport",
|
|
2666
|
+
className: viewport({ className }),
|
|
2667
|
+
...props
|
|
2668
|
+
});
|
|
2669
|
+
}
|
|
2670
|
+
function PopoverHeader({ className, ...props }) {
|
|
2671
|
+
const { header } = popover();
|
|
2672
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2673
|
+
"data-slot": "popover-header",
|
|
2674
|
+
className: header({ className }),
|
|
2675
|
+
...props
|
|
2676
|
+
});
|
|
2677
|
+
}
|
|
2678
|
+
function PopoverSeparator({ className, ...props }) {
|
|
2679
|
+
const { separator } = popover();
|
|
2680
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2681
|
+
role: "separator",
|
|
2682
|
+
"aria-orientation": "horizontal",
|
|
2683
|
+
"data-slot": "popover-separator",
|
|
2684
|
+
className: separator({ className }),
|
|
2685
|
+
...props
|
|
2686
|
+
});
|
|
2687
|
+
}
|
|
2688
|
+
function PopoverTitle({ className, ...props }) {
|
|
2689
|
+
const { title } = popover();
|
|
2690
|
+
return /* @__PURE__ */ jsx(Popover.Title, {
|
|
2691
|
+
"data-slot": "popover-title",
|
|
2692
|
+
className: title({ className }),
|
|
2693
|
+
...props
|
|
2694
|
+
});
|
|
2695
|
+
}
|
|
2696
|
+
function PopoverDescription({ className, ...props }) {
|
|
2697
|
+
const { description } = popover();
|
|
2698
|
+
return /* @__PURE__ */ jsx(Popover.Description, {
|
|
2699
|
+
"data-slot": "popover-description",
|
|
2700
|
+
className: description({ className }),
|
|
2701
|
+
...props
|
|
2702
|
+
});
|
|
2703
|
+
}
|
|
2704
|
+
function PopoverClose({ className, ...props }) {
|
|
2705
|
+
const { close } = popover();
|
|
2706
|
+
return /* @__PURE__ */ jsx(Popover.Close, {
|
|
2707
|
+
"data-slot": "popover-close",
|
|
2708
|
+
className: close({ className }),
|
|
2709
|
+
...props
|
|
2710
|
+
});
|
|
2711
|
+
}
|
|
2712
|
+
function PopoverArrow({ className, ...props }) {
|
|
2713
|
+
const { arrow } = popover();
|
|
2714
|
+
return /* @__PURE__ */ jsx(Popover.Arrow, {
|
|
2715
|
+
"data-slot": "popover-arrow",
|
|
2716
|
+
className: arrow({ className }),
|
|
2717
|
+
...props
|
|
2718
|
+
});
|
|
2719
|
+
}
|
|
2720
|
+
function PopoverBackdrop({ className, ...props }) {
|
|
2721
|
+
const { backdrop } = popover();
|
|
2722
|
+
return /* @__PURE__ */ jsx(Popover.Backdrop, {
|
|
2723
|
+
"data-slot": "popover-backdrop",
|
|
2724
|
+
className: backdrop({ className }),
|
|
2725
|
+
...props
|
|
2726
|
+
});
|
|
2727
|
+
}
|
|
2728
|
+
/** The panel's text: a `PopoverTitle` and a `PopoverDescription`, 16px in from its edges. */
|
|
2729
|
+
function PopoverBody({ className, ...props }) {
|
|
2730
|
+
const { body } = popover();
|
|
2731
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2732
|
+
"data-slot": "popover-body",
|
|
2733
|
+
className: body({ className }),
|
|
2734
|
+
...props
|
|
2735
|
+
});
|
|
2736
|
+
}
|
|
2737
|
+
/** The panel's buttons, at its end, under `PopoverBody`: the way out first, then the confirm button. */
|
|
2738
|
+
function PopoverActions({ className, ...props }) {
|
|
2739
|
+
const { actions } = popover();
|
|
2740
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2741
|
+
"data-slot": "popover-actions",
|
|
2742
|
+
className: actions({ className }),
|
|
2743
|
+
...props
|
|
2744
|
+
});
|
|
2745
|
+
}
|
|
2746
|
+
//#endregion
|
|
2747
|
+
//#region src/components/mobile-nav/mobile-nav-server.tsx
|
|
2748
|
+
const mobileNavServer = tv$1({ slots: {
|
|
2749
|
+
item: [
|
|
2750
|
+
"select-none [-webkit-touch-callout:none]",
|
|
2751
|
+
"*:data-[slot=avatar]:size-[22px]",
|
|
2752
|
+
"data-popup-open:before:bg-fill-medium"
|
|
2753
|
+
],
|
|
2754
|
+
content: [
|
|
2755
|
+
"flex flex-col",
|
|
2756
|
+
"w-[calc(var(--anchor-width)-16px)] min-w-0",
|
|
2757
|
+
"max-h-[min(60dvh,calc(var(--available-height)-env(safe-area-inset-top,0px)))]"
|
|
2758
|
+
],
|
|
2759
|
+
list: ["flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-contain p-1"],
|
|
2760
|
+
group: ["flex shrink-0 flex-col p-1"],
|
|
2761
|
+
row: [
|
|
2762
|
+
"flex w-full shrink-0 cursor-default select-none items-center",
|
|
2763
|
+
"text-left outline-none transition-colors",
|
|
2764
|
+
"min-h-option gap-2.5 rounded-chip px-2.5 py-option-inset",
|
|
2765
|
+
"font-sans text-callout font-medium text-foreground",
|
|
2766
|
+
"hover:bg-brand-soft active:bg-brand-soft focus-visible:bg-brand-soft",
|
|
2767
|
+
"aria-[current=true]:font-bold",
|
|
2768
|
+
"[&_svg]:pointer-events-none [&_svg]:shrink-0 [&>svg:not([class*='size-'])]:size-4",
|
|
2769
|
+
"[&>svg]:text-foreground-muted",
|
|
2770
|
+
"*:data-[slot=avatar]:size-5 *:data-[slot=avatar]:shrink-0"
|
|
2771
|
+
],
|
|
2772
|
+
rowLabel: ["min-w-0 flex-1 truncate"],
|
|
2773
|
+
rowBody: ["flex min-w-0 flex-1 flex-col"],
|
|
2774
|
+
rowDescription: ["min-w-0 truncate", "font-mono text-mono-meta font-normal text-foreground-hint"],
|
|
2775
|
+
destinations: ["grid shrink-0 grid-flow-col auto-cols-fr gap-1 p-1"],
|
|
2776
|
+
destination: [
|
|
2777
|
+
"relative flex min-w-0 cursor-default select-none flex-col items-center justify-center",
|
|
2778
|
+
"outline-none transition-colors",
|
|
2779
|
+
"gap-1 rounded-chip px-1 py-2",
|
|
2780
|
+
"font-sans text-footnote font-medium text-foreground",
|
|
2781
|
+
"hover:bg-brand-soft active:bg-brand-soft focus-visible:bg-brand-soft",
|
|
2782
|
+
"[&_svg]:pointer-events-none [&_svg]:size-5 [&_svg]:shrink-0 [&_svg]:text-foreground-muted",
|
|
2783
|
+
"[&>span]:max-w-full [&>span]:truncate",
|
|
2784
|
+
"*:data-[slot=status]:absolute *:data-[slot=status]:top-1 *:data-[slot=status]:left-[calc(50%+6px)]"
|
|
2785
|
+
]
|
|
2786
|
+
} });
|
|
2787
|
+
const MobileNavServerContext = createContext(null);
|
|
2788
|
+
function useMobileNavServerContext(part) {
|
|
2789
|
+
const context = use(MobileNavServerContext);
|
|
2790
|
+
if (!context) throw new Error(`${part} must be inside MobileNavServerPopover`);
|
|
2791
|
+
return context;
|
|
2792
|
+
}
|
|
2793
|
+
/** The root: owns the popover's open state, like `Popover` (`open`, `onOpenChange`). */
|
|
2794
|
+
function MobileNavServerPopover(props) {
|
|
2795
|
+
const [handle] = useState(() => Popover.createHandle());
|
|
2796
|
+
const triggerRef = useRef(null);
|
|
2797
|
+
const context = useMemo(() => ({
|
|
2798
|
+
handle,
|
|
2799
|
+
triggerRef
|
|
2800
|
+
}), [handle]);
|
|
2801
|
+
return /* @__PURE__ */ jsx(MobileNavServerContext, {
|
|
2802
|
+
value: context,
|
|
2803
|
+
children: /* @__PURE__ */ jsx(Popover.Root, {
|
|
2804
|
+
"data-slot": "mobile-nav-server-popover",
|
|
2805
|
+
handle,
|
|
2806
|
+
...props
|
|
2807
|
+
})
|
|
2808
|
+
});
|
|
2809
|
+
}
|
|
2810
|
+
/**
|
|
2811
|
+
* The tab: the server's avatar and its name in `MobileNavLabel`, as children.
|
|
2812
|
+
* A press opens the popover, and so does holding it for 500ms (the long press
|
|
2813
|
+
* of raft-ui's menus). Lifting the finger after a hold never closes it: the
|
|
2814
|
+
* click that ends the hold is not a press, whether the hold opened the
|
|
2815
|
+
* popover or found it open.
|
|
2816
|
+
* It is never `selected`: it holds no page.
|
|
2817
|
+
*/
|
|
2818
|
+
function MobileNavServerItem({ className, id, ref, onClick, onContextMenu, onPointerDown, onPointerMove, onPointerUp, onPointerCancel, onPointerLeave, ...props }) {
|
|
2819
|
+
const { handle, triggerRef } = useMobileNavServerContext("MobileNavServerItem");
|
|
2820
|
+
const { item } = mobileNavServer();
|
|
2821
|
+
const generatedId = useId();
|
|
2822
|
+
const triggerId = id ?? generatedId;
|
|
2823
|
+
const holdTimer = useRef(void 0);
|
|
2824
|
+
const holdStart = useRef(null);
|
|
2825
|
+
const held = useRef(false);
|
|
2826
|
+
const cancelHold = useCallback(() => {
|
|
2827
|
+
clearTimeout(holdTimer.current);
|
|
2828
|
+
holdTimer.current = void 0;
|
|
2829
|
+
holdStart.current = null;
|
|
2830
|
+
}, []);
|
|
2831
|
+
useEffect(() => cancelHold, [cancelHold]);
|
|
2832
|
+
const setRef = useCallback((node) => {
|
|
2833
|
+
triggerRef.current = node;
|
|
2834
|
+
assignRef(ref, node);
|
|
2835
|
+
}, [ref, triggerRef]);
|
|
2836
|
+
const handlePointerDown = useCallback((event) => {
|
|
2837
|
+
onPointerDown?.(event);
|
|
2838
|
+
cancelHold();
|
|
2839
|
+
held.current = false;
|
|
2840
|
+
if (event.button !== 0) return;
|
|
2841
|
+
holdStart.current = {
|
|
2842
|
+
x: event.clientX,
|
|
2843
|
+
y: event.clientY
|
|
2844
|
+
};
|
|
2845
|
+
holdTimer.current = setTimeout(() => {
|
|
2846
|
+
holdTimer.current = void 0;
|
|
2847
|
+
held.current = true;
|
|
2848
|
+
if (!handle.isOpen) handle.open(triggerId);
|
|
2849
|
+
}, 500);
|
|
2850
|
+
}, [
|
|
2851
|
+
cancelHold,
|
|
2852
|
+
handle,
|
|
2853
|
+
onPointerDown,
|
|
2854
|
+
triggerId
|
|
2855
|
+
]);
|
|
2856
|
+
const handlePointerMove = useCallback((event) => {
|
|
2857
|
+
onPointerMove?.(event);
|
|
2858
|
+
const start = holdStart.current;
|
|
2859
|
+
if (!start) return;
|
|
2860
|
+
if (Math.hypot(event.clientX - start.x, event.clientY - start.y) > 10) cancelHold();
|
|
2861
|
+
}, [cancelHold, onPointerMove]);
|
|
2862
|
+
const handlePointerUp = useCallback((event) => {
|
|
2863
|
+
onPointerUp?.(event);
|
|
2864
|
+
cancelHold();
|
|
2865
|
+
}, [cancelHold, onPointerUp]);
|
|
2866
|
+
const handlePointerCancel = useCallback((event) => {
|
|
2867
|
+
onPointerCancel?.(event);
|
|
2868
|
+
cancelHold();
|
|
2869
|
+
}, [cancelHold, onPointerCancel]);
|
|
2870
|
+
const handlePointerLeave = useCallback((event) => {
|
|
2871
|
+
onPointerLeave?.(event);
|
|
2872
|
+
cancelHold();
|
|
2873
|
+
}, [cancelHold, onPointerLeave]);
|
|
2874
|
+
const handleClick = useCallback((event) => {
|
|
2875
|
+
onClick?.(event);
|
|
2876
|
+
if (!held.current) return;
|
|
2877
|
+
held.current = false;
|
|
2878
|
+
event.preventBaseUIHandler?.();
|
|
2879
|
+
}, [onClick]);
|
|
2880
|
+
const handleContextMenu = useCallback((event) => {
|
|
2881
|
+
onContextMenu?.(event);
|
|
2882
|
+
event.preventDefault();
|
|
2883
|
+
}, [onContextMenu]);
|
|
2884
|
+
return /* @__PURE__ */ jsx(Popover.Trigger, {
|
|
2885
|
+
handle,
|
|
2886
|
+
id: triggerId,
|
|
2887
|
+
ref: setRef,
|
|
2888
|
+
render: /* @__PURE__ */ jsx(MobileNavItem, {}),
|
|
2889
|
+
"data-slot": "mobile-nav-server-item",
|
|
2890
|
+
className: item({ className }),
|
|
2891
|
+
onPointerDown: handlePointerDown,
|
|
2892
|
+
onPointerMove: handlePointerMove,
|
|
2893
|
+
onPointerUp: handlePointerUp,
|
|
2894
|
+
onPointerCancel: handlePointerCancel,
|
|
2895
|
+
onPointerLeave: handlePointerLeave,
|
|
2896
|
+
onClick: handleClick,
|
|
2897
|
+
onContextMenu: handleContextMenu,
|
|
2898
|
+
...props
|
|
2899
|
+
});
|
|
2900
|
+
}
|
|
2901
|
+
/**
|
|
2902
|
+
* The popover above the tab bar, 8px from each side of it and 8px above it,
|
|
2903
|
+
* as tall as its content and at most 60% of the screen. It stops below the
|
|
2904
|
+
* top safe area (a notch in a standalone web app): Base UI's collision padding
|
|
2905
|
+
* takes only pixels, so the height cap subtracts `env(safe-area-inset-top)`.
|
|
2906
|
+
* The bottom needs none: the tab bar under the popover already pads the
|
|
2907
|
+
* bottom safe area. A column:
|
|
2908
|
+
* `MobileNavServerPopoverList` takes the room left and scrolls, and the
|
|
2909
|
+
* destinations and the footer after it stay in view.
|
|
2910
|
+
*/
|
|
2911
|
+
function MobileNavServerPopoverContent({ anchor, side = "top", align = "center", sideOffset = 8, collisionPadding = 8, className, ...props }) {
|
|
2912
|
+
const { triggerRef } = useMobileNavServerContext("MobileNavServerPopoverContent");
|
|
2913
|
+
const { content } = mobileNavServer();
|
|
2914
|
+
const tabBar = useCallback(() => {
|
|
2915
|
+
const trigger = triggerRef.current;
|
|
2916
|
+
return trigger?.closest("[data-slot=mobile-nav-root]") ?? trigger;
|
|
2917
|
+
}, [triggerRef]);
|
|
2918
|
+
return /* @__PURE__ */ jsx(PopoverContent, {
|
|
2919
|
+
"data-slot": "mobile-nav-server-popover-content",
|
|
2920
|
+
anchor: anchor ?? tabBar,
|
|
2921
|
+
side,
|
|
2922
|
+
align,
|
|
2923
|
+
sideOffset,
|
|
2924
|
+
collisionPadding,
|
|
2925
|
+
className: content({ className }),
|
|
2926
|
+
...props
|
|
2927
|
+
});
|
|
2928
|
+
}
|
|
2929
|
+
/** The server list: takes the room left in the popover and scrolls inside it. */
|
|
2930
|
+
function MobileNavServerPopoverList({ className, ...props }) {
|
|
2931
|
+
const { list } = mobileNavServer();
|
|
2932
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2933
|
+
"data-slot": "mobile-nav-server-popover-list",
|
|
2934
|
+
className: list({ className }),
|
|
2935
|
+
...props
|
|
2936
|
+
});
|
|
2937
|
+
}
|
|
2938
|
+
/** Rows that always stay in view, such as the footer actions. */
|
|
2939
|
+
function MobileNavServerPopoverGroup({ className, ...props }) {
|
|
2940
|
+
const { group } = mobileNavServer();
|
|
2941
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2942
|
+
"data-slot": "mobile-nav-server-popover-group",
|
|
2943
|
+
className: group({ className }),
|
|
2944
|
+
...props
|
|
2945
|
+
});
|
|
2946
|
+
}
|
|
2947
|
+
/**
|
|
2948
|
+
* One row at the option height of every popup list (`py-option-inset`, 36px
|
|
2949
|
+
* for one line; a second line adds its own height and nothing else): a 20px
|
|
2950
|
+
* avatar or a 16px icon, `MobileNavServerPopoverRowLabel` (or a
|
|
2951
|
+
* `MobileNavServerPopoverRowBody` with a description under the label), then
|
|
2952
|
+
* anything trailing (a check, a count), centered on the row.
|
|
2953
|
+
* `aria-current="true"` marks the current server in bold.
|
|
2954
|
+
*/
|
|
2955
|
+
function MobileNavServerPopoverRow({ className, type = "button", ...props }) {
|
|
2956
|
+
const { row } = mobileNavServer();
|
|
2957
|
+
return /* @__PURE__ */ jsx("button", {
|
|
2958
|
+
type,
|
|
2959
|
+
"data-slot": "mobile-nav-server-popover-row",
|
|
2960
|
+
className: row({ className }),
|
|
2961
|
+
...props
|
|
2962
|
+
});
|
|
2963
|
+
}
|
|
2964
|
+
/** A row's label: takes the room and truncates. */
|
|
2965
|
+
function MobileNavServerPopoverRowLabel({ className, ...props }) {
|
|
2966
|
+
const { rowLabel } = mobileNavServer();
|
|
2967
|
+
return /* @__PURE__ */ jsx("span", {
|
|
2968
|
+
"data-slot": "mobile-nav-server-popover-row-label",
|
|
2969
|
+
className: rowLabel({ className }),
|
|
2970
|
+
...props
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
/** The server's own pages, side by side in equal cells; a `group`, named with `aria-label`. */
|
|
2974
|
+
function MobileNavServerPopoverDestinations({ className, ...props }) {
|
|
2975
|
+
const { destinations } = mobileNavServer();
|
|
2976
|
+
return /* @__PURE__ */ jsx("div", {
|
|
2977
|
+
role: "group",
|
|
2978
|
+
"data-slot": "mobile-nav-server-popover-destinations",
|
|
2979
|
+
className: destinations({ className }),
|
|
1409
2980
|
...props
|
|
1410
2981
|
});
|
|
1411
2982
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
2983
|
+
/**
|
|
2984
|
+
* One of the server's pages: a 20px icon over a short label in a `span`, which
|
|
2985
|
+
* truncates. Something new on that page (a feedback reply, a computer that
|
|
2986
|
+
* needs attention) is an `accent` `Status` child, the attention dot at the
|
|
2987
|
+
* icon's top-right corner: the server tab's avatar never carries it.
|
|
2988
|
+
*/
|
|
2989
|
+
function MobileNavServerPopoverDestination({ className, type = "button", ...props }) {
|
|
2990
|
+
const { destination } = mobileNavServer();
|
|
2991
|
+
return /* @__PURE__ */ jsx("button", {
|
|
2992
|
+
type,
|
|
2993
|
+
"data-slot": "mobile-nav-server-popover-destination",
|
|
2994
|
+
className: destination({ className }),
|
|
2995
|
+
...props
|
|
2996
|
+
});
|
|
2997
|
+
}
|
|
2998
|
+
/**
|
|
2999
|
+
* The text of a two-line row: `MobileNavServerPopoverRowLabel`, then
|
|
3000
|
+
* `MobileNavServerPopoverRowDescription` under it. The second line adds its
|
|
3001
|
+
* own height and nothing else; the avatar and the trailing check or count
|
|
3002
|
+
* center on both lines.
|
|
3003
|
+
*/
|
|
3004
|
+
function MobileNavServerPopoverRowBody({ className, ...props }) {
|
|
3005
|
+
const { rowBody } = mobileNavServer();
|
|
3006
|
+
return /* @__PURE__ */ jsx("span", {
|
|
3007
|
+
"data-slot": "mobile-nav-server-popover-row-body",
|
|
3008
|
+
className: rowBody({ className }),
|
|
3009
|
+
...props
|
|
3010
|
+
});
|
|
3011
|
+
}
|
|
3012
|
+
/** A row's second line, such as a server's address: muted Space Mono; truncates. */
|
|
3013
|
+
function MobileNavServerPopoverRowDescription({ className, ...props }) {
|
|
3014
|
+
const { rowDescription } = mobileNavServer();
|
|
3015
|
+
return /* @__PURE__ */ jsx("span", {
|
|
3016
|
+
"data-slot": "mobile-nav-server-popover-row-description",
|
|
3017
|
+
className: rowDescription({ className }),
|
|
3018
|
+
...props
|
|
3019
|
+
});
|
|
3020
|
+
}
|
|
3021
|
+
function assignRef(ref, value) {
|
|
3022
|
+
if (typeof ref === "function") ref(value);
|
|
3023
|
+
else if (ref) ref.current = value;
|
|
3024
|
+
}
|
|
3025
|
+
//#endregion
|
|
3026
|
+
//#region src/components/preview-card/preview-card.tsx
|
|
3027
|
+
const previewCard = tv$1({ slots: {
|
|
3028
|
+
trigger: [],
|
|
3029
|
+
positioner: ["isolate z-50 outline-none"],
|
|
3030
|
+
popup: [
|
|
3031
|
+
"max-h-(--available-height) min-w-[220px] overflow-hidden",
|
|
3032
|
+
"outline-none",
|
|
3033
|
+
"origin-(--transform-origin) transform-gpu opacity-100 will-change-[transform,opacity]",
|
|
3034
|
+
"transition-[opacity,transform] duration-250 ease-[cubic-bezier(0.22,1,0.36,1)]",
|
|
3035
|
+
"data-starting-style:scale-[0.97] data-starting-style:opacity-0",
|
|
3036
|
+
"data-ending-style:scale-[0.99] data-ending-style:opacity-0 data-ending-style:duration-150",
|
|
3037
|
+
"data-instant:transition-none motion-reduce:transition-none",
|
|
3038
|
+
"w-[300px] rounded-container bg-layer-popover text-foreground",
|
|
3039
|
+
"shadow-float ring-1 ring-line-seam"
|
|
3040
|
+
],
|
|
3041
|
+
header: ["flex items-start gap-3", "px-4 pt-4 pb-3 last:pb-4"],
|
|
3042
|
+
separator: ["border-t", "border-line-hairline"],
|
|
3043
|
+
title: ["m-0 truncate", "font-sans text-headline font-bold text-foreground"],
|
|
3044
|
+
description: ["m-0 truncate", "px-4 py-3 last:pb-4 font-sans text-callout text-foreground-muted"],
|
|
3045
|
+
arrow: ["size-2 rotate-45", "border-l border-t border-line-seam bg-layer-popover"],
|
|
3046
|
+
body: [
|
|
3047
|
+
"flex min-w-0 flex-col gap-1.5",
|
|
3048
|
+
"px-4 pt-4 pb-3 last:pb-4",
|
|
3049
|
+
"*:data-[slot=preview-card-title]:line-clamp-3 *:data-[slot=preview-card-title]:whitespace-normal"
|
|
3050
|
+
],
|
|
3051
|
+
eyebrow: ["m-0 truncate", "font-mono text-mono-meta text-foreground-muted"],
|
|
3052
|
+
meta: ["flex min-w-0 flex-wrap items-center gap-x-2 gap-y-1", "m-0 font-sans text-callout text-foreground-muted"],
|
|
3053
|
+
actions: ["flex flex-wrap items-center gap-2", "px-4 pb-3 last:pb-4"],
|
|
3054
|
+
footer: [
|
|
3055
|
+
"m-0 truncate",
|
|
3056
|
+
"border-t border-line-hairline px-4 py-2",
|
|
3057
|
+
"font-sans text-caption text-foreground-muted"
|
|
3058
|
+
]
|
|
3059
|
+
} });
|
|
3060
|
+
function PreviewCard$1(props) {
|
|
3061
|
+
return /* @__PURE__ */ jsx(PreviewCard.Root, {
|
|
3062
|
+
"data-slot": "preview-card",
|
|
3063
|
+
...props
|
|
3064
|
+
});
|
|
3065
|
+
}
|
|
3066
|
+
function PreviewCardTrigger({ delay = 80, closeDelay = 40, className, ...props }) {
|
|
3067
|
+
const { trigger } = previewCard();
|
|
3068
|
+
return /* @__PURE__ */ jsx(PreviewCard.Trigger, {
|
|
3069
|
+
"data-slot": "preview-card-trigger",
|
|
3070
|
+
delay,
|
|
3071
|
+
closeDelay,
|
|
1420
3072
|
className: trigger({ className }),
|
|
1421
3073
|
...props
|
|
1422
3074
|
});
|
|
1423
3075
|
}
|
|
1424
|
-
function
|
|
1425
|
-
return /* @__PURE__ */ jsx(
|
|
1426
|
-
"data-slot": "
|
|
3076
|
+
function PreviewCardPortal(props) {
|
|
3077
|
+
return /* @__PURE__ */ jsx(PreviewCard.Portal, {
|
|
3078
|
+
"data-slot": "preview-card-portal",
|
|
1427
3079
|
...useThemeScopeProps(props)
|
|
1428
3080
|
});
|
|
1429
3081
|
}
|
|
1430
|
-
function
|
|
1431
|
-
const
|
|
3082
|
+
function PreviewCardPopup({ className, ...props }) {
|
|
3083
|
+
const themeState = useTheme();
|
|
3084
|
+
const { popup } = previewCard();
|
|
3085
|
+
const scopeProps = resolveThemeScopeProps({
|
|
3086
|
+
...props,
|
|
3087
|
+
className: popup({ className })
|
|
3088
|
+
}, themeState);
|
|
1432
3089
|
return /* @__PURE__ */ jsx("div", {
|
|
1433
|
-
"data-slot": "
|
|
1434
|
-
|
|
1435
|
-
...props
|
|
3090
|
+
"data-slot": "preview-card-content",
|
|
3091
|
+
...scopeProps
|
|
1436
3092
|
});
|
|
1437
3093
|
}
|
|
1438
|
-
function
|
|
1439
|
-
const
|
|
1440
|
-
|
|
1441
|
-
return /* @__PURE__ */ jsx(DropdownMenuPortal, {
|
|
3094
|
+
function PreviewCardContent({ align = "start", alignOffset = 0, anchor, arrowPadding, collisionAvoidance, collisionBoundary, collisionPadding, disableAnchorTracking, positionMethod = "fixed", side = "bottom", sideOffset = 6, sticky, portalProps, className, ...props }) {
|
|
3095
|
+
const { positioner, popup } = previewCard();
|
|
3096
|
+
return /* @__PURE__ */ jsx(PreviewCardPortal, {
|
|
1442
3097
|
...portalProps,
|
|
1443
|
-
children: /* @__PURE__ */ jsx(
|
|
3098
|
+
children: /* @__PURE__ */ jsx(PreviewCard.Positioner, {
|
|
1444
3099
|
align,
|
|
1445
3100
|
alignOffset,
|
|
1446
3101
|
anchor,
|
|
@@ -1451,195 +3106,102 @@ function DropdownMenuContent({ align = "start", alignOffset = 0, anchor, arrowPa
|
|
|
1451
3106
|
disableAnchorTracking,
|
|
1452
3107
|
positionMethod,
|
|
1453
3108
|
side,
|
|
1454
|
-
sideOffset
|
|
3109
|
+
sideOffset,
|
|
1455
3110
|
sticky,
|
|
1456
3111
|
className: positioner(),
|
|
1457
|
-
children: /* @__PURE__ */ jsx(
|
|
1458
|
-
"data-slot": "
|
|
1459
|
-
className:
|
|
3112
|
+
children: /* @__PURE__ */ jsx(PreviewCard.Popup, {
|
|
3113
|
+
"data-slot": "preview-card-content",
|
|
3114
|
+
className: popup({ className }),
|
|
1460
3115
|
...props
|
|
1461
3116
|
})
|
|
1462
3117
|
})
|
|
1463
3118
|
});
|
|
1464
3119
|
}
|
|
1465
|
-
function
|
|
1466
|
-
const {
|
|
1467
|
-
return /* @__PURE__ */ jsx(Menu.Item, {
|
|
1468
|
-
"data-slot": "dropdown-menu-item",
|
|
1469
|
-
className: item({ className }),
|
|
1470
|
-
...props
|
|
1471
|
-
});
|
|
1472
|
-
}
|
|
1473
|
-
function DropdownMenuItemLabel({ className, ...props }) {
|
|
1474
|
-
const { itemLabel } = dropdownMenu();
|
|
1475
|
-
return /* @__PURE__ */ jsx("span", {
|
|
1476
|
-
"data-slot": "dropdown-menu-item-label",
|
|
1477
|
-
className: itemLabel({ className }),
|
|
1478
|
-
...props
|
|
1479
|
-
});
|
|
1480
|
-
}
|
|
1481
|
-
function DropdownMenuFooter({ className, ...props }) {
|
|
1482
|
-
const { footer } = dropdownMenu();
|
|
3120
|
+
function PreviewCardHeader({ className, ...props }) {
|
|
3121
|
+
const { header } = previewCard();
|
|
1483
3122
|
return /* @__PURE__ */ jsx("div", {
|
|
1484
|
-
"data-slot": "
|
|
1485
|
-
className:
|
|
3123
|
+
"data-slot": "preview-card-header",
|
|
3124
|
+
className: header({ className }),
|
|
1486
3125
|
...props
|
|
1487
3126
|
});
|
|
1488
3127
|
}
|
|
1489
|
-
function
|
|
1490
|
-
const {
|
|
1491
|
-
return /* @__PURE__ */ jsxs(Menu.SubmenuTrigger, {
|
|
1492
|
-
"data-slot": "dropdown-menu-submenu-trigger",
|
|
1493
|
-
className: submenuTrigger({ className }),
|
|
1494
|
-
...props,
|
|
1495
|
-
children: [children, /* @__PURE__ */ jsx("span", {
|
|
1496
|
-
className: indicator(),
|
|
1497
|
-
"data-slot": "dropdown-menu-submenu-trigger-indicator",
|
|
1498
|
-
"aria-hidden": "true",
|
|
1499
|
-
children: /* @__PURE__ */ jsx(DropdownMenuSubmenuChevronIcon, {})
|
|
1500
|
-
})]
|
|
1501
|
-
});
|
|
1502
|
-
}
|
|
1503
|
-
function DropdownMenuLabel({ className, ...props }) {
|
|
1504
|
-
const { label } = dropdownMenu();
|
|
3128
|
+
function PreviewCardSeparator({ className, ...props }) {
|
|
3129
|
+
const { separator } = previewCard();
|
|
1505
3130
|
return /* @__PURE__ */ jsx("div", {
|
|
1506
|
-
|
|
1507
|
-
|
|
3131
|
+
role: "separator",
|
|
3132
|
+
"aria-orientation": "horizontal",
|
|
3133
|
+
"data-slot": "preview-card-separator",
|
|
3134
|
+
className: separator({ className }),
|
|
1508
3135
|
...props
|
|
1509
3136
|
});
|
|
1510
3137
|
}
|
|
1511
|
-
function
|
|
1512
|
-
const {
|
|
1513
|
-
return /* @__PURE__ */ jsx(
|
|
1514
|
-
"data-slot": "
|
|
1515
|
-
className:
|
|
3138
|
+
function PreviewCardTitle({ className, ...props }) {
|
|
3139
|
+
const { title } = previewCard();
|
|
3140
|
+
return /* @__PURE__ */ jsx("p", {
|
|
3141
|
+
"data-slot": "preview-card-title",
|
|
3142
|
+
className: title({ className }),
|
|
1516
3143
|
...props
|
|
1517
3144
|
});
|
|
1518
3145
|
}
|
|
1519
|
-
function
|
|
1520
|
-
const {
|
|
1521
|
-
return /* @__PURE__ */ jsx(
|
|
1522
|
-
"data-slot": "
|
|
1523
|
-
className:
|
|
3146
|
+
function PreviewCardDescription({ className, ...props }) {
|
|
3147
|
+
const { description } = previewCard();
|
|
3148
|
+
return /* @__PURE__ */ jsx("p", {
|
|
3149
|
+
"data-slot": "preview-card-description",
|
|
3150
|
+
className: description({ className }),
|
|
1524
3151
|
...props
|
|
1525
3152
|
});
|
|
1526
3153
|
}
|
|
1527
|
-
function
|
|
1528
|
-
const {
|
|
1529
|
-
return /* @__PURE__ */ jsx(
|
|
1530
|
-
"data-slot": "
|
|
1531
|
-
className:
|
|
3154
|
+
function PreviewCardArrow({ className, ...props }) {
|
|
3155
|
+
const { arrow } = previewCard();
|
|
3156
|
+
return /* @__PURE__ */ jsx(PreviewCard.Arrow, {
|
|
3157
|
+
"data-slot": "preview-card-arrow",
|
|
3158
|
+
className: arrow({ className }),
|
|
1532
3159
|
...props
|
|
1533
3160
|
});
|
|
1534
3161
|
}
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
className: "size-3 shrink-0",
|
|
1542
|
-
children: /* @__PURE__ */ jsx("g", {
|
|
1543
|
-
fill: "none",
|
|
1544
|
-
strokeLinecap: "round",
|
|
1545
|
-
strokeLinejoin: "round",
|
|
1546
|
-
strokeWidth: "1",
|
|
1547
|
-
stroke: "currentColor",
|
|
1548
|
-
children: /* @__PURE__ */ jsx("path", { d: "M4.25 2.5L7.75 6l-3.5 3.5" })
|
|
1549
|
-
})
|
|
1550
|
-
});
|
|
1551
|
-
}
|
|
1552
|
-
function DropdownMenuCheckIcon() {
|
|
1553
|
-
return /* @__PURE__ */ jsx("svg", {
|
|
1554
|
-
width: "12",
|
|
1555
|
-
height: "12",
|
|
1556
|
-
viewBox: "0 0 12 12",
|
|
1557
|
-
"aria-hidden": true,
|
|
1558
|
-
className: "size-3 shrink-0",
|
|
1559
|
-
children: /* @__PURE__ */ jsx("g", {
|
|
1560
|
-
fill: "none",
|
|
1561
|
-
strokeLinecap: "round",
|
|
1562
|
-
strokeLinejoin: "round",
|
|
1563
|
-
strokeWidth: "1",
|
|
1564
|
-
stroke: "currentColor",
|
|
1565
|
-
children: /* @__PURE__ */ jsx("path", { d: "M1.75 6.41L4.4 9.25L10.25 2.99" })
|
|
1566
|
-
})
|
|
1567
|
-
});
|
|
1568
|
-
}
|
|
1569
|
-
function DropdownMenuRadioGroup({ ...props }) {
|
|
1570
|
-
return /* @__PURE__ */ jsx(Menu.RadioGroup, {
|
|
1571
|
-
"data-slot": "dropdown-menu-radio-group",
|
|
3162
|
+
/** The item card's block: the id line, the title and the details row, in that order. */
|
|
3163
|
+
function PreviewCardBody({ className, ...props }) {
|
|
3164
|
+
const { body } = previewCard();
|
|
3165
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3166
|
+
"data-slot": "preview-card-body",
|
|
3167
|
+
className: body({ className }),
|
|
1572
3168
|
...props
|
|
1573
3169
|
});
|
|
1574
3170
|
}
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
children: [renderRadioItemChildren(children, radioLabel()), /* @__PURE__ */ jsx("span", {
|
|
1582
|
-
className: indicator(),
|
|
1583
|
-
children: /* @__PURE__ */ jsx(Menu.RadioItemIndicator, { children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {}) })
|
|
1584
|
-
})]
|
|
1585
|
-
});
|
|
1586
|
-
}
|
|
1587
|
-
function renderRadioItemChildren(children, className) {
|
|
1588
|
-
if (typeof children === "string" || typeof children === "number") return /* @__PURE__ */ jsx("span", {
|
|
1589
|
-
"data-slot": "dropdown-menu-radio-item-label",
|
|
1590
|
-
className,
|
|
1591
|
-
children
|
|
1592
|
-
});
|
|
1593
|
-
if (Array.isArray(children) && children.every((child) => typeof child === "string")) return /* @__PURE__ */ jsx("span", {
|
|
1594
|
-
"data-slot": "dropdown-menu-radio-item-label",
|
|
1595
|
-
className,
|
|
1596
|
-
children
|
|
1597
|
-
});
|
|
1598
|
-
return children;
|
|
1599
|
-
}
|
|
1600
|
-
function DropdownMenuRadioItemIndicator({ className, ...props }) {
|
|
1601
|
-
return /* @__PURE__ */ jsx(Menu.RadioItemIndicator, {
|
|
1602
|
-
"data-slot": "dropdown-menu-radio-item-indicator",
|
|
1603
|
-
className,
|
|
3171
|
+
/** The muted line above the title that identifies the item, such as its id. One line, truncated. */
|
|
3172
|
+
function PreviewCardEyebrow({ className, ...props }) {
|
|
3173
|
+
const { eyebrow } = previewCard();
|
|
3174
|
+
return /* @__PURE__ */ jsx("p", {
|
|
3175
|
+
"data-slot": "preview-card-eyebrow",
|
|
3176
|
+
className: eyebrow({ className }),
|
|
1604
3177
|
...props
|
|
1605
3178
|
});
|
|
1606
3179
|
}
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
className: indicator(),
|
|
1615
|
-
children: /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {}) })
|
|
1616
|
-
})]
|
|
3180
|
+
/** The row under the title: one state chip, then muted details. Wraps when it runs out of room. */
|
|
3181
|
+
function PreviewCardMeta({ className, ...props }) {
|
|
3182
|
+
const { meta } = previewCard();
|
|
3183
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3184
|
+
"data-slot": "preview-card-meta",
|
|
3185
|
+
className: meta({ className }),
|
|
3186
|
+
...props
|
|
1617
3187
|
});
|
|
1618
3188
|
}
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
3189
|
+
/** The row of at most two buttons. */
|
|
3190
|
+
function PreviewCardActions({ className, ...props }) {
|
|
3191
|
+
const { actions } = previewCard();
|
|
3192
|
+
return /* @__PURE__ */ jsx("div", {
|
|
3193
|
+
"data-slot": "preview-card-actions",
|
|
3194
|
+
className: actions({ className }),
|
|
1623
3195
|
...props
|
|
1624
3196
|
});
|
|
1625
3197
|
}
|
|
1626
|
-
/**
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
return /* @__PURE__ */ jsxs(Menu.LinkItem, {
|
|
1634
|
-
"data-slot": "dropdown-menu-link-item",
|
|
1635
|
-
"aria-current": current ? "page" : void 0,
|
|
1636
|
-
"data-checked": current ? "" : void 0,
|
|
1637
|
-
className: radioItem({ className }),
|
|
1638
|
-
...props,
|
|
1639
|
-
children: [renderRadioItemChildren(children, radioLabel()), current ? /* @__PURE__ */ jsx("span", {
|
|
1640
|
-
className: indicator(),
|
|
1641
|
-
children: /* @__PURE__ */ jsx(DropdownMenuCheckIcon, {})
|
|
1642
|
-
}) : null]
|
|
3198
|
+
/** The caption under a hairline at the bottom of the card: where the item comes from. */
|
|
3199
|
+
function PreviewCardFooter({ className, ...props }) {
|
|
3200
|
+
const { footer } = previewCard();
|
|
3201
|
+
return /* @__PURE__ */ jsx("p", {
|
|
3202
|
+
"data-slot": "preview-card-footer",
|
|
3203
|
+
className: footer({ className }),
|
|
3204
|
+
...props
|
|
1643
3205
|
});
|
|
1644
3206
|
}
|
|
1645
3207
|
function contentBox(element) {
|
|
@@ -2131,8 +3693,8 @@ const messageItem = tv$1({
|
|
|
2131
3693
|
"has-data-popup-open:[--mi-toolbar-opacity:1] has-data-popup-open:[--mi-toolbar-pe:auto]",
|
|
2132
3694
|
"min-h-0 px-2 py-timeline-row border-t-(length:--spacing-timeline-turn) border-transparent bg-clip-padding",
|
|
2133
3695
|
"hover:[--mi-ct-color:var(--foreground-hint)] has-data-popup-open:[--mi-ct-color:var(--foreground-hint)]",
|
|
2134
|
-
"bg-(--mi-row-fill) [--mi-row-fill:
|
|
2135
|
-
"hover:[--mi-row-fill:var(--fill-subtle)]",
|
|
3696
|
+
"bg-(--mi-row-fill) [--mi-row-fill:var(--located-fill)]",
|
|
3697
|
+
"not-data-located:hover:[--mi-row-fill:var(--fill-subtle)]",
|
|
2136
3698
|
"data-popup-open:[--mi-row-fill:var(--fill-subtle)] has-data-popup-open:[--mi-row-fill:var(--fill-subtle)]",
|
|
2137
3699
|
"has-data-[slot=message-item-search-match]:[--mi-row-fill:var(--info-soft)] has-data-[slot=message-item-search-match]:hover:[--mi-row-fill:var(--info-soft)]",
|
|
2138
3700
|
"has-[[data-slot=message-multi-select-checkbox][data-checked]]:[--mi-row-fill:var(--info-soft)]",
|
|
@@ -2225,11 +3787,12 @@ const messageItem = tv$1({
|
|
|
2225
3787
|
});
|
|
2226
3788
|
//#endregion
|
|
2227
3789
|
//#region src/components/message-item/message-item-root.tsx
|
|
2228
|
-
function MessageItem({ className, compact, render, ...props }) {
|
|
3790
|
+
function MessageItem({ className, compact, located = false, render, ...props }) {
|
|
2229
3791
|
const { root } = messageItem({ compact });
|
|
2230
3792
|
const defaultProps = {
|
|
2231
3793
|
"data-message-item": "",
|
|
2232
3794
|
"data-compact": compact ? "true" : void 0,
|
|
3795
|
+
"data-located": located ? "" : void 0,
|
|
2233
3796
|
className: root({ className })
|
|
2234
3797
|
};
|
|
2235
3798
|
return useRender({
|
|
@@ -2882,10 +4445,38 @@ function MessageReactionTooltipMeta({ className, render, ...props }) {
|
|
|
2882
4445
|
});
|
|
2883
4446
|
}
|
|
2884
4447
|
//#endregion
|
|
4448
|
+
//#region src/lib/inline-chip.ts
|
|
4449
|
+
/**
|
|
4450
|
+
* The one box every chip in message text shares: a reference chip (any
|
|
4451
|
+
* variant, loading or unavailable too), the yellow sticker of a mention of the
|
|
4452
|
+
* person reading, and an inline task chip. The rule and its numbers are in
|
|
4453
|
+
* message-item.md, Chips in running text.
|
|
4454
|
+
*
|
|
4455
|
+
* - Type: 0.875em of the sentence, semibold.
|
|
4456
|
+
* - Height: the line box of its text, the body line less 2px, with no padding
|
|
4457
|
+
* above or below: 20px on a desktop (body 14/22), 22px on a phone (16/24). A
|
|
4458
|
+
* chip is always 2px shorter than its line, so it never makes the line taller.
|
|
4459
|
+
* `--chip-line-height` overrides it for text that isn't on the body line.
|
|
4460
|
+
* - Alignment: the chip's text on the sentence's baseline, a leading mark
|
|
4461
|
+
* centred on the chip (alignment.md, Marks in running text).
|
|
4462
|
+
* - Sides: 6px at each end. A chip with a leading mark (a 14px icon, an app's
|
|
4463
|
+
* image or avatar in `MessageReferenceIcon`'s 14px box, the loading spinner,
|
|
4464
|
+
* a task's 10px status pixel, each in its own box) takes
|
|
4465
|
+
* 4.5px at each end, so the ink at its two ends sits equally far in
|
|
4466
|
+
* (alignment.md, Chips), and the mark is 4px from the text. A note after the
|
|
4467
|
+
* label keeps the same 4px.
|
|
4468
|
+
* - Corners: `rounded-sticker`.
|
|
4469
|
+
*/
|
|
4470
|
+
const inlineChip = [
|
|
4471
|
+
"items-baseline gap-1 px-1.5 py-0 align-baseline",
|
|
4472
|
+
"has-[>svg,>[data-slot=spinner],>[data-slot=message-reference-icon]]:px-[4.5px]",
|
|
4473
|
+
"[font-size:0.875em] leading-[var(--chip-line-height,calc(var(--text-body--line-height)-2px))] font-semibold",
|
|
4474
|
+
"rounded-sticker"
|
|
4475
|
+
];
|
|
4476
|
+
//#endregion
|
|
2885
4477
|
//#region src/components/message-item/message-reference.recipe.ts
|
|
2886
4478
|
const chip = (fill) => [
|
|
2887
|
-
|
|
2888
|
-
"[font-size:0.875em] leading-5 font-semibold",
|
|
4479
|
+
...inlineChip,
|
|
2889
4480
|
"[--message-reference-em:1em]",
|
|
2890
4481
|
"[&_svg]:size-3.5 [&_svg]:text-foreground-muted",
|
|
2891
4482
|
fill
|
|
@@ -2915,23 +4506,36 @@ const messageReference = tv$1({
|
|
|
2915
4506
|
SUFFIX_LIFT,
|
|
2916
4507
|
"font-mono text-label leading-none font-normal text-foreground-hint"
|
|
2917
4508
|
],
|
|
2918
|
-
|
|
4509
|
+
icon: ["inline-flex size-3.5 shrink-0 items-center justify-center self-center", "*:size-full *:shrink-0 [&>img]:rounded-[2px] [&>img]:object-cover"],
|
|
4510
|
+
indicator: ["pointer-events-none absolute -right-1.5 top-0 inline-flex size-2 shrink-0 items-center justify-center", "*:data-[slot=status]:size-full"],
|
|
4511
|
+
readMark: [
|
|
4512
|
+
"relative inline-block shrink-0 self-center",
|
|
4513
|
+
"-mr-[calc(0.857em*2/24)]",
|
|
4514
|
+
"align-[calc(0.7em/2-0.857em/2)] leading-none",
|
|
4515
|
+
"text-foreground-hint",
|
|
4516
|
+
"[&>svg]:block [&>svg]:size-[0.857em]"
|
|
4517
|
+
]
|
|
4518
|
+
},
|
|
4519
|
+
variants: {
|
|
4520
|
+
variant: {
|
|
4521
|
+
primary: { root: [
|
|
4522
|
+
"inline-flex cursor-pointer",
|
|
4523
|
+
...inlineChip,
|
|
4524
|
+
"[--message-reference-em:1em]",
|
|
4525
|
+
"text-sticker-ink no-underline",
|
|
4526
|
+
"bg-brand inset-ring-[1.5px] inset-ring-sticker-ink",
|
|
4527
|
+
"hover:overlay-sticker-medium active:overlay-sticker-medium"
|
|
4528
|
+
] },
|
|
4529
|
+
secondary: { root: ["inline cursor-pointer font-bold text-foreground", "decoration-foreground decoration-[1.5px] underline-offset-[3px] hover:underline"] },
|
|
4530
|
+
accent: { root: linkChip("bg-attention-soft") },
|
|
4531
|
+
muted: { root: linkChip("bg-inactive-soft") },
|
|
4532
|
+
info: { root: linkChip("bg-info-soft") },
|
|
4533
|
+
link: { root: linkChip("bg-info-soft") },
|
|
4534
|
+
pending: { root: chip("cursor-wait bg-inactive-soft text-foreground-muted") },
|
|
4535
|
+
unavailable: { root: chip("cursor-default bg-inactive-soft text-foreground-hint") }
|
|
4536
|
+
},
|
|
4537
|
+
capped: { true: { root: "max-w-[min(100%,24em)]" } }
|
|
2919
4538
|
},
|
|
2920
|
-
variants: { variant: {
|
|
2921
|
-
primary: { root: [
|
|
2922
|
-
"inline-flex cursor-pointer rounded-sticker px-[3px]",
|
|
2923
|
-
"font-semibold text-sticker-ink no-underline",
|
|
2924
|
-
"bg-brand inset-ring-[1.5px] inset-ring-sticker-ink ring-[1.5px] ring-sticker-edge",
|
|
2925
|
-
"hover:overlay-sticker-medium active:overlay-sticker-medium"
|
|
2926
|
-
] },
|
|
2927
|
-
secondary: { root: ["inline cursor-pointer font-bold text-foreground", "decoration-foreground decoration-[1.5px] underline-offset-[3px] hover:underline"] },
|
|
2928
|
-
accent: { root: linkChip("bg-attention-soft") },
|
|
2929
|
-
muted: { root: linkChip("bg-inactive-soft") },
|
|
2930
|
-
info: { root: linkChip("bg-info-soft") },
|
|
2931
|
-
link: { root: linkChip("bg-info-soft") },
|
|
2932
|
-
pending: { root: chip("cursor-wait bg-inactive-soft text-foreground-muted") },
|
|
2933
|
-
unavailable: { root: chip("cursor-default bg-inactive-soft text-foreground-hint") }
|
|
2934
|
-
} },
|
|
2935
4539
|
defaultVariants: { variant: "muted" }
|
|
2936
4540
|
});
|
|
2937
4541
|
//#endregion
|
|
@@ -2958,8 +4562,36 @@ function MessageReferenceIndicator({ className, render, ...props }) {
|
|
|
2958
4562
|
state: { slot: "message-reference-indicator" }
|
|
2959
4563
|
});
|
|
2960
4564
|
}
|
|
2961
|
-
|
|
2962
|
-
|
|
4565
|
+
/**
|
|
4566
|
+
* The read receipt on a mention: a muted check after the mention's text, once
|
|
4567
|
+
* the one mentioned has read the message. Put it last inside the mention, and
|
|
4568
|
+
* render nothing until then. The check is decorative; `children` is what a
|
|
4569
|
+
* screen reader says instead ("Read"), visually hidden.
|
|
4570
|
+
*/
|
|
4571
|
+
function MessageReferenceReadMark({ children, className, render, ...props }) {
|
|
4572
|
+
const { readMark } = messageReference();
|
|
4573
|
+
const defaultProps = {
|
|
4574
|
+
className: readMark({ className }),
|
|
4575
|
+
children: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Check, {
|
|
4576
|
+
"aria-hidden": true,
|
|
4577
|
+
strokeWidth: 2
|
|
4578
|
+
}), children == null ? null : /* @__PURE__ */ jsx("span", {
|
|
4579
|
+
className: "sr-only",
|
|
4580
|
+
children
|
|
4581
|
+
})] })
|
|
4582
|
+
};
|
|
4583
|
+
return useRender({
|
|
4584
|
+
defaultTagName: "span",
|
|
4585
|
+
render,
|
|
4586
|
+
props: mergeProps(defaultProps, props),
|
|
4587
|
+
state: { slot: "message-reference-read-mark" }
|
|
4588
|
+
});
|
|
4589
|
+
}
|
|
4590
|
+
function MessageReferenceChip({ className, render, variant = "muted", capped, ...props }) {
|
|
4591
|
+
const { root } = messageReference({
|
|
4592
|
+
variant,
|
|
4593
|
+
capped
|
|
4594
|
+
});
|
|
2963
4595
|
return useReferenceRender(root({ className }), render, props);
|
|
2964
4596
|
}
|
|
2965
4597
|
function MessageReferenceLabel({ className, render, ...props }) {
|
|
@@ -2981,11 +4613,29 @@ function MessageReferenceSuffix({ className, render, ...props }) {
|
|
|
2981
4613
|
defaultTagName: "span",
|
|
2982
4614
|
render,
|
|
2983
4615
|
props: mergeProps(defaultProps, props),
|
|
2984
|
-
state: { slot: "message-reference-suffix" }
|
|
4616
|
+
state: { slot: "message-reference-suffix" }
|
|
4617
|
+
});
|
|
4618
|
+
}
|
|
4619
|
+
/**
|
|
4620
|
+
* The chip's leading icon when it is not a plain svg: an image or a `3xs`
|
|
4621
|
+
* Avatar, such as an app's registered icon. It holds one child at 14px,
|
|
4622
|
+
* centred on the chip. Put it first in the chip.
|
|
4623
|
+
*/
|
|
4624
|
+
function MessageReferenceIcon({ className, render, ...props }) {
|
|
4625
|
+
const { icon } = messageReference();
|
|
4626
|
+
const defaultProps = { className: icon({ className }) };
|
|
4627
|
+
return useRender({
|
|
4628
|
+
defaultTagName: "span",
|
|
4629
|
+
render,
|
|
4630
|
+
props: mergeProps(defaultProps, props),
|
|
4631
|
+
state: { slot: "message-reference-icon" }
|
|
2985
4632
|
});
|
|
2986
4633
|
}
|
|
2987
|
-
function MessageReferencePending({ className, render, ...props }) {
|
|
2988
|
-
const { root } = messageReference({
|
|
4634
|
+
function MessageReferencePending({ className, render, capped, ...props }) {
|
|
4635
|
+
const { root } = messageReference({
|
|
4636
|
+
variant: "pending",
|
|
4637
|
+
capped
|
|
4638
|
+
});
|
|
2989
4639
|
return useReferenceRender(root({ className }), render, props);
|
|
2990
4640
|
}
|
|
2991
4641
|
function MessageReferenceUnavailable({ className, render, ...props }) {
|
|
@@ -3551,99 +5201,31 @@ function MessageListJumpPillDismiss({ className, children, render, nativeButton,
|
|
|
3551
5201
|
})
|
|
3552
5202
|
});
|
|
3553
5203
|
}
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
}
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
}, holdMs)
|
|
3580
|
-
};
|
|
3581
|
-
holds.set(this, state);
|
|
3582
|
-
}
|
|
3583
|
-
handleMove(event) {
|
|
3584
|
-
const self = this;
|
|
3585
|
-
const state = holds.get(this);
|
|
3586
|
-
if (self.activated || !state) {
|
|
3587
|
-
super.handleMove(event);
|
|
3588
|
-
return;
|
|
3589
|
-
}
|
|
3590
|
-
if ((event.touches?.length ?? 1) > 1) {
|
|
3591
|
-
self.handleCancel();
|
|
3592
|
-
return;
|
|
3593
|
-
}
|
|
3594
|
-
const point = touchPoint(event);
|
|
3595
|
-
const start = self.initialCoordinates;
|
|
3596
|
-
if (!point || !start) return;
|
|
3597
|
-
state.last = point;
|
|
3598
|
-
const { distance = 10, tolerance = 10 } = self.props.options;
|
|
3599
|
-
if (!state.heldAt) {
|
|
3600
|
-
if (farther(start, point, tolerance)) self.handleCancel();
|
|
3601
|
-
return;
|
|
3602
|
-
}
|
|
3603
|
-
if (event.cancelable) event.preventDefault();
|
|
3604
|
-
if (farther(state.heldAt, point, distance)) self.handleStart();
|
|
3605
|
-
}
|
|
3606
|
-
detach() {
|
|
3607
|
-
const state = holds.get(this);
|
|
3608
|
-
if (state) clearTimeout(state.timer);
|
|
3609
|
-
holds.delete(this);
|
|
3610
|
-
super.detach();
|
|
3611
|
-
}
|
|
3612
|
-
};
|
|
3613
|
-
/** A TouchSensor that starts a drag on the first move after a still hold (see above). */
|
|
3614
|
-
const HoldThenMoveTouchSensor = HoldThenMoveTouchSensorImpl;
|
|
3615
|
-
/**
|
|
3616
|
-
* dnd-kit's PointerSensor for a mouse and a pen only: a finger's pointerdown is
|
|
3617
|
-
* left to `HoldThenMoveTouchSensor`.
|
|
3618
|
-
*/
|
|
3619
|
-
var MouseOnlyPointerSensor = class extends PointerSensor {
|
|
3620
|
-
static activators = [{
|
|
3621
|
-
eventName: "onPointerDown",
|
|
3622
|
-
handler: (event, options) => event.nativeEvent.pointerType === "touch" ? false : PointerSensor.activators[0].handler(event, options)
|
|
3623
|
-
}];
|
|
3624
|
-
};
|
|
3625
|
-
/**
|
|
3626
|
-
* `useSensor(HoldThenMoveTouchSensor, …)` with the activation the sensor relies
|
|
3627
|
-
* on: no constraint dnd-kit would act on by itself, so only a move after the
|
|
3628
|
-
* hold starts the drag.
|
|
3629
|
-
*/
|
|
3630
|
-
function useHoldThenMoveTouchSensor({ holdMs = 500, distance = 10, tolerance = 10 } = {}) {
|
|
3631
|
-
const options = useMemo(() => ({
|
|
3632
|
-
holdMs,
|
|
3633
|
-
distance,
|
|
3634
|
-
tolerance,
|
|
3635
|
-
activationConstraint: { distance: Number.POSITIVE_INFINITY }
|
|
3636
|
-
}), [
|
|
3637
|
-
holdMs,
|
|
3638
|
-
distance,
|
|
3639
|
-
tolerance
|
|
3640
|
-
]);
|
|
3641
|
-
return useSensor(HoldThenMoveTouchSensor, options);
|
|
3642
|
-
}
|
|
3643
|
-
/** The mouse / pen sensor that goes with it: picks up after `distance` px. */
|
|
3644
|
-
function useMouseOnlyPointerSensor(distance) {
|
|
3645
|
-
const options = useMemo(() => ({ activationConstraint: { distance } }), [distance]);
|
|
3646
|
-
return useSensor(MouseOnlyPointerSensor, options);
|
|
5204
|
+
//#endregion
|
|
5205
|
+
//#region src/components/skeleton/skeleton.tsx
|
|
5206
|
+
const skeleton = tv$1({
|
|
5207
|
+
slots: { root: [
|
|
5208
|
+
"animate-pulse motion-reduce:animate-none",
|
|
5209
|
+
"pointer-events-none",
|
|
5210
|
+
"data-[variant=line]:rounded-sticker data-[variant=line]:bg-fill-muted",
|
|
5211
|
+
"data-[variant=block]:rounded-sticker data-[variant=block]:bg-fill-muted"
|
|
5212
|
+
] },
|
|
5213
|
+
variants: { variant: {
|
|
5214
|
+
line: { root: "h-3" },
|
|
5215
|
+
block: { root: "" },
|
|
5216
|
+
circle: { root: ["rounded-sticker bg-fill-muted"] }
|
|
5217
|
+
} },
|
|
5218
|
+
defaultVariants: { variant: "block" }
|
|
5219
|
+
});
|
|
5220
|
+
function Skeleton({ variant = "block", className, ...props }) {
|
|
5221
|
+
const { root } = skeleton({ variant });
|
|
5222
|
+
return /* @__PURE__ */ jsx("div", {
|
|
5223
|
+
"aria-hidden": "true",
|
|
5224
|
+
"data-slot": "skeleton",
|
|
5225
|
+
"data-variant": variant,
|
|
5226
|
+
className: root({ className }),
|
|
5227
|
+
...props
|
|
5228
|
+
});
|
|
3647
5229
|
}
|
|
3648
5230
|
//#endregion
|
|
3649
5231
|
//#region src/components/inbox-item/inbox-item.recipe.ts
|
|
@@ -3710,7 +5292,7 @@ const inboxItem = tv$1({
|
|
|
3710
5292
|
"flex shrink-0",
|
|
3711
5293
|
"mt-0 flex-col items-end gap-2 pt-px",
|
|
3712
5294
|
"max-md:flex-col max-md:items-end max-md:gap-2",
|
|
3713
|
-
"after:hidden after:size-
|
|
5295
|
+
"after:hidden after:size-2.5 after:-mb-px after:shrink-0 after:rounded-[2px] after:border-[1.5px] after:border-sticker-ink after:bg-attention-400",
|
|
3714
5296
|
"group-data-[unread=true]/inbox-item:after:block",
|
|
3715
5297
|
"*:not-data-[reveal=hover]:opacity-[calc(1-var(--inbox-item-reveal-aside,0)*var(--inbox-item-revealed,0))] after:opacity-[calc(1-var(--inbox-item-reveal-aside,0)*var(--inbox-item-revealed,0))]",
|
|
3716
5298
|
"*:not-data-[reveal=hover]:mr-(--inbox-item-aside-room,0px) after:mr-(--inbox-item-aside-room,0px)",
|
|
@@ -3785,4 +5367,274 @@ const inboxItem = tv$1({
|
|
|
3785
5367
|
}]
|
|
3786
5368
|
});
|
|
3787
5369
|
//#endregion
|
|
3788
|
-
|
|
5370
|
+
//#region src/components/inbox-item/inbox-item.tsx
|
|
5371
|
+
const InboxItemDisabledContext = createContext(false);
|
|
5372
|
+
const InboxItemDensityContext = createContext(void 0);
|
|
5373
|
+
function InboxItem({ active = false, className, density, disabled = false, render, ...props }) {
|
|
5374
|
+
const { item } = inboxItem({ density });
|
|
5375
|
+
const defaultProps = {
|
|
5376
|
+
"aria-disabled": disabled || void 0,
|
|
5377
|
+
"data-active": active ? "true" : void 0,
|
|
5378
|
+
"data-density": density,
|
|
5379
|
+
"data-disabled": disabled ? "true" : void 0,
|
|
5380
|
+
"data-slot": "inbox-item",
|
|
5381
|
+
className: item({ className: `group/inbox-item ${className ?? ""}`.trim() })
|
|
5382
|
+
};
|
|
5383
|
+
const element = useRender({
|
|
5384
|
+
defaultTagName: "div",
|
|
5385
|
+
render,
|
|
5386
|
+
props: mergeProps(defaultProps, props)
|
|
5387
|
+
});
|
|
5388
|
+
return /* @__PURE__ */ jsx(InboxItemDisabledContext.Provider, {
|
|
5389
|
+
value: disabled,
|
|
5390
|
+
children: /* @__PURE__ */ jsx(InboxItemDensityContext.Provider, {
|
|
5391
|
+
value: density,
|
|
5392
|
+
children: element
|
|
5393
|
+
})
|
|
5394
|
+
});
|
|
5395
|
+
}
|
|
5396
|
+
function InboxItemTrigger({ className, onClick, render, ...props }) {
|
|
5397
|
+
const disabled = useContext(InboxItemDisabledContext);
|
|
5398
|
+
const { itemTrigger } = inboxItem();
|
|
5399
|
+
const defaultProps = {
|
|
5400
|
+
type: render ? void 0 : "button",
|
|
5401
|
+
"aria-disabled": disabled || void 0,
|
|
5402
|
+
"data-disabled": disabled ? "true" : void 0,
|
|
5403
|
+
"data-slot": "inbox-item-open",
|
|
5404
|
+
disabled: render ? void 0 : disabled,
|
|
5405
|
+
tabIndex: disabled && render ? -1 : void 0,
|
|
5406
|
+
className: itemTrigger({ className }),
|
|
5407
|
+
onClick: (event) => {
|
|
5408
|
+
if (disabled) {
|
|
5409
|
+
event.preventDefault();
|
|
5410
|
+
event.stopPropagation();
|
|
5411
|
+
return;
|
|
5412
|
+
}
|
|
5413
|
+
onClick?.(event);
|
|
5414
|
+
}
|
|
5415
|
+
};
|
|
5416
|
+
return useRender({
|
|
5417
|
+
defaultTagName: "button",
|
|
5418
|
+
render,
|
|
5419
|
+
props: mergeProps(defaultProps, props)
|
|
5420
|
+
});
|
|
5421
|
+
}
|
|
5422
|
+
function InboxItemLeading({ className, render, ...props }) {
|
|
5423
|
+
const { itemLeading } = inboxItem();
|
|
5424
|
+
const defaultProps = {
|
|
5425
|
+
"data-slot": "inbox-item-leading",
|
|
5426
|
+
className: itemLeading({ className })
|
|
5427
|
+
};
|
|
5428
|
+
return useRender({
|
|
5429
|
+
defaultTagName: "div",
|
|
5430
|
+
render,
|
|
5431
|
+
props: mergeProps(defaultProps, props)
|
|
5432
|
+
});
|
|
5433
|
+
}
|
|
5434
|
+
function InboxItemSelection({ className, render, ...props }) {
|
|
5435
|
+
const { itemSelection } = inboxItem();
|
|
5436
|
+
const defaultProps = {
|
|
5437
|
+
"data-slot": "inbox-item-selection",
|
|
5438
|
+
className: itemSelection({ className })
|
|
5439
|
+
};
|
|
5440
|
+
return useRender({
|
|
5441
|
+
defaultTagName: "div",
|
|
5442
|
+
render,
|
|
5443
|
+
props: mergeProps(defaultProps, props)
|
|
5444
|
+
});
|
|
5445
|
+
}
|
|
5446
|
+
function InboxItemContent({ className, render, ...props }) {
|
|
5447
|
+
const { itemContent } = inboxItem();
|
|
5448
|
+
const defaultProps = {
|
|
5449
|
+
"data-slot": "inbox-item-content",
|
|
5450
|
+
className: itemContent({ className })
|
|
5451
|
+
};
|
|
5452
|
+
return useRender({
|
|
5453
|
+
defaultTagName: "div",
|
|
5454
|
+
render,
|
|
5455
|
+
props: mergeProps(defaultProps, props)
|
|
5456
|
+
});
|
|
5457
|
+
}
|
|
5458
|
+
function InboxItemContext({ className, render, ...props }) {
|
|
5459
|
+
const { itemContext } = inboxItem();
|
|
5460
|
+
const defaultProps = {
|
|
5461
|
+
"data-slot": "inbox-item-context",
|
|
5462
|
+
className: itemContext({ className })
|
|
5463
|
+
};
|
|
5464
|
+
return useRender({
|
|
5465
|
+
defaultTagName: "div",
|
|
5466
|
+
render,
|
|
5467
|
+
props: mergeProps(defaultProps, props)
|
|
5468
|
+
});
|
|
5469
|
+
}
|
|
5470
|
+
function InboxItemSubtitle({ className, render, ...props }) {
|
|
5471
|
+
const { itemSubtitle } = inboxItem();
|
|
5472
|
+
const defaultProps = {
|
|
5473
|
+
"data-slot": "inbox-item-subtitle",
|
|
5474
|
+
className: itemSubtitle({ className })
|
|
5475
|
+
};
|
|
5476
|
+
return useRender({
|
|
5477
|
+
defaultTagName: "div",
|
|
5478
|
+
render,
|
|
5479
|
+
props: mergeProps(defaultProps, props)
|
|
5480
|
+
});
|
|
5481
|
+
}
|
|
5482
|
+
function InboxItemHeading({ className, render, ...props }) {
|
|
5483
|
+
const { itemHeading } = inboxItem();
|
|
5484
|
+
const defaultProps = {
|
|
5485
|
+
"data-slot": "inbox-item-heading",
|
|
5486
|
+
className: itemHeading({ className })
|
|
5487
|
+
};
|
|
5488
|
+
return useRender({
|
|
5489
|
+
defaultTagName: "div",
|
|
5490
|
+
render,
|
|
5491
|
+
props: mergeProps(defaultProps, props)
|
|
5492
|
+
});
|
|
5493
|
+
}
|
|
5494
|
+
function InboxItemTitle({ className, render, ...props }) {
|
|
5495
|
+
const density = useContext(InboxItemDensityContext);
|
|
5496
|
+
const { itemTitle } = inboxItem({ density });
|
|
5497
|
+
const defaultProps = {
|
|
5498
|
+
"data-slot": "inbox-item-title",
|
|
5499
|
+
className: itemTitle({ className })
|
|
5500
|
+
};
|
|
5501
|
+
return useRender({
|
|
5502
|
+
defaultTagName: "div",
|
|
5503
|
+
render,
|
|
5504
|
+
props: mergeProps(defaultProps, props)
|
|
5505
|
+
});
|
|
5506
|
+
}
|
|
5507
|
+
function InboxItemTrailing({ className, render, ...props }) {
|
|
5508
|
+
const density = useContext(InboxItemDensityContext);
|
|
5509
|
+
const { itemTrailing } = inboxItem({ density });
|
|
5510
|
+
const defaultProps = {
|
|
5511
|
+
"data-slot": "inbox-item-trailing",
|
|
5512
|
+
className: itemTrailing({ className })
|
|
5513
|
+
};
|
|
5514
|
+
return useRender({
|
|
5515
|
+
defaultTagName: "div",
|
|
5516
|
+
render,
|
|
5517
|
+
props: mergeProps(defaultProps, props)
|
|
5518
|
+
});
|
|
5519
|
+
}
|
|
5520
|
+
function InboxItemTimestamp({ className, render, ...props }) {
|
|
5521
|
+
const density = useContext(InboxItemDensityContext);
|
|
5522
|
+
const { itemTimestamp } = inboxItem({ density });
|
|
5523
|
+
const defaultProps = {
|
|
5524
|
+
"data-slot": "inbox-item-timestamp",
|
|
5525
|
+
className: itemTimestamp({ className })
|
|
5526
|
+
};
|
|
5527
|
+
return useRender({
|
|
5528
|
+
defaultTagName: "span",
|
|
5529
|
+
render,
|
|
5530
|
+
props: mergeProps(defaultProps, props)
|
|
5531
|
+
});
|
|
5532
|
+
}
|
|
5533
|
+
function InboxItemMarks({ className, render, ...props }) {
|
|
5534
|
+
const { itemMarks } = inboxItem();
|
|
5535
|
+
const defaultProps = {
|
|
5536
|
+
"data-slot": "inbox-item-marks",
|
|
5537
|
+
className: itemMarks({ className })
|
|
5538
|
+
};
|
|
5539
|
+
return useRender({
|
|
5540
|
+
defaultTagName: "div",
|
|
5541
|
+
render,
|
|
5542
|
+
props: mergeProps(defaultProps, props)
|
|
5543
|
+
});
|
|
5544
|
+
}
|
|
5545
|
+
function InboxItemHoverTarget({ className, onClick, render, ...props }) {
|
|
5546
|
+
const { itemHoverTarget } = inboxItem();
|
|
5547
|
+
const defaultProps = {
|
|
5548
|
+
"data-slot": "inbox-item-hover-target",
|
|
5549
|
+
className: itemHoverTarget({ className }),
|
|
5550
|
+
onClick: (event) => {
|
|
5551
|
+
onClick?.(event);
|
|
5552
|
+
if (event.defaultPrevented) return;
|
|
5553
|
+
const trigger = event.currentTarget.closest("[data-slot=inbox-item]")?.querySelector("[data-slot=inbox-item-open]");
|
|
5554
|
+
if (!trigger) return;
|
|
5555
|
+
trigger.dispatchEvent(new MouseEvent("click", {
|
|
5556
|
+
bubbles: true,
|
|
5557
|
+
cancelable: true,
|
|
5558
|
+
detail: event.detail,
|
|
5559
|
+
altKey: event.altKey,
|
|
5560
|
+
ctrlKey: event.ctrlKey,
|
|
5561
|
+
metaKey: event.metaKey,
|
|
5562
|
+
shiftKey: event.shiftKey
|
|
5563
|
+
}));
|
|
5564
|
+
}
|
|
5565
|
+
};
|
|
5566
|
+
return useRender({
|
|
5567
|
+
defaultTagName: "span",
|
|
5568
|
+
render,
|
|
5569
|
+
props: mergeProps(defaultProps, props)
|
|
5570
|
+
});
|
|
5571
|
+
}
|
|
5572
|
+
function InboxItemBody({ className, render, ...props }) {
|
|
5573
|
+
const density = useContext(InboxItemDensityContext);
|
|
5574
|
+
const { itemBody } = inboxItem({ density });
|
|
5575
|
+
const defaultProps = {
|
|
5576
|
+
"data-slot": "inbox-item-body",
|
|
5577
|
+
className: itemBody({ className })
|
|
5578
|
+
};
|
|
5579
|
+
return useRender({
|
|
5580
|
+
defaultTagName: "p",
|
|
5581
|
+
render,
|
|
5582
|
+
props: mergeProps(defaultProps, props)
|
|
5583
|
+
});
|
|
5584
|
+
}
|
|
5585
|
+
function InboxItemMessage({ className, render, ...props }) {
|
|
5586
|
+
const density = useContext(InboxItemDensityContext);
|
|
5587
|
+
const { itemMessage } = inboxItem({ density });
|
|
5588
|
+
const defaultProps = {
|
|
5589
|
+
"data-slot": "inbox-item-message",
|
|
5590
|
+
className: itemMessage({ className })
|
|
5591
|
+
};
|
|
5592
|
+
return useRender({
|
|
5593
|
+
defaultTagName: "p",
|
|
5594
|
+
render,
|
|
5595
|
+
props: mergeProps(defaultProps, props)
|
|
5596
|
+
});
|
|
5597
|
+
}
|
|
5598
|
+
function InboxItemSender({ className, render, ...props }) {
|
|
5599
|
+
const { itemSender } = inboxItem();
|
|
5600
|
+
const defaultProps = {
|
|
5601
|
+
"data-slot": "inbox-item-sender",
|
|
5602
|
+
className: itemSender({ className })
|
|
5603
|
+
};
|
|
5604
|
+
return useRender({
|
|
5605
|
+
defaultTagName: "span",
|
|
5606
|
+
render,
|
|
5607
|
+
props: mergeProps(defaultProps, props)
|
|
5608
|
+
});
|
|
5609
|
+
}
|
|
5610
|
+
function InboxItemMetadata({ className, render, ...props }) {
|
|
5611
|
+
const { itemMetadata } = inboxItem();
|
|
5612
|
+
const defaultProps = {
|
|
5613
|
+
"data-slot": "inbox-item-metadata",
|
|
5614
|
+
className: itemMetadata({ className })
|
|
5615
|
+
};
|
|
5616
|
+
return useRender({
|
|
5617
|
+
defaultTagName: "div",
|
|
5618
|
+
render,
|
|
5619
|
+
props: mergeProps(defaultProps, props)
|
|
5620
|
+
});
|
|
5621
|
+
}
|
|
5622
|
+
function InboxItemActions({ className, render, reveal, ...props }) {
|
|
5623
|
+
const density = useContext(InboxItemDensityContext);
|
|
5624
|
+
const { itemActions } = inboxItem({
|
|
5625
|
+
density,
|
|
5626
|
+
reveal
|
|
5627
|
+
});
|
|
5628
|
+
const defaultProps = {
|
|
5629
|
+
"data-reveal": reveal,
|
|
5630
|
+
"data-slot": "inbox-item-actions",
|
|
5631
|
+
className: itemActions({ className })
|
|
5632
|
+
};
|
|
5633
|
+
return useRender({
|
|
5634
|
+
defaultTagName: "div",
|
|
5635
|
+
render,
|
|
5636
|
+
props: mergeProps(defaultProps, props)
|
|
5637
|
+
});
|
|
5638
|
+
}
|
|
5639
|
+
//#endregion
|
|
5640
|
+
export { MessageMultiSelect as $, PopoverTitle as $n, ListRowTitle as $r, MessageItemAvatarSlot as $t, MessageQuotedPreview as A, DialogSheetReset as Ai, MobileNavServerItem as An, PanelBody as Ar, MessageItemReadReceipt as At, MessageQuotedPreviewLoading as B, ThemeStateContext as Bi, MobileNavServerPopoverRowLabel as Bn, ScrollAreaThumb as Br, MessageItemMeta as Bt, MessageListJumpPillDismiss as C, DialogSurface as Ci, PreviewCardHeader as Cn, DropdownMenuShortcut as Cr, MessageReactionTooltipMeta as Ct, MessageListHistoryLimited as D, panelToggleAction as Di, PreviewCardSeparator as Dn, Kbd as Dr, MessageReactionTooltipSeparator as Dt, MessageListEndFade as E, PanelAction as Ei, PreviewCardPortal as En, DropdownMenuTrigger as Er, MessageReactionTooltipNames as Et, MessageQuotedPreviewAuthorMeta as F, OverlayLayerProvider as Fi, MobileNavServerPopoverGroup as Fn, PanelSection as Fr, MessageItemDelegatedBy as Ft, MessageEmbed as G, headEdgePull as Gi, PopoverBody as Gn, ListRow as Gr, MessageItemTime as Gt, MessageQuotedPreviewThread as H, stackedActions as Hi, PopoverActions as Hn, Checkbox$1 as Hr, MessageItemSenderBadges as Ht, MessageQuotedPreviewAuthorName as I, useParentOverlayKind as Ii, MobileNavServerPopoverList as In, ScrollArea$1 as Ir, MessageItemDelegatedByButton as It, MessageEmbedHeader as J, PopoverDescription as Jn, ListRowChevron as Jr, MessageItemContent as Jt, MessageEmbedContent as K, headEdges as Ki, PopoverClose as Kn, ListRowBody as Kr, MessageItemAttachments as Kt, MessageQuotedPreviewChannel as L, withOverlayLayer as Li, MobileNavServerPopoverRow as Ln, ScrollAreaContent as Lr, MessageItemDelegatedByName as Lt, MessageQuotedPreviewAttachments as M, buttonVariants as Mi, MobileNavServerPopoverContent as Mn, PanelHeaderRoot as Mr, MessageItemToolbar as Mt, MessageQuotedPreviewAuthor as N, Spinner as Ni, MobileNavServerPopoverDestination as Nn, PanelScrollContent as Nr, MessageItemToolbarButton as Nt, MessageListNotice as O, TextLink as Oi, PreviewCardTitle as On, KbdGroup as Or, MessageItemBadge as Ot, MessageQuotedPreviewAuthorDelegatedBy as P, defaultNativeButton as Pi, MobileNavServerPopoverDestinations as Pn, PanelScrollViewport as Pr, MessageItemDelegatedAvatar as Pt, MessageTextSelectionToolbarSeparator as Q, PopoverSeparator as Qn, ListRowMeta as Qr, MessageItemSearchMatch as Qt, MessageQuotedPreviewContent as R, useThemeScopeProps as Ri, MobileNavServerPopoverRowBody as Rn, ScrollAreaCorner as Rr, MessageItemHeader as Rt, MessageListJumpPill as S, DialogSheetBack as Si, PreviewCardFooter as Sn, DropdownMenuSeparator as Sr, MessageReactionTooltipContent as St, MessageListDateDivider as T, DialogTrigger as Ti, PreviewCardPopup as Tn, DropdownMenuSubmenuTrigger as Tr, MessageReactionTooltipNameText as Tt, MessageQuotedPreviewTimestamp as U, useStackedActions as Ui, PopoverArrow as Un, CheckboxIndicator as Ur, MessageItemSenderButton as Ut, MessageQuotedPreviewText as V, tv$1 as Vi, Popover$1 as Vn, ScrollAreaViewport as Vr, MessageItemSender as Vt, MessageQuotedPreviewUnavailable as W, controlIconInset as Wi, PopoverBackdrop as Wn, PRESENCE_COLOR as Wr, MessageItemSenderGroup as Wt, MessageTextSelectionToolbar as X, PopoverPopup as Xn, ListRowGroup as Xr, MessageItemEmbedded as Xt, messageEmbed as Y, PopoverHeader as Yn, ListRowDescription as Yr, MessageItemEdited as Yt, MessageTextSelectionToolbarButton as Z, PopoverPortal as Zn, ListRowIcon as Zr, MessageItemFooter as Zt, InboxItemTrailing as _, DialogFooterNote as _i, PreviewCardArrow as _n, DropdownMenuPopup as _r, MessageReactionGlyph as _t, InboxItemContext as a, TooltipProvider as ai, AvatarBadge as an, MobileNavLabel as ar, MessageReferenceIndicator as at, Skeleton as b, DialogOverlay as bi, PreviewCardDescription as bn, DropdownMenuRadioItem as br, MessageReactionQuickButton as bt, InboxItemLeading as c, DialogCommit as ci, AvatarGroupCount as cn, DropdownMenuCheckboxItem as cr, MessageReferenceReadMark as ct, InboxItemMetadata as d, Dialog$1 as di, measureContent as dn, DropdownMenuFooter as dr, MessageReferenceUnavailable as dt, ListRowTop as ei, MessageItemContinuationTime as en, PopoverTrigger as er, MessageMultiSelectCheckbox as et, InboxItemSelection as f, DialogBody as fi, measureElement as fn, DropdownMenuItem as fr, inlineChip as ft, InboxItemTitle as g, DialogFooter as gi, PreviewCardActions as gn, DropdownMenuLinkItem as gr, MessageReactionErrorIndicator as gt, InboxItemTimestamp as h, DialogDescription as hi, PreviewCard$1 as hn, DropdownMenuLabel as hr, MessageReactionCount as ht, InboxItemContent as i, TooltipPortal as ii, Avatar$1 as in, MobileNavItem as ir, MessageReferenceIcon as it, MessageQuotedPreviewAttachment as j, Button$1 as ji, MobileNavServerPopover as jn, PanelFooter as jr, MessageItemSavedIndicator as jt, MessageList as k, textLinkVariants as ki, PreviewCardTrigger as kn, Panel as kr, MessageItemBadgeSeparator as kt, InboxItemMarks as l, DialogSheetContent as li, AvatarImage as ln, DropdownMenuCheckboxItemIndicator as lr, MessageReferenceSuffix as lt, InboxItemSubtitle as m, DialogContent as mi, readNow as mn, DropdownMenuItemLabel as mr, MessageReactionChip as mt, InboxItemActions as n, Tooltip$1 as ni, MessageItem as nn, useHoldThenMoveTouchSensor as nr, MessageMultiSelectToolbarActions as nt, InboxItemHeading as o, TooltipTrigger as oi, AvatarFallback as on, MobileNavRoot as or, MessageReferenceLabel as ot, InboxItemSender as p, DialogClose as pi, observeTruncation as pn, DropdownMenuItemCount as pr, MessageReactionAddButton as pt, MessageEmbedFooter as q, decisionWidth as qi, PopoverContent as qn, ListRowBottom as qr, MessageItemBody as qt, InboxItemBody as r, TooltipContent as ri, searchHit as rn, useMouseOnlyPointerSensor as rr, MessageReferenceChip as rt, InboxItemHoverTarget as s, Badge as si, AvatarGroup as sn, DropdownMenu as sr, MessageReferencePending as st, InboxItem as t, ListRowValue as ti, MessageItemGutter as tn, PopoverViewport as tr, MessageMultiSelectToolbar as tt, InboxItemMessage as u, useMobileViewport as ui, useOverflowTitle as un, DropdownMenuContent as ur, MessageReferenceText as ut, InboxItemTrigger as v, DialogHeader as vi, PreviewCardBody as vn, DropdownMenuPortal as vr, MessageReactionPickerContent as vt, MessageListJumpPillGroup as w, DialogTitle as wi, PreviewCardMeta as wn, DropdownMenuSubmenu as wr, MessageReactionTooltipName as wt, MessageListJumpControls as x, DialogPortal as xi, PreviewCardEyebrow as xn, DropdownMenuRadioItemIndicator as xr, MessageReactionQuickRow as xt, inboxItem as y, DialogNav as yi, PreviewCardContent as yn, DropdownMenuRadioGroup as yr, MessageReactionPickerStatic as yt, MessageQuotedPreviewHeader as z, useTheme as zi, MobileNavServerPopoverRowDescription as zn, ScrollAreaScrollbar as zr, MessageItemInlineStatus as zt };
|