@parallelworks/ui 0.34.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +201 -0
  2. package/dist/ai/components/ChatEmptyState.js +71 -86
  3. package/dist/ai/components/ChatInput.d.ts +11 -3
  4. package/dist/ai/components/ChatInput.js +19 -11
  5. package/dist/ai/components/ChatSidebar.js +12 -130
  6. package/dist/ai/components/ChatStage.d.ts +11 -0
  7. package/dist/ai/components/ChatStage.js +26 -0
  8. package/dist/ai/components/ComposerChrome.d.ts +3 -0
  9. package/dist/ai/components/ComposerChrome.js +21 -2
  10. package/dist/ai/components/conversationActions.d.ts +23 -0
  11. package/dist/ai/components/conversationActions.js +108 -0
  12. package/dist/ai/index.d.ts +4 -3
  13. package/dist/ai/index.js +6 -5
  14. package/dist/ai/strings.d.ts +1 -0
  15. package/dist/ai/strings.js +2 -1
  16. package/dist/ai/ui/ConversationSidebar.d.ts +3 -2
  17. package/dist/ai/ui/ConversationSidebar.js +2 -1
  18. package/dist/ai/ui/sidebar.d.ts +6 -0
  19. package/dist/ai/ui/sidebar.js +10 -1
  20. package/dist/components/Dropdown.js +3 -1
  21. package/dist/components/FilterPill.js +1 -0
  22. package/dist/components/Provider.d.ts +14 -0
  23. package/dist/components/Provider.js +14 -3
  24. package/dist/components/SegmentedControl.d.ts +24 -0
  25. package/dist/components/SegmentedControl.js +69 -0
  26. package/dist/editing.d.ts +32 -0
  27. package/dist/editor/FormCodePanel.js +1 -1
  28. package/dist/engine.d.ts +3 -0
  29. package/dist/form/FieldWrapper.js +1 -1
  30. package/dist/form/Fields/CheckboxGroupField.d.ts +3 -3
  31. package/dist/form/Fields/CheckboxGroupField.js +10 -4
  32. package/dist/form/Fields/RadioField.d.ts +5 -3
  33. package/dist/form/Fields/RadioField.js +25 -17
  34. package/dist/form/Fields/fieldOption.d.ts +12 -0
  35. package/dist/form/Fields/fieldOption.js +17 -0
  36. package/dist/form/Form.d.ts +4 -2
  37. package/dist/form/Form.js +170 -68
  38. package/dist/form/FormLayout.d.ts +7 -0
  39. package/dist/form/FormLayout.js +144 -0
  40. package/dist/form/SpecialTypes/FormikCustomDropdown.js +1 -1
  41. package/dist/form/SpecialTypes/FormikCustomInput.js +7 -0
  42. package/dist/form/Wizard/WizardContainer.d.ts +1 -1
  43. package/dist/form/Wizard/WizardContainer.js +244 -49
  44. package/dist/form/Wizard/WizardNavigation.js +4 -4
  45. package/dist/form/Wizard/WizardStepContent.d.ts +1 -1
  46. package/dist/form/Wizard/WizardStepContent.js +19 -10
  47. package/dist/form/Wizard/WizardStepIndicator.js +35 -7
  48. package/dist/form/Wizard/types.d.ts +19 -6
  49. package/dist/form/Wizard/useWizardState.d.ts +1 -0
  50. package/dist/form/Wizard/useWizardState.js +5 -1
  51. package/dist/form/Wizard/utils.d.ts +7 -0
  52. package/dist/form/Wizard/utils.js +18 -1
  53. package/dist/form/index.d.ts +2 -0
  54. package/dist/form/index.js +4 -2
  55. package/dist/form/layout.d.ts +50 -0
  56. package/dist/form/layout.js +125 -0
  57. package/dist/form/layoutCSS.d.ts +10 -0
  58. package/dist/form/layoutCSS.js +234 -0
  59. package/dist/form/lib.d.ts +2 -0
  60. package/dist/form/lib.js +17 -2
  61. package/dist/form/types/fieldTypes.d.ts +8 -23
  62. package/dist/graph/DependencyGraph.d.ts +20 -1
  63. package/dist/graph/DependencyGraph.js +437 -142
  64. package/dist/graph/MatrixGroup.d.ts +3 -1
  65. package/dist/graph/MatrixGroup.js +31 -28
  66. package/dist/graph/TreeView.js +6 -6
  67. package/dist/graph/gridSpacing.d.ts +7 -0
  68. package/dist/graph/gridSpacing.js +10 -0
  69. package/dist/graph/util.d.ts +2 -0
  70. package/dist/graph/util.js +2 -2
  71. package/dist/index.d.ts +1 -0
  72. package/dist/index.js +2 -1
  73. package/dist/list/ListViewControls.d.ts +15 -0
  74. package/dist/list/ListViewControls.js +9 -3
  75. package/dist/list/index.d.ts +1 -1
  76. package/dist/list/index.js +2 -2
  77. package/dist/styles/base.css +3 -1
  78. package/dist/styles/theme.css +5 -0
  79. package/dist/styles.css +1 -1
  80. package/dist/theme/index.d.ts +2 -0
  81. package/dist/theme/index.js +3 -2
  82. package/package.json +4 -2
  83. package/dist/ai/components/ComposerFrame.d.ts +0 -16
  84. package/dist/ai/components/ComposerFrame.js +0 -33
