@parallelworks/ui 0.82.1 → 0.83.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.
@@ -3,7 +3,7 @@ import { CloseIcon, ErrorIcon, InfoIcon, LockIcon, SuccessIcon, WarningIcon } fr
3
3
  import { toneIcon, toneSurface } from "./tone.js";
4
4
  import { useStrings } from "./Provider.js";
5
5
  import { IconButton } from "./IconButton.js";
6
- import { jsx, jsxs } from "react/jsx-runtime";
6
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region src/components/Callout.tsx
8
8
  var toneIcons = {
9
9
  default: InfoIcon,
@@ -20,38 +20,40 @@ function Callout({ tone = "info", title, children, action, onDismiss, icon, vari
20
20
  const { common } = useStrings();
21
21
  const small = size === "sm";
22
22
  const Icon = toneIcons[tone];
23
+ const controls = /* @__PURE__ */ jsxs(Fragment, { children: [action !== void 0 && /* @__PURE__ */ jsx("div", {
24
+ className: cx("flex shrink-0 items-center gap-2", small ? "h-4" : "h-5"),
25
+ children: action
26
+ }), onDismiss && /* @__PURE__ */ jsx(IconButton, {
27
+ size: "xs",
28
+ icon: /* @__PURE__ */ jsx(CloseIcon, {}),
29
+ label: common.dismiss,
30
+ onClick: onDismiss,
31
+ className: cx("shrink-0", small ? "-my-1 -me-1" : "-my-0.5 -me-1")
32
+ })] });
23
33
  return /* @__PURE__ */ jsxs("div", {
24
34
  "data-testid": testId,
25
35
  ...live ? { role: tone === "error" ? "alert" : "status" } : {},
26
36
  className: cx("flex items-start", toneSurface[tone], small ? "gap-2 px-3 py-2 text-xs leading-4" : "gap-3 px-4 py-3 text-sm leading-5", variant === "card" ? "rounded-xl border" : "border-0 border-b", className),
27
- children: [
28
- icon !== false && /* @__PURE__ */ jsx("span", {
29
- "aria-hidden": "true",
30
- className: cx("flex shrink-0 items-center", small ? "h-4 [&_svg]:size-4" : "h-5 [&_svg]:size-5", toneIcon[tone]),
31
- children: icon ?? /* @__PURE__ */ jsx(Icon, {})
32
- }),
33
- /* @__PURE__ */ jsxs("div", {
34
- className: "min-w-0 flex-1",
35
- children: [title !== void 0 && /* @__PURE__ */ jsx("p", {
36
- className: "m-0 font-medium",
37
+ children: [icon !== false && /* @__PURE__ */ jsx("span", {
38
+ "aria-hidden": "true",
39
+ className: cx("flex shrink-0 items-center", small ? "h-4 [&_svg]:size-4" : "h-5 [&_svg]:size-5", toneIcon[tone]),
40
+ children: icon ?? /* @__PURE__ */ jsx(Icon, {})
41
+ }), title !== void 0 ? /* @__PURE__ */ jsxs("div", {
42
+ className: "min-w-0 flex-1",
43
+ children: [/* @__PURE__ */ jsxs("div", {
44
+ className: cx("flex items-start", small ? "gap-2" : "gap-3"),
45
+ children: [/* @__PURE__ */ jsx("p", {
46
+ className: "m-0 min-w-0 flex-1 font-medium",
37
47
  children: title
38
- }), children !== void 0 && /* @__PURE__ */ jsx("div", {
39
- className: cx(title !== void 0 && "mt-0.5"),
40
- children
41
- })]
42
- }),
43
- action !== void 0 && /* @__PURE__ */ jsx("div", {
44
- className: cx("flex shrink-0 items-center gap-2", small ? "h-4" : "h-5"),
45
- children: action
46
- }),
47
- onDismiss && /* @__PURE__ */ jsx(IconButton, {
48
- size: "xs",
49
- icon: /* @__PURE__ */ jsx(CloseIcon, {}),
50
- label: common.dismiss,
51
- onClick: onDismiss,
52
- className: small ? "-my-1 -me-1" : "-my-0.5 -me-1"
53
- })
54
- ]
48
+ }), controls]
49
+ }), children !== void 0 && /* @__PURE__ */ jsx("div", {
50
+ className: "mt-0.5",
51
+ children
52
+ })]
53
+ }) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
54
+ className: "min-w-0 flex-1",
55
+ children
56
+ }), controls] })]
55
57
  });
