@stll/ui 0.34.2 → 0.34.4

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.
@@ -3,6 +3,7 @@ import { CalendarCell, CalendarEntryButton, CalendarEntrySurface, CalendarHeader
3
3
  import { assertConsecutiveCalendarDates, layoutResourceCalendarEntries, nextCalendarDate } from "./resource-calendar.logic.js";
4
4
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
5
  import { useId } from "react";
6
+ import { panic } from "better-result";
6
7
  //#region src/calendar/resource-calendar.tsx
7
8
  const ResourceCalendar = ({ ariaLabel, columns, empty, entries, onSelectEntry, resourceHeader, resources }) => {
8
9
  assertResourceCalendarContract({
@@ -68,7 +69,7 @@ const ResourceCalendar = ({ ariaLabel, columns, empty, entries, onSelectEntry, r
68
69
  const visibleEntriesByColumn = /* @__PURE__ */ new Map();
69
70
  for (const placement of layout.placements) {
70
71
  const entry = resourceEntryById.get(placement.entryId);
71
- if (entry === void 0) throw new TypeError(`Resource calendar layout references unknown entry ${placement.entryId}`);
72
+ if (entry === void 0) panic(`Resource calendar layout references unknown entry ${placement.entryId}`);
72
73
  const existing = visibleEntriesByColumn.get(placement.columnStart);
73
74
  if (existing === void 0) {
74
75
  visibleEntriesByColumn.set(placement.columnStart, [{
@@ -170,7 +171,7 @@ const ResourceCalendarEntryView = ({ entry, labelledBy, onSelectEntry, placement
170
171
  });
171
172
  };
172
173
  const assertUniqueIds = (ids, label) => {
173
- if (new Set(ids).size !== ids.length) throw new TypeError(`${label} ids must be unique`);
174
+ if (new Set(ids).size !== ids.length) panic(`${label} ids must be unique`);
174
175
  };
175
176
  const assertResourceCalendarContract = ({ columns, entries, resources }) => {
176
177
  assertConsecutiveCalendarDates(columns.map(({ date }) => date));
@@ -178,7 +179,7 @@ const assertResourceCalendarContract = ({ columns, entries, resources }) => {
178
179
  assertUniqueIds(entries.map(({ id }) => id), "Resource calendar entry");
179
180
  const resourceIds = new Set(resources.map(({ id }) => id));
180
181
  const orphan = entries.find(({ resourceId }) => !resourceIds.has(resourceId));
181
- if (orphan !== void 0) throw new TypeError(`Resource calendar entry ${orphan.id} references an unknown resource`);
182
+ if (orphan !== void 0) panic(`Resource calendar entry ${orphan.id} references an unknown resource`);
182
183
  };
183
184
  //#endregion
184
185
  export { ResourceCalendar };
@@ -1,4 +1,4 @@
1
- import { Result } from "better-result";
1
+ import { Result, panic } from "better-result";
2
2
  import { Temporal } from "temporal-polyfill/full";
3
3
  //#region src/calendar/resource-calendar.logic.ts
4
4
  const ISO_DATE_PATTERN = /^\d{4}-\d{2}-\d{2}$/u;
@@ -9,13 +9,13 @@ const toPlainDate = (value) => {
9
9
  const differenceInCalendarDays = (later, earlier) => {
10
10
  const laterDate = toPlainDate(later);
11
11
  const earlierDate = toPlainDate(earlier);
12
- if (laterDate === null || earlierDate === null) throw new RangeError("Calendar dates must use normalized YYYY-MM-DD values");
12
+ if (laterDate === null || earlierDate === null) panic("Calendar dates must use normalized YYYY-MM-DD values");
13
13
  return laterDate.since(earlierDate, { largestUnit: "days" }).days;
14
14
  };
15
15
  const getResourceCalendarPlacement = ({ entry, visibleRange }) => {
16
16
  const visibleDayCount = differenceInCalendarDays(visibleRange.endDateExclusive, visibleRange.startDate);
17
17
  const entryDayCount = differenceInCalendarDays(entry.endDateExclusive, entry.startDate);
18
- if (visibleDayCount <= 0 || entryDayCount <= 0) throw new RangeError("Calendar date ranges must be non-empty and half-open");
18
+ if (visibleDayCount <= 0 || entryDayCount <= 0) panic("Calendar date ranges must be non-empty and half-open");
19
19
  if (entry.startDate >= visibleRange.endDateExclusive || entry.endDateExclusive <= visibleRange.startDate) return null;
20
20
  const clippedStart = entry.startDate < visibleRange.startDate ? visibleRange.startDate : entry.startDate;
21
21
  const clippedEnd = entry.endDateExclusive > visibleRange.endDateExclusive ? visibleRange.endDateExclusive : entry.endDateExclusive;
@@ -25,27 +25,27 @@ const getResourceCalendarPlacement = ({ entry, visibleRange }) => {
25
25
  };
26
26
  };
27
27
  const assertConsecutiveCalendarDates = (dates) => {
28
- if (dates.length === 0) throw new RangeError("A resource calendar needs at least one date column");
28
+ if (dates.length === 0) panic("A resource calendar needs at least one date column");
29
29
  const first = dates.at(0);
30
- if (first === void 0 || toPlainDate(first) === null) throw new RangeError("Resource calendar date columns must be consecutive normalized dates");
30
+ if (first === void 0 || toPlainDate(first) === null) panic("Resource calendar date columns must be consecutive normalized dates");
31
31
  for (let index = 1; index < dates.length; index += 1) {
32
32
  const previous = dates.at(index - 1);
33
33
  const current = dates.at(index);
34
- if (previous === void 0 || current === void 0 || differenceInCalendarDays(current, previous) !== 1) throw new RangeError("Resource calendar date columns must be consecutive normalized dates");
34
+ if (previous === void 0 || current === void 0 || differenceInCalendarDays(current, previous) !== 1) panic("Resource calendar date columns must be consecutive normalized dates");
35
35
  }
36
36
  };
37
37
  const nextCalendarDate = (value) => {
38
38
  const date = toPlainDate(value);
39
- if (date === null) throw new RangeError("Calendar dates must use normalized YYYY-MM-DD values");
39
+ if (date === null) panic("Calendar dates must use normalized YYYY-MM-DD values");
40
40
  const nextDate = date.add({ days: 1 }).toString();
41
- if (toPlainDate(nextDate) === null) throw new RangeError("Calendar dates must have a following normalized YYYY-MM-DD value");
41
+ if (toPlainDate(nextDate) === null) panic("Calendar dates must have a following normalized YYYY-MM-DD value");
42
42
  return nextDate;
43
43
  };
44
44
  const layoutResourceCalendarEntries = (entries, visibleRange) => {
45
45
  const visibleEntries = [];
46
46
  const entryIds = /* @__PURE__ */ new Set();
47
47
  for (const entry of entries) {
48
- if (entryIds.has(entry.id)) throw new TypeError("Resource calendar entry ids must be unique");
48
+ if (entryIds.has(entry.id)) panic("Resource calendar entry ids must be unique");
49
49
  entryIds.add(entry.id);
50
50
  const placement = getResourceCalendarPlacement({
51
51
  entry,
@@ -13,8 +13,8 @@ import { VariantProps } from "class-variance-authority";
13
13
  */
14
14
  declare const buttonAccessibleDisabledClass = "cursor-not-allowed opacity-64";
15
15
  declare const buttonVariants: (props?: ({
16
- size?: "sm" | "default" | "lg" | "row" | "icon" | "chip" | "icon-lg" | "icon-sm" | "icon-xl" | "icon-xs" | "xl" | "xs" | null | undefined;
17
- variant?: "link" | "default" | "muted" | "destructive" | "destructive-outline" | "destructive-ghost" | "ghost" | "outline" | "secondary" | null | undefined;
16
+ size?: "default" | "row" | "chip" | "icon" | "icon-lg" | "icon-sm" | "icon-xl" | "icon-xs" | "lg" | "sm" | "xl" | "xs" | null | undefined;
17
+ variant?: "link" | "default" | "destructive" | "destructive-outline" | "destructive-ghost" | "ghost" | "muted" | "outline" | "secondary" | null | undefined;
18
18
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
19
19
  type ButtonProps = {
20
20
  variant?: VariantProps<typeof buttonVariants>["variant"];
@@ -170,8 +170,9 @@ const CustomColorControls = ({ handleInputChange, handlePickerChange, inputHex,
170
170
  })
171
171
  ]
172
172
  })] });
173
- const InlineCustomColor = ({ customSelected, handleInputChange, handlePickerChange, inputHex, label, pickerHex, presets, value }) => {
174
- const customColor = customSelected ? `#${value}` : void 0;
173
+ const InlineCustomColor = ({ customHex, handleInputChange, handlePickerChange, inputHex, label, pickerHex, presets }) => {
174
+ const customSelected = customHex !== null;
175
+ const customColor = customSelected ? `#${customHex}` : void 0;
175
176
  const customGradient = `conic-gradient(${presets.map((preset) => swatchColor(preset)).join(", ")})`;
176
177
  return /* @__PURE__ */ jsxs(Popover.Root, { children: [/* @__PURE__ */ jsx(Popover.Trigger, {
177
178
  render: /* @__PURE__ */ jsx("button", {
@@ -199,11 +200,11 @@ const InlineCustomColor = ({ customSelected, handleInputChange, handlePickerChan
199
200
  })
200
201
  }) })] });
201
202
  };
202
- const ColorPickerContent = ({ value, onSelect, onClear, presets, columns, defaultExpanded, moreLabel, presentation = "popover" }) => {
203
+ const ColorPickerContent = ({ value, onSelect, onClear, presets, columns, defaultExpanded, moreLabel, presentation }) => {
203
204
  const [expanded, setExpanded] = useState(defaultExpanded);
204
205
  const [pickerHex, setPickerHex] = useState(() => (looksLikeHex(value ?? "") ? value : "000000") ?? "000000");
205
206
  const [inputHex, setInputHex] = useState("");
206
- const customSelected = !presets.some((preset) => preset.value === value) && looksLikeHex(value ?? "");
207
+ const customHex = !presets.some((preset) => preset.value === value) && value !== void 0 && looksLikeHex(value) ? value : null;
207
208
  /** Called when the visual picker (SB square / hue strip) emits a color. */
208
209
  const handlePickerChange = (hex) => {
209
210
  const normalized = normalizeHex(hex);
@@ -231,14 +232,13 @@ const ColorPickerContent = ({ value, onSelect, onClear, presets, columns, defaul
231
232
  presentation: "inline",
232
233
  selected: value === preset.value
233
234
  }, preset.value)), /* @__PURE__ */ jsx(InlineCustomColor, {
234
- customSelected,
235
+ customHex,
235
236
  handleInputChange,
236
237
  handlePickerChange,
237
238
  inputHex,
238
239
  label: moreLabel,
239
240
  pickerHex,
240
- presets,
241
- value
241
+ presets
242
242
  })]
243
243
  });
244
244
  return /* @__PURE__ */ jsxs("div", {
@@ -18,7 +18,7 @@ const DestructiveConfirmDialog = ({ cancelLabel, confirmLabel, confirmation, des
18
18
  } catch {}
19
19
  };
20
20
  const handleConfirmClick = () => {
21
- handleConfirm().catch(() => void 0);
21
+ handleConfirm().catch(reportError);
22
22
  };
23
23
  return /* @__PURE__ */ jsx(AlertDialog, {
24
24
  onOpenChange: handleOpenChange,
@@ -88,7 +88,7 @@ const Chevron = ({ open }) => /* @__PURE__ */ jsx(DirectionalIcon, {
88
88
  icon: ChevronRight
89
89
  });
90
90
  const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId, header, collapsedFromLevel, topOffset = 0, panelWidth = 300, ariaLabel = "Outline" }) => {
91
- const [pctById, setPctById] = useState({});
91
+ const [pctById, setPctById] = useState(/* @__PURE__ */ new Map());
92
92
  const [derivedActive, setDerivedActive] = useState(null);
93
93
  const [hovered, setHovered] = useState(false);
94
94
  const [pinned, setPinned] = useState(false);
@@ -133,10 +133,10 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
133
133
  const recalc = useCallback(() => {
134
134
  const container = scrollContainerRef.current;
135
135
  if (!container) return;
136
- const next = {};
136
+ const next = /* @__PURE__ */ new Map();
137
137
  for (const item of items) {
138
138
  const pct = resolvePct(item.id, container);
139
- if (pct !== null) next[item.id] = pct;
139
+ if (pct !== null) next.set(item.id, pct);
140
140
  }
141
141
  setPctById(next);
142
142
  }, [
@@ -324,10 +324,17 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
324
324
  children: node.children.map(renderNode)
325
325
  })] }, `${node.item.id}-${node.index}`);
326
326
  };
327
- const visibleTicks = items.filter((item) => item.id in pctById && item.level <= railLevelCap);
327
+ const visibleTicks = items.flatMap((item) => {
328
+ const pct = pctById.get(item.id);
329
+ return pct !== void 0 && item.level <= railLevelCap ? [{
330
+ item,
331
+ pct
332
+ }] : [];
333
+ });
328
334
  if (items.length < 2 && header === void 0) return null;
329
- const visibleTickIds = new Set(visibleTicks.map((item) => item.id));
330
- const ghostItem = hoveredId !== null && !visibleTickIds.has(hoveredId) && hoveredId in pctById ? items.find((item) => item.id === hoveredId) ?? null : null;
335
+ const visibleTickIds = new Set(visibleTicks.map(({ item }) => item.id));
336
+ const ghostPct = hoveredId !== null && !visibleTickIds.has(hoveredId) ? pctById.get(hoveredId) : void 0;
337
+ const ghostItem = ghostPct === void 0 ? void 0 : items.find((item) => item.id === hoveredId);
331
338
  return /* @__PURE__ */ jsxs("div", {
332
339
  "aria-label": ariaLabel,
333
340
  className: "absolute end-0 z-20 max-lg:hidden",
@@ -361,7 +368,7 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
361
368
  onWheel: (event) => {
362
369
  scrollContainerRef.current?.scrollBy(0, event.deltaY);
363
370
  },
364
- children: [visibleTicks.map((item) => {
371
+ children: [visibleTicks.map(({ item, pct }) => {
365
372
  const isActive = active === item.id;
366
373
  const isHovered = hoveredId === item.id;
367
374
  return /* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsx(TooltipTrigger, { render: /* @__PURE__ */ jsx("button", {
@@ -372,7 +379,7 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
372
379
  onMouseEnter: () => setHoveredId(item.id),
373
380
  onMouseLeave: () => setHoveredId(null),
374
381
  style: {
375
- top: `${pctById[item.id]}%`,
382
+ top: `${pct}%`,
376
383
  transform: "translateY(-50%)",
377
384
  width: isHovered ? tickWidth(item.level) + 8 : tickWidth(item.level),
378
385
  height: tickHeight(isHovered, isActive),
@@ -380,11 +387,11 @@ const OutlineRail = ({ items, scrollContainerRef, resolvePct, onJump, activeId,
380
387
  },
381
388
  type: "button"
382
389
  }) }), /* @__PURE__ */ jsx(TooltipPopup, { children: item.label })] }, item.id);
383
- }), ghostItem && /* @__PURE__ */ jsx("span", {
390
+ }), ghostItem && ghostPct !== void 0 && /* @__PURE__ */ jsx("span", {
384
391
  "aria-hidden": true,
385
392
  className: "absolute end-0 rounded-full opacity-100",
386
393
  style: {
387
- top: `${pctById[ghostItem.id]}%`,
394
+ top: `${ghostPct}%`,
388
395
  transform: "translateY(-50%)",
389
396
  width: tickWidth(ghostItem.level) + 8,
390
397
  height: 4,
@@ -83,7 +83,7 @@ declare const SidebarMenu: ({ className, ...props }: React.ComponentProps<"ul">)
83
83
  declare const SidebarMenuItem: ({ className, ...props }: React.ComponentProps<"li">) => import("react").JSX.Element;
84
84
  declare const sidebarMenuButtonVariants: (props?: ({
85
85
  variant?: "default" | "outline" | null | undefined;
86
- size?: "sm" | "default" | "lg" | "rail" | null | undefined;
86
+ size?: "default" | "lg" | "sm" | "rail" | null | undefined;
87
87
  } & import("class-variance-authority/types").ClassProp) | undefined) => string;
88
88
  declare const SidebarMenuButton: ({ asChild, isActive, variant, size, tooltip, className, ...props }: React.ComponentProps<"button"> & {
89
89
  asChild?: boolean;
@@ -5,6 +5,6 @@ declare const CONTROL_SIZE: Readonly<{
5
5
  readonly lg: "lg";
6
6
  }>;
7
7
  type ControlSize = (typeof CONTROL_SIZE)[keyof typeof CONTROL_SIZE];
8
- declare const CONTROL_SIZES: readonly ("sm" | "default" | "lg")[];
8
+ declare const CONTROL_SIZES: readonly ("default" | "lg" | "sm")[];
9
9
  //#endregion
10
10
  export { CONTROL_SIZE, CONTROL_SIZES, type ControlSize };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stll/ui",
3
- "version": "0.34.2",
3
+ "version": "0.34.4",
4
4
  "description": "Stella's design system: bidi-aware React primitives built on Base UI, the dockable inspector pane, and the Tailwind v4 theme they are styled with.",
5
5
  "keywords": [
6
6
  "base-ui",
@@ -349,6 +349,10 @@
349
349
  "types": "./dist/inspector/index.d.ts",
350
350
  "import": "./dist/inspector/index.js"
351
351
  },
352
+ "./layout-tokens": {
353
+ "types": "./dist/inspector/layout-tokens.d.ts",
354
+ "import": "./dist/inspector/layout-tokens.js"
355
+ },
352
356
  "./kanban": {
353
357
  "types": "./dist/kanban/index.d.ts",
354
358
  "import": "./dist/kanban/index.js"
@@ -607,7 +611,7 @@
607
611
  "test:browser": "playwright test --config playwright.config.ts",
608
612
  "pack:check": "cd ../.. && bun scripts/check-published-exports.ts packages/ui",
609
613
  "typecheck": "bun ../../packages/scripts/src/tsc-native.ts --noEmit",
610
- "lint": "cd ../.. && bun --bun oxlint -c oxlint.config.ts --report-unused-disable-directives-severity=error --deny-warnings --type-aware packages/ui",
614
+ "lint": "cd ../.. && bun --bun oxlint -c oxlint.config.ts --report-unused-disable-directives-severity=error --type-aware packages/ui",
611
615
  "lint:fix": "cd ../.. && bun --bun oxlint -c oxlint.config.ts --type-aware --fix packages/ui",
612
616
  "format": "bun ../../scripts/run-oxfmt.ts .",
613
617
  "prepack": "bun run build"
@@ -619,7 +623,7 @@
619
623
  "input-otp": "^1.5.0",
620
624
  "lucide-react": "1.41.0",
621
625
  "tailwind-merge": "^3.7.0",
622
- "temporal-polyfill": "1.0.4"
626
+ "temporal-polyfill": "1.0.5"
623
627
  },
624
628
  "devDependencies": {
625
629
  "@atlaskit/pragmatic-drag-and-drop": "^3.1.0",