@@ -1,6 +1,6 @@
1
- import { FieldProvider, useFieldControlProps, useFieldLabelledByProps, useFieldRequired } from "./fieldContext.js";
2
1
  import { applySecondaryField, findSecondaryOption } from "./utils/secondaryField.js";
3
2
  import { enforceOneMustBeTrue, flattenGroups, impureSetValueFromPath, initializeValues, resolvedFlag } from "./lib.js";
3
+ import { FieldProvider, useFieldControlProps, useFieldLabelledByProps, useFieldRequired } from "./fieldContext.js";
4
4
  import { FieldWrapper } from "./FieldWrapper.js";
5
5
  import { getInvalidDurationPaths, setDurationValidity } from "./Fields/durationValidity.js";
6
6
  import FormikCustomDropdown_default from "./SpecialTypes/FormikCustomDropdown.js";
@@ -9,10 +9,12 @@ import FormikNumericInput from "./SpecialTypes/FormikNumericInput.js";
9
9
  import { getPlaceholder } from "./utils/getPlaceholder.js";
10
10
  import NumberField from "./Fields/NumberField.js";
11
11
  import { ResourceOptionIcon } from "./Fields/ResourceOptionIcon.js";
12
+ import { resolveFormLayout } from "./layout.js";
13
+ import { resolveLayoutCSS } from "./layoutCSS.js";
12
14
  import { FLEX_MAX_DURATION_RANGE, FLEX_WAIT_TIME_RANGE, STANDARD_MAX_DURATION_RANGE, collectPartitionDurationIssues, formatDuration, parseDuration } from "./utils/duration.js";
13
15
  import { FieldRegistryContext, Registry } from "./fieldRegistry.js";
14
16
  import { useParsedOpts } from "./useParsedOpts.js";
15
17
  import { DynamicDefaultsSync, DynamicForm, FieldsFromOptions, GroupHeader, MultiSelectionDropdown, collectFieldsWithDefaults, findSelfReferencingFieldNames, resolveMetaOverrides, useDynamicDefaultsSync } from "./Form.js";
16
18
  import DropdownWrapper from "./SpecialTypes/DropdownWrapper.js";
17
19
  import FormikMultiSelectDropdown from "./SpecialTypes/FormikMultiSelectDropdown.js";
