@parallelworks/ui 0.87.0 → 0.88.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.
@@ -57,9 +57,12 @@ function useContextMenu() {
57
57
  const onWheel = (event) => {
58
58
  if (!(event.target instanceof Element && event.target.closest("[role=\"menu\"]"))) closeMenu();
59
59
  };
60
+ const onBlur = (event) => {
61
+ if (!(event.target instanceof Node)) closeMenu();
62
+ };
60
63
  const listeners = [];
61
64
  if (closeOn.includes("wheel")) listeners.push(["wheel", onWheel]);
62
- if (closeOn.includes("blur")) listeners.push(["blur", closeMenu]);
65
+ if (closeOn.includes("blur")) listeners.push(["blur", onBlur]);
63
66
  if (closeOn.includes("resize")) listeners.push(["resize", closeMenu]);
64
67
  for (const [type, listener] of listeners) window.addEventListener(type, listener, {
65
68
  capture: true,
@@ -84,29 +84,44 @@ export declare function ChartTooltipContent({ label, items, formatValue, total,
84
84
  export interface ChartTooltipPlacement {
85
85
  /** The tooltip's left edge. */
86
86
  left: number;
87
- side: 'left' | 'right';
88
- /** A width to hold it to when neither side has room for all of it, its names truncating. */
87
+ /** The tooltip's top edge: `top` beside the crosshair, or above the plot when it goes there. */
88
+ top: number;
89
+ /** Beside the crosshair to its left or right, or above the plot when neither side has room. */
90
+ side: 'left' | 'right' | 'above';
91
+ /** A width to hold it to, its names truncating, when its side has room for less than all of
92
+ * it. */
89
93
  maxWidth: number | null;
90
94
  }
91
- /** Where a chart's tooltip goes beside a crosshair at `x`, so it never covers the column under
92
- * the pointer: `gap` to the `prefer` side, or the other when it doesn't fit between `x` and
93
- * that edge, or held to the wider side when neither fits. `start` and `end` bound it, in the
94
- * same coordinates as `x`, such as the chart's box or the viewport. A charting library's
95
- * tooltip takes `left` as its position. */
96
- export declare function chartTooltipPlacement({ x, width, start, end, gap, prefer, }: {
95
+ /** Where a chart's tooltip goes so it never covers the point being read: `gap` beside the
96
+ * crosshair at `x`, on the side with more room, so it flips sides as the crosshair passes the
97
+ * middle, and held narrower when that side is short, its names truncating.
98
+ * Only when that side is narrower still does it go above the plot, clear of every point,
99
+ * where the chart must leave it room. `start` and `end`
100
+ * bound it in the same coordinates as `x`, such as the chart's own box, so it stays inside
101
+ * the card, and `top` is the plot's top. A charting library's tooltip takes `left` and `top`
102
+ * as its position. */
103
+ export declare function chartTooltipPlacement({ x, width, height, start, end, top, gap, minWidth, prefer, }: {
97
104
  x: number;
98
105
  width: number;
106
+ /** The tooltip's height, for placing it above the plot. */
107
+ height?: number | undefined;
99
108
  start: number;
100
109
  end: number;
110
+ top?: number | undefined;
101
111
  gap?: number | undefined;
112
+ /** The narrowest it is held to beside the crosshair before it goes above instead. */
113
+ minWidth?: number | undefined;
114
+ /** The side it takes when both have the same room, such as the left in a right-to-left
115
+ * layout. */
102
116
  prefer?: 'left' | 'right' | undefined;
103
117
  }): ChartTooltipPlacement;
104
118
  /** Holds a chart's tooltip beside its crosshair, inside a positioned box around the plot: `x`
105
- * is the crosshair in that box, and the tooltip sits `top` from its top, to the crosshair's
106
- * inline end, or its start where the box has no room. When neither side of the box has room,
107
- * as on a phone, it may reach past the box to the viewport's edge, and is held narrower when
108
- * even that is too small. It never covers the hovered column or the pointer on it. Nothing
109
- * shows while `x` is null. */
119
+ * is the crosshair in that box, and the tooltip sits `top` from its top on the side with more
120
+ * room, flipping as the crosshair passes the middle, held narrower and tighter when that side
121
+ * is short, and above the plot only when it is shorter still. It stays inside the box's width
122
+ * and never covers the point being read or the pointer on it. Nothing shows while `x` is null.
123
+ * On a narrow chart, such as on a phone, `ChartTooltipFrame` docks the reading in a readout
124
+ * above the plot instead. */
110
125
  export declare function ChartTooltipBeside({ x, top, gap, className, children, }: {
111
126
  x: number | null;
112
127
  top?: number | undefined;
@@ -114,3 +129,36 @@ export declare function ChartTooltipBeside({ x, top, gap, className, children, }
114
129
  className?: string | undefined;
115
130
  children: ReactNode;
116
131
  }): import("react").JSX.Element | null;
132
+ /** A point's reading: what a chart's tooltip or readout shows for it. */
133
+ export interface ChartReading {
134
+ label?: ReactNode;
135
+ items: readonly ChartTooltipItem[];
136
+ /** Formats each value and the total; numbers in the host's locale by default. */
137
+ formatValue?: ((n: number) => string) | undefined;
138
+ /** Adds the total; on by default when there is more than one series. */
139
+ total?: boolean | undefined;
140
+ highlighted?: string | null | undefined;
141
+ }
142
+ /** A point's reading docked above a chart's plot, for a chart too narrow for a tooltip beside
143
+ * its crosshair: the label and total on one line, then each series' swatch, name and value
144
+ * in two columns. Every series keeps its place and its row, zero or not, so the readout keeps
145
+ * its height and order as the reading changes. */
146
+ export declare function ChartReadout({ label, items, formatValue, total, highlighted, className, }: ChartReading & {
147
+ className?: string | undefined;
148
+ }): import("react").JSX.Element;
149
+ /** A chart's plot with its reading, placed for its width: on a wide chart a tooltip beside the
150
+ * crosshair at `x` while a point is hovered, and under 28rem, as on a phone, a readout docked
151
+ * above the plot that never covers it or its legend, showing `rest`, such as the latest point,
152
+ * while none is. `children` is the plot, and `x` the crosshair in it, or null. The app positions
153
+ * nothing: the frame measures itself through a container query. */
154
+ export declare function ChartTooltipFrame({ x, reading, rest, top, gap, className, children, }: {
155
+ x: number | null;
156
+ /** The hovered point's reading, or null while none is. */
157
+ reading: ChartReading | null;
158
+ /** What the readout shows while no point is hovered, so it keeps its place. */
159
+ rest: ChartReading;
160
+ top?: number | undefined;
161
+ gap?: number | undefined;
162
+ className?: string | undefined;
163
+ children: ReactNode;
164
+ }): import("react").JSX.Element;
@@ -1,4 +1,5 @@
1
1
  import { cx } from "../cx.js";
2
+ import { surfaceClassName } from "./floating.js";
2
3
  import { useLocale, useStrings } from "./Provider.js";
3
4
  import { CATEGORY_CLASSES } from "./category.js";
4
5
  import { useCallback, useLayoutEffect, useMemo, useRef, useState } from "react";
@@ -136,7 +137,7 @@ function ChartTooltipContent({ label, items, formatValue, total, highlighted, hi
136
137
  const sum = rows.reduce((acc, item) => acc + item.value, 0);
137
138
  const showTotal = total ?? rows.length > 1;
138
139
  return /* @__PURE__ */ jsxs("div", {
139
- className: cx("min-w-[var(--chart-tooltip-min,10rem)] rounded-lg border border-(--theme-border) bg-(--theme-panel-bg) p-3 text-xs text-(--theme-app) shadow-(--theme-shadow-lg)", className),
140
+ className: cx("relative isolate z-10 min-w-[var(--chart-tooltip-min,10rem)] p-(--chart-tooltip-pad,0.75rem) text-xs", surfaceClassName, className),
140
141
  style,
141
142
  children: [
142
143
  label != null && label !== "" && /* @__PURE__ */ jsx("div", {
@@ -170,37 +171,46 @@ function ChartTooltipContent({ label, items, formatValue, total, highlighted, hi
170
171
  ]
171
172
  });
172
173
  }
173
- /** Where a chart's tooltip goes beside a crosshair at `x`, so it never covers the column under
174
- * the pointer: `gap` to the `prefer` side, or the other when it doesn't fit between `x` and
175
- * that edge, or held to the wider side when neither fits. `start` and `end` bound it, in the
176
- * same coordinates as `x`, such as the chart's box or the viewport. A charting library's
177
- * tooltip takes `left` as its position. */
178
- function chartTooltipPlacement({ x, width, start, end, gap = 12, prefer = "right" }) {
174
+ /** Where a chart's tooltip goes so it never covers the point being read: `gap` beside the
175
+ * crosshair at `x`, on the side with more room, so it flips sides as the crosshair passes the
176
+ * middle, and held narrower when that side is short, its names truncating.
177
+ * Only when that side is narrower still does it go above the plot, clear of every point,
178
+ * where the chart must leave it room. `start` and `end`
179
+ * bound it in the same coordinates as `x`, such as the chart's own box, so it stays inside
180
+ * the card, and `top` is the plot's top. A charting library's tooltip takes `left` and `top`
181
+ * as its position. */
182
+ function chartTooltipPlacement({ x, width, height = 0, start, end, top = 0, gap = 12, minWidth = 96, prefer = "right" }) {
179
183
  const room = {
180
184
  left: x - gap - start,
181
185
  right: end - x - gap
182
186
  };
183
- let side = (prefer === "right" ? ["right", "left"] : ["left", "right"]).find((s) => width <= room[s]);
184
- let maxWidth = null;
185
- if (side === void 0) {
186
- side = room.right >= room.left ? "right" : "left";
187
- maxWidth = Math.max(0, room[side]);
187
+ const side = room.left === room.right ? prefer : room.right > room.left ? "right" : "left";
188
+ if (room[side] >= Math.min(width, minWidth)) {
189
+ const maxWidth = width <= room[side] ? null : room[side];
190
+ const shown = maxWidth ?? width;
191
+ return {
192
+ left: side === "right" ? x + gap : x - gap - shown,
193
+ top,
194
+ side,
195
+ maxWidth
196
+ };
188
197
  }
189
- const shown = maxWidth ?? width;
198
+ const span = Math.max(0, end - start);
199
+ const shown = Math.min(width, span);
190
200
  return {
191
- left: side === "right" ? x + gap : x - gap - shown,
192
- side,
193
- maxWidth
201
+ left: Math.min(Math.max(x - shown / 2, start), end - shown),
202
+ top: top - height - gap,
203
+ side: "above",
204
+ maxWidth: width > span ? span : null
194
205
  };
195
206
  }
196
- /** Keeps a tooltip clear of the viewport's edge by this much. */
197
- var VIEWPORT_MARGIN = 8;
198
207
  /** Holds a chart's tooltip beside its crosshair, inside a positioned box around the plot: `x`
199
- * is the crosshair in that box, and the tooltip sits `top` from its top, to the crosshair's
200
- * inline end, or its start where the box has no room. When neither side of the box has room,
201
- * as on a phone, it may reach past the box to the viewport's edge, and is held narrower when
202
- * even that is too small. It never covers the hovered column or the pointer on it. Nothing
203
- * shows while `x` is null. */
208
+ * is the crosshair in that box, and the tooltip sits `top` from its top on the side with more
209
+ * room, flipping as the crosshair passes the middle, held narrower and tighter when that side
210
+ * is short, and above the plot only when it is shorter still. It stays inside the box's width
211
+ * and never covers the point being read or the pointer on it. Nothing shows while `x` is null.
212
+ * On a narrow chart, such as on a phone, `ChartTooltipFrame` docks the reading in a readout
213
+ * above the plot instead. */
204
214
  function ChartTooltipBeside({ x, top = 0, gap = 12, className, children }) {
205
215
  const ref = useRef(null);
206
216
  useLayoutEffect(() => {
@@ -208,33 +218,110 @@ function ChartTooltipBeside({ x, top = 0, gap = 12, className, children }) {
208
218
  if (!el || x === null) return;
209
219
  el.style.maxWidth = "";
210
220
  el.style.removeProperty("--chart-tooltip-min");
221
+ el.style.removeProperty("--chart-tooltip-pad");
211
222
  const width = el.offsetWidth;
212
- const box = el.offsetParent;
213
- const origin = box ? box.getBoundingClientRect().left + box.clientLeft : 0;
214
- const view = document.documentElement.clientWidth || window.innerWidth;
215
- const place = (start, end) => chartTooltipPlacement({
223
+ const end = el.offsetParent?.clientWidth || document.documentElement.clientWidth || window.innerWidth;
224
+ const placement = chartTooltipPlacement({
216
225
  x,
217
226
  width,
218
- start,
227
+ height: el.offsetHeight,
228
+ start: 0,
219
229
  end,
230
+ top,
220
231
  gap,
221
232
  prefer: getComputedStyle(el).direction === "rtl" ? "left" : "right"
222
233
  });
223
- let placement = box ? place(0, box.clientWidth) : null;
224
- if (placement === null || placement.maxWidth !== null) placement = place(VIEWPORT_MARGIN - origin, view - VIEWPORT_MARGIN - origin);
225
234
  el.style.left = `${placement.left}px`;
235
+ el.style.top = `${placement.top}px`;
236
+ el.dataset["side"] = placement.side;
226
237
  if (placement.maxWidth !== null) {
227
238
  el.style.maxWidth = `${placement.maxWidth}px`;
228
239
  el.style.setProperty("--chart-tooltip-min", "0px");
240
+ el.style.setProperty("--chart-tooltip-pad", "0.5rem");
229
241
  }
230
242
  });
231
243
  if (x === null) return null;
232
244
  return /* @__PURE__ */ jsx("div", {
233
245
  ref,
234
246
  className: cx("pointer-events-none absolute z-10 w-max", className),
235
- style: { top },
236
247
  children
237
248
  });
238
249
  }
250
+ /** A point's reading docked above a chart's plot, for a chart too narrow for a tooltip beside
251
+ * its crosshair: the label and total on one line, then each series' swatch, name and value
252
+ * in two columns. Every series keeps its place and its row, zero or not, so the readout keeps
253
+ * its height and order as the reading changes. */
254
+ function ChartReadout({ label, items, formatValue, total, highlighted, className }) {
255
+ const { chart: t } = useStrings();
256
+ const locale = useLocale();
257
+ const number = useMemo(() => new Intl.NumberFormat(locale, { maximumFractionDigits: 2 }), [locale]);
258
+ const format = formatValue ?? ((n) => number.format(n));
259
+ const sum = items.reduce((acc, item) => acc + item.value, 0);
260
+ const showTotal = total ?? items.length > 1;
261
+ return /* @__PURE__ */ jsxs("div", {
262
+ "data-chart-readout": "",
263
+ className: cx("rounded-lg border border-(--theme-border) bg-(--theme-muted-panel-bg)/50 px-2 py-1.5 text-xs text-(--theme-app)", className),
264
+ children: [/* @__PURE__ */ jsxs("div", {
265
+ className: "mb-1 flex items-baseline justify-between gap-3",
266
+ children: [/* @__PURE__ */ jsx("span", {
267
+ className: "min-w-0 truncate font-semibold",
268
+ children: label
269
+ }), showTotal && /* @__PURE__ */ jsxs("span", {
270
+ className: "shrink-0 tabular-nums",
271
+ children: [
272
+ /* @__PURE__ */ jsx("span", {
273
+ className: "text-(--theme-muted-text-color)",
274
+ children: t.total
275
+ }),
276
+ " ",
277
+ /* @__PURE__ */ jsx("span", {
278
+ className: "font-semibold",
279
+ children: format(sum)
280
+ })
281
+ ]
282
+ })]
283
+ }), /* @__PURE__ */ jsx("ul", {
284
+ className: "m-0 grid list-none grid-cols-2 gap-x-2.5 gap-y-0.5 p-0",
285
+ children: items.map((item) => /* @__PURE__ */ jsxs("li", {
286
+ className: cx("flex min-w-0 items-center gap-1", highlighted === item.key ? "font-semibold" : highlighted != null && "text-(--theme-muted-text-color)"),
287
+ children: [
288
+ /* @__PURE__ */ jsx(SeriesSwatch, { color: item.color }),
289
+ /* @__PURE__ */ jsx("span", {
290
+ className: "min-w-0 flex-1 truncate",
291
+ children: item.name
292
+ }),
293
+ /* @__PURE__ */ jsx("span", {
294
+ className: "tabular-nums",
295
+ children: format(item.value)
296
+ })
297
+ ]
298
+ }, item.key))
299
+ })]
300
+ });
301
+ }
302
+ /** A chart's plot with its reading, placed for its width: on a wide chart a tooltip beside the
303
+ * crosshair at `x` while a point is hovered, and under 28rem, as on a phone, a readout docked
304
+ * above the plot that never covers it or its legend, showing `rest`, such as the latest point,
305
+ * while none is. `children` is the plot, and `x` the crosshair in it, or null. The app positions
306
+ * nothing: the frame measures itself through a container query. */
307
+ function ChartTooltipFrame({ x, reading, rest, top = 0, gap = 12, className, children }) {
308
+ const shown = x !== null && reading ? reading : null;
309
+ return /* @__PURE__ */ jsxs("div", {
310
+ className: cx("@container/chart", className),
311
+ children: [/* @__PURE__ */ jsx(ChartReadout, {
312
+ ...shown ?? rest,
313
+ className: "mb-2 @min-[28rem]/chart:hidden"
314
+ }), /* @__PURE__ */ jsxs("div", {
315
+ className: "relative",
316
+ children: [children, shown && /* @__PURE__ */ jsx(ChartTooltipBeside, {
317
+ x,
318
+ top,
319
+ gap,
320
+ className: "hidden @min-[28rem]/chart:block",
321
+ children: /* @__PURE__ */ jsx(ChartTooltipContent, { ...shown })
322
+ })]
323
+ })]
324
+ });
325
+ }
239
326
  //#endregion
240
- export { ChartLegendList, ChartTooltipBeside, ChartTooltipContent, OTHER_SERIES_COLOR, OTHER_SERIES_KEY, SERIES_LIMIT, SeriesSwatch, chartTooltipPlacement, foldSeries, seriesColors, seriesOpacity, seriesSlotColor, useSeriesHighlight };
327
+ export { ChartLegendList, ChartReadout, ChartTooltipBeside, ChartTooltipContent, ChartTooltipFrame, OTHER_SERIES_COLOR, OTHER_SERIES_KEY, SERIES_LIMIT, SeriesSwatch, chartTooltipPlacement, foldSeries, seriesColors, seriesOpacity, seriesSlotColor, useSeriesHighlight };
@@ -1,7 +1,10 @@
1
1
  /** Above dialogs, toasts and sticky chrome; hidden while its anchor is scrolled out of view. */
2
2
  export declare const positionerClassName = "z-(--layer-popover) outline-none data-anchor-hidden:invisible";
3
+ /** The opaque surface a popover stands on, and its shadow: what is under it never shows
4
+ * through, such as a chart's marks under its tooltip. */
5
+ export declare const surfaceClassName = "rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-(--theme-shadow-lg)";
3
6
  /** A panel of choices: a menu, a select's list, a combobox's list. */
4
- export declare const panelClassName = "rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-sm text-(--theme-app) shadow-(--theme-shadow-lg) outline-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
7
+ export declare const panelClassName = "rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-(--theme-shadow-lg) text-sm outline-none origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
5
8
  /** A panel's rows inset by the same gutter on every side, so a highlighted first or last row
6
9
  * keeps the margin it has at its sides rather than leaving a strip against the border. */
7
10
  export declare const panelListClassName = "p-1";
@@ -2,8 +2,11 @@
2
2
  /** Above dialogs, toasts and sticky chrome; hidden while its anchor is scrolled out of view. */
3
3
  var positionerClassName = "z-(--layer-popover) outline-none data-anchor-hidden:invisible";
4
4
  var enterExit = "origin-(--transform-origin) transition-[transform,opacity] duration-100 data-starting-style:scale-95 data-starting-style:opacity-0 data-ending-style:scale-95 data-ending-style:opacity-0 motion-reduce:data-starting-style:scale-100 motion-reduce:data-ending-style:scale-100";
5
+ /** The opaque surface a popover stands on, and its shadow: what is under it never shows
6
+ * through, such as a chart's marks under its tooltip. */
7
+ var surfaceClassName = "rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-(--theme-app) shadow-(--theme-shadow-lg)";
5
8
  /** A panel of choices: a menu, a select's list, a combobox's list. */
6
- var panelClassName = `rounded-lg border border-(--theme-border) bg-(--theme-app-bg) text-sm text-(--theme-app) shadow-(--theme-shadow-lg) outline-none ${enterExit}`;
9
+ var panelClassName = `${surfaceClassName} text-sm outline-none ${enterExit}`;
7
10
  /** A row in a panel, inside its `panelListClassName` gutter; its text lines up with a field's. */
8
11
  var panelItemClassName = "flex w-full cursor-pointer select-none items-center gap-2 rounded-md px-2 py-1.5 text-left outline-none data-highlighted:bg-(--theme-hover) data-highlighted:text-(--theme-app) data-disabled:cursor-not-allowed data-disabled:opacity-50";
9
12
  /** A tooltip's, infotip's or popover tip's surface: a small panel in the page's colors, so a
@@ -40,4 +43,4 @@ function toPositionerAnchor(at) {
40
43
  };
41
44
  }
42
45
  //#endregion
43
- export { cardClassName, panelClassName, panelItemClassName, positionerClassName, tipClassName, toPositionerAnchor };
46
+ export { cardClassName, panelClassName, panelItemClassName, positionerClassName, surfaceClassName, tipClassName, toPositionerAnchor };
package/dist/index.d.ts CHANGED
@@ -15,7 +15,7 @@ export { ContextMenuProvider, type MenuCloseTrigger, type MenuOpener, type OpenM
15
15
  export { CopyButton, type CopyButtonProps, CopyCodeBlock, type CopyCodeBlockProps, } from './components/CopyButton.js';
16
16
  export { CreateModal, CreateModalSelect, CreateModalSwitch, } from './components/CreateModal.js';
17
17
  export { categoryClasses, categoryOf } from './components/category.js';
18
- export { type ChartLegendItem, ChartLegendList, ChartTooltipBeside, ChartTooltipContent, type ChartTooltipItem, type ChartTooltipPlacement, chartTooltipPlacement, foldSeries, OTHER_SERIES_COLOR, OTHER_SERIES_KEY, SERIES_LIMIT, SeriesSwatch, type SeriesSwatchShape, seriesColors, seriesOpacity, seriesSlotColor, useSeriesHighlight, } from './components/chart.js';
18
+ export { type ChartLegendItem, ChartLegendList, type ChartReading, ChartReadout, ChartTooltipBeside, ChartTooltipContent, ChartTooltipFrame, type ChartTooltipItem, type ChartTooltipPlacement, chartTooltipPlacement, foldSeries, OTHER_SERIES_COLOR, OTHER_SERIES_KEY, SERIES_LIMIT, SeriesSwatch, type SeriesSwatchShape, seriesColors, seriesOpacity, seriesSlotColor, useSeriesHighlight, } from './components/chart.js';
19
19
  export { Calendar, type CalendarProps, DatePicker, type DatePickerProps, } from './components/DatePicker.js';
20
20
  export { DescriptionListGroup, DescriptionListItem, type DescriptionListItemProps, type DescriptionListProps, type DescriptionListSize, type DescriptionListVariant, default as DescriptionList, } from './components/DescriptionList.js';
21
21
  export { collapsePanelClassName, Disclosure, DisclosurePanel, type DisclosureProps, } from './components/Disclosure.js';
package/dist/index.js CHANGED
@@ -45,7 +45,7 @@ import { ConfirmModal, useConfirm } from "./components/ConfirmModal.js";
45
45
  import { Disclosure, DisclosurePanel, collapsePanelClassName } from "./components/Disclosure.js";
46
46
  import { Switch } from "./components/Switch.js";
47
47
  import { CreateModal, CreateModalSelect, CreateModalSwitch } from "./components/CreateModal.js";
48
- import { ChartLegendList, ChartTooltipBeside, ChartTooltipContent, OTHER_SERIES_COLOR, OTHER_SERIES_KEY, SERIES_LIMIT, SeriesSwatch, chartTooltipPlacement, foldSeries, seriesColors, seriesOpacity, seriesSlotColor, useSeriesHighlight } from "./components/chart.js";
48
+ import { ChartLegendList, ChartReadout, ChartTooltipBeside, ChartTooltipContent, ChartTooltipFrame, OTHER_SERIES_COLOR, OTHER_SERIES_KEY, SERIES_LIMIT, SeriesSwatch, chartTooltipPlacement, foldSeries, seriesColors, seriesOpacity, seriesSlotColor, useSeriesHighlight } from "./components/chart.js";
49
49
  import { Calendar, DatePicker } from "./components/DatePicker.js";
50
50
  import DescriptionList, { DescriptionListGroup, DescriptionListItem } from "./components/DescriptionList.js";
51
51
  import { Drawer } from "./components/Drawer.js";
@@ -84,4 +84,4 @@ import { ToggleGroup } from "./components/ToggleGroup.js";
84
84
  import { useClosingValue } from "./components/useClosingValue.js";
85
85
  import { useCssIsDark } from "./components/useCssIsDark.js";
86
86
  import { useApplyTheme, useColorMode } from "./components/useTheme.js";
87
- export { ActionMenu, AppLink, Autocomplete, Avatar, AvatarGroup, Breadcrumbs, Button, CODE_STYLES, Calendar, Callout, Card, CardHeader, ChartLegendList, ChartTooltipBeside, ChartTooltipContent, Checkbox, CheckboxGroup, CodeBlock, ColorInput, Combobox, ConfirmModal, ContextMenuProvider, CopyButton, CopyCodeBlock, CountBadge, CreateModal, CreateModalSelect, CreateModalSwitch, DEFAULT_CONTRAST, DEFAULT_PRESET, DEFAULT_THEME, DatePicker, DescriptionList, DescriptionListGroup, DescriptionListItem, Disclosure, DisclosurePanel, Drawer, Elapsed, EmptyState, ErrorPage, ErrorText, ExternalLink, Field, FieldOption, FieldValue, FileButton, HoverCard, IconButton, InfoTip, Input, JND, Kbd, LetterBadge, ListboxGroup, ListboxOption, ListboxPanel, Loader, LoadingRegion, Markdown, MenuCheckboxItem, MenuEntries, MenuFilter, MenuGroupLabel, MenuItem, MenuLinkItem, MenuPopup, MenuRadioItem, MenuSeparator, MenuSubmenuTrigger, Meter, MethodButton, Modal, ModalDescription, ModalTitle, NumberInput, OTHER_SERIES_COLOR, OTHER_SERIES_KEY, OneTimeCodeInput, Overview, PAGE_SIZES, PageHeader, PageHeaderAction, PageHeaderAddButton, PageHeaderBar, PageHeaderMenu, PageHeaderProvider, Pagination, Popover, PopoverClose, ProgressBar, Radio, RadioGroup, RefreshButton, RelativeTime, RequiredMark, RevealToggle, RunStatusIcon, SERIES_LIMIT, SIDEBAR_TOKENS, SearchInput, Section, SegmentedControl, Select, SelectionCopy, SeriesSwatch, SettingModeOption, SettingRow, SettingsCard, SettingsHeading, Skeleton, Slider, StatusBadge, StatusDot, StickyBar, Switch, THEME_PRESETS, THEME_TOKENS, TabPanel, Table, Tabs, Tag, TagList, Textarea, ThemeEditor, ThemePreview, TitleInput, ToggleGroup, Tooltip, TooltipProvider, UIProvider, apcaContrast, applyTheme, ariaKeyShortcuts, avatarInitials, buttonClasses, categoryClasses, categoryOf, chartTooltipPlacement, collapsePanelClassName, composite, contrastRatio, cx, deltaE, deriveTheme, errorPageKind, fieldBox, fitSeed, foldSeries, formatBytes, formatCount, formatDateTime, formatDuration, formatInfoTipContent, formatShortcut, formatTimeLeft, generateTheme, isApplePlatform, isDarkColor, isMenuDivider, isMenuGroup, isMenuSubmenu, isPlainClick, isTextEntryContext, levelTone, menuDivider, modalPanelClassName, modeOf, normalizeTags, parseColor, parseTheme, prefersWideGamut, resolveMode, runStatusTone, schemeFor, serializeTheme, seriesColors, seriesOpacity, seriesSlotColor, shortcutText, statusTextClass, tagClasses, themeStylesheet, toHex, toP3, toast, useApplyTheme, useAvatarColoring, useClock, useCloseMenu, useClosePopover, useClosingValue, useColorMode, useConfirm, useContextMenu, useCopy, useCssIsDark, useElapsed, useFieldControl, useFieldLabelledControl, useListboxNavigation, useLocale, useMenuOpenedBy, useNavigation, useOpenMenu, useOptionalWorkflowEngine, usePersistedState, useProblemText, useProvisionCorsRules, useRelativeTime, useRunFile, useSeriesHighlight, useShortcutFormat, useShortcutKey, useSlots, useStrings, useWorkflowActions, useWorkflowEditing, useWorkflowEngine, useWorkflowEngineLoader };
87
+ export { ActionMenu, AppLink, Autocomplete, Avatar, AvatarGroup, Breadcrumbs, Button, CODE_STYLES, Calendar, Callout, Card, CardHeader, ChartLegendList, ChartReadout, ChartTooltipBeside, ChartTooltipContent, ChartTooltipFrame, Checkbox, CheckboxGroup, CodeBlock, ColorInput, Combobox, ConfirmModal, ContextMenuProvider, CopyButton, CopyCodeBlock, CountBadge, CreateModal, CreateModalSelect, CreateModalSwitch, DEFAULT_CONTRAST, DEFAULT_PRESET, DEFAULT_THEME, DatePicker, DescriptionList, DescriptionListGroup, DescriptionListItem, Disclosure, DisclosurePanel, Drawer, Elapsed, EmptyState, ErrorPage, ErrorText, ExternalLink, Field, FieldOption, FieldValue, FileButton, HoverCard, IconButton, InfoTip, Input, JND, Kbd, LetterBadge, ListboxGroup, ListboxOption, ListboxPanel, Loader, LoadingRegion, Markdown, MenuCheckboxItem, MenuEntries, MenuFilter, MenuGroupLabel, MenuItem, MenuLinkItem, MenuPopup, MenuRadioItem, MenuSeparator, MenuSubmenuTrigger, Meter, MethodButton, Modal, ModalDescription, ModalTitle, NumberInput, OTHER_SERIES_COLOR, OTHER_SERIES_KEY, OneTimeCodeInput, Overview, PAGE_SIZES, PageHeader, PageHeaderAction, PageHeaderAddButton, PageHeaderBar, PageHeaderMenu, PageHeaderProvider, Pagination, Popover, PopoverClose, ProgressBar, Radio, RadioGroup, RefreshButton, RelativeTime, RequiredMark, RevealToggle, RunStatusIcon, SERIES_LIMIT, SIDEBAR_TOKENS, SearchInput, Section, SegmentedControl, Select, SelectionCopy, SeriesSwatch, SettingModeOption, SettingRow, SettingsCard, SettingsHeading, Skeleton, Slider, StatusBadge, StatusDot, StickyBar, Switch, THEME_PRESETS, THEME_TOKENS, TabPanel, Table, Tabs, Tag, TagList, Textarea, ThemeEditor, ThemePreview, TitleInput, ToggleGroup, Tooltip, TooltipProvider, UIProvider, apcaContrast, applyTheme, ariaKeyShortcuts, avatarInitials, buttonClasses, categoryClasses, categoryOf, chartTooltipPlacement, collapsePanelClassName, composite, contrastRatio, cx, deltaE, deriveTheme, errorPageKind, fieldBox, fitSeed, foldSeries, formatBytes, formatCount, formatDateTime, formatDuration, formatInfoTipContent, formatShortcut, formatTimeLeft, generateTheme, isApplePlatform, isDarkColor, isMenuDivider, isMenuGroup, isMenuSubmenu, isPlainClick, isTextEntryContext, levelTone, menuDivider, modalPanelClassName, modeOf, normalizeTags, parseColor, parseTheme, prefersWideGamut, resolveMode, runStatusTone, schemeFor, serializeTheme, seriesColors, seriesOpacity, seriesSlotColor, shortcutText, statusTextClass, tagClasses, themeStylesheet, toHex, toP3, toast, useApplyTheme, useAvatarColoring, useClock, useCloseMenu, useClosePopover, useClosingValue, useColorMode, useConfirm, useContextMenu, useCopy, useCssIsDark, useElapsed, useFieldControl, useFieldLabelledControl, useListboxNavigation, useLocale, useMenuOpenedBy, useNavigation, useOpenMenu, useOptionalWorkflowEngine, usePersistedState, useProblemText, useProvisionCorsRules, useRelativeTime, useRunFile, useSeriesHighlight, useShortcutFormat, useShortcutKey, useSlots, useStrings, useWorkflowActions, useWorkflowEditing, useWorkflowEngine, useWorkflowEngineLoader };
@@ -17,7 +17,7 @@ var RowActionsContext = createContext({
17
17
  always: false,
18
18
  columnClasses: [],
19
19
  columnStacks: [],
20
- lastLocked: false
20
+ reservesLane: false
21
21
  });
22
22
  function useRowActions(view) {
23
23
  return useMemo(() => ({
@@ -25,13 +25,14 @@ function useRowActions(view) {
25
25
  always: view?.alwaysShowActions ?? false,
26
26
  columnClasses: view?.columnClasses ?? [],
27
27
  columnStacks: view?.columnStacks ?? [],
28
- lastLocked: view?.lastColumnLocked ?? false
28
+ reservesLane: view?.reservesActionLane ?? view?.lastColumnLocked ?? false
29
29
  }), [
30
30
  view?.showActions,
31
31
  view?.alwaysShowActions,
32
32
  view?.columnClasses,
33
33
  view?.columnStacks,
34
- view?.lastColumnLocked
34
+ view?.lastColumnLocked,
35
+ view?.reservesActionLane
35
36
  ]);
36
37
  }
37
38
  function ListRowActionsProvider({ view, children }) {
@@ -93,7 +94,7 @@ var STACKED_LINE = "mt-1 flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1 emp
93
94
  var isCellElement = (child) => child.type === Table.Item || child.type === "td";
94
95
  function ListRow({ href, onActivate, items, goTo, openMenu, pinnedActions, selected, selectionActive, onRowSelect, leading, className, children }) {
95
96
  const { active: menuActive, openMenu: openRowMenu } = useRowMenuActive(openMenu);
96
- const { show: showActions, always: alwaysShowActions, columnClasses, columnStacks, lastLocked } = useContext(RowActionsContext);
97
+ const { show: showActions, always: alwaysShowActions, columnClasses, columnStacks, reservesLane } = useContext(RowActionsContext);
97
98
  const cells = Children.toArray(children);
98
99
  const stacked = cells.flatMap((child, i) => {
99
100
  if (i === 0 || !columnStacks[i] || !isValidElement(child)) return [];
@@ -134,7 +135,7 @@ function ListRow({ href, onActivate, items, goTo, openMenu, pinnedActions, selec
134
135
  children: child
135
136
  }, child.key);
136
137
  });
137
- const floating = !alwaysShowActions && !(pinnedActions && pinnedActions.length > 0) && !lastLocked;
138
+ const floating = !alwaysShowActions && !(pinnedActions && pinnedActions.length > 0) && !reservesLane;
138
139
  const stretch = !!href && !selectionActive;
139
140
  const handleRowClick = (e) => {
140
141
  if (e.target.closest("a,button,input,label")) return;
@@ -348,7 +349,7 @@ function shareWidths(columns, stacks) {
348
349
  }
349
350
  function headerStyle(col, tiers) {
350
351
  const style = {};
351
- if (col.alwaysVisible) style["minWidth"] = 200;
352
+ if (col.alwaysVisible) style["minWidth"] = `min(200px, 50cqw)`;
352
353
  if (tiers) {
353
354
  style["--share-wide"] = `${tiers.wide}%`;
354
355
  style["--share-medium"] = `${tiers.medium}%`;
@@ -28,6 +28,12 @@ export declare const listTableFixedProps: {
28
28
  /** True inside a `ListRow` whose name link should take the whole row's
29
29
  * clicks: it navigates, and no selection claims plain clicks. */
30
30
  export declare const StretchRowLinkContext: import("react").Context<boolean>;
31
+ /** Makes a link cover its `ListRow` (the row's `<tr>` is its containing block),
32
+ * so the whole row is the link: it opens in a new tab, shows its URL and
33
+ * reads as a link, where a row click handler could do none of these. Its
34
+ * focus ring follows, drawn around the row. A `NameCell` with an `href` does
35
+ * this itself; a row whose link is elsewhere, such as a run's number before
36
+ * its workflow's name, gives its own link this class and `data-row-link`. */
31
37
  export declare const STRETCHED_LINK = "after:absolute after:inset-0 after:content-[''] focus-visible:outline-none focus-visible:after:outline-2 focus-visible:after:-outline-offset-2 focus-visible:after:outline-(--theme-link)";
32
38
  /** The row a `STRETCHED_LINK` covers: its box, with the row's other controls
33
39
  * raised above the link so they keep their own clicks. A checkbox is a
@@ -75,7 +81,8 @@ export declare function ListError({ noun, message, }: {
75
81
  export declare function ListSkeleton({ rows }: {
76
82
  rows?: number;
77
83
  }): import("react").JSX.Element;
78
- /** Prevents the name column from being crushed under table-auto layout during horizontal overflow. */
84
+ /** Prevents the name column from being crushed under table-auto layout during horizontal
85
+ * overflow. In a list under 400px wide, the floor is half the list instead. */
79
86
  export declare const NAME_MIN_WIDTH_PX = 200;
80
87
  /** Width the trailing actions column reserves for `count` always-visible
81
88
  * buttons plus the hover `⋯` trigger. */
@@ -114,6 +121,7 @@ export declare function ListActionsHeader({ view, actionCount, className, }: {
114
121
  actionsWidth: number;
115
122
  alwaysShowActions?: boolean;
116
123
  lastColumnLocked?: boolean | undefined;
124
+ reservesActionLane?: boolean | undefined;
117
125
  };
118
126
  actionCount?: number;
119
127
  className?: string;
@@ -37,6 +37,12 @@ var listTableFixedProps = {
37
37
  /** True inside a `ListRow` whose name link should take the whole row's
38
38
  * clicks: it navigates, and no selection claims plain clicks. */
39
39
  var StretchRowLinkContext = createContext(false);
40
+ /** Makes a link cover its `ListRow` (the row's `<tr>` is its containing block),
41
+ * so the whole row is the link: it opens in a new tab, shows its URL and
42
+ * reads as a link, where a row click handler could do none of these. Its
43
+ * focus ring follows, drawn around the row. A `NameCell` with an `href` does
44
+ * this itself; a row whose link is elsewhere, such as a run's number before
45
+ * its workflow's name, gives its own link this class and `data-row-link`. */
40
46
  var STRETCHED_LINK = "after:absolute after:inset-0 after:content-[''] focus-visible:outline-none focus-visible:after:outline-2 focus-visible:after:-outline-offset-2 focus-visible:after:outline-(--theme-link)";
41
47
  /** The row a `STRETCHED_LINK` covers: its box, with the row's other controls
42
48
  * raised above the link so they keep their own clicks. A checkbox is a
@@ -160,7 +166,8 @@ function ListSkeleton({ rows = 6 }) {
160
166
  }, key))
161
167
  });
162
168
  }
163
- /** Prevents the name column from being crushed under table-auto layout during horizontal overflow. */
169
+ /** Prevents the name column from being crushed under table-auto layout during horizontal
170
+ * overflow. In a list under 400px wide, the floor is half the list instead. */
164
171
  var NAME_MIN_WIDTH_PX = 200;
165
172
  /** Trailing cell geometry: icon-button size + gap + padding, sized to fit exactly its pinned buttons. */
166
173
  var ACTION_BUTTON_PX = 24;
@@ -215,7 +222,7 @@ function ListActionsHeader({ view, actionCount = 0, className }) {
215
222
  const { common } = useStrings();
216
223
  if (view && !view.showActions) return null;
217
224
  const width = view ? view.actionsWidth : actionsColumnWidth(actionCount);
218
- const floating = !!view && !view.alwaysShowActions && !view.lastColumnLocked && view.actionsWidth === actionsColumnWidth(0);
225
+ const floating = !!view && !view.alwaysShowActions && !(view.reservesActionLane ?? view.lastColumnLocked) && view.actionsWidth === actionsColumnWidth(0);
219
226
  return /* @__PURE__ */ jsx(Table.Header, {
220
227
  caps: false,
221
228
  style: {
@@ -2,7 +2,7 @@ export { ActionToolbar, type ToolbarAction, ToolbarButton, type ToolbarItem, } f
2
2
  export { type ActiveFilter, applyFilterList, DATE_PRESETS, type DatePreset, dateFilterRange, decodeFilters, encodeFilters, type FilterField, type FilterKind, type FilterOperator, type FilterOption, type FilterRowValue, filterParamKeys, matchFilter, sameFilters, withFilterParams, } from './filterModel.js';
3
3
  export { AddFilterMenu, type AddFilterMenuProps, FilterChip, ListFilterBar, type ListFilterBarProps, } from './ListFilterBar.js';
4
4
  export { GroupRow, ListColumns, ListPage, type ListPageEmpty, type ListPageProps, ListRow, ListRowActionsProvider, useListNavigate, } from './ListPage.js';
5
- export { actionsColumnWidth, ListActionsHeader, ListCellText, ListError, ListSelectAllHeader, ListSkeleton, listTableFixedProps, listTableProps, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, } from './ListTable.js';
5
+ export { actionsColumnWidth, ListActionsHeader, ListCellText, ListError, ListSelectAllHeader, ListSkeleton, listTableFixedProps, listTableProps, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, STRETCHED_LINK, } from './ListTable.js';
6
6
  export { ListToolbar, type ListToolbarProps } from './ListToolbar.js';
7
7
  export { ListDisplayMenu, ListFilterMenu } from './ListViewControls.js';
8
8
  export { ListCount, type ListSearch, ListSearchControl, useListSearch, } from './listSearch.js';
@@ -1,7 +1,7 @@
1
1
  import { ActionToolbar, ToolbarButton } from "./ActionToolbar.js";
2
2
  import { DATE_PRESETS, applyFilterList, dateFilterRange, decodeFilters, encodeFilters, filterParamKeys, matchFilter, sameFilters, withFilterParams } from "./filterModel.js";
3
3
  import { AddFilterMenu, FilterChip, ListFilterBar } from "./ListFilterBar.js";
4
- import { ListActionsHeader, ListCellText, ListError, ListSelectAllHeader, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, actionsColumnWidth, listTableFixedProps, listTableProps } from "./ListTable.js";
4
+ import { ListActionsHeader, ListCellText, ListError, ListSelectAllHeader, ListSkeleton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, STRETCHED_LINK, actionsColumnWidth, listTableFixedProps, listTableProps } from "./ListTable.js";
5
5
  import { MoreButton, assembleRowMenu, rowContextMenuProps, useCopySubmenu, useRowMenuActive } from "./rowMenu.js";
6
6
  import { GroupRow, ListColumns, ListPage, ListRow, ListRowActionsProvider, useListNavigate } from "./ListPage.js";
7
7
  import { useListFilters } from "./useListFilters.js";
@@ -10,4 +10,4 @@ import { ListDisplayMenu, ListFilterMenu } from "./ListViewControls.js";
10
10
  import { ListCount, ListSearchControl, useListSearch } from "./listSearch.js";
11
11
  import { ListToolbar } from "./ListToolbar.js";
12
12
  import { HoverCardRow, UserHoverCard } from "./UserHoverCard.js";
13
- export { ActionToolbar, AddFilterMenu, DATE_PRESETS, DEFAULT_ORDER, FAVORITE_ORDER, FilterChip, GroupRow, HoverCardRow, ListActionsHeader, ListCellText, ListColumns, ListCount, ListDisplayMenu, ListError, ListFilterBar, ListFilterMenu, ListPage, ListRow, ListRowActionsProvider, ListSearchControl, ListSelectAllHeader, ListSkeleton, ListToolbar, MoreButton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, ToolbarButton, UserHoverCard, actionsColumnWidth, applyFilterList, assembleRowMenu, dateFilterRange, decodeFilters, encodeFilters, filterParamKeys, listTableFixedProps, listTableProps, matchFilter, rowContextMenuProps, sameFilters, useCopySubmenu, useListFilters, useListNavigate, useListSearch, useListView, useRowMenuActive, withFilterParams };
13
+ export { ActionToolbar, AddFilterMenu, DATE_PRESETS, DEFAULT_ORDER, FAVORITE_ORDER, FilterChip, GroupRow, HoverCardRow, ListActionsHeader, ListCellText, ListColumns, ListCount, ListDisplayMenu, ListError, ListFilterBar, ListFilterMenu, ListPage, ListRow, ListRowActionsProvider, ListSearchControl, ListSelectAllHeader, ListSkeleton, ListToolbar, MoreButton, NAME_MIN_WIDTH_PX, NameCell, RowSelectCheckbox, STRETCHED_LINK, ToolbarButton, UserHoverCard, actionsColumnWidth, applyFilterList, assembleRowMenu, dateFilterRange, decodeFilters, encodeFilters, filterParamKeys, listTableFixedProps, listTableProps, matchFilter, rowContextMenuProps, sameFilters, useCopySubmenu, useListFilters, useListNavigate, useListSearch, useListView, useRowMenuActive, withFilterParams };
@@ -131,6 +131,13 @@ export interface ListViewChrome {
131
131
  * row's `⋯` then keeps a lane of its own after it rather than floating over
132
132
  * it on hover. */
133
133
  lastColumnLocked?: boolean | undefined;
134
+ /** Whether the row's `⋯` keeps a lane of its own after the last column
135
+ * rather than floating over its end on hover: the last column is locked, or
136
+ * it is end-aligned, such as a runtime or a count, whose value sits right
137
+ * where the `⋯` would and would be hidden while the row is hovered. A text
138
+ * column's end is usually blank, so the `⋯` floats there and the list keeps
139
+ * its width. */
140
+ reservesActionLane?: boolean | undefined;
134
141
  /** The list's filters, which `ListPage` shows as a row of chips under its toolbar. */
135
142
  filterState?: ListFilters | undefined;
136
143
  }
@@ -95,6 +95,7 @@ function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], def
95
95
  const columnStacks = useMemo(() => visibleColumns.map(stacks), [visibleColumns]);
96
96
  const last = visibleColumns.at(-1);
97
97
  const lastColumnLocked = visibleColumns.length > 1 && last !== void 0 && !last.alwaysVisible && isLocked(last);
98
+ const reservesActionLane = lastColumnLocked || visibleColumns.length > 1 && last?.align === "end";
98
99
  const toggleColumn = useCallback((key) => setHiddenKeys((prev) => prev.includes(key) ? prev.filter((k) => k !== key) : [...prev, key]), [setHiddenKeys]);
99
100
  const { add, update, remove } = filterState;
100
101
  const setFacetValues = useCallback((key, values) => {
@@ -190,6 +191,7 @@ function useListView({ storageKey, sharedFiltersKey, columns, groupBys = [], def
190
191
  columnClasses,
191
192
  columnStacks,
192
193
  lastColumnLocked,
194
+ reservesActionLane,
193
195
  isVisible,
194
196
  pinnedCount,
195
197
  actionsWidth,