@parallelworks/ui 0.82.2 → 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.
- package/dist/components/CodeBlock.d.ts +4 -1
- package/dist/components/CodeBlock.js +2 -2
- package/dist/components/Meter.d.ts +6 -2
- package/dist/components/Meter.js +21 -3
- package/dist/components/Pagination.js +8 -3
- package/dist/components/Table.d.ts +3 -3
- package/dist/components/Table.js +9 -4
- package/dist/components/ToggleGroup.js +2 -2
- package/dist/components/buttonClasses.js +1 -1
- package/dist/graph/DependencyGraph.d.ts +4 -1
- package/dist/graph/DependencyGraph.js +85 -39
- package/dist/graph/GraphNode.d.ts +3 -0
- package/dist/graph/GraphNode.js +7 -4
- package/dist/graph/TreeView.js +4 -4
- package/dist/graph/connectorGeometry.d.ts +10 -1
- package/dist/graph/connectorGeometry.js +81 -3
- package/dist/graph/viewport.d.ts +25 -1
- package/dist/graph/viewport.js +27 -5
- package/dist/list/ListTable.d.ts +4 -1
- package/dist/list/ListTable.js +4 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
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. */
|
package/dist/components/Meter.js
CHANGED
|
@@ -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
|
-
|
|
49
|
-
|
|
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-
|
|
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: "
|
|
136
|
-
}), "min-w-
|
|
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 = {
|
|
@@ -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
|
|
5
|
-
*
|
|
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. */
|
package/dist/components/Table.js
CHANGED
|
@@ -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-
|
|
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-
|
|
18
|
-
dense: "py-1
|
|
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],
|
|
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(
|
|
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>;
|
|
@@ -26,7 +26,7 @@ import { WarningMarks, WarningMarksContext, locateWarnings, rowKey } from "./run
|
|
|
26
26
|
import { Joblist } from "./JobSummary.js";
|
|
27
27
|
import { Store, useStore } from "./store.js";
|
|
28
28
|
import { Lineage, LineageContext } from "./lineage.js";
|
|
29
|
-
import { DimRules, JobNode, setDimmed } from "./GraphNode.js";
|
|
29
|
+
import { DimRules, GraphDirectionContext, JobNode, setDimmed } from "./GraphNode.js";
|
|
30
30
|
import { ConnectorLayers } from "./Connectors.js";
|
|
31
31
|
import { routeConnectors, sameBoxes } from "./connectorGeometry.js";
|
|
32
32
|
import { isJobRecord } from "./types.js";
|
|
@@ -35,7 +35,7 @@ import { MatrixGroupNode, MatrixGroupSummaryItem } from "./MatrixGroup.js";
|
|
|
35
35
|
import { StepSummaries } from "./StepSummaries.js";
|
|
36
36
|
import { StepSummary } from "./StepSummary.js";
|
|
37
37
|
import TreeView from "./TreeView.js";
|
|
38
|
-
import { FIT_MAX_SCALE, MIN_SCALE, ZOOM_STEP, enclosing, fitMargin, fitView, intoView, viewShowing, wheelFactor, zoomAround, zoomLimit } from "./viewport.js";
|
|
38
|
+
import { FIT_MAX_SCALE, MIN_SCALE, ZOOM_STEP, enclosing, fitMargin, fitView, intoReach, intoView, reachKept, viewShowing, wheelFactor, zoomAround, zoomLimit } from "./viewport.js";
|
|
39
39
|
import { Fragment, Suspense, createContext, lazy, memo, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
40
40
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
41
41
|
import { flushSync } from "react-dom";
|
|
@@ -294,14 +294,20 @@ function sizingTransitions(el) {
|
|
|
294
294
|
}
|
|
295
295
|
var HOLD_MS = 2e3;
|
|
296
296
|
var skippedBefore = (slots, i) => (slots?.[i] ?? i) - (i === 0 ? 0 : (slots?.[i - 1] ?? i - 1) + 1);
|
|
297
|
-
function measureBoxes(cols, pathPrefix, wrapper) {
|
|
297
|
+
function measureBoxes(cols, pathPrefix, wrapper, down) {
|
|
298
298
|
const boxes = /* @__PURE__ */ new Map();
|
|
299
299
|
for (const col of cols) for (const [head] of col) {
|
|
300
300
|
const el = head === void 0 ? null : document.getElementById(`node_${pathPrefix}${head}`);
|
|
301
|
-
if (head !== void 0 && el)
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
301
|
+
if (head !== void 0 && el) {
|
|
302
|
+
const box = {
|
|
303
|
+
...offsetWithin(el, wrapper),
|
|
304
|
+
width: el.offsetWidth
|
|
305
|
+
};
|
|
306
|
+
boxes.set(head, down ? {
|
|
307
|
+
...box,
|
|
308
|
+
height: el.offsetHeight
|
|
309
|
+
} : box);
|
|
310
|
+
}
|
|
305
311
|
}
|
|
306
312
|
return boxes;
|
|
307
313
|
}
|
|
@@ -313,6 +319,7 @@ var Subgraph = memo(function Subgraph({ jobs, pathPrefix, depth, preview, animT,
|
|
|
313
319
|
const editorApi = useGraphEditor();
|
|
314
320
|
const selectedEdges = useSelectedEdges(editable ? editorApi : null);
|
|
315
321
|
const zBase = depth * SUBGRAPH_Z_STEP;
|
|
322
|
+
const down = useContext(GraphDirectionContext) === "down";
|
|
316
323
|
const placed = useMemo(() => layout ?? (depth > 0 ? positionsOf(jobs) : void 0), [
|
|
317
324
|
layout,
|
|
318
325
|
depth,
|
|
@@ -459,9 +466,10 @@ var Subgraph = memo(function Subgraph({ jobs, pathPrefix, depth, preview, animT,
|
|
|
459
466
|
toggleMatrix
|
|
460
467
|
]);
|
|
461
468
|
const nodes = useMemo(() => /* @__PURE__ */ jsx("div", {
|
|
462
|
-
className: "flex relative",
|
|
469
|
+
className: down ? "flex flex-col items-center relative" : "flex relative",
|
|
463
470
|
children: dependencyCols.map((col, c) => /* @__PURE__ */ jsx("div", {
|
|
464
|
-
|
|
471
|
+
className: down ? "flex items-start justify-center" : void 0,
|
|
472
|
+
style: down ? void 0 : emptyColumnsStyle(skippedBefore(colSlots, c)),
|
|
465
473
|
children: col.map((jobNames, i) => {
|
|
466
474
|
const first = jobNames[0];
|
|
467
475
|
if (first === void 0) return null;
|
|
@@ -504,16 +512,17 @@ var Subgraph = memo(function Subgraph({ jobs, pathPrefix, depth, preview, animT,
|
|
|
504
512
|
pathPrefix,
|
|
505
513
|
toggleMatrix,
|
|
506
514
|
handlers,
|
|
507
|
-
editable
|
|
515
|
+
editable,
|
|
516
|
+
down
|
|
508
517
|
]);
|
|
509
518
|
if (!dependencyCols[0]?.length) return null;
|
|
510
519
|
const wrapper = wrapperRef.current;
|
|
511
|
-
const measured = wrapper ? measureBoxes(dependencyCols, pathPrefix, wrapper) : /* @__PURE__ */ new Map();
|
|
520
|
+
const measured = wrapper ? measureBoxes(dependencyCols, pathPrefix, wrapper, down) : /* @__PURE__ */ new Map();
|
|
512
521
|
const last = routedRef.current;
|
|
513
522
|
const routed = last && last.directDeps === directDeps && sameBoxes(last.boxes, measured) ? last : {
|
|
514
523
|
boxes: measured,
|
|
515
524
|
directDeps,
|
|
516
|
-
edges: routeConnectors(dependencyCols, colSlots.some((column, c) => column !== c), directDeps, measured, 37)
|
|
525
|
+
edges: routeConnectors(dependencyCols, colSlots.some((column, c) => column !== c), directDeps, measured, 37, down ? "down" : "right")
|
|
517
526
|
};
|
|
518
527
|
routedRef.current = routed;
|
|
519
528
|
const { boxes, edges } = routed;
|
|
@@ -704,6 +713,8 @@ function graphBounds(content) {
|
|
|
704
713
|
}
|
|
705
714
|
return bounds;
|
|
706
715
|
}
|
|
716
|
+
var NARROW_PANEL_PX = 640;
|
|
717
|
+
var BLEED_CLASSES = "max-sm:-mx-4 max-sm:w-auto max-sm:rounded-none max-sm:border-x-0";
|
|
707
718
|
var READABLE_SCALE = .5;
|
|
708
719
|
/** Where a job or step a warning took the graph to shows: the panel below its view bar, which sits
|
|
709
720
|
* over the graph. */
|
|
@@ -741,7 +752,7 @@ function flash(row) {
|
|
|
741
752
|
easing: "ease-in"
|
|
742
753
|
});
|
|
743
754
|
}
|
|
744
|
-
function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag", setViewMode, layout, editor, yamlJobs, inputs, workflow, fixedHeight, openStep, onCloseLog, stickySidebar }) {
|
|
755
|
+
function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag", setViewMode, layout, editor, yamlJobs, inputs, workflow, fixedHeight, openStep, onCloseLog, stickySidebar, bleed = false }) {
|
|
745
756
|
const engine = useWorkflowEngine();
|
|
746
757
|
const animT = .125;
|
|
747
758
|
const rootJobs = isJobRecord(run.executedJobs) ? run.executedJobs : {};
|
|
@@ -800,6 +811,7 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
|
|
|
800
811
|
}
|
|
801
812
|
}, [preview]);
|
|
802
813
|
const containerHeight = topOffset !== null ? Math.max(windowSize.h - topOffset - 16, 160) : null;
|
|
814
|
+
const runsDown = !preview && !editor && windowSize.w < NARROW_PANEL_PX;
|
|
803
815
|
const [fitHeight, setFitHeight] = useState(null);
|
|
804
816
|
const fitViewRef = useRef(null);
|
|
805
817
|
useEffect(() => {
|
|
@@ -1012,7 +1024,12 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
|
|
|
1012
1024
|
if (dx !== 0 || dy !== 0) requestAnimationFrame(() => transform.setTransform(x + dx, y + dy, scale, FIT_ANIMATION_MS, "easeOut"));
|
|
1013
1025
|
return;
|
|
1014
1026
|
}
|
|
1015
|
-
const
|
|
1027
|
+
const narrow = !preview && !editor && panel.width < NARROW_PANEL_PX;
|
|
1028
|
+
const view = fitView(bounds, panel, narrow || runsDown ? {
|
|
1029
|
+
minScale: READABLE_SCALE,
|
|
1030
|
+
top: viewBarRef.current?.offsetHeight ?? 0,
|
|
1031
|
+
width: runsDown
|
|
1032
|
+
} : void 0);
|
|
1016
1033
|
if (!view) return;
|
|
1017
1034
|
if (animationMs > 0) {
|
|
1018
1035
|
transform.setTransform(view.x, view.y, view.scale, animationMs, "easeOut");
|
|
@@ -1024,7 +1041,9 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
|
|
|
1024
1041
|
}, [
|
|
1025
1042
|
nodeOf,
|
|
1026
1043
|
editor,
|
|
1027
|
-
runRowOf
|
|
1044
|
+
runRowOf,
|
|
1045
|
+
preview,
|
|
1046
|
+
runsDown
|
|
1028
1047
|
]);
|
|
1029
1048
|
const fitWhenSettled = useCallback(async () => {
|
|
1030
1049
|
const container = containerRef.current;
|
|
@@ -1135,6 +1154,27 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
|
|
|
1135
1154
|
const moved = useCallback(() => {
|
|
1136
1155
|
viewed.current = true;
|
|
1137
1156
|
}, []);
|
|
1157
|
+
const keepInReach = useCallback(() => {
|
|
1158
|
+
const container = containerRef.current;
|
|
1159
|
+
const content = graphContentRef.current;
|
|
1160
|
+
const transform = transformRef.current;
|
|
1161
|
+
const bounds = content ? graphBounds(content) : null;
|
|
1162
|
+
if (!container || !transform || !bounds) return;
|
|
1163
|
+
const { positionX, positionY, scale } = transform.instance.state;
|
|
1164
|
+
const panel = {
|
|
1165
|
+
left: 0,
|
|
1166
|
+
top: 0,
|
|
1167
|
+
width: container.clientWidth,
|
|
1168
|
+
height: container.clientHeight
|
|
1169
|
+
};
|
|
1170
|
+
const { dx, dy } = intoReach({
|
|
1171
|
+
left: positionX + bounds.minX * scale,
|
|
1172
|
+
top: positionY + bounds.minY * scale,
|
|
1173
|
+
width: (bounds.maxX - bounds.minX) * scale,
|
|
1174
|
+
height: (bounds.maxY - bounds.minY) * scale
|
|
1175
|
+
}, panel, reachKept(panel));
|
|
1176
|
+
if (dx !== 0 || dy !== 0) transform.setTransform(positionX + dx, positionY + dy, scale, FIT_ANIMATION_MS, "easeOut");
|
|
1177
|
+
}, []);
|
|
1138
1178
|
const zoomTo = useCallback((view, animationMs) => {
|
|
1139
1179
|
viewed.current = true;
|
|
1140
1180
|
transformRef.current?.setTransform(view.x, view.y, view.scale, animationMs, "easeOut");
|
|
@@ -1484,7 +1524,7 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
|
|
|
1484
1524
|
}),
|
|
1485
1525
|
!preview && /* @__PURE__ */ jsx(AnnotationBanner, { executedJobs: jobs }),
|
|
1486
1526
|
viewMode === "tree" && !preview ? /* @__PURE__ */ jsx("div", {
|
|
1487
|
-
className: "panel w-full border",
|
|
1527
|
+
className: cx("panel w-full border", bleed && BLEED_CLASSES),
|
|
1488
1528
|
children: /* @__PURE__ */ jsx(TreeView, {
|
|
1489
1529
|
jobs,
|
|
1490
1530
|
slug: runSlug,
|
|
@@ -1494,9 +1534,9 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
|
|
|
1494
1534
|
}) : /* @__PURE__ */ jsxs("div", {
|
|
1495
1535
|
ref: setContainerElement,
|
|
1496
1536
|
tabIndex: editor ? -1 : void 0,
|
|
1497
|
-
className: cx("panel w-full overflow-hidden relative", removeBorder ? "border-none" : "border", editor && "outline-none"),
|
|
1537
|
+
className: cx("panel w-full overflow-hidden relative", removeBorder ? "border-none" : "border", bleed && BLEED_CLASSES, editor && "outline-none"),
|
|
1498
1538
|
style: {
|
|
1499
|
-
height: fixedHeight ?? (fitHeight !== null ? `${Math.min(fitHeight, containerHeight ?? windowSize.h * .62)}px` : containerHeight !== null ? `${containerHeight}px` : "62vh"),
|
|
1539
|
+
height: fixedHeight ?? (fitHeight !== null ? `${Math.min(fitHeight, runsDown ? Math.max(containerHeight ?? 0, windowSize.h * .75) : containerHeight ?? windowSize.h * .62)}px` : containerHeight !== null ? `${containerHeight}px` : "62vh"),
|
|
1500
1540
|
minHeight: preview && fitHeight !== null && !fixedHeight ? 0 : 160
|
|
1501
1541
|
},
|
|
1502
1542
|
children: [/* @__PURE__ */ jsx(TransformWrapper, {
|
|
@@ -1511,8 +1551,11 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
|
|
|
1511
1551
|
onInit: () => {
|
|
1512
1552
|
setTimeout(invalidateConnectors, 0);
|
|
1513
1553
|
},
|
|
1554
|
+
velocityAnimation: { sensitivityTouch: 0 },
|
|
1514
1555
|
onPanning: moved,
|
|
1556
|
+
onPanningStop: keepInReach,
|
|
1515
1557
|
onPinch: moved,
|
|
1558
|
+
onPinchStop: keepInReach,
|
|
1516
1559
|
onTransform,
|
|
1517
1560
|
children: () => /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
|
|
1518
1561
|
ref: viewBarRef,
|
|
@@ -1542,27 +1585,30 @@ function DependencyGraph({ run, preview = false, removeBorder, viewMode = "dag",
|
|
|
1542
1585
|
ref: graphContentRef,
|
|
1543
1586
|
className: "flex relative mr-[8000px]",
|
|
1544
1587
|
style: { minHeight: 4e3 },
|
|
1545
|
-
children: /* @__PURE__ */ jsx(
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1588
|
+
children: /* @__PURE__ */ jsx(GraphDirectionContext, {
|
|
1589
|
+
value: runsDown ? "down" : "right",
|
|
1590
|
+
children: /* @__PURE__ */ jsx(Subgraph, {
|
|
1591
|
+
jobs,
|
|
1592
|
+
pathPrefix: pathAppend,
|
|
1593
|
+
depth: 0,
|
|
1594
|
+
preview,
|
|
1595
|
+
animT,
|
|
1596
|
+
expandedSubworkflows,
|
|
1597
|
+
toggleSubworkflow,
|
|
1598
|
+
openInNewGraph,
|
|
1599
|
+
setSublogOpen,
|
|
1600
|
+
invalidateConnectors,
|
|
1601
|
+
requestFit: fitWhenSettled,
|
|
1602
|
+
stepsOpen,
|
|
1603
|
+
toggleSteps,
|
|
1604
|
+
matrixOpen,
|
|
1605
|
+
toggleMatrix,
|
|
1606
|
+
onReady: fitGraph,
|
|
1607
|
+
layout: placed,
|
|
1608
|
+
graph: rootGraph,
|
|
1609
|
+
editable: !!editorApi && currentPath.length === 0
|
|
1610
|
+
}, `sg-${executedJobsPathIdx}-${pathAppend}`)
|
|
1611
|
+
})
|
|
1566
1612
|
})
|
|
1567
1613
|
}) })] })
|
|
1568
1614
|
}), editorOverlays]
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
|
+
import type { GraphDirection } from './connectorGeometry.js';
|
|
2
3
|
import { type JobHandlers } from './JobSummary.js';
|
|
3
4
|
import type { WorkflowJob } from './types.js';
|
|
4
5
|
export declare const dimClasses: (depth: number) => {
|
|
@@ -10,6 +11,8 @@ export declare const dimClasses: (depth: number) => {
|
|
|
10
11
|
export declare function DimRules({ depth }: {
|
|
11
12
|
depth: number;
|
|
12
13
|
}): import("react").JSX.Element;
|
|
14
|
+
/** Which way the graph around a node runs; a run's graph on a phone runs down. */
|
|
15
|
+
export declare const GraphDirectionContext: import("react").Context<GraphDirection>;
|
|
13
16
|
/** Dims what of a graph level isn't in the hovered job's lineage, while one is hovered. */
|
|
14
17
|
export declare function setDimmed(wrapper: HTMLElement, depth: number, hovering: boolean): void;
|
|
15
18
|
interface NodeShell {
|
package/dist/graph/GraphNode.js
CHANGED
|
@@ -2,7 +2,7 @@ import { BoxGrip } from "./editorRows.js";
|
|
|
2
2
|
import { emptyRowsStyle } from "./gridSpacing.js";
|
|
3
3
|
import { Joblist } from "./JobSummary.js";
|
|
4
4
|
import { useLineage, useLineageStore } from "./lineage.js";
|
|
5
|
-
import { memo } from "react";
|
|
5
|
+
import { createContext, memo, useContext } from "react";
|
|
6
6
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
7
|
//#region src/graph/GraphNode.tsx
|
|
8
8
|
var dimClasses = (depth) => ({
|
|
@@ -19,6 +19,8 @@ function DimRules({ depth }) {
|
|
|
19
19
|
children: [`.${level} .${node}{opacity:.5}.${level} .${edge}{opacity:.3}`, "@media (prefers-reduced-motion:reduce){.dag-fade{transition:none!important}}"]
|
|
20
20
|
});
|
|
21
21
|
}
|
|
22
|
+
/** Which way the graph around a node runs; a run's graph on a phone runs down. */
|
|
23
|
+
var GraphDirectionContext = createContext("right");
|
|
22
24
|
/** Dims what of a graph level isn't in the hovered job's lineage, while one is hovered. */
|
|
23
25
|
function setDimmed(wrapper, depth, hovering) {
|
|
24
26
|
wrapper.classList.toggle(dimClasses(depth).level, hovering);
|
|
@@ -26,6 +28,7 @@ function setDimmed(wrapper, depth, hovering) {
|
|
|
26
28
|
/** One node of the graph: its jobs' box, raised above connectors while its lineage is shown. */
|
|
27
29
|
function GraphNode({ id, job, rowsAbove, zBase, children }) {
|
|
28
30
|
const lineage = useLineageStore();
|
|
31
|
+
const down = useContext(GraphDirectionContext) === "down";
|
|
29
32
|
const highlight = useLineage((l) => l.node(job), 0);
|
|
30
33
|
const enter = () => lineage?.hover(job);
|
|
31
34
|
const leave = () => lineage?.hover(null);
|
|
@@ -35,10 +38,10 @@ function GraphNode({ id, job, rowsAbove, zBase, children }) {
|
|
|
35
38
|
return /* @__PURE__ */ jsxs("div", {
|
|
36
39
|
id,
|
|
37
40
|
role: "none",
|
|
38
|
-
className: "relative m-24",
|
|
41
|
+
className: down ? "relative mx-8 my-16" : "relative m-24",
|
|
39
42
|
style: {
|
|
40
43
|
zIndex: (highlight > 0 ? 25 : 1) + zBase,
|
|
41
|
-
...emptyRowsStyle(rowsAbove)
|
|
44
|
+
...down ? {} : emptyRowsStyle(rowsAbove)
|
|
42
45
|
},
|
|
43
46
|
onMouseEnter: enter,
|
|
44
47
|
onMouseLeave: leave,
|
|
@@ -70,4 +73,4 @@ var JobNode = memo(function JobNode({ jobNames, jobs, handlers, preview, editabl
|
|
|
70
73
|
});
|
|
71
74
|
});
|
|
72
75
|
//#endregion
|
|
73
|
-
export { DimRules, GraphNode, JobNode, dimClasses, setDimmed };
|
|
76
|
+
export { DimRules, GraphDirectionContext, GraphNode, JobNode, dimClasses, setDimmed };
|
package/dist/graph/TreeView.js
CHANGED
|
@@ -77,20 +77,20 @@ function JobRow({ jobName, job, isExpanded, onToggle, prefix, state }) {
|
|
|
77
77
|
className: "flex min-w-0 items-center gap-1.5",
|
|
78
78
|
children: [/* @__PURE__ */ jsx("span", {
|
|
79
79
|
className: "font-mono text-sm text-[var(--theme-app)] truncate",
|
|
80
|
+
title: jobLabel(jobName, job),
|
|
80
81
|
children: jobLabel(jobName, job)
|
|
81
82
|
}), /* @__PURE__ */ jsx(WarningMark, { warnings: marks.under(rowKey(prefix, jobName)) })]
|
|
82
83
|
}), dependencies.length > 0 && /* @__PURE__ */ jsxs("div", {
|
|
83
|
-
className: "flex items-center gap-1.5",
|
|
84
|
+
className: "flex flex-wrap items-center gap-x-1.5 gap-y-1",
|
|
84
85
|
children: [/* @__PURE__ */ jsx("span", {
|
|
85
|
-
className: "text-
|
|
86
|
+
className: "whitespace-nowrap text-xs text-[var(--theme-muted-text-color)]",
|
|
86
87
|
children: t.dependsOn
|
|
87
88
|
}), dependencies.map((dep) => /* @__PURE__ */ jsx("span", {
|
|
88
|
-
className: "text-
|
|
89
|
+
className: "whitespace-nowrap text-xs px-1.5 py-0.5 rounded bg-[var(--theme-muted-panel-bg)] text-[var(--theme-muted-text-color)]",
|
|
89
90
|
children: jobLabel(dep)
|
|
90
91
|
}, dep))]
|
|
91
92
|
})]
|
|
92
93
|
}),
|
|
93
|
-
/* @__PURE__ */ jsx("div", { className: "flex-1" }),
|
|
94
94
|
/* @__PURE__ */ jsx(Elapsed, {
|
|
95
95
|
start: job.startedAt,
|
|
96
96
|
end: job.completedAt,
|
|
@@ -18,11 +18,18 @@ export declare function arcRadius(minRise: number, colGap: number): number;
|
|
|
18
18
|
* a connector spanning several columns runs level until it nears its target.
|
|
19
19
|
*/
|
|
20
20
|
export declare function connectorPath(start: Point, end: Point, r: number, colGap: number): ConnectorPath;
|
|
21
|
+
/** Which way a graph runs: ranks as columns, left to right, or as rows, top to bottom. */
|
|
22
|
+
export type GraphDirection = 'right' | 'down';
|
|
23
|
+
/** A connector from a node's bottom to a later row's node's top: `connectorPath` turned a quarter,
|
|
24
|
+
* with the bends in the gap above `end`'s row. */
|
|
25
|
+
export declare function connectorPathDown(start: Point, end: Point, r: number, rowGap: number): ConnectorPath;
|
|
21
26
|
/** A node's place in its graph level, as laid out. */
|
|
22
27
|
export interface Box {
|
|
23
28
|
x: number;
|
|
24
29
|
y: number;
|
|
25
30
|
width: number;
|
|
31
|
+
/** Where a graph runs down, its height, which places the connectors leaving its bottom. */
|
|
32
|
+
height?: number | undefined;
|
|
26
33
|
}
|
|
27
34
|
/** Whether two layouts put every node in the same place. */
|
|
28
35
|
export declare function sameBoxes(a: Map<string, Box>, b: Map<string, Box>): boolean;
|
|
@@ -41,4 +48,6 @@ export interface DrawnEdge {
|
|
|
41
48
|
*/
|
|
42
49
|
export declare function routeConnectors(cols: string[][][],
|
|
43
50
|
/** Whether columns are left empty between those drawn, so gaps between them differ. */
|
|
44
|
-
spaced: boolean, deps: Record<string, string[]>, boxes: Map<string, Box>, connectorY: number
|
|
51
|
+
spaced: boolean, deps: Record<string, string[]>, boxes: Map<string, Box>, connectorY: number,
|
|
52
|
+
/** `down` joins a node's bottom to the top of one in a later row, at their middles. */
|
|
53
|
+
direction?: GraphDirection): DrawnEdge[];
|