@stll/ui 0.44.1 → 0.46.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 (71) hide show
  1. package/README.md +2 -1
  2. package/dist/components/accordion.d.ts +6 -2
  3. package/dist/components/accordion.js +4 -4
  4. package/dist/components/ai-label.d.ts +33 -0
  5. package/dist/components/ai-label.js +42 -0
  6. package/dist/components/alert-dialog.js +3 -2
  7. package/dist/components/animated-count.d.ts +13 -0
  8. package/dist/components/animated-count.js +63 -0
  9. package/dist/components/application-rail.js +1 -1
  10. package/dist/components/brand-icons.d.ts +1 -0
  11. package/dist/components/brand-icons.js +8 -1
  12. package/dist/components/button.d.ts +2 -2
  13. package/dist/components/button.js +12 -2
  14. package/dist/components/command.js +1 -1
  15. package/dist/components/court-badge.d.ts +7 -1
  16. package/dist/components/court-badge.js +13 -3
  17. package/dist/components/date-picker-popover.d.ts +12 -2
  18. package/dist/components/date-picker-popover.js +105 -9
  19. package/dist/components/date-range-filter.d.ts +14 -0
  20. package/dist/components/date-range-filter.js +74 -0
  21. package/dist/components/dialog.js +3 -2
  22. package/dist/components/document-identity-badge.d.ts +12 -0
  23. package/dist/components/document-identity-badge.js +94 -0
  24. package/dist/components/document-identity-badge.logic.d.ts +25 -0
  25. package/dist/components/document-identity-badge.logic.js +25 -0
  26. package/dist/components/input-group.d.ts +1 -1
  27. package/dist/components/input-otp.js +1 -1
  28. package/dist/components/landing.d.ts +1 -1
  29. package/dist/components/landing.js +2 -2
  30. package/dist/components/list.d.ts +8 -2
  31. package/dist/components/list.js +10 -4
  32. package/dist/components/matter-icon.d.ts +16 -0
  33. package/dist/components/matter-icon.js +56 -0
  34. package/dist/components/menu.d.ts +9 -1
  35. package/dist/components/menu.js +6 -6
  36. package/dist/components/outline-rail.d.ts +8 -1
  37. package/dist/components/outline-rail.js +95 -48
  38. package/dist/components/popover.js +1 -1
  39. package/dist/components/search-field.js +30 -34
  40. package/dist/components/select.js +1 -1
  41. package/dist/components/sheet.js +22 -4
  42. package/dist/components/sidebar.d.ts +1 -1
  43. package/dist/components/sidebar.js +8 -8
  44. package/dist/components/status-mark.d.ts +17 -0
  45. package/dist/components/status-mark.js +74 -0
  46. package/dist/components/swap-text.d.ts +16 -0
  47. package/dist/components/swap-text.js +52 -0
  48. package/dist/components/switch.d.ts +5 -0
  49. package/dist/components/switch.js +16 -0
  50. package/dist/components/table-chart.d.ts +20 -0
  51. package/dist/components/table-chart.js +274 -0
  52. package/dist/components/tabs.js +1 -1
  53. package/dist/components/toast.js +42 -25
  54. package/dist/components/workspace-shell.js +5 -4
  55. package/dist/icons.js +1 -2
  56. package/dist/index.d.ts +3 -3
  57. package/dist/index.js +3 -3
  58. package/dist/inspector/dock.js +1 -1
  59. package/dist/inspector/entity-tab.js +1 -2
  60. package/dist/kanban/virtual-cell.js +1 -1
  61. package/dist/lib/control-size.d.ts +1 -1
  62. package/dist/lib/matter-colors.d.ts +38 -0
  63. package/dist/lib/matter-colors.js +65 -0
  64. package/dist/lib/panel-inset.d.ts +11 -0
  65. package/dist/lib/panel-inset.js +33 -0
  66. package/dist/lib/safe-area.d.ts +11 -0
  67. package/dist/lib/safe-area.js +12 -0
  68. package/dist/review/review-word-diff.d.ts +13 -0
  69. package/dist/review/review-word-diff.js +78 -0
  70. package/dist/styles/theme.css +79 -0
  71. package/package.json +64 -8
