@stll/ui 0.52.1 → 0.54.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 (45) hide show
  1. package/dist/components/alert-dialog.d.ts +1 -1
  2. package/dist/components/alert-dialog.js +2 -2
  3. package/dist/components/bidi-text.d.ts +1 -1
  4. package/dist/components/button.d.ts +33 -3
  5. package/dist/components/button.js +43 -3
  6. package/dist/components/combobox.d.ts +1 -2
  7. package/dist/components/combobox.js +1 -2
  8. package/dist/components/details-grid.js +1 -1
  9. package/dist/components/dialog.d.ts +15 -4
  10. package/dist/components/dialog.js +26 -6
  11. package/dist/components/inline-rename.js +1 -0
  12. package/dist/components/input-group.d.ts +1 -1
  13. package/dist/components/landing.js +27 -21
  14. package/dist/components/menu.d.ts +2 -2
  15. package/dist/components/menu.js +1 -1
  16. package/dist/components/outline-rail.d.ts +1 -1
  17. package/dist/components/outline-rail.js +126 -85
  18. package/dist/components/outline-rail.logic.d.ts +25 -0
  19. package/dist/components/outline-rail.logic.js +40 -0
  20. package/dist/components/popover.js +1 -1
  21. package/dist/components/prose.d.ts +1 -1
  22. package/dist/components/scroll-area.js +2 -2
  23. package/dist/components/sheet.d.ts +1 -1
  24. package/dist/components/sheet.js +2 -2
  25. package/dist/components/sidebar.d.ts +16 -3
  26. package/dist/components/sidebar.js +32 -12
  27. package/dist/components/split-button.d.ts +3 -1
  28. package/dist/components/split-button.js +3 -1
  29. package/dist/components/steps.d.ts +2 -2
  30. package/dist/components/tabs.js +1 -1
  31. package/dist/components/tooltip-trigger-helper.d.ts +5 -0
  32. package/dist/components/tooltip-trigger-helper.js +5 -0
  33. package/dist/icons.d.ts +2 -2
  34. package/dist/icons.js +1 -1
  35. package/dist/index.d.ts +7 -7
  36. package/dist/index.js +5 -5
  37. package/dist/kanban/card-shell.d.ts +14 -1
  38. package/dist/kanban/card-shell.js +33 -4
  39. package/dist/kanban/column-header.js +1 -0
  40. package/dist/kanban/drag-interactions.d.ts +14 -1
  41. package/dist/kanban/drag-interactions.js +19 -7
  42. package/dist/kanban/index.d.ts +3 -3
  43. package/dist/lib/control-size.d.ts +1 -1
  44. package/dist/lib/matter-colors.d.ts +1 -1
  45. package/package.json +5 -3
@@ -5,8 +5,9 @@ import { Tooltip, TooltipContent as TooltipPopup, TooltipTrigger } from "./toolt
5
5
  import { ScrollArea } from "./scroll-area.js";
6
6
  import { DirectionalIcon } from "./directional-icon.js";
7
7
  import "../lib/panel-inset.js";