18
- export { DropdownWrapper, DynamicDefaultsSync, DynamicForm, FLEX_MAX_DURATION_RANGE, FLEX_WAIT_TIME_RANGE, FieldProvider, FieldRegistryContext, FieldWrapper, FieldsFromOptions, FormikCustomDropdown_default as FormikCustomDropdown, FormikCustomInput, FormikMultiSelectDropdown, FormikNumericInput, GroupHeader, MultiSelectionDropdown, NumberField, Registry, ResourceOptionIcon, STANDARD_MAX_DURATION_RANGE, applySecondaryField, collectFieldsWithDefaults, collectPartitionDurationIssues, enforceOneMustBeTrue, findSecondaryOption, findSelfReferencingFieldNames, flattenGroups, formatDuration, getInvalidDurationPaths, getPlaceholder, impureSetValueFromPath, initializeValues, parseDuration, resolveMetaOverrides, resolvedFlag, setDurationValidity, useDynamicDefaultsSync, useFieldControlProps, useFieldLabelledByProps, useFieldRequired, useParsedOpts };
20
+ export { DropdownWrapper, DynamicDefaultsSync, DynamicForm, FLEX_MAX_DURATION_RANGE, FLEX_WAIT_TIME_RANGE, FieldProvider, FieldRegistryContext, FieldWrapper, FieldsFromOptions, FormikCustomDropdown_default as FormikCustomDropdown, FormikCustomInput, FormikMultiSelectDropdown, FormikNumericInput, GroupHeader, MultiSelectionDropdown, NumberField, Registry, ResourceOptionIcon, STANDARD_MAX_DURATION_RANGE, applySecondaryField, collectFieldsWithDefaults, collectPartitionDurationIssues, enforceOneMustBeTrue, findSecondaryOption, findSelfReferencingFieldNames, flattenGroups, formatDuration, getInvalidDurationPaths, getPlaceholder, impureSetValueFromPath, initializeValues, parseDuration, resolveFormLayout, resolveLayoutCSS, resolveMetaOverrides, resolvedFlag, setDurationValidity, useDynamicDefaultsSync, useFieldControlProps, useFieldLabelledByProps, useFieldRequired, useParsedOpts };
@@ -0,0 +1,50 @@
1
+ export type LayoutBreakpoint = 'base' | 'sm' | 'md' | 'lg';
2
+ export type ResponsiveLayout<T> = T | Partial<Record<LayoutBreakpoint, T>>;
3
+ export type LayoutTracks = number | number[];
4
+ export type LayoutGap = 'none' | 'sm' | 'md' | 'lg';
5
+ interface LayoutPlacement {
6
+ span?: ResponsiveLayout<number>;
7
+ css?: string;
8
+ }
9
+ export type FormLayoutNode = (LayoutPlacement & {
10
+ type: 'field';
11
+ field: string;
12
+ }) | (LayoutPlacement & {
13
+ type: 'stack';
14
+ gap?: LayoutGap;
15
+ children: FormLayoutNode[];
16
+ }) | (LayoutPlacement & {
17
+ type: 'grid';
18
+ columns: ResponsiveLayout<LayoutTracks>;
19
+ align?: 'independent' | 'rows';
20
+ gap?: LayoutGap;
21
+ children: FormLayoutNode[];
22
+ }) | (LayoutPlacement & {
23
+ type: 'section';
24
+ label: string;
25
+ description?: string;
26
+ gap?: LayoutGap;
27
+ children: FormLayoutNode[];
28
+ });
29
+ export interface FormLayoutIssue {
30
+ path: (string | number)[];
31
+ code: 'invalid_node' | 'unknown_field' | 'duplicate_field';
32
+ }
33
+ export declare const layoutLimits: {
34
+ readonly depth: 64;
35
+ readonly nodes: 1024;
36
+ readonly children: 128;
37
+ readonly css: 65536;
38
+ readonly label: 256;
39
+ readonly description: 4096;
40
+ readonly field: 256;
41
+ };
42
+ export declare const layoutBreakpoints: readonly ['base', 'sm', 'md', 'lg'];
43
+ export declare function responsiveLayoutValues<T>(value: ResponsiveLayout<T> | undefined, fallback: T): Record<LayoutBreakpoint, T>;
44
+ /** Invalid layouts fall back as a whole, so a typo cannot hide an input or render it twice. */
45
+ export declare function resolveFormLayout(value: unknown, fieldNames: readonly string[]): {
46
+ layout: FormLayoutNode | undefined;
47
+ remaining: string[];
48
+ issues: FormLayoutIssue[];
49
+ };
50
+ export {};
@@ -0,0 +1,125 @@
1
+ //#region src/form/layout.ts
2
+ var layoutLimits = {
3
+ depth: 64,
4
+ nodes: 1024,
5
+ children: 128,
6
+ css: 65536,
7
+ label: 256,
8
+ description: 4096,
9
+ field: 256
10
+ };
11
+ var layoutBreakpoints = [
12
+ "base",
13
+ "sm",
14
+ "md",
15
+ "lg"
16
+ ];
17
+ function responsiveLayoutValues(value, fallback) {
18
+ const responsive = isRecord(value) ? value : void 0;
19
+ let current = responsive ? fallback : value ?? fallback;
20
+ return Object.fromEntries(layoutBreakpoints.map((key) => {
21
+ current = responsive?.[key] ?? current;
22
+ return [key, current];
23
+ }));
24
+ }
25
+ function isRecord(value) {
26
+ return value !== null && typeof value === "object" && !Array.isArray(value);
27
+ }
28
+ var count = (value) => typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 12;
29
+ function tracks(value) {
30
+ return count(value) || Array.isArray(value) && value.length >= 1 && value.length <= 12 && value.every((n) => typeof n === "number" && Number.isFinite(n) && n > 0);
31
+ }
32
+ function responsive(value, valid) {
33
+ return valid(value) || isRecord(value) && Object.keys(value).length > 0 && Object.entries(value).every(([key, entry]) => layoutBreakpoints.some((breakpoint) => breakpoint === key) && valid(entry));
34
+ }
35
+ /** Invalid layouts fall back as a whole, so a typo cannot hide an input or render it twice. */
36
+ function resolveFormLayout(value, fieldNames) {
37
+ const issues = [];
38
+ const used = /* @__PURE__ */ new Set();
39
+ const available = new Set(fieldNames);
40
+ const ancestors = /* @__PURE__ */ new Set();
41
+ let visited = 0;
42
+ let cssLength = 0;
43
+ let exhausted = false;
44
+ const visit = (node, path, depth) => {
45
+ const invalid = () => {
46
+ issues.push({
47
+ path,
48
+ code: "invalid_node"
49
+ });
50
+ return false;
51
+ };
52
+ if (++visited > layoutLimits.nodes) {
53
+ exhausted = true;
54
+ return invalid();
55
+ }
56
+ if (!isRecord(node) || depth > layoutLimits.depth || ancestors.has(node)) return invalid();
57
+ const type = node["type"];
58
+ const allowed = [
59
+ "type",
60
+ "span",
61
+ "css"
62
+ ];
63
+ if (node["css"] !== void 0 && (typeof node["css"] !== "string" || node["css"].length > 4096)) return invalid();
64
+ cssLength += typeof node["css"] === "string" ? node["css"].length : 0;
65
+ if (cssLength > layoutLimits.css) {
66
+ exhausted = true;
67
+ return invalid();
68
+ }
69
+ if (node["span"] !== void 0 && !responsive(node["span"], count)) return invalid();
70
+ if (type === "field") {
71
+ allowed.push("field");
72
+ if (Object.keys(node).some((key) => !allowed.includes(key))) return invalid();
73
+ const name = node["field"];
74
+ if (typeof name !== "string" || name.length > layoutLimits.field || !/^[a-zA-Z0-9_-]+$/.test(name)) return invalid();
75
+ if (!available.has(name)) issues.push({
76
+ path,
77
+ code: "unknown_field"
78
+ });
79
+ else if (used.has(name)) issues.push({
80
+ path,
81
+ code: "duplicate_field"
82
+ });
83
+ used.add(name);
84
+ return true;
85
+ }
86
+ if (type !== "stack" && type !== "grid" && type !== "section") return invalid();
87
+ if (!Array.isArray(node["children"]) || node["children"].length > layoutLimits.children) return invalid();
88
+ allowed.push("children", "gap");
89
+ if (type === "grid") {
90
+ allowed.push("columns", "align");
91
+ if (!responsive(node["columns"], tracks)) return invalid();
92
+ if (node["align"] !== void 0 && node["align"] !== "independent" && node["align"] !== "rows") return invalid();
93
+ if (node["align"] === "rows" && (!Array.isArray(node["children"]) || node["children"].some((child) => !isRecord(child) || child["type"] !== "section"))) return invalid();
94
+ }
95
+ if (type === "section") {
96
+ allowed.push("label", "description");
97
+ if (typeof node["label"] !== "string" || !node["label"] || node["label"].length > layoutLimits.label) return invalid();
98
+ if (node["description"] !== void 0 && (typeof node["description"] !== "string" || node["description"].length > layoutLimits.description)) return invalid();
99
+ }
100
+ if (Object.keys(node).some((key) => !allowed.includes(key))) return invalid();
101
+ if (node["gap"] !== void 0 && (typeof node["gap"] !== "string" || ![
102
+ "none",
103
+ "sm",
104
+ "md",
105
+ "lg"
106
+ ].includes(node["gap"]))) return invalid();
107
+ ancestors.add(node);
108
+ for (let index = 0; index < node["children"].length && !exhausted; index++) visit(node["children"][index], [
109
+ ...path,
110
+ "children",
111
+ index
112
+ ], depth + 1);
113
+ ancestors.delete(node);
114
+ return true;
115
+ };
116
+ if (value !== void 0) visit(value, [], 0);
117
+ const layout = value !== void 0 && issues.length === 0 ? value : void 0;
118
+ return {
119
+ layout,
120
+ remaining: fieldNames.filter((name) => !layout || !used.has(name)),
121
+ issues
122
+ };
123
+ }
124
+ //#endregion
125
+ export { layoutBreakpoints, layoutLimits, resolveFormLayout, responsiveLayoutValues };
@@ -0,0 +1,10 @@
1
+ import type { CSSProperties } from 'react';
2
+ export interface LayoutCSSIssue {
3
+ code: 'invalid_css' | 'unsupported_property' | 'unsupported_value';
4
+ property?: string;
5
+ }
6
+ /** Only generated declaration values reach React; authored CSS is never inserted into a stylesheet. */
7
+ export declare function resolveLayoutCSS(css: unknown): {
8
+ style: CSSProperties;
9
+ issues: LayoutCSSIssue[];
10
+ };
@@ -0,0 +1,234 @@
1
+ import { deriveTheme, isDarkColor } from "../theme/index.js";
2
+ import { generate, lexer, parse, walk } from "css-tree";
3
+ //#region src/form/layoutCSS.ts
4
+ var properties = /* @__PURE__ */ new Set([
5
+ "gap",
6
+ "row-gap",
7
+ "column-gap",
8
+ "padding",
9
+ "padding-inline",
10
+ "padding-block",
11
+ "padding-top",
12
+ "padding-right",
13
+ "padding-bottom",
14
+ "padding-left",
15
+ "max-width",
16
+ "width",
17
+ "align-items",
18
+ "justify-items",
19
+ "align-self",
20
+ "justify-self",
21
+ "background-color",
22
+ "color",
23
+ "border",
24
+ "border-top",
25
+ "border-bottom",
26
+ "border-left",
27
+ "border-right",
28
+ "border-color",
29
+ "border-width",
30
+ "border-style",
31
+ "border-radius",
32
+ "font-family",
33
+ "font-size",
34
+ "font-weight",
35
+ "font-style",
36
+ "line-height",
37
+ "letter-spacing",
38
+ "text-align"
39
+ ]);
40
+ var alignmentKeywords = [
41
+ "auto",
42
+ "normal",
43
+ "start",
44
+ "end",
45
+ "center",
46
+ "stretch",
47
+ "baseline"
48
+ ];
49
+ var borderKeywords = [
50
+ "solid",
51
+ "dashed",
52
+ "dotted",
53
+ "double",
54
+ "none"
55
+ ];
56
+ var keywords = {
57
+ gap: ["normal"],
58
+ "row-gap": ["normal"],
59
+ "column-gap": ["normal"],
60
+ width: ["auto"],
61
+ "max-width": ["none"],
62
+ "align-items": alignmentKeywords,
63
+ "justify-items": alignmentKeywords,
64
+ "align-self": alignmentKeywords,
65
+ "justify-self": alignmentKeywords,
66
+ border: borderKeywords,
67
+ "border-top": borderKeywords,
68
+ "border-bottom": borderKeywords,
69
+ "border-left": borderKeywords,
70
+ "border-right": borderKeywords,
71
+ "border-style": borderKeywords,
72
+ "font-family": [
73
+ "serif",
74
+ "sans-serif",
75
+ "monospace"
76
+ ],
77
+ "font-style": ["normal", "italic"],
78
+ "line-height": ["normal"],
79
+ "letter-spacing": ["normal"],
80
+ "text-align": [
81
+ "start",
82
+ "end",
83
+ "left",
84
+ "right",
85
+ "center"
86
+ ]
87
+ };
88
+ var proportionalLengths = /* @__PURE__ */ new Set([
89
+ "gap",
90
+ "row-gap",
91
+ "column-gap",
92
+ "width",
93
+ "max-width",
94
+ "padding",
95
+ "padding-inline",
96
+ "padding-block",
97
+ "padding-top",
98
+ "padding-right",
99
+ "padding-bottom",
100
+ "padding-left"
101
+ ]);
102
+ var functions = /* @__PURE__ */ new Set([
103
+ "min",
104
+ "max",
105
+ "clamp"
106
+ ]);
107
+ var units = /* @__PURE__ */ new Set([
108
+ "px",
109
+ "rem",
110
+ "em",
111
+ "ch"
112
+ ]);
113
+ var themeProperties = /* @__PURE__ */ new Set(["--form-surface", "--form-accent"]);
114
+ var opaqueHex = /^(?:[a-f\d]{3}|[a-f\d]{6})$/i;
115
+ var expandHex = (hex) => hex.length === 4 ? `#${[...hex.slice(1)].map((c) => c + c).join("")}` : hex;
116
+ function validToken(node, property) {
117
+ const color = property === "color" || property === "background-color" || themeProperties.has(property);
118
+ const border = property.startsWith("border");
119
+ switch (node.type) {
120
+ case "Value": return true;
121
+ case "Hash": return (color || border) && opaqueHex.test(node.value);
122
+ case "Identifier": return keywords[property]?.includes(node.name.toLowerCase()) ?? false;
123
+ case "String": return property === "font-family" && ["Geist Sans", "Geist Mono"].includes(node.value);
124
+ case "Function": return !color && !border && functions.has(node.name.toLowerCase());
125
+ case "Operator": return !color && node.value === ",";
126
+ case "Number": {
127
+ const n = Number(node.value);
128
+ if (color) return false;
129
+ if (property === "font-weight") return Number.isInteger(n) && n >= 100 && n <= 900;
130
+ if (property === "line-height") return n >= 1 && n <= 2;
131
+ if (property === "font-size") return false;
132
+ return n >= 0 && n <= (border || property === "letter-spacing" ? 0 : 12);
133
+ }
134
+ case "Percentage": return proportionalLengths.has(property) && Number(node.value) >= 0 && Number(node.value) <= 100;
135
+ case "Dimension": {
136
+ const n = Number(node.value);
137
+ const unit = node.unit.toLowerCase();
138
+ if (color || !units.has(unit)) return false;
139
+ if (property === "font-size") return unit === "px" && n >= 12 && n <= 96 || unit === "rem" && n >= .75 && n <= 6;
140
+ if (property === "letter-spacing") return unit === "px" && n >= 0 && n <= 2 || unit === "em" && n >= 0 && n <= .1;
141
+ if (property === "line-height") return false;
142
+ if (border) {
143
+ const cap = property === "border-radius" ? 32 : 8;
144
+ return unit === "px" && n >= 0 && n <= cap || unit === "rem" && n >= 0 && n <= cap / 16;
145
+ }
146
+ return n >= 0 && n <= 256;
147
+ }
148
+ default: return false;
149
+ }
150
+ }
151
+ /** Only generated declaration values reach React; authored CSS is never inserted into a stylesheet. */
152
+ function resolveLayoutCSS(css) {
153
+ if (css === void 0 || css === "") return {
154
+ style: {},
155
+ issues: []
156
+ };
157
+ const issues = [];
158
+ const style = {};
159
+ if (typeof css !== "string" || css.length > 4096) return {
160
+ style,
161
+ issues: [{ code: "invalid_css" }]
162
+ };
163
+ try {
164
+ const ast = parse(css, {
165
+ context: "declarationList",
166
+ parseCustomProperty: true,
167
+ onParseError: () => {
168
+ throw new Error("Invalid CSS");
169
+ }
170
+ });
171
+ if (ast.type !== "DeclarationList" || ast.children.size > 32) throw new Error("Invalid CSS");
172
+ ast.children.forEach((declaration) => {
173
+ if (declaration.type !== "Declaration") throw new Error("Invalid CSS");
174
+ const property = declaration.property.startsWith("--") ? declaration.property : declaration.property.toLowerCase();
175
+ if (!properties.has(property) && !themeProperties.has(property)) {
176
+ issues.push({
177
+ code: "unsupported_property",
178
+ property
179
+ });
180
+ return;
181
+ }
182
+ let valid = !declaration.important;
183
+ let tokens = 0;
184
+ walk(declaration.value, (node) => {
185
+ if (++tokens > 128) valid = false;
186
+ valid &&= validToken(node, property);
187
+ });
188
+ if (!valid || lexer.matchProperty(themeProperties.has(property) ? "color" : property, declaration.value).error) {
189
+ issues.push({
190
+ code: "unsupported_value",
191
+ property
192
+ });
193
+ return;
194
+ }
195
+ style[themeProperties.has(property) ? property : property.replace(/-([a-z])/g, (_, letter) => letter.toUpperCase())] = generate(declaration.value);
196
+ });
197
+ } catch {
198
+ issues.push({ code: "invalid_css" });
199
+ }
200
+ const surface = style["--form-surface"];
201
+ const accent = style["--form-accent"];
202
+ if (Boolean(surface) !== Boolean(accent)) issues.push({
203
+ code: "unsupported_value",
204
+ property: surface ? "--form-accent" : "--form-surface"
205
+ });
206
+ if (issues.length) return {
207
+ style: {},
208
+ issues
209
+ };
210
+ if (surface && accent) {
211
+ const background = expandHex(surface);
212
+ delete style["--form-surface"];
213
+ delete style["--form-accent"];
214
+ return {
215
+ style: {
216
+ ...deriveTheme({
217
+ background,
218
+ accent: expandHex(accent)
219
+ }),
220
+ backgroundColor: background,
221
+ color: "var(--theme-app)",
222
+ colorScheme: isDarkColor(background) ? "dark" : "light",
223
+ ...style
224
+ },
225
+ issues
226
+ };
227
+ }
228
+ return {
229
+ style,
230
+ issues
231
+ };
232
+ }
233
+ //#endregion
234
+ export { resolveLayoutCSS };
@@ -8,6 +8,8 @@ import type { DynamicFormSchema } from './types/fieldTypes.js';
8
8
  * that default instead of reading an expression it never evaluated as "yes".
