@stll/ui 0.44.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/dist/components/accordion.d.ts +6 -2
  2. package/dist/components/accordion.js +4 -4
  3. package/dist/components/button.d.ts +1 -1
  4. package/dist/components/court-badge.d.ts +7 -1
  5. package/dist/components/court-badge.js +13 -3
  6. package/dist/components/date-picker-popover.d.ts +12 -2
  7. package/dist/components/date-picker-popover.js +105 -9
  8. package/dist/components/date-range-filter.d.ts +14 -0
  9. package/dist/components/date-range-filter.js +74 -0
  10. package/dist/components/document-identity-badge.d.ts +12 -0
  11. package/dist/components/document-identity-badge.js +94 -0
  12. package/dist/components/document-identity-badge.logic.d.ts +25 -0
  13. package/dist/components/document-identity-badge.logic.js +25 -0
  14. package/dist/components/landing.d.ts +1 -1
  15. package/dist/components/landing.js +1 -1
  16. package/dist/components/matter-icon.d.ts +16 -0
  17. package/dist/components/matter-icon.js +56 -0
  18. package/dist/components/menu.d.ts +9 -1
  19. package/dist/components/menu.js +6 -6
  20. package/dist/components/outline-rail.d.ts +8 -1
  21. package/dist/components/outline-rail.js +95 -48
  22. package/dist/components/search-field.js +30 -34
  23. package/dist/components/sheet.js +3 -2
  24. package/dist/components/switch.d.ts +5 -0
  25. package/dist/components/switch.js +16 -0
  26. package/dist/components/toast.js +39 -23
  27. package/dist/index.d.ts +2 -2
  28. package/dist/index.js +2 -2
  29. package/dist/inspector/entity-tab.js +1 -2
  30. package/dist/lib/matter-colors.d.ts +38 -0
  31. package/dist/lib/matter-colors.js +65 -0
  32. package/dist/lib/panel-inset.d.ts +11 -0
  33. package/dist/lib/panel-inset.js +33 -0
  34. package/dist/review/review-word-diff.d.ts +13 -0
  35. package/dist/review/review-word-diff.js +78 -0
  36. package/dist/styles/theme.css +9 -0
  37. package/package.json +37 -5