@@ -0,0 +1,74 @@
1
+ "use client";
2
+ import { cn } from "../lib/utils.js";
3
+ import { DatePickerPopover } from "./date-picker-popover.js";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import { useId, useRef, useState } from "react";
6
+ //#region src/components/date-range-filter.tsx
7
+ /** Both calendar bounds and the focus handoff belong to the range. */
8
+ const DateRangeFilter = ({ from, to, onFromChange, onToChange, fromLabel, toLabel, className, maxDate, ...pickerProps }) => {
9
+ const id = useId();
10
+ const toRef = useRef(null);
11
+ const [picker, setPicker] = useState({ type: "closed" });
12
+ const fromMax = to && (!maxDate || to < maxDate) ? to : maxDate;
13
+ return /* @__PURE__ */ jsxs("div", {
14
+ className: cn("flex flex-col gap-1.5", className),
15
+ children: [/* @__PURE__ */ jsxs("div", {
16
+ className: "flex min-w-0 flex-col gap-1.5",
17
+ children: [/* @__PURE__ */ jsx("span", {
18
+ id: `${id}-from-label`,
19
+ className: "text-muted-foreground text-xs font-medium",
20
+ children: fromLabel
21
+ }), /* @__PURE__ */ jsx(DatePickerPopover, {
22
+ ...pickerProps,
23
+ mode: "date",
24
+ id: `${id}-from`,
25
+ defaultOpen: false,
26
+ labelledBy: `${id}-from-label`,
27
+ dateInputLabel: fromLabel,
28
+ value: from,
29
+ maxDate: fromMax,
30
+ minDate: void 0,
31
+ triggerRef: void 0,
32
+ focusTriggerOnOpen: false,
33
+ open: picker.type === "from",
34
+ onOpenChange: (open) => setPicker(open ? { type: "from" } : { type: "closed" }),
35
+ onChange: (next) => {
36
+ onFromChange(next);
37
+ if (!next) return;
38
+ setPicker({
39
+ type: "to",
40
+ focus: "trigger"
41
+ });
42
+ toRef.current?.focus();
43
+ }
44
+ })]
45
+ }), /* @__PURE__ */ jsxs("div", {
46
+ className: "flex min-w-0 flex-col gap-1.5",
47
+ children: [/* @__PURE__ */ jsx("span", {
48
+ id: `${id}-to-label`,
49
+ className: "text-muted-foreground text-xs font-medium",
50
+ children: toLabel
51
+ }), /* @__PURE__ */ jsx(DatePickerPopover, {
52
+ ...pickerProps,
53
+ mode: "date",
54
+ id: `${id}-to`,
55
+ defaultOpen: false,
56
+ labelledBy: `${id}-to-label`,
57
+ dateInputLabel: toLabel,
58
+ value: to,
59
+ minDate: from ?? void 0,
60
+ maxDate,
61
+ open: picker.type === "to",
62
+ triggerRef: toRef,
63
+ focusTriggerOnOpen: picker.type === "to" && picker.focus === "trigger",
64
+ onOpenChange: (open) => setPicker(open ? {
65
+ type: "to",
66
+ focus: "calendar"
67
+ } : { type: "closed" }),
68
+ onChange: onToChange
69
+ })]
70
+ })]
71
+ });
72
+ };
73
+ //#endregion
74
+ export { DateRangeFilter };
@@ -2,6 +2,7 @@
2
2
  import { XIcon } from "../icons.js";
3
3
  import { cn } from "../lib/utils.js";
4
4
  import { OVERLAY_LAYER_CLASS_NAMES } from "../lib/overlay-layer.js";
5
+ import { SAFE_AREA_OVERLAY_GUTTER_CLASS_NAME } from "../lib/safe-area.js";
5
6
  import { renderTooltipTrigger } from "./tooltip-trigger-helper.js";
6
7
  import { Button } from "./button.js";
7
8
  import { ScrollArea } from "./scroll-area.js";
@@ -86,7 +87,7 @@ const DialogBackdrop = ({ className, layer = "default", ...props }) => /* @__PUR
86
87
  ...props
87
88
  });
88
89
  const DialogViewport = ({ className, layer = "default", ...props }) => /* @__PURE__ */ jsx(Dialog$1.Viewport, {
89
- className: cn("fixed inset-0 grid grid-rows-[1fr_auto_3fr] justify-items-center p-4", OVERLAY_LAYER_CLASS_NAMES[layer], className),
90
+ className: cn("fixed inset-0 grid grid-rows-[1fr_auto_3fr] justify-items-center", SAFE_AREA_OVERLAY_GUTTER_CLASS_NAME, OVERLAY_LAYER_CLASS_NAMES[layer], className),
90
91
  "data-slot": "dialog-viewport",
91
92
  ...props
92
93
  });