9
9
  */
10
10
  export declare function resolvedFlag<T>(value: T): T | undefined;
11
+ /** An input's `width` as CSS: a number of pixels or a percentage; undefined for anything else. */
12
+ export declare function inputWidth(value: unknown): string | undefined;
11
13
  export declare function enforceOneMustBeTrue(items: Array<Record<string, unknown>>, options: DynamicFormSchema, promoteIndex: number): Array<Record<string, unknown>>;
12
14
  export declare function flattenGroups(schema: unknown): Record<string, unknown>;
13
15
  /**Not a pure function. Edits 'obj' passed into function */
package/dist/form/lib.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { applySecondaryField, findSecondaryOption } from "./utils/secondaryField.js";
2
+ import { copyBounds } from "./Wizard/utils.js";
2
3
  //#region src/form/lib.ts
3
4
  var EMPTY_ENTRY = {};
4
5
  function asRecord(value) {
@@ -15,6 +16,12 @@ function asRecord(value) {
15
16
  function resolvedFlag(value) {
16
17
  return typeof value === "string" && value.includes("${{") ? void 0 : value;
17
18
  }
19
+ var PERCENT_WIDTH = /^(100|[1-9]\d?(\.\d+)?)%$/;
20
+ /** An input's `width` as CSS: a number of pixels or a percentage; undefined for anything else. */
21
+ function inputWidth(value) {
22
+ if (typeof value === "number") return Number.isFinite(value) && value > 0 ? `${value}px` : void 0;
23
+ return typeof value === "string" && PERCENT_WIDTH.test(value) ? value : void 0;
24
+ }
18
25
  function enforceOneMustBeTrue(items, options, promoteIndex) {
19
26
  if (items.length === 0) return items;
20
27
  const clampedIndex = Math.max(0, Math.min(promoteIndex, items.length - 1));
@@ -70,6 +77,14 @@ function initializeValues(options, srcData = {}, blankDefault) {
70
77
  return Object.keys(schema).reduce((acc, field) => {
71
78
  if (field.startsWith("$")) return acc;
72
79
  const fieldSchema = schema[field] ?? EMPTY_ENTRY;
80
+ if (fieldSchema.type === "step" && fieldSchema.multi === true) {
81
+ const saved = data[field];
82
+ const rows = Array.isArray(saved) ? saved : [];
83
+ const { lo, hi } = copyBounds(fieldSchema.min, fieldSchema.max);
84
+ const length = Math.min(Math.max(rows.length, lo), hi ?? Number.POSITIVE_INFINITY);
85
+ acc[field] = Array.from({ length }, (_, i) => rows[i] ?? {}).map((row) => initializeValues(fieldSchema.options, asRecord(row)));
86
+ return acc;
87
+ }
73
88
  const flattenStep = fieldSchema.type === "step" && schema["$meta"]?.wizard?.flatten !== false;
74
89
  if (fieldSchema.type === "group" || flattenStep) {
75
90
  const values = initializeValues(fieldSchema.options, data);
@@ -81,7 +96,7 @@ function initializeValues(options, srcData = {}, blankDefault) {
81
96
  return acc;
82
97
  }
83
98
  if (fieldSchema.type === "list") {
84
- const listData = data[field];
99
+ const listData = data[field] !== void 0 ? data[field] : fieldSchema.default;
85
100
  if (fieldSchema.options === "string") {
86
101
  acc[field] = Array.isArray(listData) ? [...listData] : [];
87
102
  return acc;
@@ -127,4 +142,4 @@ function initializeValues(options, srcData = {}, blankDefault) {
127
142
  }, data);
128
143
  }
129
144
  //#endregion
130
- export { enforceOneMustBeTrue, flattenGroups, impureSetValueFromPath, initializeValues, resolvedFlag };
145
+ export { enforceOneMustBeTrue, flattenGroups, impureSetValueFromPath, initializeValues, inputWidth, resolvedFlag };
@@ -1,3 +1,5 @@
1
+ import type { FormLayoutNode } from '../layout.js';
2
+ import type { StepFieldConfig, WizardConfig } from '../Wizard/types.js';
1
3
  export type LabelPosition = 'left' | 'top';
2
4
  /** A flag a field may set literally or as an expression the engine resolves. */
3
5
  export type FieldFlag = string | boolean;
@@ -29,36 +31,19 @@ export interface BaseField extends RuntimeFieldExtensions {
29
31
  collapsed?: boolean;
30
32
  disabled?: boolean;
31
33
  default?: unknown;
34
+ /** Pixels, or a share of the row such as '50%'; inputs whose widths fit share a row. */
35
+ width?: number | string;
36
+ /** Under the input before it, in that input's column. */
37
+ 'anchor-below'?: boolean;
32
38
  }
33
39
  type AnyField = BaseField;
34
- interface WizardConfig {
35
- /** Must be 'wizard' to activate wizard mode */
36
- mode: 'wizard';
37
- /** Navigation UI options */
38
- navigation?: {
39
- showSteps?: boolean;
40
- allowJump?: boolean;
41
- hideStepNumbers?: boolean;
42
- };
43
- /** Label for the final submit button */
44
- submitLabel?: string;
45
- /** Flatten step fields into top-level inputs (default: true). Set false to keep each step's fields under its key. */
46
- flatten?: boolean;
47
- }
48
- interface StepField extends BaseField {
49
- type: 'step';
50
- title: string;
51
- description?: string;
52
- options: Record<string, AnyField>;
53
- validateOnNext?: boolean;
54
- nextLabel?: string;
55
- prevLabel?: string;
56
- }
40
+ type StepField = Omit<BaseField, 'type' | 'description'> & StepFieldConfig;
57
41
  export interface DynamicFormSchema {
58
42
  $meta?: {
59
43
  labelPosition?: LabelPosition;
60
44
  spaceCompact?: boolean;
61
45
  wizard?: WizardConfig;
46
+ layout?: FormLayoutNode;
62
47
  };
63
48
  [fieldName: string]: AnyField | StepField | DynamicFormSchema['$meta'] | undefined;
64
49
  }
@@ -1,9 +1,22 @@
1
1
  import React from 'react';
2
+ import type { GraphLayout } from '../editing.js';
3
+ import type { MatrixGroup, WorkflowEngine } from '../engine.js';
2
4
  import { type RunLink, type WorkflowJob } from './types.js';
3
5
  export type ViewMode = 'dag' | 'tree';
4
6
  export declare const addCleanupSteps: ({ jobs }: {
5
7
  jobs: Record<string, WorkflowJob>;
6
8
  }) => Record<string, WorkflowJob>;
9
+ export declare function computeGraphLayout(engine: WorkflowEngine, jobs: Record<string, WorkflowJob>, matrixGroups: Record<string, MatrixGroup>, layout?: GraphLayout): {
10
+ dependencyCols: string[][][];
11
+ /** Each box's row; above a box, rows the layout leaves empty stay as space. */
12
+ rowSlots: number[][];
13
+ /** Each column's number; between two drawn columns, numbers skipped stay as space. */
14
+ colSlots: number[];
15
+ directDeps: Record<string, string[]>;
16
+ displayJobs: Record<string, WorkflowJob>;
17
+ ancestorDists: Record<string, Map<string, number>>;
18
+ descendantDists: Record<string, Map<string, number>>;
19
+ };
7
20
  interface GraphRun {
8
21
  id?: string | undefined;
9
22
  number?: number | undefined;
@@ -16,15 +29,21 @@ interface GraphRun {
16
29
  executedJobs?: Record<string, unknown> | undefined;
17
30
  links?: Record<string, RunLink> | undefined;
18
31
  }
19
- export default function DependencyGraph({ run, preview, removeBorder, viewMode, setViewMode, }: {
32
+ export default function DependencyGraph({ run, preview, removeBorder, viewMode, setViewMode, layout, yamlJobs, fixedHeight, }: {
20
33
  run: GraphRun;
21
34
  preview?: boolean;
22
35
  removeBorder?: boolean | undefined;
23
36
  viewMode?: ViewMode;
24
37
  setViewMode?: (mode: ViewMode) => void;
38
+ layout?: GraphLayout | undefined;
39
+ yamlJobs?: Record<string, unknown> | undefined;
40
+ fixedHeight?: string | undefined;
25
41
  }): React.JSX.Element | undefined;
26
42
  export declare function DependencyGraphPreview(inputs: {
27
43
  yml?: Record<string, unknown>;
28
44
  removeBorder?: boolean;
45
+ layout?: GraphLayout | undefined;
46
+ /** A fixed panel height; without one the panel is only as tall as the graph. */
47
+ height?: string | undefined;
29
48
  }): React.JSX.Element;
30
49
  export {};