8
+ import { reconcileOutlineItems, retainOutlineIdSet } from "./outline-rail.logic.js";
8
9
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
- import { useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
10
+ import { memo, useCallback, useEffect, useId, useLayoutEffect, useMemo, useRef, useState } from "react";
10
11
  import { Temporal } from "temporal-polyfill/full";
11
12
  //#region src/components/outline-rail.tsx
12
13
  /**
@@ -90,7 +91,101 @@ const Chevron = ({ open }) => /* @__PURE__ */ jsx(DirectionalIcon, {
90
91
  flip: !open,
91
92
  icon: ChevronRightIcon
92
93
  });
93
- const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId, header, formatMetaLabel, collapsedFromLevel, presentation = "popover", bottomInset, topOffset = 0, panelWidth = 300, ariaLabel = "Outline" }) => {
94
+ const OutlineRowView = ({ item, hasChildren, isCollapsed, isActive, isHovered, indent, metaLabel, onJump, onToggle, onHover }) => /* @__PURE__ */ jsxs("div", {
95
+ className: cn("flex items-center rounded-md pe-2.5", (isActive || isHovered) && "bg-accent"),
96
+ ...isActive ? { [ACTIVE_ROW_ATTRIBUTE]: "" } : {},
97
+ onMouseEnter: () => onHover(item.id),
98
+ onMouseLeave: () => onHover(null),
99
+ children: [
100
+ hasChildren ? /* @__PURE__ */ jsx("button", {
101
+ "aria-expanded": !isCollapsed,
102
+ "aria-label": isCollapsed ? "Expand" : "Collapse",
103
+ className: cn("text-muted-foreground hover:text-foreground flex shrink-0 items-center justify-center", OUTLINE_CONTROL_TARGET_CLASS),
104
+ style: {
105
+ minWidth: 32,
106
+ minHeight: 32,
107
+ marginInlineStart: indent - 4
108
+ },
109
+ onClick: (event) => onToggle(item.id, event.currentTarget.parentElement),
110
+ type: "button",
111
+ children: /* @__PURE__ */ jsx(Chevron, { open: !isCollapsed })
112
+ }) : /* @__PURE__ */ jsx("span", {
113
+ "aria-hidden": true,
114
+ className: "shrink-0",
115
+ style: { width: indent + 4 }
116
+ }),
117
+ item.color && /* @__PURE__ */ jsx("span", {
118
+ "aria-hidden": true,
119
+ className: "me-1.5 size-1.5 shrink-0 rounded-full",
120
+ style: { background: `var(${item.color})` }
121
+ }),
122
+ /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
123
+ render: /* @__PURE__ */ jsx("button", {
124
+ "aria-current": isActive ? "true" : void 0,
125
+ className: cn("flex min-w-0 flex-1 items-baseline gap-1.5 py-1.5 text-start text-[13px] leading-snug", rowTextClass(isActive, item.emphasis ?? OUTLINE_EMPHASIS.primary)),
126
+ onClick: () => onJump(item.id),
127
+ type: "button"
128
+ }),
129
+ children: item.title === void 0 ? /* @__PURE__ */ jsx("span", {
130
+ className: "min-w-0 truncate",
131
+ children: item.label
132
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
133
+ className: "shrink-0 font-medium",
134
+ children: item.label
135
+ }), /* @__PURE__ */ jsx("span", {
136
+ className: "min-w-0 truncate font-normal",
137
+ children: item.title
138
+ })] })
139
+ }), /* @__PURE__ */ jsx(TooltipPopup, { children: outlineEntryText(item) })] }),
140
+ item.meta !== void 0 && (metaLabel !== void 0 ? /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
141
+ render: /* @__PURE__ */ jsx("button", {
142
+ "aria-label": metaLabel,
143
+ className: cn("text-foreground-placeholder text-2xs shrink-0 tabular-nums", OUTLINE_CONTROL_TARGET_CLASS),
144
+ style: {
145
+ minWidth: 32,
146
+ minHeight: 32
147
+ },
148
+ onClick: () => onJump(item.id),
149
+ type: "button"
150
+ }),
151
+ children: item.meta
152
+ }), /* @__PURE__ */ jsx(TooltipPopup, { children: metaLabel })] }) : /* @__PURE__ */ jsx("span", {
153
+ className: "text-foreground-placeholder text-2xs shrink-0 ps-2 tabular-nums",
154
+ children: item.meta
155
+ }))
156
+ ]
157
+ });
158
+ const OutlineRow = memo(OutlineRowView);
159
+ const OutlineTreeView = ({ tree, collapsed, toggled, activeAncestorIds, activeId, hoveredId, maxLevel, formatMetaLabel, onJump, onToggle, onHover }) => {
160
+ const renderNode = (node) => {
161
+ const hasChildren = node.children.length > 0;
162
+ const isCollapsed = collapsed.has(node.item.id) && !(activeAncestorIds.has(node.item.id) && !toggled.has(node.item.id));
163
+ return /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsx(OutlineRow, {
164
+ hasChildren,
165
+ indent: 8 + Math.min(node.item.level, maxLevel) * 12,
166
+ isActive: activeId === node.item.id,
167
+ isCollapsed,
168
+ isHovered: hoveredId === node.item.id,
169
+ item: node.item,
170
+ metaLabel: node.item.meta === void 0 ? void 0 : formatMetaLabel?.(node.item.meta),
171
+ onHover,
172
+ onJump,
173
+ onToggle
174
+ }), hasChildren && !isCollapsed && /* @__PURE__ */ jsx("ul", {
175
+ className: "m-0 list-none p-0",
176
+ children: node.children.map(renderNode)
177
+ })] }, `${node.item.id}-${node.index}`);
178
+ };
179
+ return /* @__PURE__ */ jsx("ul", {
180
+ className: "m-0 list-none p-0 pb-2",
181
+ children: tree.map(renderNode)
182
+ });
183
+ };
184
+ const OutlineTree = memo(OutlineTreeView);
185
+ const OutlineRail = ({ items: itemsProp, scrollContainerRef, resolvePct, onJump, activeId, header, formatMetaLabel, collapsedFromLevel, presentation = "popover", bottomInset, topOffset = 0, panelWidth = 300, ariaLabel = "Outline" }) => {
186
+ const [stableItems, setStableItems] = useState(itemsProp);
187
+ const items = reconcileOutlineItems(stableItems, itemsProp);
188
+ if (items !== stableItems) setStableItems(items);
94
189
  const [pctById, setPctById] = useState(/* @__PURE__ */ new Map());
95
190
  const [derivedActive, setDerivedActive] = useState(null);
96
191
  const [hovered, setHovered] = useState(false);
@@ -109,11 +204,22 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
109
204
  const resolvePctRef = useRef(resolvePct);
110
205
  const onJumpRef = useRef(onJump);
111
206
  const tree = useMemo(() => buildTree(items), [items]);
112
- const [seededItems, setSeededItems] = useState(null);
207
+ const [seededItems, setSeededItems] = useState([]);
113
208
  if (seededItems !== items) {
209
+ const knownIds = new Set(seededItems.map((item) => item.id));
114
210
  setSeededItems(items);
115
- setToggled(/* @__PURE__ */ new Set());
116
- setCollapsed(collapsedFromLevel === void 0 ? /* @__PURE__ */ new Set() : new Set(items.filter((item) => item.level >= collapsedFromLevel).map((item) => item.id)));
211
+ setToggled((previous) => retainOutlineIdSet({
212
+ ids: previous,
213
+ knownIds,
214
+ seed: () => false,
215
+ items
216
+ }));
217
+ setCollapsed((previous) => retainOutlineIdSet({
218
+ ids: previous,
219
+ knownIds,
220
+ seed: (item) => collapsedFromLevel !== void 0 && item.level >= collapsedFromLevel,
221
+ items
222
+ }));
117
223
  }
118
224
  const maxLevel = useMemo(() => {
119
225
  let max = 0;
@@ -210,15 +316,16 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
210
316
  }
211
317
  return ancestors;
212
318
  }, [active, items]);