@@ -100,7 +101,7 @@ const DialogPopup = ({ backdropClassName, className, children, showCloseButton =
100
101
  className: cn(bottomStickOnMobile && "max-sm:grid-rows-[1fr_auto] max-sm:p-0 max-sm:pt-12", viewportClassName),
101
102
  layer,
102
103
  children: /* @__PURE__ */ jsxs(Dialog$1.Popup, {
103
- className: cn("bg-popover text-popover-foreground relative row-start-2 flex max-h-full min-h-0 w-full max-w-lg min-w-0 -translate-y-[calc(1.25rem*var(--nested-dialogs))] scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 transition-[scale,opacity,translate] duration-200 ease-in-out will-change-transform not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:scale-98 data-ending-style:opacity-0 data-nested:data-ending-style:translate-y-8 data-nested-dialog-open:origin-top data-starting-style:scale-98 data-starting-style:opacity-0 data-nested:data-starting-style:translate-y-8 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", bottomStickOnMobile && "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:before:hidden max-sm:before:rounded-none max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4", className),
104
+ className: cn("bg-popover text-popover-foreground relative row-start-2 flex max-h-full min-h-0 w-full max-w-lg min-w-0 -translate-y-[calc(1.25rem*var(--nested-dialogs))] scale-[calc(1-0.1*var(--nested-dialogs))] flex-col rounded-2xl border opacity-[calc(1-0.1*var(--nested-dialogs))] shadow-lg/5 transition-[scale,opacity,translate] duration-200 ease-out will-change-transform not-dark:bg-clip-padding before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--radius-2xl)-1px)] before:shadow-[0_1px_--theme(--color-black/4%)] data-ending-style:scale-98 data-ending-style:opacity-0 data-ending-style:duration-150 data-nested:data-ending-style:translate-y-8 data-nested-dialog-open:origin-top data-starting-style:scale-98 data-starting-style:opacity-0 data-nested:data-starting-style:translate-y-8 dark:before:shadow-[0_-1px_--theme(--color-white/6%)]", bottomStickOnMobile && "max-sm:max-w-none max-sm:rounded-none max-sm:border-x-0 max-sm:border-t max-sm:border-b-0 max-sm:pb-(--safe-area-bottom) max-sm:opacity-[calc(1-min(var(--nested-dialogs),1))] max-sm:before:hidden max-sm:before:rounded-none max-sm:data-ending-style:translate-y-4 max-sm:data-starting-style:translate-y-4", className),
104
105
  "data-slot": "dialog-popup",
105
106
  ...props,
106
107
  children: [
@@ -0,0 +1,12 @@
1
+ import { CourtBadgeSize } from "./court-badge.js";
2
+ import { DocumentIdentity } from "./document-identity-badge.logic.js";
3
+ //#region src/components/document-identity-badge.d.ts
4
+ export declare const DocumentIdentityBadge: ({ identity, size, title }: DocumentIdentityBadgeProps) => import("react").JSX.Element;
5
+ type DocumentIdentityBadgeProps = {
6
+ identity: DocumentIdentity;
7
+ /** Court chip size; statute labels follow their container width. */
8
+ size?: CourtBadgeSize | undefined;
9
+ /** Omit when the enclosing tab already supplies the full-title tooltip. */
10
+ title?: string | undefined;
11
+ };
12
+ //#endregion
@@ -0,0 +1,94 @@
1
+ import { FileTextIcon } from "../icons.js";
2
+ import { cn } from "../lib/utils.js";
3
+ import { Tooltip, TooltipContent as TooltipPopup, TooltipTrigger } from "./tooltip.js";
4
+ import { CourtBadge } from "./court-badge.js";
5
+ import { COURT_TIER_WEIGHT, statuteIdentityLabels } from "./document-identity-badge.logic.js";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { panic } from "better-result";
8
+ //#region src/components/document-identity-badge.tsx
9
+ const DocumentIdentityBadge = ({ identity, size, title }) => {
10
+ const content = documentIdentityMark(identity, size);
11
+ if (title === void 0) return content;
12
+ return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
13
+ render: /* @__PURE__ */ jsx("span", { className: "inline-flex max-w-full min-w-0" }),
14
+ children: content
15
+ }), /* @__PURE__ */ jsx(TooltipPopup, { children: title })] });
16
+ };
17
+ function documentIdentityMark(identity, size) {
18
+ switch (identity.kind) {
19
+ case "statute": {
20
+ const labels = statuteIdentityLabels(identity);
21
+ if (labels === null) return unknownDocumentMark();
22
+ return /* @__PURE__ */ jsxs("span", {
23
+ className: cn("@container/document-identity inline-flex max-w-full shrink-0 items-center justify-center font-mono leading-4 font-semibold tabular-nums", labels.short.length > 7 ? "text-[9px]" : "text-[10px]"),
24
+ style: { width: `${labels.long.length + .25}ch` },
25
+ "data-slot": "document-identity-badge",
26
+ "data-kind": "statute",
27
+ dir: "ltr",
28
+ children: [/* @__PURE__ */ jsx("bdi", {
29
+ className: statuteLabelClasses(labels.long.length).short,
30
+ children: labels.short
31
+ }), /* @__PURE__ */ jsx("bdi", {
32
+ className: statuteLabelClasses(labels.long.length).long,
33
+ children: labels.long
34
+ })]
35
+ });
36
+ }
37
+ case "decision": {
38
+ const abbreviation = identity.courtAbbreviation?.trim();
39
+ if (!abbreviation) return unknownDocumentMark();
40
+ return /* @__PURE__ */ jsx("span", {
41
+ className: "inline-flex max-w-full min-w-0",
42
+ "data-slot": "document-identity-badge",
43
+ "data-kind": "decision",
44
+ children: /* @__PURE__ */ jsx(CourtBadge, {
45
+ abbreviation,
46
+ size,
47
+ weight: identity.courtTier === void 0 ? "outline" : COURT_TIER_WEIGHT[identity.courtTier],
48
+ className: cn("max-w-full overflow-hidden", abbreviation.length > 4 && "px-0.5 text-[9px] tracking-normal")
49
+ })
50
+ });
51
+ }
52
+ case "unknown": return unknownDocumentMark();
53
+ default: return panic("Unhandled document identity");
54
+ }
55
+ }
56
+ function unknownDocumentMark() {
57
+ return /* @__PURE__ */ jsx(FileTextIcon, {
58
+ "aria-hidden": "true",
59
+ className: "size-3.5 shrink-0",
60
+ "data-slot": "document-identity-badge",
61
+ "data-kind": "unknown"
62
+ });
63
+ }
64
+ const STATUTE_LABEL_CLASSES = {
65
+ 6: {
66
+ short: "@[6ch]/document-identity:hidden",
67
+ long: "hidden @[6ch]/document-identity:inline"
68
+ },
69
+ 7: {
70
+ short: "@[7ch]/document-identity:hidden",
71
+ long: "hidden @[7ch]/document-identity:inline"
72
+ },
73
+ 8: {
74
+ short: "@[8ch]/document-identity:hidden",
75
+ long: "hidden @[8ch]/document-identity:inline"
76
+ },
77
+ 9: {
78
+ short: "@[9ch]/document-identity:hidden",
79
+ long: "hidden @[9ch]/document-identity:inline"
80
+ },
81
+ 10: {
82
+ short: "@[10ch]/document-identity:hidden",
83
+ long: "hidden @[10ch]/document-identity:inline"
84
+ }
85
+ };
86
+ function statuteLabelClasses(length) {
87
+ if (length <= 6) return STATUTE_LABEL_CLASSES[6];
88
+ if (length === 7) return STATUTE_LABEL_CLASSES[7];
89
+ if (length === 8) return STATUTE_LABEL_CLASSES[8];
90
+ if (length === 9) return STATUTE_LABEL_CLASSES[9];
91
+ return STATUTE_LABEL_CLASSES[10];
92
+ }
93
+ //#endregion
94
+ export { DocumentIdentityBadge };
@@ -0,0 +1,25 @@
1
+ //#region src/components/document-identity-badge.logic.d.ts
2
+ export declare const COURT_TIER_WEIGHT: {
3
+ readonly constitutional: "solid";
4
+ readonly supreme: "tinted";
5
+ readonly regional: "outline";
6
+ readonly other: "dashed";
7
+ };
8
+ export type DocumentIdentity = {
9
+ kind: "statute";
10
+ number: string | null;
11
+ year: string | null;
12
+ } | {
13
+ kind: "decision";
14
+ courtAbbreviation: string | null | undefined;
15
+ courtTier?: keyof typeof COURT_TIER_WEIGHT | undefined;
16
+ } | {
17
+ kind: "unknown";
18
+ };
19
+ export declare const statuteIdentityLabels: ({ number, year }: Extract<DocumentIdentity, {
20
+ kind: "statute";
21
+ }>) => {
22
+ short: string;
23
+ long: string;
24
+ } | null;
25
+ //#endregion
@@ -0,0 +1,25 @@
1
+ //#region src/components/document-identity-badge.logic.ts
2
+ const COURT_TIER_WEIGHT = {
3
+ constitutional: "solid",
4
+ supreme: "tinted",
5
+ regional: "outline",
6
+ other: "dashed"
7
+ };
8
+ const statuteIdentityLabels = ({ number, year }) => {
9
+ const ordinal = number?.trim() || null;
10
+ const fullYear = year?.trim() || null;
11
+ if (ordinal === null) return fullYear === null ? null : {
12
+ short: fullYear,
13
+ long: fullYear
14
+ };
15
+ if (fullYear === null) return {
16
+ short: ordinal,
17
+ long: ordinal
18
+ };
19
+ return {
20
+ short: `${ordinal}/${fullYear.slice(-2)}`,
21
+ long: `${ordinal}/${fullYear}`
22
+ };
23
+ };
24
+ //#endregion
25
+ export { COURT_TIER_WEIGHT, statuteIdentityLabels };
@@ -5,7 +5,7 @@ import { VariantProps } from "class-variance-authority";
5
5
  //#region src/components/input-group.d.ts