@@ -0,0 +1,56 @@
1
+ import { LayersIcon } from "../icons.js";
2
+ import { cn } from "../lib/utils.js";
3
+ import { resolveMatterColor } from "../lib/matter-colors.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ //#region src/components/matter-icon.tsx
6
+ const ALL_MATTERS_STACK_COLORS = [
7
+ "var(--option-red)",
8
+ "var(--option-blue)",
9
+ "var(--option-green)"
10
+ ];
11
+ const MatterIcon = (props) => {
12
+ if ("matter" in props) return /* @__PURE__ */ jsx(LayersIcon, {
13
+ "aria-hidden": "true",
14
+ className: cn(props.className),
15
+ style: { color: resolveMatterColor(props.matter.id, props.matter.color) }
16
+ });
17
+ if (props.variant === "all") return /* @__PURE__ */ jsxs("svg", {
18
+ "aria-hidden": "true",
19
+ className: cn(props.className),
20
+ fill: "none",
21
+ viewBox: "0 0 24 24",
22
+ children: [
23
+ /* @__PURE__ */ jsx("path", {
24
+ d: "M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",
25
+ stroke: ALL_MATTERS_STACK_COLORS[0],
26
+ strokeLinecap: "round",
27
+ strokeLinejoin: "round",
28
+ strokeWidth: "2"
29
+ }),
30
+ /* @__PURE__ */ jsx("path", {
31
+ d: "M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",
32
+ stroke: ALL_MATTERS_STACK_COLORS[1],
33
+ strokeLinecap: "round",
34
+ strokeLinejoin: "round",
35
+ strokeWidth: "2"
36
+ }),
37
+ /* @__PURE__ */ jsx("path", {
38
+ d: "M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",
39
+ stroke: ALL_MATTERS_STACK_COLORS[2],
40
+ strokeLinecap: "round",
41
+ strokeLinejoin: "round",
42
+ strokeWidth: "2"
43
+ })
44
+ ]
45
+ });
46
+ return /* @__PURE__ */ jsx(LayersIcon, {
47
+ "aria-hidden": "true",
48
+ className: cn(props.className)
49
+ });
50
+ };
51
+ const MattersNavIcon = ({ className }) => /* @__PURE__ */ jsx(MatterIcon, {
52
+ className,
53
+ variant: "none"
54
+ });
55
+ //#endregion
56
+ export { MatterIcon, MattersNavIcon };
@@ -7,7 +7,13 @@ declare const MenuPortal: React$1.ForwardRefExoticComponent<Omit<import("@base-u
7
7
  declare function MenuTrigger({ tooltip, ...props }: Menu$1.Trigger.Props & {
8
8
  tooltip?: React$1.ReactNode;
9
9
  }): React$1.JSX.Element;
10
- declare const MenuPopup: ({ children, className, sideOffset, align, alignOffset, side, anchor, collisionAvoidance, ...props }: Menu$1.Popup.Props & {
10
+ declare const MenuPopup: ({ children, className, sideOffset, align, alignOffset, side, anchor, collisionAvoidance, header, viewportClassName, ...props }: Menu$1.Popup.Props & {
11
+ /** Content pinned above the scrolling list, such as a search field. It sits
12
+ * outside the list, so a row scrolled into view can never hide under it. */
13
+ header?: React$1.ReactNode;
14
+ /** Classes for the scrolling list inside the popup, e.g. a lower height
15
+ * cap for a long searchable list. */
16
+ viewportClassName?: string;
11
17
  align?: Menu$1.Positioner.Props["align"];
12
18
  sideOffset?: Menu$1.Positioner.Props["sideOffset"];
13
19
  alignOffset?: Menu$1.Positioner.Props["alignOffset"];
@@ -37,6 +43,8 @@ declare const MenuSubTrigger: ({ className, inset, children, ...props }: Menu$1.
37
43
  inset?: boolean;
38
44
  }) => React$1.JSX.Element;
39
45
  declare const MenuSubPopup: ({ className, sideOffset, alignOffset, align, ...props }: Menu$1.Popup.Props & {
46
+ header?: React$1.ReactNode;
47
+ viewportClassName?: string;
40
48
  align?: Menu$1.Positioner.Props["align"];
41
49
  sideOffset?: Menu$1.Positioner.Props["sideOffset"];
42
50
  alignOffset?: Menu$1.Positioner.Props["alignOffset"];
@@ -20,7 +20,7 @@ function MenuTrigger({ tooltip, ...props }) {
20
20
  })
21
21
  });
22
22
  }
23
- const MenuPopup = ({ children, className, sideOffset = 4, align, alignOffset, side = "bottom", anchor, collisionAvoidance, ...props }) => /* @__PURE__ */ jsx(Menu$1.Portal, { children: /* @__PURE__ */ jsx(Menu$1.Positioner, {
23
+ const MenuPopup = ({ children, className, sideOffset = 4, align, alignOffset, side = "bottom", anchor, collisionAvoidance, header, viewportClassName, ...props }) => /* @__PURE__ */ jsx(Menu$1.Portal, { children: /* @__PURE__ */ jsx(Menu$1.Positioner, {
24
24
  align: align ?? (anchor ? "start" : "center"),
25
25
  alignOffset,
26
26
  anchor,
@@ -29,14 +29,14 @@ const MenuPopup = ({ children, className, sideOffset = 4, align, alignOffset, si
29
29
  "data-slot": "menu-positioner",
30
30
  side,
31
31
  sideOffset,
32
- children: /* @__PURE__ */ jsx(Menu$1.Popup, {
33
- className: cn("bg-popover relative flex origin-(--transform-origin) rounded-lg border shadow-lg/5 outline-none not-dark:bg-clip-padding not-[class*='w-']:min-w-32 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] focus:outline-none dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", className),
32
+ children: /* @__PURE__ */ jsxs(Menu$1.Popup, {
33
+ className: cn("bg-popover relative flex max-h-(--available-height) origin-(--transform-origin) flex-col rounded-lg border shadow-lg/5 outline-none not-dark:bg-clip-padding not-[class*='w-']:min-w-32 before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-lg)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] focus:outline-none dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", className),
34
34
  "data-slot": "menu-popup",
35
35
  ...props,
36
- children: /* @__PURE__ */ jsx("div", {
37
- className: "max-h-(--available-height) w-full overflow-y-auto p-1",
36
+ children: [header, /* @__PURE__ */ jsx("div", {
37
+ className: cn("min-h-0 w-full flex-1 overflow-y-auto p-1", viewportClassName),
38
38
  children
39
- })
39
+ })]
40
40
  })
41
41
  }) });
42
42
  const MenuGroup = (props) => /* @__PURE__ */ jsx(Menu$1.Group, {
@@ -41,16 +41,23 @@ export type OutlineRailProps = {
41
41
  * filter that narrows the tree to nothing cannot take its own control
42
42
  * away; the caller decides whether the document has an outline at all. */
43
43
  header?: ReactNode;
44
+ /** Name a trailing annotation for assistive technology and its tooltip. */
45
+ formatMetaLabel?: (meta: string) => string;
44
46
  /** Depth from which entries start collapsed. Their ancestors still open on
45
47
  * their own while one of their descendants is active, so a deep outline
46
48
  * reads as the chain down to where the reader is rather than as every
47
49
  * branch at once. Omitted: everything starts expanded. */
48
50
  collapsedFromLevel?: number;
51
+ /** `panel` and `rail` fill a host-owned track; `popover` owns its disclosure. */
52
+ presentation?: "panel" | "rail" | "popover";
53
+ /** A host that already reserves docked chrome passes zero. */
54
+ bottomInset?: number;
49
55
  topOffset?: number;
50
56
  panelWidth?: number;
51
57
  ariaLabel?: string;
52
58
  };
59
+ export declare const OUTLINE_CONTROL_MIN_SIZE = 32;
53
60
  /** The entry as one line of text: label, then its title when it has one. */
54
61
  export declare const outlineEntryText: (item: OutlineItem) => string;
55
- export declare const OutlineRail: ({ items, scrollContainerRef, resolvePct, onJump, activeId, header, collapsedFromLevel, topOffset, panelWidth, ariaLabel }: OutlineRailProps) => import("react").JSX.Element | null;
62
+ export declare const OutlineRail: ({ items, scrollContainerRef, resolvePct, onJump, activeId, header, formatMetaLabel, collapsedFromLevel, presentation, bottomInset, topOffset, panelWidth, ariaLabel }: OutlineRailProps) => import("react").JSX.Element | null;
56
63
  //#endregion
@@ -1,19 +1,20 @@
1
1
  "use client";
2
- import { ChevronRightIcon } from "../icons.js";
2
+ import { ChevronRightIcon, PanelLeftIcon } from "../icons.js";
3
3
  import { cn } from "../lib/utils.js";
4
4
  import { Tooltip, TooltipContent as TooltipPopup, TooltipTrigger } from "./tooltip.js";
5
+ import { ScrollArea } from "./scroll-area.js";
5
6
  import { DirectionalIcon } from "./directional-icon.js";
7
+ import "../lib/panel-inset.js";
6
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
9
  import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
8
10
  import { Temporal } from "temporal-polyfill/full";
9
11
  //#region src/components/outline-rail.tsx
10
12
  /**
11
- * Outline rail — the shared right-edge navigation rail.
13
+ * Outline navigation: an embedded panel, a host-owned rail, or a disclosure.
12
14
  *
13
- * Always-visible thin column of ticks (width tapering by nesting level).
14
- * Hovering reveals a single popover panel with the outline as a collapsible
15
- * tree; the entry currently in view is emphasised. Click a tick or a row to
16
- * jump.
15
+ * Embedded presentations fill their host track. The standalone disclosure
16
+ * owns a toggle above a thin column of ticks; hovering or pressing it reveals
17
+ * the collapsible tree. Click a tick or a row to jump.
17
18
  *
18
19
  * Generic over the position source: callers supply `resolvePct` (vertical % for
19
20
  * a tick) and `onJump`. Active tracking is derived from `resolvePct` by default,
@@ -29,8 +30,9 @@ const OUTLINE_EMPHASIS = {
29
30
  primary: "primary",
30
31
  secondary: "secondary"
31
32
  };
32
- const RAIL_WIDTH = 20;
33
- const ROW_H = 29;
33
+ const OUTLINE_CONTROL_MIN_SIZE = 32;
34
+ const OUTLINE_CONTROL_TARGET_CLASS = "relative pointer-coarse:after:absolute pointer-coarse:after:size-full pointer-coarse:after:min-h-11 pointer-coarse:after:min-w-11";
35
+ const RAIL_WIDTH = 32;
34
36
  const TICK_BASE_WIDTH = 6;
35
37
  const TICK_LEVEL_STEP = 2;
36
38
  const TICK_MAX_LEVEL = 5;
@@ -87,7 +89,7 @@ const Chevron = ({ open }) => /* @__PURE__ */ jsx(DirectionalIcon, {
87
89
  flip: !open,
88
90
  icon: ChevronRightIcon
89
91
  });
90
- const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId, header, collapsedFromLevel, topOffset = 0, panelWidth = 300, ariaLabel = "Outline" }) => {
92
+ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId, header, formatMetaLabel, collapsedFromLevel, presentation = "popover", bottomInset, topOffset = 0, panelWidth = 300, ariaLabel = "Outline" }) => {
91
93
  const [pctById, setPctById] = useState(/* @__PURE__ */ new Map());
92
94
  const [derivedActive, setDerivedActive] = useState(null);
93
95
  const [hovered, setHovered] = useState(false);
@@ -97,7 +99,7 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
97
99
  const [hoveredId, setHoveredId] = useState(null);
98
100
  const panelId = useId();
99
101
  const triggerId = useId();
100
- const panelOpen = hovered || pinned;
102
+ const panelOpen = presentation === "panel" || hovered || pinned;
101
103
  const closeTimer = useRef(null);
102
104
  const manualLockUntil = useRef(0);
103
105
  const panelRef = useRef(null);
@@ -232,7 +234,13 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
232
234
  const activeIndex = items.findIndex((item) => item.id === activeId);
233
235
  useLayoutEffect(() => {
234
236
  if (activeIndex === -1 || !panelOpen) return;
235
- panelRef.current?.querySelector(`[${ACTIVE_ROW_ATTRIBUTE}]`)?.scrollIntoView({ block: "nearest" });
237
+ const panel = panelRef.current;
238
+ const row = panel?.querySelector(`[${ACTIVE_ROW_ATTRIBUTE}]`);
239
+ if (!panel || !(row instanceof HTMLElement)) return;
240
+ const panelBox = panel.getBoundingClientRect();
241
+ const rowBox = row.getBoundingClientRect();
242
+ if (rowBox.top < panelBox.top) panel.scrollTop += rowBox.top - panelBox.top;
243
+ else if (rowBox.bottom > panelBox.bottom) panel.scrollTop += rowBox.bottom - panelBox.bottom;
236
244
  }, [activeIndex, panelOpen]);
237
245
  const openPanel = useCallback(() => {
238
246
  if (closeTimer.current !== null) {
@@ -246,7 +254,7 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
246
254
  closeTimer.current = setTimeout(() => setHovered(false), 120);
247
255
  }, []);
248
256
  useEffect(() => {
249
- if (pinned) panelRef.current?.focus();
257
+ if (pinned) panelRef.current?.closest("nav")?.focus();
250
258
  }, [pinned]);
251
259
  const closePanel = useCallback(() => {
252
260
  if (closeTimer.current !== null) {
@@ -267,23 +275,21 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
267
275
  const highlighted = isActive || isHovered;
268
276
  const indent = 8 + Math.min(node.item.level, maxLevel) * 12;
269
277
  return /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsxs("div", {
270
- className: cn("flex items-center rounded-md pe-2.5", !hasChildren && highlighted && "bg-accent"),
278
+ className: cn("flex items-center rounded-md pe-2.5", highlighted && "bg-accent"),
271
279
  ...isActive ? { [ACTIVE_ROW_ATTRIBUTE]: "" } : {},
272
280
  onMouseEnter: () => setHoveredId(node.item.id),
273
281
  onMouseLeave: () => setHoveredId(null),
274
- style: hasChildren ? {
275
- position: "sticky",
276
- top: node.item.level * ROW_H,
277
- zIndex: 40 - node.item.level,
278
- background: highlighted ? "linear-gradient(var(--color-accent), var(--color-accent)), var(--color-popover)" : "var(--color-popover)"
279
- } : void 0,
280
282
  children: [
281
283
  hasChildren ? /* @__PURE__ */ jsx("button", {
282
284
  "aria-expanded": !isCollapsed,
283
285
  "aria-label": isCollapsed ? "Expand" : "Collapse",
284
- className: "text-muted-foreground hover:text-foreground flex size-5 shrink-0 items-center justify-center",
286
+ className: cn("text-muted-foreground hover:text-foreground flex shrink-0 items-center justify-center", OUTLINE_CONTROL_TARGET_CLASS),
287
+ style: {
288
+ minWidth: 32,
289
+ minHeight: 32,
290
+ marginInlineStart: indent - 4
291
+ },
285
292
  onClick: (event) => toggleCollapse(node.item.id, event.currentTarget.parentElement),
286
- style: { marginInlineStart: indent - 4 },
287
293
  type: "button",
288
294
  children: /* @__PURE__ */ jsx(Chevron, { open: !isCollapsed })
289
295
  }) : /* @__PURE__ */ jsx("span", {
@@ -314,10 +320,22 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
314
320
  children: node.item.title
315
321
  })] })
316
322
  }), /* @__PURE__ */ jsx(TooltipPopup, { children: outlineEntryText(node.item) })] }),
317
- node.item.meta !== void 0 && /* @__PURE__ */ jsx("span", {
323
+ node.item.meta !== void 0 && (formatMetaLabel ? /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
324
+ render: /* @__PURE__ */ jsx("button", {
325
+ "aria-label": formatMetaLabel(node.item.meta),
326
+ className: cn("text-foreground-placeholder text-2xs shrink-0 tabular-nums", OUTLINE_CONTROL_TARGET_CLASS),
327
+ style: {
328
+ minWidth: 32,
329
+ minHeight: 32
330
+ },
331
+ onClick: () => jumpTo(node.item.id),
332
+ type: "button"
333
+ }),
334
+ children: node.item.meta
335
+ }), /* @__PURE__ */ jsx(TooltipPopup, { children: formatMetaLabel(node.item.meta) })] }) : /* @__PURE__ */ jsx("span", {
318
336
  className: "text-foreground-placeholder text-2xs shrink-0 ps-2 tabular-nums",
319
337
  children: node.item.meta
320
- })
338
+ }))
321
339
  ]
322
340
  }), hasChildren && !isCollapsed && /* @__PURE__ */ jsx("ul", {
323
341
  className: "m-0 list-none p-0",
@@ -337,18 +355,26 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
337
355
  const ghostItem = ghostPct === void 0 ? void 0 : items.find((item) => item.id === hoveredId);
338
356
  return /* @__PURE__ */ jsxs("div", {
339
357
  "aria-label": ariaLabel,
340
- className: "absolute end-0 z-20 max-lg:hidden",
358
+ className: OUTLINE_PRESENTATION_CLASS[presentation],
341
359
  role: "group",
342
360
  style: {
343
- top: topOffset,
344
- bottom: 0,
345
- width: RAIL_WIDTH
361
+ marginBlockEnd: bottomInset ?? "max(env(safe-area-inset-bottom, 0px), var(--document-panel-bottom-inset, 0px))",
362
+ ...presentation === "popover" ? {
363
+ top: topOffset,
364
+ bottom: 0,
365
+ width: RAIL_WIDTH
366
+ } : {}
346
367
  },
347
368
  children: [
348
- /* @__PURE__ */ jsx("button", {
369
+ presentation === "popover" && /* @__PURE__ */ jsx("button", {
349
370
  "aria-controls": panelId,
350
371
  "aria-expanded": panelOpen,
351
- className: "focus-visible:ring-ring bg-popover text-popover-foreground sr-only end-0 top-2 z-30 -translate-x-6 text-xs focus:not-sr-only focus:absolute focus:w-max focus:rounded-md focus:border focus:px-2 focus:py-1 focus-visible:ring-2 focus-visible:outline-none",
372
+ className: cn("focus-visible:ring-ring bg-popover text-popover-foreground flex shrink-0 items-center justify-center self-start rounded-md focus-visible:ring-2 focus-visible:outline-none", "pointer-coarse:my-1.5", OUTLINE_CONTROL_TARGET_CLASS),
373
+ "aria-label": ariaLabel,
374
+ style: {
375
+ minWidth: 32,
376
+ minHeight: 32
377
+ },
352
378
  id: triggerId,
353
379
  onClick: () => {
354
380
  if (pinned) {
@@ -359,10 +385,15 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
359
385
  openPanel();
360
386
  },
361
387
  type: "button",
362
- children: ariaLabel
388
+ children: /* @__PURE__ */ jsx(PanelLeftIcon, {
389
+ "aria-hidden": true,
390
+ className: "size-4 rtl:-scale-x-100"
391
+ })
363
392
  }),
364
- /* @__PURE__ */ jsxs("div", {
365
- className: "relative h-full",
393
+ presentation !== "panel" && /* @__PURE__ */ jsxs("div", {
394
+ className: "relative min-h-0 flex-1",
395
+ "data-outline-ticks": true,
396
+ style: presentation === "popover" ? { height: `calc(100% - 32px)` } : void 0,
366
397
  onMouseEnter: openPanel,
367
398
  onMouseLeave: scheduleClose,
368
399
  onWheel: (event) => {
@@ -379,7 +410,7 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
379
410
  onMouseEnter: () => setHoveredId(item.id),
380
411
  onMouseLeave: () => setHoveredId(null),
381
412
  style: {
382
- top: `${pct}%`,
413
+ top: `clamp(2px, ${pct}%, calc(100% - 2px))`,
383
414
  transform: "translateY(-50%)",
384
415
  width: isHovered ? tickWidth(item.level) + 8 : tickWidth(item.level),
385
416
  height: tickHeight(isHovered, isActive),
@@ -391,7 +422,7 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
391
422
  "aria-hidden": true,
392
423
  className: "absolute end-0 rounded-full opacity-100",
393
424
  style: {
394
- top: `${ghostPct}%`,
425
+ top: `clamp(2px, ${ghostPct}%, calc(100% - 2px))`,
395
426
  transform: "translateY(-50%)",
396
427
  width: tickWidth(ghostItem.level) + 8,
397
428
  height: 4,
@@ -399,13 +430,15 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
399
430
  }
400
431
  })]
401
432
  }),
402
- /* @__PURE__ */ jsxs("nav", {
433
+ presentation !== "rail" && /* @__PURE__ */ jsxs("nav", {
434
+ "aria-label": ariaLabel,
403
435
  "aria-hidden": !panelOpen,
404
436
  id: panelId,
405
437
  inert: panelOpen ? void 0 : true,
406
438
  tabIndex: -1,
407
- className: cn("border-border bg-popover text-popover-foreground absolute overflow-y-auto rounded-xl border pb-2 shadow-lg transition-[opacity,transform] duration-150", panelOpen ? "translate-x-0 opacity-100" : "pointer-events-none translate-x-2 opacity-0"),
439
+ className: cn("bg-popover text-popover-foreground flex min-h-0 flex-col", presentation === "popover" && "border-border absolute rounded-xl border shadow-lg transition-[opacity,transform] duration-150", outlinePanelVisibilityClass(presentation, panelOpen)),
408
440
  onBlurCapture: (event) => {
441
+ if (event.relatedTarget instanceof HTMLElement && event.relatedTarget.id === triggerId) return;
409
442
  if (pinned && !event.currentTarget.contains(event.relatedTarget)) {
410
443
  closePanel();
411
444
  return;
@@ -422,28 +455,42 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
422
455
  onMouseEnter: openPanel,
423
456
  onMouseLeave: scheduleClose,
424
457
  onWheel: (event) => {
425
- const panel = event.currentTarget;
458
+ const panel = panelRef.current;
459
+ if (!panel) return;
426
460
  const room = panel.scrollHeight - panel.clientHeight;
427
461
  if (room > WHEEL_EDGE_TOLERANCE && (event.deltaY < 0 ? panel.scrollTop > WHEEL_EDGE_TOLERANCE : panel.scrollTop < room - WHEEL_EDGE_TOLERANCE)) return;
428
462
  scrollContainerRef.current?.scrollBy(0, event.deltaY);
429
463
  },
430
- ref: panelRef,
431
- style: {
464
+ style: presentation === "popover" ? {
432
465
  top: 0,
433
- insetInlineEnd: 26,
434
- width: panelWidth,
435
- maxHeight: "calc(100% - 24px)"
436
- },
466
+ insetInlineEnd: 38,
467
+ width: `min(${panelWidth}px, calc(100vw - 54px))`,
468
+ height: "calc(100% - 24px)"
469
+ } : void 0,
437
470
  children: [header !== void 0 && /* @__PURE__ */ jsx("div", {
438
- className: "bg-popover sticky top-0 z-50 border-b p-2",
471
+ className: "bg-popover z-50 shrink-0 border-b p-2",
439
472
  children: header
440
- }), /* @__PURE__ */ jsx("ul", {
441
- className: "m-0 list-none p-0",
442
- children: tree.map(renderNode)
473
+ }), /* @__PURE__ */ jsx(ScrollArea, {
474
+ axis: "vertical",
475
+ className: "min-h-0 flex-1",
476
+ viewportRef: panelRef,
477
+ children: /* @__PURE__ */ jsx("ul", {
478
+ className: "m-0 list-none p-0 pb-2",
479
+ children: tree.map(renderNode)
480
+ })
443
481
  })]
444
482
  })
445
483
  ]
446
484
  });
447
485
  };
486
+ const OUTLINE_PRESENTATION_CLASS = {
487
+ popover: "absolute end-0 z-20 flex flex-col pointer-coarse:min-w-11",
488
+ panel: "relative flex min-h-0 w-full flex-1 flex-col",
489
+ rail: "relative flex min-h-0 w-full flex-1 flex-col"
490
+ };
491
+ function outlinePanelVisibilityClass(presentation, open) {
492
+ if (presentation === "panel") return "flex-1";
493
+ return open ? "translate-x-0 opacity-100" : "pointer-events-none translate-x-2 opacity-0";
494
+ }
448
495
  //#endregion
449
- export { OUTLINE_EMPHASIS, OutlineRail, outlineEntryText };
496
+ export { OUTLINE_CONTROL_MIN_SIZE, OUTLINE_EMPHASIS, OutlineRail, outlineEntryText };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { SearchIcon, XIcon } from "../icons.js";
2
+ import { XIcon } from "../icons.js";
3
3
  import { cn } from "../lib/utils.js";
4
4
  import { Button } from "./button.js";
5
5
  import { InputGroup, InputGroupAddon, InputGroupInput } from "./input-group.js";
@@ -13,41 +13,37 @@ import { jsx, jsxs } from "react/jsx-runtime";
13
13
  */
14
14
  const SearchField = ({ value, onValueChange, clearLabel, groupClassName, className, onKeyDown, ...props }) => /* @__PURE__ */ jsxs(InputGroup, {
15
15
  className: cn("min-h-11 sm:min-h-0", groupClassName),
16
- children: [
17
- /* @__PURE__ */ jsx(InputGroupAddon, { children: /* @__PURE__ */ jsx(SearchIcon, {}) }),
18
- /* @__PURE__ */ jsx(InputGroupInput, {
19
- autoComplete: "off",
20
- spellCheck: false,
21
- ...props,
22
- className: cn("max-sm:h-11 max-sm:leading-11", className),
23
- onChange: (event) => {
24
- onValueChange(event.target.value);
16
+ children: [/* @__PURE__ */ jsx(InputGroupInput, {
17
+ autoComplete: "off",
18
+ spellCheck: false,
19
+ ...props,
20
+ className: cn("max-sm:h-11 max-sm:leading-11", className),
21
+ onChange: (event) => {
22
+ onValueChange(event.target.value);
23
+ },
24
+ onKeyDown: (event) => {
25
+ if (event.key === "Escape" && value.length > 0) {
26
+ event.preventDefault();
27
+ event.stopPropagation();
28
+ onValueChange("");
29
+ }
30
+ onKeyDown?.(event);
31
+ },
32
+ type: "search",
33
+ value
34
+ }), value.length > 0 && /* @__PURE__ */ jsx(InputGroupAddon, {
35
+ align: "inline-end",
36
+ children: /* @__PURE__ */ jsx(Button, {
37
+ "aria-label": clearLabel,
38
+ onClick: () => {
39
+ onValueChange("");
25
40
  },
26
- onKeyDown: (event) => {
27
- if (event.key === "Escape" && value.length > 0) {
28
- event.preventDefault();
29
- event.stopPropagation();
30
- onValueChange("");
31
- }
32
- onKeyDown?.(event);
33
- },
34
- type: "search",
35
- value
36
- }),
37
- value.length > 0 && /* @__PURE__ */ jsx(InputGroupAddon, {
38
- align: "inline-end",
39
- children: /* @__PURE__ */ jsx(Button, {
40
- "aria-label": clearLabel,
41
- onClick: () => {
42
- onValueChange("");
43
- },
44
- size: "icon-xs",
45
- type: "button",
46
- variant: "ghost",
47
- children: /* @__PURE__ */ jsx(XIcon, {})
48
- })
41
+ size: "icon-xs",
42
+ type: "button",
43
+ variant: "ghost",
44
+ children: /* @__PURE__ */ jsx(XIcon, {})
49
45
  })
50
- ]
46
+ })]
51
47
  });
52
48
  //#endregion
53
49
  export { SearchField };
@@ -1,6 +1,7 @@
1
1
  "use client";
2
2
  import { XIcon } from "../icons.js";
3
3
  import { cn } from "../lib/utils.js";
4
+ import { OVERLAY_LAYER_CLASS_NAMES } from "../lib/overlay-layer.js";
4
5
  import { renderTooltipTrigger } from "./tooltip-trigger-helper.js";
5
6
  import { Button } from "./button.js";
6
7
  import { ScrollArea } from "./scroll-area.js";
@@ -28,12 +29,12 @@ function SheetClose({ tooltip, ...props }) {
28
29
  });
29
30
  }
30
31
  const SheetBackdrop = ({ className, ...props }) => /* @__PURE__ */ jsx(Dialog.Backdrop, {
31
- className: cn("fixed inset-0 z-50 bg-black/32 backdrop-blur-sm transition-opacity duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0", className),
32
+ className: cn("fixed inset-0 bg-black/32 backdrop-blur-sm transition-opacity duration-200 data-ending-style:opacity-0 data-starting-style:opacity-0", OVERLAY_LAYER_CLASS_NAMES.default, className),
32
33
  "data-slot": "sheet-backdrop",
33
34
  ...props
34
35
  });
35
36
  const SheetViewport = ({ className: _className, side, variant = "default", ...props }) => /* @__PURE__ */ jsx(Dialog.Viewport, {
36
- className: cn("fixed inset-0 z-50", side === "block-end" && "grid grid-rows-[1fr_auto] pt-12", side === "block-start" && "grid grid-rows-[auto_1fr] pb-12", side === "inline-start" && "flex justify-start", side === "inline-end" && "flex justify-end", variant === "inset" && "sm:p-4"),
37
+ className: cn("fixed inset-0", OVERLAY_LAYER_CLASS_NAMES.default, side === "block-end" && "grid grid-rows-[1fr_auto] pt-12", side === "block-start" && "grid grid-rows-[auto_1fr] pb-12", side === "inline-start" && "flex justify-start", side === "inline-end" && "flex justify-end", variant === "inset" && "sm:p-4"),
37
38
  "data-slot": "sheet-viewport",
38
39
  ...props
39
40
  });
@@ -0,0 +1,5 @@
1
+ import { Switch as Switch$1 } from "@base-ui/react/switch";
2
+ //#region src/components/switch.d.ts
3
+ declare const Switch: ({ className, ...props }: Switch$1.Root.Props) => import("react").JSX.Element;
4
+ //#endregion
5
+ export { Switch };
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { cn } from "../lib/utils.js";
3
+ import { jsx } from "react/jsx-runtime";
4
+ import { Switch as Switch$1 } from "@base-ui/react/switch";
5
+ //#region src/components/switch.tsx
6
+ const Switch = ({ className, ...props }) => /* @__PURE__ */ jsx(Switch$1.Root, {
7
+ className: cn("focus-visible:ring-ring focus-visible:ring-offset-background data-checked:bg-primary data-unchecked:bg-input relative inline-flex h-5 w-9 shrink-0 items-center rounded-full outline-none focus-visible:ring-2 focus-visible:ring-offset-1 data-disabled:opacity-64", className),
8
+ "data-slot": "switch",
9
+ ...props,
10
+ children: /* @__PURE__ */ jsx(Switch$1.Thumb, {
11
+ className: "bg-background pointer-events-none block size-4 rounded-full shadow-xs transition-transform ltr:data-checked:translate-x-4.5 ltr:data-unchecked:translate-x-0.5 rtl:data-checked:-translate-x-4.5 rtl:data-unchecked:-translate-x-0.5",
12
+ "data-slot": "switch-thumb"
13
+ })
14
+ });
15
+ //#endregion
16
+ export { Switch };
@@ -3,6 +3,7 @@ import { CircleAlertIcon, CircleCheckIcon, InfoIcon, TriangleAlertIcon, XIcon }
3
3
  import { cn } from "../lib/utils.js";
4
4
  import { Loader } from "./loader.js";
5
5
  import { buttonVariants } from "./button.js";
6
+ import { ScrollArea } from "./scroll-area.js";
6
7
  import { jsx, jsxs } from "react/jsx-runtime";
7
8
  import * as React$1 from "react";
8
9
  import { Toast } from "@base-ui/react/toast";
@@ -74,18 +75,22 @@ const Toasts = ({ position }) => {
74
75
  })(),
75
76
  toast: toastItem,
76
77
  children: /* @__PURE__ */ jsxs(Toast.Content, {
77
- className: "pointer-events-auto flex items-center gap-3 overflow-hidden px-3.5 py-3 text-sm transition-opacity duration-250 select-text data-behind:opacity-0 data-behind:not-data-expanded:pointer-events-none data-expanded:opacity-100",
78
+ className: "pointer-events-auto flex items-center gap-3 px-3.5 py-3 text-sm transition-opacity duration-250 select-text data-behind:opacity-0 data-behind:not-data-expanded:pointer-events-none data-expanded:opacity-100",
78
79
  children: [/* @__PURE__ */ jsxs("div", {
79
80
  className: "flex min-w-0 flex-1 gap-2",
80
- children: [/* @__PURE__ */ jsx(ToastIcon, { type: toastItem.type }), /* @__PURE__ */ jsxs("div", {
81
- className: "flex min-w-0 flex-col gap-0.5 select-text",
82
- children: [/* @__PURE__ */ jsx(Toast.Title, {
83
- className: "truncate font-medium",
84
- "data-slot": "toast-title"
85
- }), /* @__PURE__ */ jsx(Toast.Description, {
86
- className: "text-muted-foreground wrap-break-word",
87
- "data-slot": "toast-description"
88
- })]
81
+ children: [/* @__PURE__ */ jsx(ToastIcon, { type: toastItem.type }), /* @__PURE__ */ jsx(ScrollArea, {
82
+ axis: "vertical",
83
+ className: "h-auto min-w-0 flex-1 [&_[data-slot=scroll-area-viewport]]:max-h-[60dvh]",
84
+ children: /* @__PURE__ */ jsxs("div", {
85
+ className: "flex min-w-0 flex-col gap-0.5 select-text",
86
+ children: [/* @__PURE__ */ jsx(Toast.Title, {
87
+ className: "font-medium wrap-anywhere whitespace-pre-wrap",
88
+ "data-slot": "toast-title"
89
+ }), /* @__PURE__ */ jsx(Toast.Description, {
90
+ className: "text-muted-foreground wrap-anywhere whitespace-pre-wrap",
91
+ "data-slot": "toast-description"
92
+ })]
93
+ })
89
94
  })]
90
95
  }), /* @__PURE__ */ jsxs("div", {
91
96
  className: "ms-auto flex shrink-0 items-center gap-2",
@@ -132,20 +137,31 @@ const AnchoredToasts = () => {
132
137
  toast: toastItem,
133
138
  children: tooltipStyle ? /* @__PURE__ */ jsx(Toast.Content, {
134
139
  className: "pointer-events-auto px-2 py-1 select-text",
135
- children: /* @__PURE__ */ jsx(Toast.Title, { "data-slot": "toast-title" })
140
+ children: /* @__PURE__ */ jsx(ScrollArea, {
141
+ axis: "vertical",
142
+ className: "h-auto [&_[data-slot=scroll-area-viewport]]:max-h-[60dvh]",
143
+ children: /* @__PURE__ */ jsx(Toast.Title, {
144
+ className: "wrap-anywhere whitespace-pre-wrap",
145
+ "data-slot": "toast-title"
146
+ })
147
+ })
136
148
  }) : /* @__PURE__ */ jsxs(Toast.Content, {
137
- className: "pointer-events-auto flex items-center gap-3 overflow-hidden px-3.5 py-3 text-sm select-text",
149
+ className: "pointer-events-auto flex items-center gap-3 px-3.5 py-3 text-sm select-text",
138
150
  children: [/* @__PURE__ */ jsxs("div", {
139
151
  className: "flex min-w-0 flex-1 gap-2",
140
- children: [/* @__PURE__ */ jsx(ToastIcon, { type: toastItem.type }), /* @__PURE__ */ jsxs("div", {
141
- className: "flex min-w-0 flex-col gap-0.5 select-text",
142
- children: [/* @__PURE__ */ jsx(Toast.Title, {
143
- className: "truncate font-medium",
144
- "data-slot": "toast-title"
145
- }), /* @__PURE__ */ jsx(Toast.Description, {
146
- className: "text-muted-foreground wrap-break-word",
147
- "data-slot": "toast-description"
148
- })]
152
+ children: [/* @__PURE__ */ jsx(ToastIcon, { type: toastItem.type }), /* @__PURE__ */ jsx(ScrollArea, {
153
+ axis: "vertical",
154
+ className: "h-auto min-w-0 flex-1 [&_[data-slot=scroll-area-viewport]]:max-h-[60dvh]",
155
+ children: /* @__PURE__ */ jsxs("div", {
156
+ className: "flex min-w-0 flex-col gap-0.5 select-text",
157
+ children: [/* @__PURE__ */ jsx(Toast.Title, {
158
+ className: "font-medium wrap-anywhere whitespace-pre-wrap",
159
+ "data-slot": "toast-title"
160
+ }), /* @__PURE__ */ jsx(Toast.Description, {
161
+ className: "text-muted-foreground wrap-anywhere whitespace-pre-wrap",
162
+ "data-slot": "toast-description"
163
+ })]
164
+ })
149
165
  })]
150
166
  }), toastItem.actionProps && /* @__PURE__ */ jsx(Toast.Action, {
151
167
  className: cn(buttonVariants({ size: "xs" }), "ms-auto shrink-0 select-none"),
@@ -204,8 +220,8 @@ function normalizeToastActionProps({ action, actionProps, includeEmptyActionProp
204
220
  } };
205
221
  }
206
222
  function getDefaultToastTimeout(type) {
207
- if (type === "loading") return 0;
208
- if (type === "error" || type === "warning") return 6e3;
223
+ if (type === "loading" || type === "error") return 0;
224
+ if (type === "warning") return 6e3;
209
225
  return 4e3;
210
226
  }
211
227
  function normalizeToastTimeout(timeout) {