@parallelworks/ui 0.6.0 → 0.8.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.
@@ -1,5 +1,5 @@
1
1
  import { t as useCssIsDark } from "./useCssIsDark-DHr05dG2.js";
2
- import { n as defineEditorThemes, r as getThemeName, t as setupMonacoWorkers } from "./editor-lOWwO2bq.js";
2
+ import { n as defineEditorThemes, r as getThemeName, t as setupMonacoWorkers } from "./editor-BH81U5-C.js";
3
3
  import { jsx } from "react/jsx-runtime";
4
4
  import { useEffect, useRef } from "react";
5
5
  import * as monaco from "monaco-editor";
@@ -1,8 +1,9 @@
1
1
  import { AlertIcon, CheckIcon, ChevronDownIcon, ChevronRightIcon, ClipboardIcon, CopyIcon, FavoriteIcon, FavoriteOutlineIcon, HiddenFalseIcon, HiddenTrueIcon, InfoIcon, TooltipIcon } from "../icons.js";
2
- import { c as useStrings, i as useNotify } from "./Provider-3382VBAv.js";
2
+ import { c as useStrings, i as useNotify } from "./Provider-CfFV-vc5.js";
3
3
  import { n as TOOLTIP_ID } from "./Tooltip-WgbiC_nd.js";
4
+ import { i as filterOption, l as toggleSetMember } from "./StatusBadge-P6C2tv7Z.js";
4
5
  import { n as modalPanelClasses, t as BareModal } from "./BareModal-DBa84JZO.js";
5
- import { S as AddMenu } from "./PageHeader-Dz4A_w6J.js";
6
+ import { S as AddMenu } from "./PageHeader-8xP84_KS.js";
6
7
  import cx from "classnames";
7
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
8
9
  import { Suspense, lazy, useCallback, useEffect, useRef, useState } from "react";
@@ -462,19 +463,55 @@ function CreateModalSwitch({ label, checked, onChange, tooltip }) {
462
463
  })
463
464
  });
464
465
  }