319
+ const activeControlled = activeId !== void 0;
213
320
  const jumpTo = useCallback((id) => {
214
321
  const container = scrollContainerRef.current;
215
322
  if (!container) return;
216
- if (activeId === void 0) {
323
+ if (!activeControlled) {
217
324
  setDerivedActive(id);
218
325
  manualLockUntil.current = Temporal.Now.instant().epochMilliseconds + 900;
219
326
  }
220
327
  onJumpRef.current(id, container);
221
- }, [activeId, scrollContainerRef]);
328
+ }, [activeControlled, scrollContainerRef]);
222
329
  const toggleCollapse = useCallback((id, rowEl) => {
223
330
  const rowTop = rowEl?.getBoundingClientRect().top;
224
331
  setToggled((prev) => new Set(prev).add(id));
@@ -283,81 +390,6 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
283
390
  if (closeTimer.current !== null) clearTimeout(closeTimer.current);
284
391
  if (openTimer.current !== null) clearTimeout(openTimer.current);
285
392
  }, []);
286
- const renderNode = (node) => {
287
- const hasChildren = node.children.length > 0;
288
- const isCollapsed = collapsed.has(node.item.id) && !(activeAncestorIds.has(node.item.id) && !toggled.has(node.item.id));
289
- const isActive = active === node.item.id;
290
- const isHovered = hoveredId === node.item.id;
291
- const highlighted = isActive || isHovered;
292
- const indent = 8 + Math.min(node.item.level, maxLevel) * 12;
293
- return /* @__PURE__ */ jsxs("li", { children: [/* @__PURE__ */ jsxs("div", {
294
- className: cn("flex items-center rounded-md pe-2.5", highlighted && "bg-accent"),
295
- ...isActive ? { [ACTIVE_ROW_ATTRIBUTE]: "" } : {},
296
- onMouseEnter: () => setHoveredId(node.item.id),
297
- onMouseLeave: () => setHoveredId(null),
298
- children: [
299
- hasChildren ? /* @__PURE__ */ jsx("button", {
300
- "aria-expanded": !isCollapsed,
301
- "aria-label": isCollapsed ? "Expand" : "Collapse",
302
- className: cn("text-muted-foreground hover:text-foreground flex shrink-0 items-center justify-center", OUTLINE_CONTROL_TARGET_CLASS),
303
- style: {
304
- minWidth: 32,
305
- minHeight: 32,
306
- marginInlineStart: indent - 4
307
- },
308
- onClick: (event) => toggleCollapse(node.item.id, event.currentTarget.parentElement),
309
- type: "button",
310
- children: /* @__PURE__ */ jsx(Chevron, { open: !isCollapsed })
311
- }) : /* @__PURE__ */ jsx("span", {
312
- "aria-hidden": true,
313
- className: "shrink-0",
314
- style: { width: indent + 4 }
315
- }),
316
- node.item.color && /* @__PURE__ */ jsx("span", {
317
- "aria-hidden": true,
318
- className: "me-1.5 size-1.5 shrink-0 rounded-full",
319
- style: { background: `var(${node.item.color})` }
320
- }),
321
- /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
322
- render: /* @__PURE__ */ jsx("button", {
323
- "aria-current": isActive ? "true" : void 0,
324
- className: cn("flex min-w-0 flex-1 items-baseline gap-1.5 py-1.5 text-start text-[13px] leading-snug", rowTextClass(isActive, node.item.emphasis ?? OUTLINE_EMPHASIS.primary)),
325
- onClick: () => jumpTo(node.item.id),
326
- type: "button"
327
- }),
328
- children: node.item.title === void 0 ? /* @__PURE__ */ jsx("span", {
329
- className: "min-w-0 truncate",
330
- children: node.item.label
331
- }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", {
332
- className: "shrink-0 font-medium",
333
- children: node.item.label
334
- }), /* @__PURE__ */ jsx("span", {
335
- className: "min-w-0 truncate font-normal",
336
- children: node.item.title
337
- })] })
338
- }), /* @__PURE__ */ jsx(TooltipPopup, { children: outlineEntryText(node.item) })] }),
339
- node.item.meta !== void 0 && (formatMetaLabel ? /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, {
340
- render: /* @__PURE__ */ jsx("button", {
341
- "aria-label": formatMetaLabel(node.item.meta),
342
- className: cn("text-foreground-placeholder text-2xs shrink-0 tabular-nums", OUTLINE_CONTROL_TARGET_CLASS),
343
- style: {
344
- minWidth: 32,
345
- minHeight: 32
346
- },
347
- onClick: () => jumpTo(node.item.id),
348
- type: "button"
349
- }),
350
- children: node.item.meta
351
- }), /* @__PURE__ */ jsx(TooltipPopup, { children: formatMetaLabel(node.item.meta) })] }) : /* @__PURE__ */ jsx("span", {
352
- className: "text-foreground-placeholder text-2xs shrink-0 ps-2 tabular-nums",
353
- children: node.item.meta
354
- }))
355
- ]
356
- }), hasChildren && !isCollapsed && /* @__PURE__ */ jsx("ul", {
357
- className: "m-0 list-none p-0",
358
- children: node.children.map(renderNode)
359
- })] }, `${node.item.id}-${node.index}`);
360
- };
361
393
  const visibleTicks = items.flatMap((item) => {
362
394
  const pct = pctById.get(item.id);
363
395
  return pct !== void 0 && item.level <= railLevelCap ? [{
@@ -483,9 +515,18 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
483
515
  axis: "vertical",
484
516
  className: "min-h-0 flex-1",
485
517
  viewportRef: panelRef,
486
- children: /* @__PURE__ */ jsx("ul", {
487
- className: "m-0 list-none p-0 pb-2",
488
- children: tree.map(renderNode)
518
+ children: /* @__PURE__ */ jsx(OutlineTree, {
519
+ activeAncestorIds,
520
+ activeId: active,
521
+ collapsed,
522
+ formatMetaLabel,
523
+ hoveredId,
524
+ maxLevel,
525
+ onHover: setHoveredId,
526
+ onJump: jumpTo,
527
+ onToggle: toggleCollapse,
528
+ toggled,
529
+ tree
489
530
  })
490
531
  })]
491
532
  })]