6
6
  declare const InputGroup: ({ className, ...props }: React$1.ComponentProps<"div">) => React$1.JSX.Element;
7
7
  declare const inputGroupAddonVariants: (props?: ({
8
- align?: "inline-end" | "inline-start" | "block-end" | "block-start" | null | undefined;
8
+ align?: "inline-end" | "inline-start" | "block-start" | "block-end" | null | undefined;
9
9
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
10
10
  declare const InputGroupAddon: ({ className, align, ...props }: React$1.ComponentProps<"div"> & VariantProps<typeof inputGroupAddonVariants>) => React$1.JSX.Element;
11
11
  declare const InputGroupText: ({ className, ...props }: React$1.ComponentProps<"span">) => React$1.JSX.Element;
@@ -25,7 +25,7 @@ const InputOTPSlot = ({ index, className, ...props }) => {
25
25
  ...props,
26
26
  children: [char, hasFakeCaret && /* @__PURE__ */ jsx("div", {
27
27
  className: "pointer-events-none absolute inset-0 flex items-center justify-center",
28
- children: /* @__PURE__ */ jsx("div", { className: "animate-caret-blink bg-foreground h-4 w-px duration-1000" })
28
+ children: /* @__PURE__ */ jsx("div", { className: "animate-caret-blink bg-foreground h-4 w-px" })
29
29
  })]
30
30
  });
31
31
  };
@@ -25,7 +25,7 @@ type LandingGreetingProps = {
25
25
  };
26
26
  export declare const LandingGreeting: ({ icon, children }: LandingGreetingProps) => import("react").JSX.Element;
27
27
  /** A column's heading: an icon and a sentence-case label, as a link or a trigger. */
28
- export declare const LANDING_SECTION_HEADING_CLASS = "text-muted-foreground hover:text-foreground focus-visible:ring-ring flex items-center gap-2 rounded-md px-2 text-sm font-medium outline-none focus-visible:ring-2";
28
+ export declare const LANDING_SECTION_HEADING_CLASS = "text-foreground hover:bg-accent/50 focus-visible:ring-ring [&>svg]:text-muted-foreground flex items-center gap-2 rounded-md px-2 py-0.5 text-sm font-semibold outline-none focus-visible:ring-2";
29
29
  /** A row of a column, as a link or a button. Its text lines up with the
30
30
  * heading's label; the icon slot under the heading icon stays empty unless
31
31
  * the icon identifies the row (a matter's colour). */
@@ -22,7 +22,7 @@ const LandingLayout = ({ actions, hero, children, footer }) => /* @__PURE__ */ j
22
22
  children
23
23
  }),