56
58
  }
57
59
  //#endregion
@@ -31,10 +31,13 @@ export interface CodeBlockProps {
31
31
  /** The code is still arriving: highlighting is not cached, and the block
32
32
  * keeps its end in view while it is scrolled there. */
33
33
  streaming?: boolean | undefined;
34
+ /** On a phone, reaches past the host's content padding (`p-4`) to the
35
+ * container's edges, without corners, so long lines get the screen's width. */
36
+ bleed?: boolean | undefined;
34
37
  className?: string | undefined;
35
38
  }
36
39
  /** Code to read on a page, such as a workflow's YAML: highlighted in the
37
40
  * theme's code colors, with numbered lines, a copy button and, given a
38
41
  * `filename`, a download button. Markdown's fenced code renders as one, so
39
42
  * both read the same. */
40
- export declare function CodeBlock({ code, language, title, label, filename, headerActions, copy, lineNumbers, startLine, wrap, fill, maxHeight, streaming, className, }: CodeBlockProps): import("react").JSX.Element;
43
+ export declare function CodeBlock({ code, language, title, label, filename, headerActions, copy, lineNumbers, startLine, wrap, fill, maxHeight, streaming, bleed, className, }: CodeBlockProps): import("react").JSX.Element;
@@ -66,7 +66,7 @@ function downloadText(text, name) {
66
66
  * theme's code colors, with numbered lines, a copy button and, given a
67
67
  * `filename`, a download button. Markdown's fenced code renders as one, so
68
68
  * both read the same. */
69
- function CodeBlock({ code, language = "", title, label, filename, headerActions, copy = true, lineNumbers, startLine = 1, wrap = false, fill = false, maxHeight, streaming = false, className }) {
69
+ function CodeBlock({ code, language = "", title, label, filename, headerActions, copy = true, lineNumbers, startLine = 1, wrap = false, fill = false, maxHeight, streaming = false, bleed = false, className }) {
70
70
  const { common: t } = useStrings();
71
71
  const source = code.endsWith("\n") ? code.slice(0, -1) : code;
72
72
  const lines = useMemo(() => source.split("\n"), [source]);
@@ -100,7 +100,7 @@ function CodeBlock({ code, language = "", title, label, filename, headerActions,
100
100
  };
101
101
  return /* @__PURE__ */ jsxs("div", {
102
102
  "data-code-block": "",
103
- className: cx("not-prose flex flex-col overflow-hidden rounded-xl bg-(--theme-code-bg) text-(--theme-code-text) shadow-[0_0_0_1px_color-mix(in_oklab,var(--theme-app)_8%,transparent)]", fill && "h-full min-h-0", className),
103
+ className: cx("not-prose flex flex-col overflow-hidden rounded-xl bg-(--theme-code-bg) text-(--theme-code-text) shadow-[0_0_0_1px_color-mix(in_oklab,var(--theme-app)_8%,transparent)]", fill && "h-full min-h-0", bleed && "max-sm:-mx-4 max-sm:rounded-none", className),
104
104
  children: [/* @__PURE__ */ jsxs("div", {
105
105
  className: "flex h-9 shrink-0 items-center gap-2 pr-1.5 pl-4",
106
106
  children: [/* @__PURE__ */ jsx("div", {
@@ -1,4 +1,4 @@
1
- import type { ReactNode } from 'react';
1
+ import { type ReactNode } from 'react';
2
2
  /** A bar's color: the theme's accent, a status, or muted for something stopped. */
3
3
  export type BarTone = 'accent' | 'info' | 'success' | 'warning' | 'error' | 'muted';
4
4
  declare const trackHeight: {
@@ -32,7 +32,7 @@ export declare function levelTone(value: number, warnAt: number, errorAt: number
32
32
  /** How full something is, such as storage used or a quota spent: a reading,
33
33
  * not a task under way (that's `ProgressBar`). The bar is blue, then turns
34
34
  * amber, then red, as it passes `warnAt` and `errorAt`; `tone` overrides that. */
35
- export declare function Meter({ value, min, max, warnAt, errorAt, tone, okTone, format, showValue, valueText, getAriaValueText, minFill, locale, label, 'aria-label': ariaLabel, detail, size, trackClassName, className, }: BarProps & {
35
+ export declare function Meter({ value, min, max, warnAt, errorAt, tone, okTone, format, showValue, valueText, getAriaValueText, minFill, animate, locale, label, 'aria-label': ariaLabel, detail, size, trackClassName, className, }: BarProps & {
36
36
  value: number;
37
37
  min?: number;
38
38
  max?: number;
@@ -54,6 +54,10 @@ export declare function Meter({ value, min, max, warnAt, errorAt, tone, okTone,
54
54
  /** The narrowest the fill draws for a value above `min`, as a percent of the
55
55
  * track, so a small reading still shows. */
56
56
  minFill?: number | undefined;
57
+ /** Grows the fill in from empty when the bar first shows, and eases it to each new value, for a
58
+ * reading that arrives with the page, such as a dashboard's. Still for anyone who asks for
59
+ * less motion. */
60
+ animate?: boolean | undefined;
57
61
  }): import("react").JSX.Element;
58
62
  /** A further stretch of a progress bar, drawn after its value, such as work
59
63
  * started but not yet done. */
@@ -1,6 +1,7 @@
1
1
  import { cx } from "../cx.js";
2
2
  import { toneFill } from "./tone.js";
3
3
  import { Tooltip } from "./Tooltip.js";
4
+ import { useEffect, useState } from "react";
4
5
  import { jsx, jsxs } from "react/jsx-runtime";
5
6
  import { Meter } from "@base-ui/react/meter";
6
7
  import { Progress } from "@base-ui/react/progress";
@@ -26,6 +27,21 @@ var trackBaseClassName = "w-full overflow-hidden rounded-full bg-(--theme-border
26
27
  function trackClasses(size, end, own) {
27
28
  return cx(trackBaseClassName, trackHeight[size], end && (own ? "shrink-0" : "min-w-0 flex-1"), own);
28
29
  }
30
+ function useGrown(grow) {
31
+ const [grown, setGrown] = useState(!grow);
32
+ useEffect(() => {
33
+ if (grown) return;
34
+ let inner = 0;
35
+ const outer = requestAnimationFrame(() => {
36
+ inner = requestAnimationFrame(() => setGrown(true));
37
+ });
38
+ return () => {
39
+ cancelAnimationFrame(outer);
40
+ cancelAnimationFrame(inner);
41
+ };
42
+ }, [grown]);
43
+ return grown;
44
+ }
29
45
  /** The tone for `value` against a warning and an error level, for a measure
30
46
  * that's worse the higher it reads; `okTone` below the warning. */
31
47
  function levelTone(value, warnAt, errorAt, okTone = "info") {
@@ -34,7 +50,8 @@ function levelTone(value, warnAt, errorAt, okTone = "info") {
34
50
  /** How full something is, such as storage used or a quota spent: a reading,
35
51
  * not a task under way (that's `ProgressBar`). The bar is blue, then turns
36
52
  * amber, then red, as it passes `warnAt` and `errorAt`; `tone` overrides that. */
37
- function Meter$1({ value, min = 0, max = 100, warnAt = 80, errorAt = 90, tone, okTone = "info", format, showValue = "beside", valueText, getAriaValueText, minFill = 0, locale, label, "aria-label": ariaLabel, detail, size = "md", trackClassName, className }) {
53
+ function Meter$1({ value, min = 0, max = 100, warnAt = 80, errorAt = 90, tone, okTone = "info", format, showValue = "beside", valueText, getAriaValueText, minFill = 0, animate = false, locale, label, "aria-label": ariaLabel, detail, size = "md", trackClassName, className }) {
54
+ const grown = useGrown(animate);
38
55
  const clamped = Math.min(Math.max(value, min), max);
39
56
  const fill = fillClassName[tone ?? levelTone(clamped, warnAt, errorAt, okTone)];
40
57
  const end = showValue === "end";
@@ -45,8 +62,9 @@ function Meter$1({ value, min = 0, max = 100, warnAt = 80, errorAt = 90, tone, o
45
62
  const track = /* @__PURE__ */ jsx(Meter.Track, {
46
63
  className: trackClasses(size, end, trackClassName),
47
64
  children: /* @__PURE__ */ jsx(Meter.Indicator, {
48
- className: cx("h-full rounded-full transition-[width] duration-300 motion-reduce:transition-none", fill),
49
- style: minFill > 0 && clamped > min ? { minWidth: `${minFill}%` } : void 0
65
+ "data-growing": grown ? void 0 : "",
66
+ className: cx("h-full rounded-full motion-reduce:transition-none", animate ? "transition-[width,min-width] delay-150 duration-500 ease-[cubic-bezier(0.25,0.1,0.25,1)]" : "transition-[width] duration-300", fill),
67
+ style: !grown ? { width: 0 } : minFill > 0 && clamped > min ? { minWidth: `${minFill}%` } : void 0
50
68
  })
51
69
  });
52
70
  const share = formatShare(clamped, min, max, format, locale);
@@ -59,7 +59,7 @@ function Pagination({ page, pageSize, total, hasNext, onPageChange, pageHref, pa
59
59
  const position = summary ?? (known ? t.pager(start, end, pastCap ? end : total, pastCap) : null);
60
60
  return /* @__PURE__ */ jsxs("nav", {
61
61
  "aria-label": t.pagination,
62
- className: cx("flex h-10 shrink-0 items-center justify-between gap-3 px-2.5", className),
62
+ className: cx("flex h-8 shrink-0 items-center justify-between gap-3 px-2.5", className),
63
63
  children: [/* @__PURE__ */ jsxs("div", {
64
64
  className: "flex min-w-0 flex-1 items-center gap-3 text-xs text-(--theme-muted-text-color)",
65
65
  children: [position && /* @__PURE__ */ jsx("span", {
@@ -118,22 +118,27 @@ function Pagination({ page, pageSize, total, hasNext, onPageChange, pageHref, pa
118
118
  function PageStep({ to, enabled, label, icon, onPageChange, pageHref }) {
119
119
  if (pageHref && enabled) return /* @__PURE__ */ jsx(IconButton, {
120
120
  variant: "outline",
121
+ size: "xs",
122
+ className: STEP_TOUCH,
121
123
  icon,
122
124
  label,
123
125
  href: pageHref(to)
124
126
  });
125
127
  return /* @__PURE__ */ jsx(IconButton, {
126
128
  variant: "outline",
129
+ size: "xs",
130
+ className: STEP_TOUCH,
127
131
  icon,
128
132
  label,
129
133
  disabled: !enabled,
130
134
  onClick: () => onPageChange?.(to)
131
135
  });
132
136
  }
137
+ var STEP_TOUCH = "pointer-coarse:size-8";
133
138
  var numberClassName = cx(buttonClasses({
134
139
  variant: "outline",
135
- size: "sm"
136
- }), "min-w-7 px-1.5 tabular-nums aria-[current=page]:border-(--theme-link) aria-[current=page]:bg-(--theme-accent-subtle) aria-[current=page]:text-(--theme-app)");
140
+ size: "xs"
141
+ }), "min-w-6 px-1 tabular-nums pointer-coarse:h-8 pointer-coarse:min-w-8 aria-[current=page]:border-(--theme-link) aria-[current=page]:bg-(--theme-accent-subtle) aria-[current=page]:text-(--theme-app)");
137
142
  function PageNumber({ to, current, label, onPageChange, pageHref }) {
138
143
  const number = formatInteger(to + 1, useLocale());
139
144
  const props = {
@@ -13,20 +13,30 @@ export interface SettingsCardProps {
13
13
  /** Lets content such as an inline menu reach past the card's rounded edge. */
14
14
  overflowVisible?: boolean | undefined;
15
15
  className?: string | undefined;
16
+ /** An error about the whole card, such as its header's Save or Connect failing: at the top of
17
+ * the card, inside it, above its rows, and announced. */
18
+ error?: ReactNode;
16
19
  /** SettingRows, and SettingsHeadings between groups of them. */
17
20
  children: ReactNode;
18
21
  }
19
22
  /** A page's settings, grouped: a titled panel of SettingRows, each ruled off from
20
23
  * the next. Cards stack with room between them. An error about one setting, such as its
21
- * Save failing, is its row's `error`; one about the whole card goes outside it, such as
22
- * an ErrorText under the card, since every child of the card is ruled off as a row. */
23
- export declare function SettingsCard({ title, description, action, actionAlign, headingLevel, overflowVisible, className, children, }: SettingsCardProps): import("react").JSX.Element;
24
+ * Save failing, is its row's `error`; one about the whole card, such as its header's
25
+ * action failing, is the card's `error`. */
26
+ export declare function SettingsCard({ title, description, action, actionAlign, headingLevel, overflowVisible, className, error, children, }: SettingsCardProps): import("react").JSX.Element;
24
27
  /** A heading between groups of rows inside a SettingsCard, a level below the card's title. */
25
28
  export declare function SettingsHeading({ children }: {
26
29
  children: ReactNode;
27
30
  }): import("react").JSX.Element;
28
31
  /** One setting in a SettingsCard: its name and help on the left, its control on
29
- * the right, or under the name across the row with `fill`. */
32
+ * the right, or under the name across the row with `fill`.
33
+ *
34
+ * On a phone a small control, such as a switch, a checkbox or a short select, stays on the
35
+ * name's line at the row's end. One too wide to fit beside the name, such as a text field
36
+ * with its Save button, moves to a line of its own and takes the row's width from its start:
37
+ * a single wrapper the host puts it in, such as a `<form className="flex gap-2">`, stretches
38
+ * across the row, and a text field in it grows while a button beside it keeps its size. A
39
+ * control of the host's own that should grow there takes `flex-1`. */
30
40
  export declare function SettingRow({ label, htmlFor, helpText, fill, onClick, error, children, }: {
31
41
  label: string;
32
42
  /** The control's id, so the name labels it. */
@@ -37,8 +47,9 @@ export declare function SettingRow({ label, htmlFor, helpText, fill, onClick, er
37
47
  /** Makes the whole row press its control, such as a switch. The control still
38
48
  * handles its own press; stop it propagating there so it doesn't toggle twice. */
39
49
  onClick?: (() => void) | undefined;
40
- /** An error about this setting, such as its Save failing: under the control, at its start,
41
- * announced, and describing the control that `htmlFor` names. */
50
+ /** An error about this setting, such as its Save failing: under the row from the name's
51
+ * start, or with `fill` under the control from its start, on a wide screen; across the row
52
+ * on a phone. It is announced and describes the control that `htmlFor` names. */
42
53
  error?: ReactNode;
43
54
  children: ReactNode;
44
55
  }): import("react").JSX.Element;
@@ -6,9 +6,9 @@ import { jsx, jsxs } from "react/jsx-runtime";
6
6
  var HeadingLevel = createContext(3);
7
7
  /** A page's settings, grouped: a titled panel of SettingRows, each ruled off from
8
8
  * the next. Cards stack with room between them. An error about one setting, such as its
9
- * Save failing, is its row's `error`; one about the whole card goes outside it, such as
10
- * an ErrorText under the card, since every child of the card is ruled off as a row. */
11
- function SettingsCard({ title, description, action, actionAlign = "beside", headingLevel = 2, overflowVisible = false, className, children }) {
9
+ * Save failing, is its row's `error`; one about the whole card, such as its header's
10
+ * action failing, is the card's `error`. */
11
+ function SettingsCard({ title, description, action, actionAlign = "beside", headingLevel = 2, overflowVisible = false, className, error, children }) {
12
12
  const Heading = `h${headingLevel}`;
13
13
  return /* @__PURE__ */ jsx(HeadingLevel, {
14
14
  value: headingLevel + 1,
@@ -26,9 +26,16 @@ function SettingsCard({ title, description, action, actionAlign = "beside", head
26
26
  className: "mt-1.5 text-xs leading-snug theme-muted-text",
27
27
  children: description
28
28
  })]
29
- }), /* @__PURE__ */ jsx("div", {
30
- className: cx("panel divide-y divide-(--theme-border)", overflowVisible ? "overflow-visible" : "overflow-hidden"),
31
- children
29
+ }), /* @__PURE__ */ jsxs("div", {
30
+ className: cx("panel", overflowVisible ? "overflow-visible" : "overflow-hidden"),
31
+ children: [/* @__PURE__ */ jsx(ErrorText, {
32
+ size: "md",
33
+ className: "px-4 pt-4",
34
+ children: error
35
+ }), /* @__PURE__ */ jsx("div", {
36
+ className: "divide-y divide-(--theme-border)",
37
+ children
38
+ })]
32
39
  })]
33
40
  })
34
41
  });
@@ -42,7 +49,14 @@ function SettingsHeading({ children }) {
42
49
  });
43
50
  }
44
51
  /** One setting in a SettingsCard: its name and help on the left, its control on
45
- * the right, or under the name across the row with `fill`. */
52
+ * the right, or under the name across the row with `fill`.
53
+ *
54
+ * On a phone a small control, such as a switch, a checkbox or a short select, stays on the
55
+ * name's line at the row's end. One too wide to fit beside the name, such as a text field
56
+ * with its Save button, moves to a line of its own and takes the row's width from its start:
57
+ * a single wrapper the host puts it in, such as a `<form className="flex gap-2">`, stretches
58
+ * across the row, and a text field in it grows while a button beside it keeps its size. A
59
+ * control of the host's own that should grow there takes `flex-1`. */
46
60
  function SettingRow({ label, htmlFor, helpText, fill = false, onClick, error, children }) {
47
61
  const errorId = useId();
48
62
  const hasError = error !== void 0 && error !== null && error !== false && error !== "";
@@ -61,39 +75,33 @@ function SettingRow({ label, htmlFor, helpText, fill = false, onClick, error, ch
61
75
  htmlFor,
62
76
  errorId
63
77
  ]);
64
- const control = /* @__PURE__ */ jsx("div", {
65
- className: cx("min-w-0", !fill && [
66
- "flex items-center justify-end",
67
- "max-md:has-[input:not([type=checkbox],[type=radio],[type=hidden],[type=range]),textarea,[role=combobox]]:justify-start",
68
- "max-md:has-[input:not([type=checkbox],[type=radio],[type=hidden],[type=range]),textarea,[role=combobox]]:*:grow",
69
- "max-md:[&_:is(input:not([type=checkbox],[type=radio],[type=hidden],[type=range]),textarea,[role=combobox])]:min-w-0",
70
- "max-md:[&_:is(input:not([type=checkbox],[type=radio],[type=hidden],[type=range]),textarea,[role=combobox])]:flex-1"
71
- ]),
72
- children
73
- });
74
78
  return /* @__PURE__ */ jsxs("div", {
75
79
  role: "none",
76
80
  onClick,
77
- className: cx("grid grid-cols-1 gap-x-8 gap-y-2 p-4", fill ? "md:grid-cols-[200px_1fr]" : "md:grid-cols-[1fr_auto]", onClick && "cursor-pointer transition-colors select-none hover:theme-hover"),
78
- children: [/* @__PURE__ */ jsxs("div", {
79
- className: "flex flex-col gap-y-1",
80
- children: [/* @__PURE__ */ jsx("label", {
81
- htmlFor,
82
- className: "leading-tight font-semibold theme-text",
83
- children: label
84
- }), helpText && /* @__PURE__ */ jsx("div", {
85
- className: "text-xs leading-snug theme-muted-text",
86
- children: helpText
87
- })]
88
- }), hasError ? /* @__PURE__ */ jsxs("div", {
89
- className: "flex min-w-0 flex-col gap-y-2",
90
- children: [control, /* @__PURE__ */ jsx(ErrorText, {
81
+ className: cx("gap-x-8 gap-y-2 p-4", "flex flex-wrap items-center", "md:grid md:items-stretch", fill ? "md:grid-cols-[200px_1fr]" : "md:grid-cols-[1fr_auto]", onClick && "cursor-pointer transition-colors select-none hover:theme-hover"),
82
+ children: [
83
+ /* @__PURE__ */ jsxs("div", {
84
+ className: cx("flex min-w-0 grow-[999] basis-48 flex-col gap-y-1 self-start", fill && "max-md:basis-full"),
85
+ children: [/* @__PURE__ */ jsx("label", {
86
+ htmlFor,
87
+ className: "leading-tight font-semibold theme-text",
88
+ children: label
89
+ }), helpText && /* @__PURE__ */ jsx("div", {
90
+ className: "text-xs leading-snug theme-muted-text",
91
+ children: helpText
92
+ })]
93
+ }),
94
+ /* @__PURE__ */ jsx("div", {
95
+ className: cx("flex min-w-0 grow items-center gap-2", !fill && "md:justify-end", fill ? "[&>:only-child]:grow" : "max-md:[&>:only-child]:grow", "max-md:[&_:is(input:not([type=checkbox],[type=radio],[type=hidden],[type=file]),textarea)]:min-w-0", "max-md:[&_:is(input:not([type=checkbox],[type=radio],[type=hidden],[type=file]),textarea)]:flex-1"),
96
+ children
97
+ }),
98
+ hasError && /* @__PURE__ */ jsx(ErrorText, {
91
99
  id: errorId,
92
100
  size: "md",
93
- className: "md:w-0 md:min-w-full",
101
+ className: cx("basis-full", fill ? "md:col-start-2 md:w-0 md:min-w-full" : "md:col-span-2"),
94
102
  children: error
95
- })]
96
- }) : control]
103
+ })
104
+ ]
97
105
  });
98
106
  }
99
107
  //#endregion
@@ -1,9 +1,9 @@
1
1
  import React from 'react';
2
2
  /** Which way a column sorts, as `aria-sort` names it. */
3
3
  export type SortDirection = 'ascending' | 'descending' | 'none';
4
- /** `comfortable` gives each row room; `compact` fits a short key and value
5
- * list; `dense` sets small type in tight rows, for a table inside a card or
6
- * a panel of figures. */
4
+ /** `comfortable` gives each row room; `compact` fits many rows in little
5
+ * height, such as a key and value list or a dashboard card's runs; `dense`
6
+ * sets small type in tight rows, for a panel of figures. */
7
7
  export type TableDensity = 'comfortable' | 'compact' | 'dense';
8
8
  interface ItemProps extends React.TdHTMLAttributes<HTMLTableCellElement> {
9
9
  /** Lets the text wrap; by default a cell keeps to one line. */
@@ -9,20 +9,25 @@ var DensityContext = createContext("comfortable");
9
9
  var CapsContext = createContext(true);
10
10
  var cellY = {
11
11
  comfortable: "py-4",
12
- compact: "py-1.5 text-sm",
12
+ compact: "h-8 py-0 text-sm",
13
13
  dense: "py-1 text-xs"
14
14
  };
15
+ var wrapY = {
16
+ comfortable: false,
17
+ compact: "py-1",
18
+ dense: false
19
+ };
15
20
  var headerY = {
16
21
  comfortable: "py-3",
17
- compact: "py-2",
18
- dense: "py-1.5"
22
+ compact: "py-1.5",
23
+ dense: "py-1"
19
24
  };
20
25
  var cellX = "px-3";
21
26
  function Item({ wrap = false, children, className, ...tdProps }) {
22
27
  const density = useContext(DensityContext);
23
28
  return /* @__PURE__ */ jsx("td", {
24
29
  ...tdProps,
25
- className: cx(cellX, cellY[density], !wrap && "whitespace-nowrap", className),
30
+ className: cx(cellX, cellY[density], wrap ? wrapY[density] : "whitespace-nowrap", className),
26
31
  children
27
32
  });
28
33
  }
@@ -61,10 +61,10 @@ function Chip({ value, label, icon, count, disabled, testId, size }) {
61
61
  value,
62
62
  disabled,
63
63
  "data-testid": testId,
64
- className: cx("group aria-pressed:bg-(--theme-accent-subtle)", buttonClasses({
64
+ className: cx(buttonClasses({
65
65
  variant: "outline",
66
66
  size
67
- })),
67
+ }), "group focus-visible:-outline-offset-2 aria-pressed:bg-(--theme-accent-subtle)"),
68
68
  children: [
69
69
  icon && /* @__PURE__ */ jsx("span", {
70
70
  className: "inline-flex shrink-0 transition-opacity group-not-aria-pressed:opacity-55",
@@ -1,6 +1,6 @@
1
1
  import { cx } from "../cx.js";
2
2
  //#region src/components/buttonClasses.ts
3
- var base = "relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-md font-medium no-underline transition-[color,background-color,border-color,box-shadow,translate] duration-150 hover:not-disabled:not-aria-disabled:-translate-y-px hover:not-disabled:not-aria-disabled:shadow-(--theme-shadow-sm) active:not-disabled:not-aria-disabled:translate-y-0 active:not-disabled:not-aria-disabled:shadow-none motion-reduce:hover:not-disabled:not-aria-disabled:translate-y-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--theme-focus) disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50";
3
+ var base = "relative inline-flex shrink-0 cursor-pointer items-center justify-center gap-1.5 whitespace-nowrap rounded-md font-medium no-underline transition-[color,background-color,border-color,box-shadow,translate] duration-150 hover:not-disabled:not-aria-disabled:not-[[aria-pressed]]:-translate-y-px hover:not-disabled:not-aria-disabled:not-[[aria-pressed]]:shadow-(--theme-shadow-sm) active:not-disabled:not-aria-disabled:translate-y-0 active:not-disabled:not-aria-disabled:shadow-none motion-reduce:hover:not-disabled:not-aria-disabled:translate-y-0 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-(--theme-focus) disabled:cursor-not-allowed disabled:opacity-50 aria-disabled:cursor-not-allowed aria-disabled:opacity-50";
4
4
  var variants = {
5
5
  primary: "bg-(--theme-accent) text-(--theme-accent-text) hover:not-disabled:not-aria-disabled:bg-(--theme-accent-hover)",
6
6
  secondary: "border border-(--theme-border) bg-(--theme-panel-bg) text-(--theme-app) hover:not-disabled:not-aria-disabled:bg-(--theme-hover)",
@@ -21,7 +21,7 @@ interface GraphRun {
21
21
  * marked on each job and step they're about, which matters most for one skipped without a log. */
22
22
  warnings?: readonly RunWarning[] | null | undefined;
23
23
  }
24
- export default function DependencyGraph({ run, preview, removeBorder, viewMode, setViewMode, layout, editor, yamlJobs, inputs, workflow, fixedHeight, openStep, onCloseLog, stickySidebar, }: {
24
+ export default function DependencyGraph({ run, preview, removeBorder, viewMode, setViewMode, layout, editor, yamlJobs, inputs, workflow, fixedHeight, openStep, onCloseLog, stickySidebar, bleed, }: {
25
25
  run: GraphRun;
26
26
  preview?: boolean;
27
27
  removeBorder?: boolean | undefined;
@@ -44,6 +44,9 @@ export default function DependencyGraph({ run, preview, removeBorder, viewMode,
44
44
  onCloseLog?: (() => void) | undefined;
45
45
  /** Pins the job list as the page scrolls, under whatever the page already pins at its top. */
46
46
  stickySidebar?: boolean | undefined;
47
+ /** On a phone, the jobs' panel, graph or list, reaches past the host's content padding (`p-4`) to
48
+ * the container's edges, without side borders or corners, so a job's row has the screen's width. */
49
+ bleed?: boolean | undefined;
47
50
  }): import("react").JSX.Element | undefined;
48
51
  export declare function DependencyGraphPreview(inputs: {
49
52
  yml?: Record<string, unknown>;