@@ -0,0 +1,25 @@
1
+ import { OutlineItem } from "./outline-rail.js";
2
+ //#region src/components/outline-rail.logic.d.ts
3
+ /**
4
+ * `next` with every entry whose content `previous` already holds under the
5
+ * same id replaced by that earlier object, and `previous` itself when nothing
6
+ * changed. Callers rebuild their items from scratch on every change (an
7
+ * editor re-collects its headings after each edit); reconciling keeps an
8
+ * unchanged entry's identity, so only the rows that actually changed render.
9
+ */
10
+ export declare const reconcileOutlineItems: (previous: OutlineItem[], next: OutlineItem[]) => OutlineItem[];
11
+ type RetainKnownIdsOptions = {
12
+ ids: ReadonlySet<string>;
13
+ /** The ids present before `items` replaced them. */
14
+ knownIds: ReadonlySet<string>;
15
+ /** What an id seen for the first time gets. */
16
+ seed: (item: OutlineItem) => boolean;
17
+ items: OutlineItem[];
18
+ };
19
+ /**
20
+ * Carry a per-entry flag set (collapsed, toggled by hand) across an items
21
+ * change: an entry that was already there keeps the reader's state, an entry
22
+ * seen for the first time gets `seed`, and a removed entry drops out.
23
+ */
24
+ export declare const retainOutlineIdSet: ({ ids, knownIds, seed, items }: RetainKnownIdsOptions) => ReadonlySet<string>;
25
+ //#endregion
@@ -0,0 +1,40 @@
1
+ //#region src/components/outline-rail.logic.ts
2
+ const OUTLINE_ITEM_EQUALS = Object.values({
3
+ id: (a, b) => a.id === b.id,
4
+ label: (a, b) => a.label === b.label,
5
+ title: (a, b) => a.title === b.title,
6
+ level: (a, b) => a.level === b.level,
7
+ meta: (a, b) => a.meta === b.meta,
8
+ color: (a, b) => a.color === b.color,
9
+ emphasis: (a, b) => a.emphasis === b.emphasis
10
+ });
11
+ const sameOutlineItem = (a, b) => OUTLINE_ITEM_EQUALS.every((equals) => equals(a, b));
12
+ /**
13
+ * `next` with every entry whose content `previous` already holds under the
14
+ * same id replaced by that earlier object, and `previous` itself when nothing
15
+ * changed. Callers rebuild their items from scratch on every change (an
16
+ * editor re-collects its headings after each edit); reconciling keeps an
17
+ * unchanged entry's identity, so only the rows that actually changed render.
18
+ */
19
+ const reconcileOutlineItems = (previous, next) => {
20
+ if (previous === next) return previous;
21
+ const previousById = /* @__PURE__ */ new Map();
22
+ for (const item of previous) previousById.set(item.id, item);
23
+ const reconciled = next.map((item) => {
24
+ const earlier = previousById.get(item.id);
25
+ return earlier !== void 0 && sameOutlineItem(earlier, item) ? earlier : item;
26
+ });
27
+ return reconciled.length === previous.length && reconciled.every((item, index) => item === previous[index]) ? previous : reconciled;
28
+ };
29
+ /**
30
+ * Carry a per-entry flag set (collapsed, toggled by hand) across an items
31
+ * change: an entry that was already there keeps the reader's state, an entry
32
+ * seen for the first time gets `seed`, and a removed entry drops out.
33
+ */
34
+ const retainOutlineIdSet = ({ ids, knownIds, seed, items }) => {
35
+ const next = /* @__PURE__ */ new Set();
36
+ for (const item of items) if (knownIds.has(item.id) ? ids.has(item.id) : seed(item)) next.add(item.id);
37
+ return next.size === ids.size && [...next].every((id) => ids.has(id)) ? ids : next;
38
+ };
39
+ //#endregion
40
+ export { reconcileOutlineItems, retainOutlineIdSet };
@@ -10,7 +10,7 @@ const PopoverCreateHandle = Popover$1.createHandle;
10
10
  const Popover = Popover$1.Root;