24
24
  footer !== void 0 && /* @__PURE__ */ jsx(ScrollArea, {
25
- className: "h-auto max-h-[40vh] w-full shrink-0",
25
+ className: "h-auto max-h-[40dvh] w-full shrink-0",
26
26
  scrollFade: true,
27
27
  children: footer
28
28
  })
@@ -40,7 +40,7 @@ const LandingGreeting = ({ icon, children }) => /* @__PURE__ */ jsxs("div", {
40
40
  })]
41
41
  });
42
42
  /** A column's heading: an icon and a sentence-case label, as a link or a trigger. */
43
- const LANDING_SECTION_HEADING_CLASS = "text-muted-foreground hover:text-foreground focus-visible:ring-ring flex items-center gap-2 rounded-md px-2 text-sm font-medium outline-none focus-visible:ring-2";
43
+ const LANDING_SECTION_HEADING_CLASS = "text-foreground hover:bg-accent/50 focus-visible:ring-ring [&>svg]:text-muted-foreground flex items-center gap-2 rounded-md px-2 py-0.5 text-sm font-semibold outline-none focus-visible:ring-2";
44
44
  /** A row of a column, as a link or a button. Its text lines up with the
45
45
  * heading's label; the icon slot under the heading icon stays empty unless
46
46
  * the icon identifies the row (a matter's colour). */