465
- function buildOptionNodes(options, query, onChange) {
466
- const normalize = (s) => s.toLowerCase().replace(/-/g, "");
467
- const q = normalize(query.trim());
468
- const filtered = q ? options.filter((o) => normalize(o.label).includes(q)) : options;
466
+ /** Callers gating collapse on list size must use this same threshold so
467
+ * collapsed rows always come with a filter to find them. */
468
+ var CREATE_MODAL_SELECT_FILTER_THRESHOLD = 8;
469
+ function collapseToggleRow(group, count, collapse) {
470
+ const expanded = collapse.expanded.has(group);
471
+ return /* @__PURE__ */ jsxs("button", {
472
+ type: "button",
473
+ "aria-expanded": expanded,
474
+ onMouseDown: (e) => e.preventDefault(),
475
+ onClick: () => collapse.onToggle(group),
476
+ className: "flex w-full items-center gap-1.5 px-3 py-1.5 text-[13px] text-(--theme-muted-text-color) hover:text-(--theme-app) cursor-pointer",
477
+ children: [/* @__PURE__ */ jsx(expanded ? ChevronDownIcon : ChevronRightIcon, { className: "w-3 h-3" }), collapse.label(count)]
478
+ }, `toggle-${group}`);
479
+ }
480
+ function collapsibleCountsByGroup(options) {
481
+ const counts = /* @__PURE__ */ new Map();
482
+ for (const o of options) if (o.collapsible && o.group) counts.set(o.group, (counts.get(o.group) ?? 0) + 1);
483
+ return counts;
484
+ }
485
+ function pushToggleRowOnce(nodes, toggledGroups, group, count, collapse) {
486
+ if (!toggledGroups.has(group)) {
487
+ toggledGroups.add(group);
488
+ nodes.push(collapseToggleRow(group, count, collapse));
489
+ }
490
+ }
491
+ function buildOptionNodes(options, query, onChange, collapse) {
492
+ const trimmed = query.trim();
493
+ const q = trimmed.toLowerCase().replace(/-/g, "");
494
+ const filtered = q ? options.filter((o) => filterOption({
495
+ label: o.label,
496
+ value: o.value,
497
+ secondaryValue: o.group
498
+ }, trimmed)) : options;
499
+ const collapsibleCounts = !q && collapse ? collapsibleCountsByGroup(filtered) : /* @__PURE__ */ new Map();
469
500
  const nodes = [];
470
501
  let lastGroup;
502
+ const toggledGroups = /* @__PURE__ */ new Set();
471
503
  for (const option of filtered) {
472
- if (option.group && option.group !== lastGroup) {
473
- lastGroup = option.group;
504
+ const { group } = option;
505
+ if (group && group !== lastGroup) {
506
+ lastGroup = group;
474
507
  nodes.push(/* @__PURE__ */ jsx("div", {
475
508
  className: "px-3 pt-2 pb-1 text-[10px] font-semibold uppercase tracking-wide text-(--theme-muted-text-color)",
476
- children: option.group
477
- }, `group-${option.group}`));
509
+ children: group
510
+ }, `group-${group}`));
511
+ }
512
+ if (!q && collapse && option.collapsible && group) {
513
+ pushToggleRowOnce(nodes, toggledGroups, group, collapsibleCounts.get(group) ?? 0, collapse);
514
+ if (!collapse.expanded.has(group)) continue;
478
515
  }
479
516
  nodes.push(/* @__PURE__ */ jsx(MenuItem, { children: /* @__PURE__ */ jsxs("button", {
480
517
  type: "button",
@@ -485,21 +522,30 @@ function buildOptionNodes(options, query, onChange) {
485
522
  }
486
523
  return nodes;
487
524
  }
488
- function CreateModalSelectPanel({ open, options, label, onChange }) {
525
+ function CreateModalSelectPanel({ open, options, label, value, collapsedLabel, onChange }) {
489
526
  const t = useStrings().modal;
490
527
  const [query, setQuery] = useState("");
491
528
  const inputRef = useRef(null);
492
529
  const showFilter = options.length > 8;
530
+ const selectedGroup = options.find((o) => o.value === value)?.group;
531
+ const initialExpandedGroups = useCallback(() => new Set(selectedGroup ? [selectedGroup] : []), [selectedGroup]);
532
+ const [expandedGroups, setExpandedGroups] = useState(initialExpandedGroups);
493
533
  useEffect(() => {
494
534
  if (!open) {
495
535
  setQuery("");
496
- return;
536
+ setExpandedGroups(initialExpandedGroups());
497
537
  }
498
- if (!showFilter) return;
538
+ }, [open, initialExpandedGroups]);
539
+ useEffect(() => {
540
+ if (!open || !showFilter) return;
499
541
  const raf = requestAnimationFrame(() => inputRef.current?.focus());
500
542
  return () => cancelAnimationFrame(raf);
501
543
  }, [open, showFilter]);
502
- const rendered = buildOptionNodes(options, query, onChange);
544
+ const rendered = buildOptionNodes(options, query, onChange, collapsedLabel ? {
545
+ expanded: expandedGroups,
546
+ onToggle: (group) => setExpandedGroups((prev) => toggleSetMember(prev, group)),
547
+ label: collapsedLabel
548
+ } : void 0);
503
549
  return /* @__PURE__ */ jsxs(MenuItems, {
504
550
  anchor: "bottom start",
505
551
  className: "z-[9999] mt-1 min-w-44 [--anchor-max-height:16rem] overflow-auto rounded-lg border border-(--theme-border) bg-(--theme-app-bg) shadow-lg focus:outline-none",
@@ -530,7 +576,7 @@ function CreateModalSelectPanel({ open, options, label, onChange }) {
530
576
  }
531
577
  /** Compact property pill with a dropdown of options. Options may carry leading
532
578
  * icons and group headers (e.g. Clusters vs Kubernetes). */
533
- function CreateModalSelect({ label, icon, value, options, onChange, disabled = false, disabledHint, tooltip }) {
579
+ function CreateModalSelect({ label, icon, value, options, onChange, disabled = false, disabledHint, tooltip, collapsedLabel }) {
534
580
  const selected = options.find((o) => o.value === value);
535
581
  const pillContent = /* @__PURE__ */ jsxs(Fragment, { children: [
536
582
  selected?.icon ?? icon,
@@ -563,8 +609,10 @@ function CreateModalSelect({ label, icon, value, options, onChange, disabled = f
563
609
  open,
564
610
  options,
565
611
  label,
612
+ value,
613
+ collapsedLabel,
566
614
  onChange
567
615
  })] }) });
568
616
  }
569
617
  //#endregion
570
- export { CreateModalSwitch as a, dangerButtonClasses as c, Markdown as d, CopyCodeBlock as f, CreateModalSelect as i, ghostButtonClasses as l, Callout as m, CreateModalAutocompleteField as n, CreateModalTextarea as o, CopyToClipboard as p, CreateModalField as r, ConfirmModal as s, CreateModal as t, primaryButtonClasses as u };
618
+ export { CreateModalSelect as a, ConfirmModal as c, primaryButtonClasses as d, Markdown as f, Callout as h, CreateModalField as i, dangerButtonClasses as l, CopyToClipboard as m, CreateModal as n, CreateModalSwitch as o, CopyCodeBlock as p, CreateModalAutocompleteField as r, CreateModalTextarea as s, CREATE_MODAL_SELECT_FILTER_THRESHOLD as t, ghostButtonClasses as u };
@@ -1,6 +1,6 @@
1
- import { l as useWorkflowJsonResolver } from "./Provider-3382VBAv.js";
1
+ import { l as useWorkflowJsonResolver } from "./Provider-CfFV-vc5.js";
2
2
  import { t as useCssIsDark } from "./useCssIsDark-DHr05dG2.js";
3
- import { n as defineEditorThemes, r as getThemeName, t as setupMonacoWorkers } from "./editor-lOWwO2bq.js";
3
+ import { n as defineEditorThemes, r as getThemeName, t as setupMonacoWorkers } from "./editor-BH81U5-C.js";
4
4
  import cx from "classnames";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  import { useEffect, useRef } from "react";
@@ -1,5 +1,5 @@
1
1
  import { AddIcon, CheckIcon, ChevronRightIcon, ClipboardIcon, CopyIcon, EditIcon, IdCardIcon, KeyIcon, LinkIcon, MailIcon, MoreIcon, UserIcon } from "../icons.js";
2
- import { c as useStrings, i as useNotify, n as useLink, s as useSlots } from "./Provider-3382VBAv.js";
2
+ import { c as useStrings, i as useNotify, n as useLink, s as useSlots } from "./Provider-CfFV-vc5.js";
3
3
  import { n as TOOLTIP_ID } from "./Tooltip-WgbiC_nd.js";
4
4
  import cx from "classnames";
5
5
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -140,7 +140,7 @@ function assembleRowMenu({ lead = [], actions, exclude, order, trailing = ["dele
140
140
  ...trailingItems.map(toRowMenuItem)
141
141
  ];
142
142
  }
143
- function useRowMenu() {
143
+ function useRowMenu(zClassName = "z-50") {
144
144
  const [menu, setMenu] = useState(null);
145
145
  const onCloseRef = useRef(void 0);
146
146
  return {
@@ -161,7 +161,8 @@ function useRowMenu() {
161
161
  onCloseRef.current?.();
162
162
  onCloseRef.current = void 0;
163
163
  setMenu(null);
164
- }, [])
164
+ }, []),
165
+ zClassName
165
166
  })
166
167
  };
167
168
  }
@@ -183,7 +184,7 @@ function rowContextMenuProps(openMenu, items) {
183
184
  openMenu(e.clientX, e.clientY, items);
184
185
  } };
185
186
  }
186
- function RowContextMenu({ state, onClose }) {
187
+ function RowContextMenu({ state, onClose, zClassName }) {
187
188
  const ref = useRef(null);
188
189
  const [pos, setPos] = useState(null);
189
190
  useLayoutEffect(() => {
@@ -217,7 +218,7 @@ function RowContextMenu({ state, onClose }) {
217
218
  if (!state) return null;
218
219
  const side = (pos?.left ?? state.x) > window.innerWidth * .6 ? "left" : "right";
219
220
  return createPortal(/* @__PURE__ */ jsx("div", {
220
- className: "fixed inset-0 z-50",
221
+ className: cx("fixed inset-0", zClassName),
221
222
  onClick: onClose,
222
223
  onContextMenu: (e) => {
223
224
  e.preventDefault();
@@ -346,6 +346,7 @@ var DEFAULTS = {
346
346
  filter: "Filter",
347
347
  clear: "Clear",
348
348
  pager: (start, end, total) => `${start}–${end} of ${total}`,
349
+ perPage: "Per page",
349
350
  paginationPrevious: "Previous",
350
351
  paginationNext: "Next",
351
352
  paginationShowing: (name, start, end) => /* @__PURE__ */ jsxs(Fragment, { children: [
@@ -0,0 +1,144 @@
1
+ import { CaretDownIcon } from "../icons.js";
2
+ import cx from "classnames";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/components/dropdownUtils.tsx
5
+ function isCategory(option) {
6
+ return typeof option === "object" && option !== null && option !== void 0 && "options" in option;
7
+ }
8
+ function isOption(option) {
9
+ return typeof option === "object" && "label" in option && "value" in option && !("category" in option);
10
+ }
11
+ function normalizeOption(option) {
12
+ if (typeof option === "string") return {
13
+ label: option,
14
+ value: option
15
+ };
16
+ if (typeof option === "object" && option !== null && option !== void 0 && "options" in option) return {
17
+ category: option.category,
18
+ options: Array.isArray(option.options) ? option.options : []
19
+ };
20
+ return option;
21
+ }
22
+ function flattenOptions(options) {
23
+ const result = [];
24
+ for (const opt of options) {
25
+ const normalized = normalizeOption(opt);
26
+ if (isCategory(normalized)) {
27
+ const children = Array.isArray(normalized.options) ? normalized.options : [];
28
+ result.push(normalized);
29
+ result.push(...children);
30
+ } else result.push(normalized);
31
+ }
32
+ return result;
33
+ }
34
+ function filterOption(option, query) {
35
+ if (query === "") return true;
36
+ const q = String(query ?? "").toLowerCase().replace(/(-)/gm, "");
37
+ const label = String(option.label ?? "").toLowerCase().replace(/(-)/gm, "");
38
+ const secondary = option.secondaryValue !== void 0 ? String(option.secondaryValue).toLowerCase().replace(/(-)/gm, "") : "";
39
+ return label.includes(q) || secondary.includes(q);
40
+ }
41
+ function toggleSetMember(set, member) {
42
+ const next = new Set(set);
43
+ if (next.has(member)) next.delete(member);
44
+ else next.add(member);
45
+ return next;
46
+ }
47
+ function collapsedCategoryRows(category, filtered, collapsibleCount, expanded, collapsedLabel) {
48
+ const rows = [];
49
+ let togglePlaced = false;
50
+ for (const opt of filtered) {
51
+ if (!opt.collapsible) {
52
+ rows.push(opt);
53
+ continue;
54
+ }
55
+ if (!togglePlaced) {
56
+ togglePlaced = true;
57
+ rows.push({
58
+ label: collapsedLabel(collapsibleCount),
59
+ value: `__toggle__:${category}`,
60
+ disabled: true,
61
+ toggle: category
62
+ });
63
+ }
64
+ if (expanded) rows.push(opt);
65
+ }
66
+ return rows;
67
+ }
68
+ function flattenDisplayOptions({ options, query, expandedCategories, collapsedLabel, selectedValue, allowCustomValue, customValueLabel }) {
69
+ const flatOptions = options.flatMap((option) => {
70
+ if (!isCategory(option)) return filterOption(option, query) ? [option] : [];
71
+ const filtered = option.options.filter((opt) => filterOption(opt, query));
72
+ if (filtered.length === 0) return [];
73
+ const category = option.category || "";
74
+ const header = {
75
+ label: category,
76
+ value: category,
77
+ disabled: true,
78
+ category: true
79
+ };
80
+ const collapsibleCount = filtered.filter((o) => o.collapsible).length;
81
+ if (query || collapsibleCount === 0 || !collapsedLabel) return [header, ...filtered];
82
+ return [header, ...collapsedCategoryRows(category, filtered, collapsibleCount, expandedCategories?.has(category) ?? false, collapsedLabel)];
83
+ });
84
+ if (allowCustomValue) {
85
+ const customValue = query || selectedValue || "";
86
+ flatOptions.unshift({
87
+ label: `${customValueLabel} "${customValue}"`,
88
+ value: customValue
89
+ });
90
+ } else if (flatOptions.length === 0) return [{
91
+ label: "No options found",
92
+ value: "",
93
+ disabled: true
94
+ }];
95
+ return flatOptions;
96
+ }
97
+ function DropdownCaret() {
98
+ return /* @__PURE__ */ jsx(CaretDownIcon, { className: "text-neutral-400 absolute top-1/2 -translate-y-1/2 right-0 mr-3 inset-y-0 flex items-center rounded-r-md focus:outline-hidden" });
99
+ }
100
+ //#endregion
101
+ //#region src/components/StatusBadge.tsx
102
+ var variantStyles = {
103
+ default: "bg-(--theme-muted-panel-bg) text-(--theme-app) border-(--theme-border)",
104
+ success: "bg-emerald-500/10 text-emerald-500 border-emerald-500/20",
105
+ warning: "bg-amber-500/10 text-amber-500 border-amber-500/20",
106
+ error: "bg-red-500/10 text-red-500 border-red-500/20",
107
+ info: "bg-blue-500/10 text-blue-500 border-blue-500/20",
108
+ muted: "bg-(--theme-muted-panel-bg)/50 text-(--theme-muted-text-color) border-(--theme-border)"
109
+ };
110
+ var pulseColors = {
111
+ default: "bg-(--theme-app)",
112
+ success: "bg-emerald-500",
113
+ warning: "bg-amber-500",
114
+ error: "bg-red-500",
115
+ info: "bg-blue-500",
116
+ muted: "bg-(--theme-muted-text-color)"
117
+ };
118
+ function StatusBadge({ children, variant = "default", pulse = false, size = "sm", className }) {
119
+ return /* @__PURE__ */ jsxs("span", {
120
+ className: cx("inline-flex items-center gap-1.5 rounded-full border font-medium", size === "sm" && "px-2 py-0.5 text-[10px]", size === "md" && "px-2.5 py-1 text-xs", variantStyles[variant], className),
121
+ children: [pulse && /* @__PURE__ */ jsxs("span", {
122
+ className: "relative flex h-2 w-2",
123
+ children: [/* @__PURE__ */ jsx("span", { className: cx("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75", pulseColors[variant]) }), /* @__PURE__ */ jsx("span", { className: cx("relative inline-flex h-2 w-2 rounded-full", pulseColors[variant]) })]
124
+ }), children]
125
+ });
126
+ }
127
+ function StatusDot({ variant = "default", pulse = false, size = 8, className }) {
128
+ return /* @__PURE__ */ jsxs("span", {
129
+ className: cx("relative inline-flex", className),
130
+ style: {
131
+ width: size,
132
+ height: size
133
+ },
134
+ children: [pulse && /* @__PURE__ */ jsx("span", { className: cx("absolute inline-flex h-full w-full animate-ping rounded-full opacity-75", pulseColors[variant]) }), /* @__PURE__ */ jsx("span", {
135
+ className: cx("relative inline-flex rounded-full", pulseColors[variant]),
136
+ style: {
137
+ width: size,
138
+ height: size
139
+ }
140
+ })]
141
+ });
142
+ }
143
+ //#endregion
144
+ export { flattenDisplayOptions as a, isOption as c, filterOption as i, toggleSetMember as l, StatusDot as n, flattenOptions as o, DropdownCaret as r, isCategory as s, StatusBadge as t };
@@ -1,6 +1,7 @@
1
1
  import { AngleDownIcon, AngleRightIcon, CaretDownIcon } from "../icons.js";
2
- import { c as useStrings } from "./Provider-3382VBAv.js";
2
+ import { c as useStrings } from "./Provider-CfFV-vc5.js";
3
3
  import { n as TOOLTIP_ID } from "./Tooltip-WgbiC_nd.js";
4
+ import { a as flattenDisplayOptions, c as isOption, i as filterOption, l as toggleSetMember, o as flattenOptions, r as DropdownCaret, s as isCategory } from "./StatusBadge-P6C2tv7Z.js";
4
5
  import cx from "classnames";
5
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
6
7
  import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -9,76 +10,8 @@ import { Combobox, ComboboxButton, ComboboxInput, ComboboxOption, ComboboxOption
9
10
  import deepEqual from "fast-deep-equal";
10
11
  import { usePopper } from "react-popper";
11
12
  import AnimateHeight from "react-animate-height";
12
- //#region src/components/dropdownUtils.tsx
13
- function isCategory(option) {
14
- return typeof option === "object" && option !== null && option !== void 0 && "options" in option;
15
- }
16
- function isOption(option) {
17
- return typeof option === "object" && "label" in option && "value" in option && !("category" in option);
18
- }
19
- function normalizeOption(option) {
20
- if (typeof option === "string") return {
21
- label: option,
22
- value: option
23
- };
24
- if (typeof option === "object" && option !== null && option !== void 0 && "options" in option) return {
25
- category: option.category,
26
- options: Array.isArray(option.options) ? option.options : []
27
- };
28
- return option;
29
- }
30
- function flattenOptions(options) {
31
- const result = [];
32
- for (const opt of options) {
33
- const normalized = normalizeOption(opt);
34
- if (isCategory(normalized)) {
35
- const children = Array.isArray(normalized.options) ? normalized.options : [];
36
- result.push(normalized);
37
- result.push(...children);
38
- } else result.push(normalized);
39
- }
40
- return result;
41
- }
42
- function filterOption(option, query) {
43
- if (query === "") return true;
44
- const q = String(query ?? "").toLowerCase().replace(/(-)/gm, "");
45
- const label = String(option.label ?? "").toLowerCase().replace(/(-)/gm, "");
46
- const secondary = option.secondaryValue !== void 0 ? String(option.secondaryValue).toLowerCase().replace(/(-)/gm, "") : "";
47
- return label.includes(q) || secondary.includes(q);
48
- }
49
- function flattenFilteredOptions({ options, query, selectedValue, allowCustomValue, customValueLabel }) {
50
- const flatOptions = options.flatMap((option) => {
51
- if (isCategory(option)) {
52
- const filteredOptions = option.options.filter((opt) => filterOption(opt, query));
53
- if (filteredOptions.length === 0) return [];
54
- return [{
55
- label: option.category || "",
56
- value: option.category || "",
57
- disabled: true,
58
- category: true
59
- }, ...filteredOptions];
60
- }
61
- if (filterOption(option, query)) return option;
62
- return [];
63
- });
64
- if (allowCustomValue) {
65
- const customValue = query || selectedValue || "";
66
- flatOptions.unshift({
67
- label: `${customValueLabel} "${customValue}"`,
68
- value: customValue
69
- });
70
- } else if (flatOptions.length === 0) return [{
71
- label: "No options found",
72
- value: "",
73
- disabled: true
74
- }];
75
- return flatOptions;
76
- }
77
- function DropdownCaret() {
78
- return /* @__PURE__ */ jsx(CaretDownIcon, { className: "text-neutral-400 absolute top-1/2 -translate-y-1/2 right-0 mr-3 inset-y-0 flex items-center rounded-r-md focus:outline-hidden" });
79
- }
80
- //#endregion
81
13
  //#region src/components/Dropdown.tsx
14
+ var defaultCollapsedLabel = (count) => `${count} more`;
82
15
  var checkEquals = (val, opt) => {
83
16
  if (typeof val === "object" && !Array.isArray(val)) return deepEqual(val, opt);
84
17
  return val === opt;
@@ -153,7 +86,51 @@ function ActiveWatcher({ active, onChange }) {
153
86
  }, [active, onChange]);
154
87
  return null;
155
88
  }
156
- function DropdownHelper({ wrapperClassName = "w-full", className, textBoxClassName = "w-full", options = [], onChange, onActiveChange, value, id, ariaLabel, disabled = false, placeholder = "", loading = false, allowCustomValue = false, customValueLabel = "Use", portalEl }) {
89
+ function OptionContent({ label, description, icon }) {
90
+ return /* @__PURE__ */ jsxs("span", {
91
+ className: cx("flex gap-1.5", description ? "items-start min-w-0" : "items-center truncate"),
92
+ "data-tooltip-id": TOOLTIP_ID,
93
+ onMouseEnter: (e) => {
94
+ const inner = e.currentTarget.lastElementChild;
95
+ if (inner && inner.scrollWidth > inner.clientWidth) e.currentTarget.setAttribute("data-tooltip-content", label);
96
+ else e.currentTarget.removeAttribute("data-tooltip-content");
97
+ },
98
+ children: [icon, description ? /* @__PURE__ */ jsxs("span", {
99
+ className: "flex flex-col min-w-0",
100
+ children: [/* @__PURE__ */ jsx("span", {
101
+ className: "truncate",
102
+ children: label
103
+ }), /* @__PURE__ */ jsx("span", {
104
+ className: "text-(--theme-muted-text-color) text-xs leading-snug",
105
+ children: description
106
+ })]
107
+ }) : /* @__PURE__ */ jsx("span", {
108
+ className: "truncate",
109
+ children: label
110
+ })]
111
+ });
112
+ }
113
+ function ToggleOption({ option, category, expanded, onToggle, ...virtualRowProps }) {
114
+ return /* @__PURE__ */ jsx(ComboboxOption, {
115
+ ...virtualRowProps,
116
+ "data-testid": "combobox-toggle",
117
+ value: option,
118
+ className: "w-full h-10 flex items-center pl-3",
119
+ children: /* @__PURE__ */ jsxs("button", {
120
+ type: "button",
121
+ "aria-expanded": expanded,
122
+ className: "flex items-center gap-1.5 cursor-pointer text-(--theme-muted-text-color) hover:text-(--theme-app)",
123
+ onMouseDown: (e) => e.preventDefault(),
124
+ onClick: (e) => {
125
+ e.preventDefault();
126
+ e.stopPropagation();
127
+ onToggle(category);
128
+ },
129
+ children: [/* @__PURE__ */ jsx(CaretDownIcon, { className: cx("w-3 h-3", !expanded && "-rotate-90") }), option.label]
130
+ })
131
+ });
132
+ }
133
+ function DropdownHelper({ wrapperClassName = "w-full", className, textBoxClassName = "w-full", options = [], onChange, onActiveChange, value, id, ariaLabel, disabled = false, placeholder = "", loading = false, allowCustomValue = false, customValueLabel = "Use", collapsedLabel = defaultCollapsedLabel, portalEl }) {
157
134
  const DEBOUNCE_MS = 500;
158
135
  const [query, setQuery] = useState("");
159
136
  const [selectedIconWidth, setSelectedIconWidth] = useState(0);
@@ -215,6 +192,15 @@ function DropdownHelper({ wrapperClassName = "w-full", className, textBoxClassNa
215
192
  });
216
193
  const debounceRef = useRef(null);
217
194
  const buttonRef = useRef(null);
195
+ const [expandedCategories, setExpandedCategories] = useState(/* @__PURE__ */ new Set());
196
+ const selectedCategory = useMemo(() => options.find((o) => isCategory(o) && o.options.some((opt) => checkEquals(value.value, opt.value)))?.category, [options, value]);
197
+ const lastAutoExpandedValue = useRef(void 0);
198
+ useEffect(() => {
199
+ if (!selectedCategory || checkEquals(lastAutoExpandedValue.current, value.value)) return;
200
+ lastAutoExpandedValue.current = value.value;
201
+ setExpandedCategories((prev) => prev.size === 1 && prev.has(selectedCategory) ? prev : new Set([selectedCategory]));
202
+ }, [selectedCategory, value]);
203
+ const toggleCategory = (category) => setExpandedCategories((prev) => toggleSetMember(prev, category));
218
204
  const flushCustomValue = useCallback((val) => {
219
205
  onChange({
220
206
  label: val,
@@ -246,15 +232,19 @@ function DropdownHelper({ wrapperClassName = "w-full", className, textBoxClassNa
246
232
  }
247
233
  onChange(val);
248
234
  };
249
- const flattenedOptions = useMemo(() => flattenFilteredOptions({
235
+ const flattenedOptions = useMemo(() => flattenDisplayOptions({
250
236
  options,
251
237
  query,
238
+ expandedCategories,
239
+ collapsedLabel,
252
240
  selectedValue: value?.value,
253
241
  allowCustomValue,
254
242
  customValueLabel
255
243
  }), [
256
244
  options,
257
245
  query,
246
+ expandedCategories,
247
+ collapsedLabel,
258
248
  value,
259
249
  allowCustomValue,
260
250
  customValueLabel
@@ -268,31 +258,21 @@ function DropdownHelper({ wrapperClassName = "w-full", className, textBoxClassNa
268
258
  const optionLabel = typeof option === "string" ? option : option.label;
269
259
  const optionDescription = typeof option !== "string" ? option.description : void 0;
270
260
  const isCategoryRow = typeof option !== "string" && Boolean(option.category);
261
+ const toggleTarget = typeof option !== "string" ? option.toggle : void 0;
262
+ if (toggleTarget !== void 0) return /* @__PURE__ */ jsx(ToggleOption, {
263
+ option,
264
+ category: toggleTarget,
265
+ expanded: expandedCategories.has(toggleTarget),
266
+ onToggle: toggleCategory
267
+ });
271
268
  return /* @__PURE__ */ jsx(ComboboxOption, {
272
269
  "data-testid": "combobox-option",
273
270
  value: option,
274
271
  className: cx("w-full group relative flex", optionDescription ? "min-h-10 py-2" : "h-10", isCategoryRow ? "text-[14px] font-bold pl-1 items-center" : "data-disabled:text-(--theme-muted-text-color) data-disabled:bg-(--theme-muted-panel-bg) data-disabled:cursor-not-allowed cursor-pointer items-center pr-9 pl-3 leading-5 data-focus:bg-(--theme-muted-panel-bg) data-focus:text-(--theme-app) data-focus:outline-hidden", "data-selected:border-l-2 data-selected:border-(--theme-link) data-selected:bg-(--theme-muted-panel-bg) data-selected:text-(--theme-app)"),
275
- children: /* @__PURE__ */ jsxs("span", {
276
- className: cx("flex gap-1.5", optionDescription ? "items-start min-w-0" : "items-center truncate"),
277
- "data-tooltip-id": TOOLTIP_ID,
278
- onMouseEnter: (e) => {
279
- const inner = e.currentTarget.lastElementChild;
280
- if (inner && inner.scrollWidth > inner.clientWidth) e.currentTarget.setAttribute("data-tooltip-content", optionLabel);
281
- else e.currentTarget.removeAttribute("data-tooltip-content");
282
- },
283
- children: [typeof option !== "string" && option.icon, optionDescription ? /* @__PURE__ */ jsxs("span", {
284
- className: "flex flex-col min-w-0",
285
- children: [/* @__PURE__ */ jsx("span", {
286
- className: "truncate",
287
- children: optionLabel
288
- }), /* @__PURE__ */ jsx("span", {
289
- className: "text-(--theme-muted-text-color) text-xs leading-snug",
290
- children: optionDescription
291
- })]
292
- }) : /* @__PURE__ */ jsx("span", {
293
- className: "truncate",
294
- children: optionLabel
295
- })]
272
+ children: /* @__PURE__ */ jsx(OptionContent, {
273
+ label: optionLabel,
274
+ description: optionDescription,
275
+ icon: typeof option !== "string" && option.icon
296
276
  })
297
277
  });
298
278
  }
@@ -306,7 +286,10 @@ function DropdownHelper({ wrapperClassName = "w-full", className, textBoxClassNa
306
286
  by: checkEquals,
307
287
  value,
308
288
  onChange: onComboboxChange,
309
- onClose: () => setQuery(""),
289
+ onClose: () => {
290
+ setQuery("");
291
+ setExpandedCategories(new Set(selectedCategory ? [selectedCategory] : []));
292
+ },
310
293
  immediate: true,
311
294
  virtual: {
312
295
  options: flattenedOptions,
@@ -740,4 +723,4 @@ function SwitchToggle({ onChange, value = void 0, yesLabel = "Yes", noLabel = "N
740
723
  });
741
724
  }
742
725
  //#endregion
743
- export { UncontrolledCollapsiblePanel as a, Dropdown as c, flattenOptions as d, isCategory as f, MaybeCollapsible as i, DropdownCaret as l, SectionHeader as n, MultiSelectDropdown as o, isOption as p, ControlledCollapsiblePanel as r, CustomListbox as s, SwitchToggle as t, filterOption as u };
726
+ export { UncontrolledCollapsiblePanel as a, Dropdown as c, MaybeCollapsible as i, SectionHeader as n, MultiSelectDropdown as o, ControlledCollapsiblePanel as r, CustomListbox as s, SwitchToggle as t };
@@ -1,5 +1,5 @@
1
1
  import { CheckIcon, CopyIcon, EyeIcon, EyeOffIcon } from "../icons.js";
2
- import { c as useStrings, i as useNotify } from "./Provider-3382VBAv.js";
2
+ import { c as useStrings, i as useNotify } from "./Provider-CfFV-vc5.js";
3
3
  import { t as Loader } from "./Loader-BXoXTZGc.js";
4
4
  import { t as SwitchToggleSmall } from "./SwitchToggleSmall-FcsaYSTs.js";
5
5
  import { c as FieldWrapper, n as flattenGroups, r as impureSetValueFromPath } from "./lib-DTgL8klR.js";
@@ -9,7 +9,7 @@ import { Suspense, lazy, useMemo, useRef, useState } from "react";
9
9
  import { convertToDynamicForm, parseStringsOfObj, parseStringsOfObjReady } from "@parallelworks/workflow-parser";
10
10
  import { useField, useFormikContext } from "formik";
11
11
  //#region src/editor/Editor.tsx
12
- var MonacoEditor = lazy(() => import("./Monaco-BuhN8wP_.js"));
12
+ var MonacoEditor = lazy(() => import("./Monaco-DCwRDrPm.js"));
13
13
  function Editor({ ...props }) {
14
14
  return /* @__PURE__ */ jsx(Suspense, {
15
15
  fallback: /* @__PURE__ */ jsx(Loader, { text: "Loading editor..." }),
@@ -1,7 +1,7 @@
1
1
  import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, DisplayIcon, ErrorIcon, FilterIcon, MenuIcon, SearchIcon, XIcon } from "../icons.js";
2
- import { c as useStrings, n as useLink, r as useNavigation } from "./Provider-3382VBAv.js";
2
+ import { c as useStrings, n as useLink, r as useNavigation } from "./Provider-CfFV-vc5.js";
3
3
  import { n as TOOLTIP_ID, r as TooltipInfo } from "./Tooltip-WgbiC_nd.js";
4
- import { c as PageHeader, f as rowContextMenuProps, g as useRowMenuActive, l as MoreButton } from "./PageHeader-Dz4A_w6J.js";
4
+ import { c as PageHeader, f as rowContextMenuProps, g as useRowMenuActive, l as MoreButton } from "./PageHeader-8xP84_KS.js";
5
5
  import { n as Table } from "./Table-ByziK93x.js";
6
6
  import cx from "classnames";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -1154,18 +1154,37 @@ function ListCount({ total, matches, filterActive, noun, nounPlural }) {
1154
1154
  }
1155
1155
  //#endregion
1156
1156
  //#region src/list/ListPager.tsx
1157
+ /** Page sizes offered by the pager's per-page select; first entry is the default. */
1158
+ var LIST_PAGE_SIZES = [
1159
+ 50,
1160
+ 100,
1161
+ 200
1162
+ ];
1157
1163
  /** Footer pager bar for paginated new-nav lists: "{start}–{end} of {total}" with prev/next. Renders nothing for a single page. */
1158
- function ListPager({ page, pageSize, total, onPageChange }) {
1164
+ function ListPager({ page, pageSize, total, onPageChange, pageSizes = LIST_PAGE_SIZES, onPageSizeChange }) {
1159
1165
  const t = useStrings().list;
1160
1166
  const pageCount = Math.max(1, Math.ceil(total / pageSize));
1161
1167
  const start = page * pageSize + 1;
1162
1168
  const end = Math.min(total, (page + 1) * pageSize);
1163
- if (pageCount <= 1) return null;
1169
+ const smallest = Math.min(...pageSizes);
1170
+ const showSizeSelect = !!onPageSizeChange && (total > smallest || pageSize > smallest);
1171
+ if (pageCount <= 1 && !showSizeSelect) return null;
1164
1172
  return /* @__PURE__ */ jsxs("div", {
1165
1173
  className: "flex items-center justify-between px-2.5 h-10 shrink-0 border-t theme-border",
1166
- children: [/* @__PURE__ */ jsx("span", {
1167
- className: "text-xs text-(--theme-muted-text-color)",
1168
- children: t.pager(start, end, total)
1174
+ children: [/* @__PURE__ */ jsxs("div", {
1175
+ className: "flex items-center gap-3 text-xs text-(--theme-muted-text-color)",
1176
+ children: [/* @__PURE__ */ jsx("span", { children: t.pager(start, end, total) }), showSizeSelect && /* @__PURE__ */ jsxs("label", {
1177
+ className: "flex items-center gap-1",
1178
+ children: [t.perPage, /* @__PURE__ */ jsx("select", {
1179
+ value: pageSize,
1180
+ onChange: (e) => onPageSizeChange(Number(e.target.value)),
1181
+ className: "theme-input rounded px-1.5 py-0.5 text-xs",
1182
+ children: pageSizes.map((size) => /* @__PURE__ */ jsx("option", {
1183
+ value: size,
1184
+ children: size
1185
+ }, size))
1186
+ })]
1187
+ })]
1169
1188
  }), /* @__PURE__ */ jsxs("div", {
1170
1189
  className: "flex items-center gap-1",
1171
1190
  children: [/* @__PURE__ */ jsx("button", {
@@ -1250,4 +1269,4 @@ function HoverCardSurface({ x, y, widthClassName, relayoutKey, onKeepOpen, onLea
1250
1269
  }), document.querySelector(".ds-root") ?? document.body);
1251
1270
  }
1252
1271
  //#endregion
1253
- export { ToolbarButton as A, NAME_MIN_WIDTH_PX as C, listTableFixedProps as D, actionsColumnWidth as E, stickyTabsUnderHeaderClasses as M, stickyToolbarClasses as N, listTableProps as O, ListSkeleton as S, RowSelectCheckbox as T, ListRowActionsProvider as _, ListSearchControl as a, ListActionsHeader as b, ListFilterMenu as c, DEFAULT_ORDER as d, FAVORITE_ORDER as f, ListRow as g, ListPage as h, ListCount as i, stickyTabsClasses as j, ActionToolbar as k, listControlButtonClasses as l, ListColumns as m, useHoverCard as n, useListSearch as o, useListView as p, ListPager as r, ListDisplayMenu as s, HoverCardSurface as t, isTextEntryContext as u, useListNavigate as v, NameCell as w, ListError as x, GroupRow as y };
1272
+ export { ActionToolbar as A, ListSkeleton as C, actionsColumnWidth as D, RowSelectCheckbox as E, stickyTabsClasses as M, stickyTabsUnderHeaderClasses as N, listTableFixedProps as O, stickyToolbarClasses as P, ListError as S, NameCell as T, ListRow as _, ListCount as a, GroupRow as b, ListDisplayMenu as c, isTextEntryContext as d, DEFAULT_ORDER as f, ListPage as g, ListColumns as h, ListPager as i, ToolbarButton as j, listTableProps as k, ListFilterMenu as l, useListView as m, useHoverCard as n, ListSearchControl as o, FAVORITE_ORDER as p, LIST_PAGE_SIZES as r, useListSearch as s, HoverCardSurface as t, listControlButtonClasses as u, ListRowActionsProvider as v, NAME_MIN_WIDTH_PX as w, ListActionsHeader as x, useListNavigate as y };