11
11
  function PopoverTrigger({ tooltip, ...props }) {
12
12
  return renderTooltipTrigger({
13
- tooltip: tooltip ?? props["aria-label"] ?? props.title,
13
+ tooltip,
14
14
  trigger: /* @__PURE__ */ jsx(Popover$1.Trigger, {
15
15
  "data-slot": "popover-trigger",
16
16
  ...props
@@ -2,7 +2,7 @@ import * as React$1 from "react";
2
2
  import { VariantProps } from "class-variance-authority";
3
3
  //#region src/components/prose.d.ts
4
4
  declare const proseVariants: (props?: ({
5
- size?: "default" | "sm" | "lede" | null | undefined;
5
+ size?: "default" | "lede" | "sm" | null | undefined;
6
6
  tone?: "default" | "muted" | null | undefined;
7
7
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
8
8
  /** A readable measure for narrative copy; task-shaped text should not use it. */
@@ -8,12 +8,12 @@ const AXIS_VIEWPORT_CLASS = {
8
8
  vertical: "overflow-x-hidden"
9
9
  };
10
10
  const ScrollArea = ({ axis = "both", className, children, scrollFade = false, scrollbarClassName, scrollbarGutter = false, viewportRef, ...props }) => /* @__PURE__ */ jsxs(ScrollArea$1.Root, {
11
- className: cn("size-full min-h-0", className),
11
+ className: cn("flex size-full min-h-0 flex-col", className),
12
12
  ...props,
13
13
  children: [
14
14
  /* @__PURE__ */ jsx(ScrollArea$1.Viewport, {
15
15
  ref: viewportRef,
16
- className: cn("focus-visible:ring-ring focus-visible:ring-offset-background h-full max-h-[inherit] overscroll-contain rounded-[inherit] outline-none focus-visible:ring-2 focus-visible:ring-offset-1 data-has-overflow-x:overscroll-x-contain", scrollFade && "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] [--fade-size:1.5rem]", scrollbarGutter && "data-has-overflow-x:pb-2.5 data-has-overflow-y:pe-2.5", AXIS_VIEWPORT_CLASS[axis]),
16
+ className: cn("focus-visible:ring-ring focus-visible:ring-offset-background min-h-0 flex-1 overscroll-contain rounded-[inherit] outline-none focus-visible:ring-2 focus-visible:ring-offset-1 data-has-overflow-x:overscroll-x-contain", scrollFade && "mask-t-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-start)))] mask-r-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-end)))] mask-b-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-y-end)))] mask-l-from-[calc(100%-min(var(--fade-size),var(--scroll-area-overflow-x-start)))] [--fade-size:1.5rem]", scrollbarGutter && "data-has-overflow-x:pb-2.5 data-has-overflow-y:pe-2.5", AXIS_VIEWPORT_CLASS[axis]),
17
17
  "data-slot": "scroll-area-viewport",
18
18
  children
19
19
  }),
@@ -2,7 +2,7 @@ import * as React$1 from "react";
2
2
  import { Dialog } from "@base-ui/react/dialog";
3
3
  //#region src/components/sheet.d.ts
4
4
  declare const Sheet: <Payload>(props: Dialog.Root.Props<Payload>) => React$1.JSX.Element;
5
- declare const SheetPortal: React$1.ForwardRefExoticComponent<Omit<import("@base-ui/react").AlertDialogPortalProps, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
5
+ declare const SheetPortal: React$1.ForwardRefExoticComponent<Omit<import("@base-ui/react").DialogPortalProps, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
6
6
  type SheetSide = "inline-start" | "inline-end" | "block-start" | "block-end";
7
7
  declare function SheetTrigger({ tooltip, ...props }: Dialog.Trigger.Props & {
8
8
  tooltip?: React$1.ReactNode;
@@ -13,7 +13,7 @@ const Sheet = Dialog.Root;
13
13
  const SheetPortal = Dialog.Portal;
14
14
  function SheetTrigger({ tooltip, ...props }) {
15
15
  return renderTooltipTrigger({
16
- tooltip: tooltip ?? props["aria-label"] ?? props.title,
16
+ tooltip,
17
17
  trigger: /* @__PURE__ */ jsx(Dialog.Trigger, {
18
18
  "data-slot": "sheet-trigger",
19
19
  ...props
@@ -22,7 +22,7 @@ function SheetTrigger({ tooltip, ...props }) {
22
22
  }
23
23
  function SheetClose({ tooltip, ...props }) {
24
24
  return renderTooltipTrigger({
25
- tooltip: tooltip ?? props["aria-label"] ?? props.title,
25
+ tooltip,
26
26
  trigger: /* @__PURE__ */ jsx(Dialog.Close, {
27
27
  "data-slot": "sheet-close",
28
28
  ...props
@@ -60,7 +60,20 @@ declare const Sidebar: ({ side, variant, collapsible, mobileTitle, mobileDescrip
60
60
  /** Screen-reader description for the mobile sheet. Override to localize. */
61
61
  mobileDescription?: string;
62
62
  }) => import("react").JSX.Element;
63
- declare const SidebarTrigger: ({ className, onClick, label, "aria-label": ariaLabel, ...props }: React.ComponentProps<typeof Button> & {
63
+ type PanelTriggerSide = "left" | "right";
64
+ /**
65
+ * Toolbar toggle for a panel docked at one edge. `SidebarTrigger` is the
66
+ * `"left"` one; a host panel docked at the opposite edge (an inspector)
67
+ * renders the `"right"` one, so the two buttons stay mirror images instead
68
+ * of two hand-kept copies of the same chrome. `side` is logical, like
69
+ * `Sidebar`'s.
70
+ */
71
+ declare const PanelTrigger: ({ side, label, "aria-label": ariaLabel, ...props }: Omit<React.ComponentProps<typeof Button>, "children"> & {
72
+ side: PanelTriggerSide;
73
+ /** Accessible name for the trigger. */
74
+ label: string;
75
+ }) => import("react").JSX.Element;
76
+ declare const SidebarTrigger: ({ onClick, label, ...props }: Omit<React.ComponentProps<typeof PanelTrigger>, "label" | "side"> & {
64
77
  /** Accessible name for the trigger. Override to localize. */
65
78
  label?: string;
66
79
  }) => import("react").JSX.Element;
@@ -86,7 +99,7 @@ declare const SidebarMenu: ({ className, ...props }: React.ComponentProps<"ul">)
86
99
  declare const SidebarMenuItem: ({ className, ...props }: React.ComponentProps<"li">) => import("react").JSX.Element;
87
100
  declare const sidebarMenuButtonVariants: (props?: ({
88
101
  variant?: "default" | "outline" | null | undefined;
89
- size?: "default" | "sm" | "lg" | "rail" | null | undefined;
102
+ size?: "default" | "lg" | "rail" | "sm" | null | undefined;
90
103
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
91
104
  /**
92
105
  * The footer's avatar trigger, signed in (user menu) or not (sign-in): a
@@ -120,4 +133,4 @@ declare const SidebarMenuSubButton: ({ asChild, size, isActive, className, ...pr
120
133
  isActive?: boolean;
121
134
  }) => import("react").JSX.Element;
122
135
  //#endregion
123
- export { Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, sidebarIdentityTriggerClassName, useSidebar, useSidebarInlineSize, useSidebarLayout };
136
+ export { PanelTrigger, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, sidebarIdentityTriggerClassName, useSidebar, useSidebarInlineSize, useSidebarLayout };
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { PanelLeftIcon } from "../icons.js";
2
+ import { PanelLeftIcon, PanelRightIcon } from "../icons.js";
3
3
  import { cn } from "../lib/utils.js";
4
4
  import { Tooltip, TooltipContent as TooltipPopup, TooltipTrigger } from "./tooltip.js";
5
5
  import { useIsMobile } from "../hooks/use-mobile.js";
@@ -12,8 +12,8 @@ import { deriveSidebarState, nextOpenMobile, nextRequestedOpen, resolveSidebarLa
12
12
  import { Skeleton } from "./skeleton.js";
13
13
  import { jsx, jsxs } from "react/jsx-runtime";
14
14
  import { createContext, use, useId, useState } from "react";
15
- import { cva } from "class-variance-authority";
16
15
  import { panic } from "better-result";
16
+ import { cva } from "class-variance-authority";
17
17
  //#region src/components/sidebar.tsx
18
18
  /**
19
19
  * Inline size the sidebar takes out of the layout row when expanded, in CSS
@@ -157,24 +157,44 @@ const Sidebar = ({ side = "left", variant = "sidebar", collapsible = "offcanvas"
157
157
  })]
158
158
  });
159
159
  };
160
- const SidebarTrigger = ({ className, onClick, label = DEFAULT_TOGGLE_LABEL, "aria-label": ariaLabel, ...props }) => {
161
- const { toggleSidebar } = useSidebar();
160
+ const PANEL_TRIGGER_ICONS = {
161
+ left: PanelLeftIcon,
162
+ right: PanelRightIcon
163
+ };
164
+ /**
165
+ * Toolbar toggle for a panel docked at one edge. `SidebarTrigger` is the
166
+ * `"left"` one; a host panel docked at the opposite edge (an inspector)
167
+ * renders the `"right"` one, so the two buttons stay mirror images instead
168
+ * of two hand-kept copies of the same chrome. `side` is logical, like
169
+ * `Sidebar`'s.
170
+ */
171
+ const PanelTrigger = ({ side, label, "aria-label": ariaLabel, ...props }) => {
172
+ const Icon = PANEL_TRIGGER_ICONS[side];
162
173
  return /* @__PURE__ */ jsxs(Button, {
163
174
  "aria-label": ariaLabel ?? label,
164
- className: cn("size-7", className),
175
+ "data-slot": "panel-trigger",
176
+ size: "icon",
177
+ variant: "ghost",
178
+ ...props,
179
+ className: cn("size-7", props.className),
180
+ children: [/* @__PURE__ */ jsx(Icon, {}), /* @__PURE__ */ jsx("span", {
181
+ className: "sr-only",
182
+ children: label
183
+ })]
184
+ });
185
+ };
186
+ const SidebarTrigger = ({ onClick, label = DEFAULT_TOGGLE_LABEL, ...props }) => {
187
+ const { toggleSidebar } = useSidebar();
188
+ return /* @__PURE__ */ jsx(PanelTrigger, {
165
189
  "data-sidebar": "trigger",
166
190
  "data-slot": "sidebar-trigger",
191
+ label,
167
192
  onClick: (event) => {
168
193
  onClick?.(event);
169
194
  toggleSidebar();
170
195
  },
171
- size: "icon",
172
- variant: "ghost",
173
196
  ...props,
174
- children: [/* @__PURE__ */ jsx(PanelLeftIcon, {}), /* @__PURE__ */ jsx("span", {
175
- className: "sr-only",
176
- children: label
177
- })]
197
+ side: "left"
178
198
  });
179
199
  };
180
200
  const SidebarRail = ({ className, label = DEFAULT_TOGGLE_LABEL, "aria-label": ariaLabel, ...props }) => {
@@ -373,4 +393,4 @@ const SidebarMenuSubButton = ({ asChild = false, size = "md", isActive = false,
373
393
  });
374
394
  };
375
395
  //#endregion
376
- export { SIDEBAR_WIDTH_ICON_PX, SIDEBAR_WIDTH_PX, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, sidebarIdentityTriggerClassName, useSidebar, useSidebarInlineSize, useSidebarLayout };
396
+ export { PanelTrigger, SIDEBAR_WIDTH_ICON_PX, SIDEBAR_WIDTH_PX, Sidebar, SidebarContent, SidebarFooter, SidebarGroup, SidebarGroupAction, SidebarGroupContent, SidebarGroupLabel, SidebarHeader, SidebarInput, SidebarInset, SidebarMenu, SidebarMenuAction, SidebarMenuBadge, SidebarMenuButton, SidebarMenuItem, SidebarMenuSkeleton, SidebarMenuSub, SidebarMenuSubButton, SidebarMenuSubItem, SidebarProvider, SidebarRail, SidebarSeparator, SidebarTrigger, sidebarIdentityTriggerClassName, useSidebar, useSidebarInlineSize, useSidebarLayout };
@@ -1,4 +1,4 @@
1
- import { Button } from "./button.js";
1
+ import { BUTTON_CONTENT, Button } from "./button.js";
2
2
  import { DropdownMenu as Menu } from "./menu.js";
3
3
  import { Popover } from "./popover.js";
4
4
  import { ComponentProps, ReactNode } from "react";
@@ -16,9 +16,11 @@ type SplitButtonSharedProps = {
16
16
  };
17
17
  type SplitButtonPrimary = {
18
18
  children: ReactNode;
19
+ primaryContent: (typeof BUTTON_CONTENT)[keyof typeof BUTTON_CONTENT];
19
20
  primaryLabel: string;
20
21
  } | {
21
22
  children: string;
23
+ primaryContent?: never;
22
24
  primaryLabel?: never;
23
25
  };
24
26
  type SplitButtonProps = SplitButtonSharedProps & SplitButtonPrimary & ({
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { ChevronDownIcon } from "../icons.js";
3
3
  import { cn } from "../lib/utils.js";
4
- import { Button } from "./button.js";
4
+ import { BUTTON_CONTENT, Button } from "./button.js";
5
5
  import { Popover, PopoverTrigger } from "./popover.js";
6
6
  import { DropdownMenu as Menu, DropdownMenuTrigger as MenuTrigger } from "./menu.js";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -12,6 +12,7 @@ const SplitButton = (props) => {
12
12
  const triggerButton = /* @__PURE__ */ jsx(Button, {
13
13
  className: cn("text-muted-foreground group-hover/split:bg-muted group-focus-within/split:bg-muted hover:bg-accent hover:text-foreground active:bg-accent/80 data-popup-open:bg-accent rounded-md [&_svg]:mx-0", size === "sm" ? "h-7 w-3.5 px-0 sm:h-7" : "h-8 w-4 px-0 sm:h-8", "pointer-coarse:min-h-11 pointer-coarse:min-w-11"),
14
14
  size: "sm",
15
+ tooltip: menuLabel,
15
16
  variant: "ghost"
16
17
  });
17
18
  const controls = /* @__PURE__ */ jsxs("div", {
@@ -25,6 +26,7 @@ const SplitButton = (props) => {
25
26
  disabled: primaryDisabled,
26
27
  onClick: onPrimaryClick,
27
28
  size: "sm",
29
+ tooltip: props.primaryContent === BUTTON_CONTENT.icon ? primaryLabel : void 0,
28
30
  variant: "ghost",
29
31
  children
30
32
  }), props.surface === "popover" ? /* @__PURE__ */ jsx(PopoverTrigger, {
@@ -2,7 +2,7 @@ import * as React$1 from "react";
2
2
  import { VariantProps } from "class-variance-authority";
3
3
  //#region src/components/steps.d.ts
4
4
  declare const stepsVariants: (props?: ({
5
- variant?: "progress" | "default" | "compact" | null | undefined;
5
+ variant?: "compact" | "default" | "progress" | null | undefined;
6
6
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
7
7
  type StepsProps = React$1.ComponentProps<"ol"> & VariantProps<typeof stepsVariants> & {
8
8
  markerStyle: NonNullable<React$1.CSSProperties["listStyleType"]>;
@@ -10,7 +10,7 @@ type StepsProps = React$1.ComponentProps<"ol"> & VariantProps<typeof stepsVarian
10
10
  /** A meaningful procedure, timeline, or ranking. The list owns its numbers. */
11
11
  declare const Steps: ({ className, variant, markerStyle, style, ...props }: StepsProps) => React$1.JSX.Element;
12
12
  declare const stepVariants: (props?: ({
13
- status?: "current" | "completed" | "upcoming" | null | undefined;
13
+ status?: "completed" | "current" | "upcoming" | null | undefined;
14
14
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
15
15
  type StepProps = React$1.ComponentProps<"li"> & VariantProps<typeof stepVariants>;
16
16
  declare const Step: ({ className, status, ...props }: StepProps) => React$1.JSX.Element;
@@ -22,7 +22,7 @@ const TabsList = ({ variant = "default", className, children, ref, ...props }) =
22
22
  if (hasTabOrderChanged(previousTabs, tabs)) setRemeasureToken((token) => token + 1);
23
23
  });
24
24
  return /* @__PURE__ */ jsxs(Tabs$1.List, {
25
- className: cn("text-muted-foreground relative z-0 flex w-fit max-w-full items-center justify-center-safe gap-x-0.5", "scrollbar-none overflow-auto data-[orientation=horizontal]:overscroll-x-contain data-[orientation=vertical]:overscroll-y-contain [&::-webkit-scrollbar]:hidden", "data-[orientation=vertical]:flex-col", variant === "default" ? "bg-muted text-foreground-label rounded-lg p-0.5" : "*:data-[slot=tabs-tab]:hover:bg-accent data-[orientation=horizontal]:py-1 data-[orientation=vertical]:px-1", className),
25
+ className: cn("text-muted-foreground relative z-0 flex w-fit max-w-full items-center justify-center-safe gap-x-0.5", "scrollbar-none overflow-auto data-[orientation=horizontal]:overflow-y-hidden data-[orientation=horizontal]:overscroll-x-contain data-[orientation=vertical]:overflow-x-hidden data-[orientation=vertical]:overscroll-y-contain [&::-webkit-scrollbar]:hidden", "data-[orientation=vertical]:flex-col", variant === "default" ? "bg-muted text-foreground-label rounded-lg p-0.5" : "*:data-[slot=tabs-tab]:hover:bg-accent data-[orientation=horizontal]:py-1 data-[orientation=vertical]:px-1", className),
26
26
  "data-slot": "tabs-list",
27
27
  ref: composeRefs(listRef, ref),
28
28
  ...props,
@@ -4,6 +4,11 @@ type TooltipTriggerOptions = {
4
4
  trigger: React$1.ReactElement;
5
5
  tooltip: React$1.ReactNode;
6
6
  };
7
+ /**
8
+ * Overlay triggers pass only an explicit `tooltip`. They cannot tell whether
9
+ * their content is a label or a glyph; a trigger rendered as `Button` hands its
10
+ * `aria-label` to that Button, whose size decides (see `BUTTON_SIZE_CONTENT`).
11
+ */
7
12
  declare const renderTooltipTrigger: ({ trigger, tooltip }: TooltipTriggerOptions) => React$1.JSX.Element;
8
13
  //#endregion
9
14
  export { renderTooltipTrigger };
@@ -3,6 +3,11 @@ import { hasTooltipContent } from "../lib/tooltip-content.js";
3
3
  import { Tooltip, TooltipContent as TooltipPopup, TooltipTrigger } from "./tooltip.js";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  //#region src/components/tooltip-trigger-helper.tsx
6
+ /**
7
+ * Overlay triggers pass only an explicit `tooltip`. They cannot tell whether
8
+ * their content is a label or a glyph; a trigger rendered as `Button` hands its
9
+ * `aria-label` to that Button, whose size decides (see `BUTTON_SIZE_CONTENT`).
10
+ */
6
11
  const renderTooltipTrigger = ({ trigger, tooltip }) => {
7
12
  if (!hasTooltipContent(tooltip)) return trigger;
8
13
  return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: trigger }), /* @__PURE__ */ jsx(TooltipPopup, { children: tooltip })] });