@@ -15,8 +15,14 @@ declare const ListGroupHeader: ({ className, ...props }: React$1.ComponentProps<
15
15
  /** Title and description stack; takes the space the header action leaves. */
16
16
  declare const ListGroupHeading: ({ className, ...props }: React$1.ComponentProps<"div">) => React$1.JSX.Element;
17
17
  declare const ListGroupTitle: ({ className, children, ...props }: React$1.ComponentProps<"h2">) => React$1.JSX.Element;
18
- /** Quiet item count after the title; shows matches while a search filters. */
19
- declare const ListGroupCount: ({ className, ...props }: React$1.ComponentProps<"span">) => React$1.JSX.Element;
18
+ /**
19
+ * Quiet item count after the title; shows matches while a search filters,
20
+ * and a changed digit fades in rather than snapping. `count` arrives
21
+ * formatted in the caller's locale.
22
+ */
23
+ declare const ListGroupCount: ({ className, count, ...props }: Omit<React$1.ComponentProps<"span">, "children"> & {
24
+ count: string;
25
+ }) => React$1.JSX.Element;
20
26
  declare const ListGroupDescription: ({ className, ...props }: React$1.ComponentProps<"p">) => React$1.JSX.Element;
21
27
  /** Quiet trailing affordance on the header row (a link or ghost button). */
22
28
  declare const ListGroupAction: ({ className, ...props }: React$1.ComponentProps<"div">) => React$1.JSX.Element;
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
  import { cn } from "../lib/utils.js";
3
+ import { AnimatedCount } from "./animated-count.js";
3
4
  import { jsx, jsxs } from "react/jsx-runtime";
4
5
  import { mergeProps } from "@base-ui/react/merge-props";
5
6
  import { useRender } from "@base-ui/react/use-render";
@@ -35,11 +36,16 @@ const ListGroupTitle = ({ className, children, ...props }) => /* @__PURE__ */ js
35
36
  ...props,
36
37
  children
37
38
  });
38
- /** Quiet item count after the title; shows matches while a search filters. */
39
- const ListGroupCount = ({ className, ...props }) => /* @__PURE__ */ jsx("span", {
40
- className: cn("text-muted-foreground ms-1.5 font-normal tabular-nums", className),
39
+ /**
40
+ * Quiet item count after the title; shows matches while a search filters,
41
+ * and a changed digit fades in rather than snapping. `count` arrives
42
+ * formatted in the caller's locale.
43
+ */
44
+ const ListGroupCount = ({ className, count, ...props }) => /* @__PURE__ */ jsx("span", {
45
+ className: cn("text-muted-foreground ms-1.5 font-normal", className),
41
46
  "data-slot": "list-group-count",
42
- ...props
47
+ ...props,
48
+ children: /* @__PURE__ */ jsx(AnimatedCount, { value: count })
43
49
  });
44
50
  const ListGroupDescription = ({ className, ...props }) => /* @__PURE__ */ jsx("p", {
45
51
  className: cn("text-muted-foreground text-xs text-pretty", className),
@@ -0,0 +1,16 @@
1
+ //#region src/components/matter-icon.d.ts
2
+ type MatterIconProps = {
3
+ className?: string | undefined;
4
+ } & ({
5
+ matter: {
6
+ id: string;
7
+ color: string | null;
8
+ };
9
+ } | {
10
+ variant: "none" | "all";
11
+ });
12
+ export declare const MatterIcon: (props: MatterIconProps) => import("react").JSX.Element;
13
+ export declare const MattersNavIcon: ({ className }: {
14
+ className?: string | undefined;
15
+ }) => import("react").JSX.Element;
16
+ //#endregion
@@ -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 overscroll-contain 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