@parallelworks/ui 0.13.0 → 0.15.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 (33) hide show
  1. package/dist/chunks/{CodePreviewEditor-67A_SKgE.js → CodePreviewEditor-DuMZMA_k.js} +1 -1
  2. package/dist/chunks/{CreateModal-k_e1dI8c.js → CreateModal-B7smkHkv.js} +26 -10
  3. package/dist/chunks/{Monaco-H56KyEdb.js → Monaco-BXDH2jVj.js} +1 -1
  4. package/dist/chunks/{PageHeader-FhtphHGc.js → PageHeader-CoZWF0pJ.js} +22 -9
  5. package/dist/chunks/{StatusBadge-RRUI78iS.js → StatusBadge-zvEtK16a.js} +1 -1
  6. package/dist/chunks/{SwitchToggle-DbW0Q__L.js → SwitchToggle-3V836Cn7.js} +23 -15
  7. package/dist/chunks/{editor-BZ9Buwi4.js → editor-Ko3zX22Y.js} +2 -2
  8. package/dist/chunks/{lib-DyJvmbzj.js → lib-CUTRDpa6.js} +3 -5
  9. package/dist/chunks/{list-3jZW8rmM.js → list-CduoUsMW.js} +13 -10
  10. package/dist/components/CollapsiblePanel.d.ts +3 -1
  11. package/dist/components/CreateModal.d.ts +6 -0
  12. package/dist/components/PageHeader.d.ts +5 -1
  13. package/dist/components/dropdownUtils.d.ts +2 -0
  14. package/dist/editor/index.js +1 -1
  15. package/dist/file-explorer/FileExplorer.d.ts +19 -2
  16. package/dist/file-explorer/index.js +65 -35
  17. package/dist/form/Form.d.ts +12 -8
  18. package/dist/form/index.d.ts +1 -1
  19. package/dist/form/index.js +84 -32
  20. package/dist/icons.d.ts +3 -3
  21. package/dist/icons.js +4 -4
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.js +8 -8
  24. package/dist/list/HoverCard.d.ts +3 -1
  25. package/dist/list/ListPage.d.ts +2 -0
  26. package/dist/list/ListTable.d.ts +2 -1
  27. package/dist/list/RowContextMenu.d.ts +5 -0
  28. package/dist/list/index.js +2 -2
  29. package/dist/styles/theme.css +40 -0
  30. package/dist/styles.css +1 -1
  31. package/dist/workflow/index.js +50 -2
  32. package/dist/workflow/types.d.ts +2 -0
  33. package/package.json +2 -2
@@ -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-BZ9Buwi4.js";
2
+ import { n as defineEditorThemes, r as getThemeName, t as setupMonacoWorkers } from "./editor-Ko3zX22Y.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,9 +1,9 @@
1
1
  import { AlertIcon, CheckIcon, ChevronDownIcon, ChevronRightIcon, ClipboardIcon, CopyIcon, FavoriteIcon, FavoriteOutlineIcon, HiddenFalseIcon, HiddenTrueIcon, InfoIcon, TooltipIcon } from "../icons.js";
2
2
  import { c as useStrings, i as useNotify } from "./Provider-CbFVFB7O.js";
3
3
  import { n as TOOLTIP_ID } from "./Tooltip-n9w7Tkt1.js";
4
- import { i as filterOption, l as toggleSetMember } from "./StatusBadge-RRUI78iS.js";
4
+ import { i as filterOption, l as toggleSetMember } from "./StatusBadge-zvEtK16a.js";
5
5
  import { n as modalPanelClasses, t as BareModal } from "./BareModal-BqroLbZ5.js";
6
- import { C as AddMenu } from "./PageHeader-FhtphHGc.js";
6
+ import { w as AddMenu } from "./PageHeader-CoZWF0pJ.js";
7
7
  import cx from "classnames";
8
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
9
9
  import { Suspense, lazy, useCallback, useEffect, useRef, useState } from "react";
@@ -280,7 +280,7 @@ function CreateModal({ open, onClose, typeIcon, typeLabel, typeMenu, namePlaceho
280
280
  if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) handleSubmit();
281
281
  },
282
282
  onPanelMouseDown: (e) => {
283
- if (e.target instanceof Element && !e.target.closest("input, textarea, button, a, select, label")) e.preventDefault();
283
+ if (e.target instanceof Element && !e.target.closest("input, textarea, button, a, select, label, code, pre, .select-all")) e.preventDefault();
284
284
  },
285
285
  children: [/* @__PURE__ */ jsxs("div", {
286
286
  className: "flex-1 min-h-0 overflow-y-auto overscroll-contain",
@@ -514,7 +514,7 @@ function pushToggleRowOnce(nodes, toggledGroups, group, count, collapse) {
514
514
  function buildOptionNodes(options, query, onChange, collapse) {
515
515
  const trimmed = query.trim();
516
516
  const q = trimmed.toLowerCase().replace(/-/g, "");
517
- const filtered = q ? options.filter((o) => filterOption({
517
+ const filtered = q ? options.filter((o) => o.pinned || filterOption({
518
518
  label: o.label,
519
519
  value: o.value,
520
520
  secondaryValue: o.group
@@ -536,12 +536,28 @@ function buildOptionNodes(options, query, onChange, collapse) {
536
536
  pushToggleRowOnce(nodes, toggledGroups, group, collapsibleCounts.get(group) ?? 0, collapse);
537
537
  if (!collapse.expanded.has(group)) continue;
538
538
  }
539
- nodes.push(/* @__PURE__ */ jsx(MenuItem, { children: /* @__PURE__ */ jsxs("button", {
540
- type: "button",
541
- onClick: () => onChange(option.value),
542
- className: "flex w-full items-center gap-2 px-3 py-1.5 text-[13px] text-(--theme-app) data-[focus]:bg-(--theme-muted-panel-bg) cursor-pointer",
543
- children: [option.icon, option.label]
544
- }) }, option.value));
539
+ nodes.push(/* @__PURE__ */ jsx(MenuItem, {
540
+ disabled: option.disabled ?? false,
541
+ children: /* @__PURE__ */ jsxs("button", {
542
+ type: "button",
543
+ disabled: option.disabled,
544
+ "aria-disabled": option.disabled,
545
+ "aria-label": option.description ? option.label : void 0,
546
+ "aria-description": option.description,
547
+ onClick: () => onChange(option.value),
548
+ className: cx("flex w-full items-center gap-2 px-3 py-1.5 text-left text-[13px] data-[focus]:bg-(--theme-muted-panel-bg)", option.disabled ? "cursor-not-allowed text-(--theme-muted-text-color)" : "cursor-pointer text-(--theme-app)", option.pinned && "mt-1 border-t border-(--theme-border) pt-2 font-medium"),
549
+ children: [option.icon, option.description ? /* @__PURE__ */ jsxs("span", {
550
+ className: "flex min-w-0 flex-col",
551
+ children: [/* @__PURE__ */ jsx("span", {
552
+ className: "truncate",
553
+ children: option.label
554
+ }), /* @__PURE__ */ jsx("span", {
555
+ className: "truncate text-[11px] font-normal text-(--theme-muted-text-color)",
556
+ children: option.description
557
+ })]
558
+ }) : option.label]
559
+ })
560
+ }, option.value));
545
561
  }
546
562
  return nodes;
547
563
  }
@@ -1,6 +1,6 @@
1
1
  import { l as useWorkflowJsonResolver } from "./Provider-CbFVFB7O.js";
2
2
  import { t as useCssIsDark } from "./useCssIsDark-DHr05dG2.js";
3
- import { n as defineEditorThemes, r as getThemeName, t as setupMonacoWorkers } from "./editor-BZ9Buwi4.js";
3
+ import { n as defineEditorThemes, r as getThemeName, t as setupMonacoWorkers } from "./editor-Ko3zX22Y.js";
4
4
  import cx from "classnames";
5
5
  import { jsx } from "react/jsx-runtime";
6
6
  import { useEffect, useRef } from "react";
@@ -128,7 +128,8 @@ function toRowMenuItem(action) {
128
128
  icon: action.icon,
129
129
  onSelect: action.onSelect ?? (() => {}),
130
130
  disabled: action.disabled,
131
- destructive: action.destructive
131
+ destructive: action.destructive,
132
+ tooltip: action.tooltip
132
133
  };
133
134
  }
134
135
  /** Bridges a toolbar action (icon component, `onClick`/`href`) onto the shared
@@ -143,7 +144,8 @@ function toolbarActionToRowAction(action) {
143
144
  onSelect: action.onClick,
144
145
  to: action.href,
145
146
  disabled: action.disabled,
146
- destructive: action.key === "delete" || action.key === "destroy"
147
+ destructive: action.key === "delete" || action.key === "destroy",
148
+ tooltip: action.disabledTooltip
147
149
  };
148
150
  }
149
151
  /** Assembles a row's context menu in the shared order: leading link(s) → middle
@@ -344,8 +346,14 @@ function MenuRow({ item, onClose, side }) {
344
346
  });
345
347
  return /* @__PURE__ */ jsxs("button", {
346
348
  type: "button",
347
- disabled: item.disabled,
349
+ disabled: item.disabled && !item.tooltip,
350
+ "aria-disabled": item.disabled,
351
+ ...item.disabled && item.tooltip ? {
352
+ "data-tooltip-id": TOOLTIP_ID,
353
+ "data-tooltip-content": item.tooltip
354
+ } : {},
348
355
  onClick: () => {
356
+ if (item.disabled) return;
349
357
  if (item.copy) {
350
358
  const { text, label } = item.copy;
351
359
  navigator.clipboard?.writeText(text).then(() => notify.success(t.copied(label)), () => notify.error(t.couldntCopy(label)));
@@ -440,7 +448,7 @@ var headerAddButtonClasses = "inline-flex h-[26px] shrink-0 items-center gap-1.5
440
448
  /** With menu, the plus opens a chooser; otherwise navigates to or calls the
441
449
  * single action. `word` is the visible verb; `label` stays the full action for
442
450
  * the tooltip and screen readers. */
443
- function HeaderAddButton({ to, onClick, label, word = "add", menu }) {
451
+ function HeaderAddButton({ to, onClick, label, word = "add", menu, disabled = false, disabledHint }) {
444
452
  const { common } = useStrings();
445
453
  const Link = useLink();
446
454
  const content = /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(AddIcon, { className: "w-3 h-3" }), /* @__PURE__ */ jsx("span", { children: word === "create" ? common.create : common.add })] });
@@ -457,12 +465,17 @@ function HeaderAddButton({ to, onClick, label, word = "add", menu }) {
457
465
  className: headerAddButtonClasses,
458
466
  children: content
459
467
  });
468
+ const hint = disabled ? disabledHint : void 0;
460
469
  return /* @__PURE__ */ jsx("button", {
461
470
  type: "button",
462
- onClick,
463
- "aria-label": label,
464
- title: label,
465
- className: headerAddButtonClasses,
471
+ onClick: disabled ? void 0 : onClick,
472
+ "aria-label": hint ? `${label}. ${hint}` : label,
473
+ "aria-disabled": disabled || void 0,
474
+ ...hint ? {
475
+ "data-tooltip-id": TOOLTIP_ID,
476
+ "data-tooltip-content": hint
477
+ } : { title: label },
478
+ className: cx(headerAddButtonClasses, "aria-disabled:cursor-not-allowed aria-disabled:opacity-50 aria-disabled:hover:bg-transparent aria-disabled:hover:text-(--theme-muted-text-color)"),
466
479
  children: content
467
480
  });
468
481
  }
@@ -611,4 +624,4 @@ function PageHeader({ breadcrumbs, title, titleAction, actions, toolbar, classNa
611
624
  });
612
625
  }
613
626
  //#endregion
614
- export { AddMenu as C, useSetDefaultBreadcrumbs as S, BreadcrumbProvider as _, HeaderAddButton as a, useHideBreadcrumbs as b, PageHeader as c, copySubmenu as d, rowContextMenuProps as f, useRowMenuActive as g, useRowMenu as h, HeaderActionMenu as i, MoreButton as l, toolbarActionToRowAction as m, BreadcrumbsRightAction as n, HeaderEditButton as o, toRowMenuItem as p, BreadcrumbsTitleAction as r, HeaderRowMenu as s, BreadcrumbsActionButton as t, assembleRowMenu as u, useBreadcrumb as v, useSetBreadcrumbs as x, useClearBreadcrumbs as y };
627
+ export { useSetDefaultBreadcrumbs as C, useSetBreadcrumbs as S, useRowMenuActive as _, HeaderAddButton as a, useClearBreadcrumbs as b, PageHeader as c, assembleRowMenu as d, copySubmenu as f, useRowMenu as g, toolbarActionToRowAction as h, HeaderActionMenu as i, headerActionButtonClasses as l, toRowMenuItem as m, BreadcrumbsRightAction as n, HeaderEditButton as o, rowContextMenuProps as p, BreadcrumbsTitleAction as r, HeaderRowMenu as s, BreadcrumbsActionButton as t, MoreButton as u, BreadcrumbProvider as v, AddMenu as w, useHideBreadcrumbs as x, useBreadcrumb as y };
@@ -67,7 +67,7 @@ function collapsedCategoryRows(category, filtered, collapsibleCount, expanded, c
67
67
  }
68
68
  function flattenDisplayOptions({ options, query, expandedCategories, collapsedLabel, selectedValue, allowCustomValue, customValueLabel }) {
69
69
  const flatOptions = options.flatMap((option) => {
70
- if (!isCategory(option)) return filterOption(option, query) ? [option] : [];
70
+ if (!isCategory(option)) return option.pinned || filterOption(option, query) ? [option] : [];
71
71
  const filtered = option.options.filter((opt) => filterOption(opt, query));
72
72
  if (filtered.length === 0) return [];
73
73
  const category = option.category || "";
@@ -1,7 +1,7 @@
1
1
  import { AngleDownIcon, AngleRightIcon, CaretDownIcon } from "../icons.js";
2
2
  import { c as useStrings } from "./Provider-CbFVFB7O.js";
3
3
  import { a as truncationTooltipProps, n as TOOLTIP_ID } from "./Tooltip-n9w7Tkt1.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-RRUI78iS.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-zvEtK16a.js";
5
5
  import cx from "classnames";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -263,6 +263,7 @@ function DropdownHelper({ wrapperClassName = "w-full", className, textBoxClassNa
263
263
  const optionDescription = typeof option !== "string" ? option.description : void 0;
264
264
  const isCategoryRow = typeof option !== "string" && Boolean(option.category);
265
265
  const toggleTarget = typeof option !== "string" ? option.toggle : void 0;
266
+ const isPinned = typeof option !== "string" && Boolean(option.pinned);
266
267
  if (toggleTarget !== void 0) return /* @__PURE__ */ jsx(ToggleOption, {
267
268
  option,
268
269
  category: toggleTarget,
@@ -272,7 +273,7 @@ function DropdownHelper({ wrapperClassName = "w-full", className, textBoxClassNa
272
273
  return /* @__PURE__ */ jsx(ComboboxOption, {
273
274
  "data-testid": "combobox-option",
274
275
  value: option,
275
- 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)"),
276
+ 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)", isPinned && "border-t border-(--theme-border) font-medium"),
276
277
  children: /* @__PURE__ */ jsx(OptionContent, {
277
278
  label: optionLabel,
278
279
  description: optionDescription,
@@ -619,7 +620,7 @@ function MultiSelectDropdown({ options, value, onChange, id, "aria-labelledby":
619
620
  //#endregion
620
621
  //#region src/components/CollapsiblePanel.tsx
621
622
  var noop = () => {};
622
- function ControlledCollapsiblePanel({ title = "", children, open, setOpen, onClick = noop, wrapperElement, customizedButton = null, className }) {
623
+ function ControlledCollapsiblePanel({ title = "", children, open, setOpen, onClick = noop, wrapperElement, customizedButton = null, titleAction, className }) {
623
624
  const toggleOpen = useCallback(() => {
624
625
  if (onClick) onClick();
625
626
  setOpen?.((open) => !open);
@@ -633,18 +634,25 @@ function ControlledCollapsiblePanel({ title = "", children, open, setOpen, onCli
633
634
  }) })] }) : /* @__PURE__ */ jsxs("div", {
634
635
  className: cx("border border-solid rounded-2xl border-(--theme-border) bg-(--theme-panel-bg) text-(--theme-panel) mb-1 w-full", className),
635
636
  children: [/* @__PURE__ */ jsxs("div", {
636
- className: cx(!open && "text-(--theme-muted-text-color)", "w-full flex items-center justify-between transform ease-in transition text-sm py-3 px-2 cursor-pointer"),
637
- children: [/* @__PURE__ */ jsxs("button", {
638
- type: "button",
639
- "aria-expanded": !!open,
640
- "aria-controls": contentId,
641
- className: "flex items-center w-full text-left cursor-pointer",
642
- onClick: toggleOpen,
643
- children: [/* @__PURE__ */ jsx(AngleRightIcon, { className: cx(open && "transform rotate-90", "px-1 ml-1 text-[1.25rem] transition ease-in") }), /* @__PURE__ */ jsx("span", {
644
- className: "px-2 font-bold select-none",
645
- children: title?.toString()
646
- })]
647
- }), /* @__PURE__ */ jsx("div", { children: customizedButton })]
637
+ className: cx(!open && "text-(--theme-muted-text-color)", "w-full flex items-center transform ease-in transition text-sm py-3 px-2 cursor-pointer"),
638
+ children: [
639
+ /* @__PURE__ */ jsxs("button", {
640
+ type: "button",
641
+ "aria-expanded": !!open,
642
+ "aria-controls": contentId,
643
+ className: cx("flex items-center text-left cursor-pointer", !titleAction && "w-full"),
644
+ onClick: toggleOpen,
645
+ children: [/* @__PURE__ */ jsx(AngleRightIcon, { className: cx(open && "transform rotate-90", "px-1 ml-1 text-[1.25rem] transition ease-in") }), /* @__PURE__ */ jsx("span", {
646
+ className: "px-2 font-bold select-none",
647
+ children: title?.toString()
648
+ })]
649
+ }),
650
+ titleAction,
651
+ /* @__PURE__ */ jsx("div", {
652
+ className: "ml-auto",
653
+ children: customizedButton
654
+ })
655
+ ]
648
656
  }), /* @__PURE__ */ jsx(AnimateHeight, {
649
657
  id: contentId,
650
658
  duration: 300,
@@ -2,14 +2,14 @@ import { CheckIcon, CopyIcon, EyeIcon, EyeOffIcon } from "../icons.js";
2
2
  import { c as useStrings, i as useNotify } from "./Provider-CbFVFB7O.js";
3
3
  import { t as Loader } from "./Loader-BXoXTZGc.js";
4
4
  import { t as SwitchToggleSmall } from "./SwitchToggleSmall-FcsaYSTs.js";
5
- import { c as FieldWrapper, n as flattenGroups, r as impureSetValueFromPath } from "./lib-DyJvmbzj.js";
5
+ import { c as FieldWrapper, n as flattenGroups, r as impureSetValueFromPath } from "./lib-CUTRDpa6.js";
6
6
  import cx from "classnames";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  import { Suspense, lazy, useId, 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-H56KyEdb.js"));
12
+ var MonacoEditor = lazy(() => import("./Monaco-BXDH2jVj.js"));
13
13
  function Editor({ ...props }) {
14
14
  return /* @__PURE__ */ jsx(Suspense, {
15
15
  fallback: /* @__PURE__ */ jsx(Loader, { text: "Loading editor..." }),
@@ -115,11 +115,9 @@ function enforceOneMustBeTrue(items, options, promoteIndex) {
115
115
  const oneMustBeTrueFields = Object.keys(options).filter((key) => typeof options[key] === "object" && options[key] !== null && "one_must_be_true" in options[key] && options[key].one_must_be_true);
116
116
  for (const fieldKey of oneMustBeTrueFields) {
117
117
  const fieldSchema = options[fieldKey];
118
- if (typeof fieldSchema === "object" && fieldSchema && "options" in fieldSchema) {
119
- const opts = fieldSchema.options;
120
- const trueOption = typeof opts === "object" && opts && !Array.isArray(opts) && "onOption" in opts ? opts.onOption || true : true;
121
- if (!items.some((item) => item[fieldKey] === trueOption)) items[clampedIndex][fieldKey] = trueOption;
122
- }
118
+ const opts = typeof fieldSchema === "object" && fieldSchema && "options" in fieldSchema ? fieldSchema.options : void 0;
119
+ const trueOption = typeof opts === "object" && opts && !Array.isArray(opts) && "onOption" in opts ? opts.onOption || true : true;
120
+ if (!items.some((item) => item[fieldKey] === trueOption)) items[clampedIndex][fieldKey] = trueOption;
123
121
  }
124
122
  return items;
125
123
  }
@@ -1,7 +1,7 @@
1
1
  import { CheckIcon, ChevronDownIcon, ChevronLeftIcon, ChevronRightIcon, ChevronUpIcon, DisplayIcon, ErrorIcon, FilterIcon, MenuIcon, SearchIcon, XIcon } from "../icons.js";
2
2
  import { c as useStrings, n as useLink, r as useNavigation } from "./Provider-CbFVFB7O.js";
3
3
  import { n as TOOLTIP_ID, r as TooltipInfo } from "./Tooltip-n9w7Tkt1.js";
4
- import { c as PageHeader, f as rowContextMenuProps, g as useRowMenuActive, l as MoreButton } from "./PageHeader-FhtphHGc.js";
4
+ import { _ as useRowMenuActive, c as PageHeader, p as rowContextMenuProps, u as MoreButton } from "./PageHeader-CoZWF0pJ.js";
5
5
  import { n as Table } from "./Table-CutP1bRt.js";
6
6
  import cx from "classnames";
7
7
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -203,13 +203,16 @@ var listTableFixedProps = {
203
203
  ...listTableProps,
204
204
  className: listTableProps.className.replace("table-auto", "table-fixed")
205
205
  };
206
- function NameCell({ icon, name, subtitle, href }) {
206
+ function NameCell({ icon, name, subtitle, href, badge }) {
207
207
  const Link = useLink();
208
208
  const inner = /* @__PURE__ */ jsxs(Fragment, { children: [icon, /* @__PURE__ */ jsxs("div", {
209
209
  className: "min-w-0",
210
- children: [/* @__PURE__ */ jsx("div", {
211
- className: NAME_TEXT,
212
- children: name
210
+ children: [/* @__PURE__ */ jsxs("div", {
211
+ className: "flex min-w-0 items-center gap-1.5",
212
+ children: [/* @__PURE__ */ jsx("div", {
213
+ className: cx(NAME_TEXT, href && "group-hover/namecell:text-(--theme-link)"),
214
+ children: name
215
+ }), badge]
213
216
  }), subtitle && /* @__PURE__ */ jsx("div", {
214
217
  className: SUBTITLE_TEXT,
215
218
  children: subtitle
@@ -219,7 +222,7 @@ function NameCell({ icon, name, subtitle, href }) {
219
222
  className: "py-2.5",
220
223
  children: href ? /* @__PURE__ */ jsx(Link, {
221
224
  to: href,
222
- className: "flex items-center gap-2.5 min-w-0 cursor-pointer",
225
+ className: "group/namecell flex items-center gap-2.5 min-w-0 cursor-pointer",
223
226
  children: inner
224
227
  }) : /* @__PURE__ */ jsx("div", {
225
228
  className: "flex items-center gap-2.5 min-w-0",
@@ -390,7 +393,7 @@ function ListPage({ section = "main", shellPadded, embedded = false, breadcrumbs
390
393
  actions,
391
394
  toolbar
392
395
  }), /* @__PURE__ */ jsx("div", {
393
- className: "@container min-h-0 flex-1 overflow-auto",
396
+ className: "relative @container min-h-0 flex-1 overflow-auto",
394
397
  children: /* @__PURE__ */ jsx(ListRowActionsProvider, {
395
398
  view,
396
399
  children
@@ -434,7 +437,7 @@ function ListRow({ href, items, goTo, openMenu, pinnedActions, selected, selecti
434
437
  type: "button",
435
438
  "aria-label": action.label,
436
439
  "data-tooltip-id": "global-tooltip",
437
- "data-tooltip-content": action.label,
440
+ "data-tooltip-content": action.disabled && action.tooltip ? action.tooltip : action.label,
438
441
  "data-tooltip-place": "bottom",
439
442
  "aria-disabled": action.disabled,
440
443
  onClick: (e) => {
@@ -1259,7 +1262,7 @@ function useHoverCard() {
1259
1262
  keepOpen
1260
1263
  };
1261
1264
  }
1262
- function HoverCardSurface({ x, y, widthClassName, relayoutKey, onKeepOpen, onLeave, children }) {
1265
+ function HoverCardSurface({ x, y, widthClassName, padded = true, relayoutKey, onKeepOpen, onLeave, children }) {
1263
1266
  const ref = useRef(null);
1264
1267
  const [pos, setPos] = useState(null);
1265
1268
  useLayoutEffect(() => {
@@ -1282,7 +1285,7 @@ function HoverCardSurface({ x, y, widthClassName, relayoutKey, onKeepOpen, onLea
1282
1285
  onMouseLeave: onLeave,
1283
1286
  onFocus: onKeepOpen,
1284
1287
  onBlur: onLeave,
1285
- className: cx("fixed z-[9999] rounded-xl border border-(--theme-border) bg-(--theme-panel-bg) p-3 shadow-xl backdrop-blur-xl", widthClassName),
1288
+ className: cx("fixed z-[9999] overflow-hidden rounded-xl border border-(--theme-border) bg-(--theme-panel-bg) shadow-xl backdrop-blur-xl", padded && "p-3", widthClassName),
1286
1289
  style: {
1287
1290
  left: pos?.left ?? x,
1288
1291
  top: pos?.top ?? y + 8,
@@ -9,9 +9,11 @@ interface IControlledCollapsiblePanelProps {
9
9
  shown?: boolean;
10
10
  setShown?: (shown: boolean) => void;
11
11
  customizedButton?: React.ReactNode;
12
+ /** Rendered right after the title, outside the toggle button. */
13
+ titleAction?: React.ReactNode;
12
14
  className?: string | undefined;
13
15
  }
14
- export declare function ControlledCollapsiblePanel({ title, children, open, setOpen, onClick, wrapperElement, customizedButton, className, }: IControlledCollapsiblePanelProps): React.JSX.Element;
16
+ export declare function ControlledCollapsiblePanel({ title, children, open, setOpen, onClick, wrapperElement, customizedButton, titleAction, className, }: IControlledCollapsiblePanelProps): React.JSX.Element;
15
17
  export declare function UncontrolledCollapsiblePanel({ title, children, onClick, wrapperElement, initialState, className, customizedButton, }: Omit<IControlledCollapsiblePanelProps, 'setOpen' | 'open'> & {
16
18
  initialState?: boolean | undefined;
17
19
  }): React.JSX.Element;
@@ -75,6 +75,12 @@ export interface CreateModalSelectOption {
75
75
  group?: string;
76
76
  /** Hidden behind the group's toggle row until the group is expanded. */
77
77
  collapsible?: boolean;
78
+ /** Always listed regardless of the typed filter (e.g. a trailing "Create new…" action). */
79
+ pinned?: boolean;
80
+ /** Muted second line under the label (e.g. the allocation a group bills to). */
81
+ description?: string;
82
+ /** Listed but not selectable (e.g. a group whose allocation is exhausted). */
83
+ disabled?: boolean;
78
84
  }
79
85
  /** Callers gating collapse on list size must use this same threshold so
80
86
  * collapsed rows always come with a filter to find them. */
@@ -10,15 +10,19 @@ export interface PageHeaderCrumb {
10
10
  label: ReactNode;
11
11
  href?: string;
12
12
  }
13
+ export declare const headerActionButtonClasses = "flex items-center justify-center w-6 h-6 rounded-md shrink-0 cursor-pointer text-(--theme-muted-text-color) hover:text-(--theme-app) hover:bg-(--theme-muted-panel-bg) transition-colors";
13
14
  /** With menu, the plus opens a chooser; otherwise navigates to or calls the
14
15
  * single action. `word` is the visible verb; `label` stays the full action for
15
16
  * the tooltip and screen readers. */
16
- export declare function HeaderAddButton({ to, onClick, label, word, menu, }: {
17
+ export declare function HeaderAddButton({ to, onClick, label, word, menu, disabled, disabledHint, }: {
17
18
  to?: string;
18
19
  onClick?: () => void;
19
20
  label: string;
20
21
  word?: 'add' | 'create';
21
22
  menu?: HeaderAddMenuItem[];
23
+ /** Button mode only. */
24
+ disabled?: boolean;
25
+ disabledHint?: string;
22
26
  }): import("react").JSX.Element;
23
27
  export declare function HeaderActionMenu({ items, label, }: {
24
28
  items: HeaderAddMenuItem[];
@@ -8,6 +8,8 @@ export interface IOptions<T = unknown> {
8
8
  description?: string | undefined;
9
9
  /** Hidden behind the category's toggle row until the category is expanded. */
10
10
  collapsible?: boolean | undefined;
11
+ /** Always listed regardless of the typed query (e.g. a trailing "Create new…" action). */
12
+ pinned?: boolean | undefined;
11
13
  }
12
14
  export interface ICategory<T = unknown> {
13
15
  category?: string | undefined;
@@ -1,2 +1,2 @@
1
- import { a as EditorField, i as FormCodePanel, n as defineEditorThemes, o as Editor, r as getThemeName, t as setupMonacoWorkers } from "../chunks/editor-BZ9Buwi4.js";
1
+ import { a as EditorField, i as FormCodePanel, n as defineEditorThemes, o as Editor, r as getThemeName, t as setupMonacoWorkers } from "../chunks/editor-Ko3zX22Y.js";
2
2
  export { Editor, EditorField, FormCodePanel, defineEditorThemes, getThemeName, setupMonacoWorkers };
@@ -1,5 +1,5 @@
1
1
  import type { IFileExplorerClient, IFileExplorerProvider } from './lib/fileExplorer.js';
2
- import type { TStorage } from './lib/types.js';
2
+ import type { TreeNode, TStorage } from './lib/types.js';
3
3
  interface IFileExplorerProps {
4
4
  storages: TStorage[];
5
5
  /** Resolves the storage's injected provider pair; the host owns caching. */
@@ -8,6 +8,10 @@ interface IFileExplorerProps {
8
8
  client: IFileExplorerClient | null;
9
9
  }>;
10
10
  onRefresh?: () => void;
11
+ /** Handles activating a file instead of previewing it. Lets a host embed the
12
+ * explorer as a file picker (the notebook editor opens .ipynb this way);
13
+ * return false to fall through to the built-in preview. */
14
+ onOpenFile?: ((node: TreeNode, storage: TStorage | undefined) => boolean) | undefined;
11
15
  showUserHierarchy?: boolean;
12
16
  groups?: {
13
17
  name: string;
@@ -16,6 +20,19 @@ interface IFileExplorerProps {
16
20
  groupsLoading?: boolean;
17
21
  selectedPath: string;
18
22
  onPathChange: (path: string) => void;
23
+ /** Renders only the tree, for a host that embeds it as a navigator and owns
24
+ * the pane beside it (the notebooks page puts its editor there). The host
25
+ * also owns the width, so the tree fills its container instead of carrying
26
+ * its own resize handle. */
27
+ treeOnly?: boolean;
28
+ /** Changes when the host has written into the selected folder outside the
29
+ * explorer's own upload flow, so the folder is re-listed and shows the file. */
30
+ refreshKey?: unknown;
31
+ /** Seeds the expanded folders on mount, so a host can restore the tree's open
32
+ * state across reloads. Read once; the explorer owns the set from then on. */
33
+ initialExpandedPaths?: readonly string[];
34
+ /** Fires whenever the expanded folders change, for the host to persist. */
35
+ onExpandedPathsChange?: (paths: string[]) => void;
19
36
  }
20
- export default function FileExplorer({ storages, onRefresh, showUserHierarchy, groups, groupsLoading, selectedPath, onPathChange, getProviderAndClient, }: IFileExplorerProps): import("react").JSX.Element;
37
+ export default function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHierarchy, groups, groupsLoading, selectedPath, onPathChange, getProviderAndClient, treeOnly, initialExpandedPaths, onExpandedPathsChange, }: IFileExplorerProps): import("react").JSX.Element;
21
38
  export {};
@@ -1,12 +1,12 @@
1
1
  import { AccessIcon, AlertIcon, AngleDownIcon, AngleRightIcon, AngleUpIcon, ChevronLeftIcon, ChevronRightIcon, ClipboardIcon, CloseIcon, CloudUploadIcon, CollapseIcon, CustomizeIcon, DiskIcon, DocumentIcon, DownloadFileIcon, ExpandIcon, ExpandVerticalIcon, EyeIcon, FileIcon, FileTextIcon, FolderIcon, FolderOpenIcon, ImageIcon, LinkIcon, LockIcon, MinimizeIcon, MusicIcon, PdfIcon, RefreshIcon, ShareIcon, SpreadsheetIcon, SuccessIcon, TrashIcon, UploadIcon, UserIcon, VideoIcon, ZipIcon, ZoomInIcon, ZoomOutIcon } from "../icons.js";
2
- import { c as ConfirmModal, f as Markdown, h as Callout, m as CopyToClipboard, n as CreateModal, p as CopyCodeBlock } from "../chunks/CreateModal-k_e1dI8c.js";
2
+ import { c as ConfirmModal, f as Markdown, h as Callout, m as CopyToClipboard, n as CreateModal, p as CopyCodeBlock } from "../chunks/CreateModal-B7smkHkv.js";
3
3
  import { a as useProvisionCorsRules, c as useStrings, i as useNotify, s as useSlots } from "../chunks/Provider-CbFVFB7O.js";
4
4
  import { n as TOOLTIP_ID, r as TooltipInfo } from "../chunks/Tooltip-n9w7Tkt1.js";
5
- import { t as StatusBadge } from "../chunks/StatusBadge-RRUI78iS.js";
5
+ import { t as StatusBadge } from "../chunks/StatusBadge-zvEtK16a.js";
6
6
  import { t as Loader } from "../chunks/Loader-BXoXTZGc.js";
7
- import { d as copySubmenu, g as useRowMenuActive, h as useRowMenu, l as MoreButton, u as assembleRowMenu } from "../chunks/PageHeader-FhtphHGc.js";
7
+ import { _ as useRowMenuActive, d as assembleRowMenu, f as copySubmenu, g as useRowMenu, u as MoreButton } from "../chunks/PageHeader-CoZWF0pJ.js";
8
8
  import { n as Table } from "../chunks/Table-CutP1bRt.js";
9
- import { C as ListSkeleton, E as RowSelectCheckbox, O as listTableFixedProps, k as listTableProps, x as ListActionsHeader } from "../chunks/list-3jZW8rmM.js";
9
+ import { C as ListSkeleton, E as RowSelectCheckbox, O as listTableFixedProps, k as listTableProps, x as ListActionsHeader } from "../chunks/list-CduoUsMW.js";
10
10
  import cx from "classnames";
11
11
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
12
12
  import { Suspense, createContext, lazy, memo, use, useCallback, useEffect, useEffectEvent, useId, useLayoutEffect, useMemo, useRef, useState, useTransition } from "react";
@@ -2537,7 +2537,7 @@ function useObjectText(url, { enabled, maxBytes, maxLines, knownSize, cacheKey,
2537
2537
  }
2538
2538
  //#endregion
2539
2539
  //#region src/file-explorer/FilePreview/Viewer.tsx
2540
- var CodePreviewEditor = lazy(() => import("../chunks/CodePreviewEditor-67A_SKgE.js"));
2540
+ var CodePreviewEditor = lazy(() => import("../chunks/CodePreviewEditor-DuMZMA_k.js"));
2541
2541
  var PREVIEW_MARKDOWN_CONTROLS = {
2542
2542
  table: { fullscreen: false },
2543
2543
  mermaid: { fullscreen: false }
@@ -4313,7 +4313,37 @@ function getAncestorDirPaths(path) {
4313
4313
  for (let i = 1; i <= dirSegmentCount; i++) ancestors.push(segments.slice(0, i).join("/") + "/");
4314
4314
  return ancestors;
4315
4315
  }
4316
- function FileExplorer({ storages, onRefresh, showUserHierarchy = false, groups = [], groupsLoading = false, selectedPath, onPathChange, getProviderAndClient }) {
4316
+ /** The tree's width is the host's when the tree is embedded on its own, and this
4317
+ * component's when it is showing the details pane beside it. */
4318
+ function TreeFrame({ treeOnly, sidebarWidth, onWidthChange, children }) {
4319
+ if (treeOnly) return /* @__PURE__ */ jsx("div", {
4320
+ className: "h-full w-full min-w-0",
4321
+ children
4322
+ });
4323
+ return /* @__PURE__ */ jsx(Resizable, {
4324
+ size: {
4325
+ width: sidebarWidth,
4326
+ height: "100%"
4327
+ },
4328
+ minWidth: 200,
4329
+ maxWidth: 800,
4330
+ enable: {
4331
+ top: false,
4332
+ right: true,
4333
+ bottom: false,
4334
+ left: false,
4335
+ topRight: false,
4336
+ bottomRight: false,
4337
+ bottomLeft: false,
4338
+ topLeft: false
4339
+ },
4340
+ onResizeStop: (_e, _direction, _ref, d) => {
4341
+ onWidthChange((prev) => prev + d.width);
4342
+ },
4343
+ children
4344
+ });
4345
+ }
4346
+ function FileExplorer({ storages, onRefresh, refreshKey, onOpenFile, showUserHierarchy = false, groups = [], groupsLoading = false, selectedPath, onPathChange, getProviderAndClient, treeOnly = false, initialExpandedPaths, onExpandedPathsChange }) {
4317
4347
  const t = useStrings().fileExplorer;
4318
4348
  const notify = useNotify();
4319
4349
  const slots = useSlots();
@@ -4348,8 +4378,19 @@ function FileExplorer({ storages, onRefresh, showUserHierarchy = false, groups =
4348
4378
  return `${selectedPath}/`;
4349
4379
  }, [selectedPath, treeMap]);
4350
4380
  const setSelectedPath = onPathChange;
4351
- const [expandedPaths, setExpandedPaths] = useState(() => new Set([""]));
4381
+ const [expandedPaths, setExpandedPaths] = useState(() => new Set(["", ...initialExpandedPaths ?? []]));
4382
+ const onExpandedPathsChangeRef = useRef(onExpandedPathsChange);
4383
+ onExpandedPathsChangeRef.current = onExpandedPathsChange;
4384
+ useEffect(() => {
4385
+ onExpandedPathsChangeRef.current?.([...expandedPaths]);
4386
+ }, [expandedPaths]);
4352
4387
  const selectedNode = useMemo(() => normalizedSelectedPath ? treeMap[normalizedSelectedPath] ?? null : null, [normalizedSelectedPath, treeMap]);
4388
+ const seenRefreshKey = useRef(refreshKey);
4389
+ useEffect(() => {
4390
+ if (refreshKey === seenRefreshKey.current) return;
4391
+ seenRefreshKey.current = refreshKey;
4392
+ if (selectedNode?.type === "directory" && selectedNode.storageId) fetchNodeChildren(selectedNode, { replace: true });
4393
+ }, [refreshKey, selectedNode]);
4353
4394
  const [checkedItems, setCheckedItems] = useState(/* @__PURE__ */ new Set());
4354
4395
  const [checkedItemsPath, setCheckedItemsPath] = useState(selectedPath);
4355
4396
  if (checkedItemsPath !== selectedPath) {
@@ -4714,6 +4755,7 @@ function FileExplorer({ storages, onRefresh, showUserHierarchy = false, groups =
4714
4755
  const previewSiblings = useMemo(() => previewNode ? getNodeChildren(parentToChildrenMap, getParentPath(previewNode.path)).filter((n) => n.type === "file") : [], [previewNode, parentToChildrenMap]);
4715
4756
  const openPreview = (node) => {
4716
4757
  if (node.type !== "file") return;
4758
+ if (onOpenFile?.(node, storages.find((s) => s.id === node.storageId))) return;
4717
4759
  setPreviewNode(node);
4718
4760
  setPreviewOpen(true);
4719
4761
  };
@@ -5073,12 +5115,13 @@ function FileExplorer({ storages, onRefresh, showUserHierarchy = false, groups =
5073
5115
  }
5074
5116
  };
5075
5117
  const handleRefresh = async (opts) => {
5076
- if (selectedNode?.storageId) {
5077
- if (selectedNode.type === "directory") return fetchNodeChildren(selectedNode, {
5078
- replace: true,
5079
- silent: opts?.silent
5080
- });
5081
- } else if (onRefresh) onRefresh();
5118
+ const dirs = [...expandedPaths].map((path) => treeMap[path]).filter((node) => !!node && node.type === "directory" && !!node.storageId);
5119
+ if (selectedNode?.type === "directory" && selectedNode.storageId && !dirs.some((dir) => dir.path === selectedNode.path)) dirs.push(selectedNode);
5120
+ if (dirs.length > 0) return { corsError: (await Promise.all(dirs.map((dir) => fetchNodeChildren(dir, {
5121
+ replace: true,
5122
+ silent: opts?.silent
5123
+ })))).some((result) => result.corsError) };
5124
+ if (onRefresh) onRefresh();
5082
5125
  return { corsError: false };
5083
5126
  };
5084
5127
  return /* @__PURE__ */ jsxs("div", {
@@ -5086,26 +5129,10 @@ function FileExplorer({ storages, onRefresh, showUserHierarchy = false, groups =
5086
5129
  children: [
5087
5130
  /* @__PURE__ */ jsxs("div", {
5088
5131
  className: "w-full h-full flex",
5089
- children: [/* @__PURE__ */ jsx(Resizable, {
5090
- size: {
5091
- width: sidebarWidth,
5092
- height: "100%"
5093
- },
5094
- minWidth: 200,
5095
- maxWidth: 800,
5096
- enable: {
5097
- top: false,
5098
- right: true,
5099
- bottom: false,
5100
- left: false,
5101
- topRight: false,
5102
- bottomRight: false,
5103
- bottomLeft: false,
5104
- topLeft: false
5105
- },
5106
- onResizeStop: (e, direction, ref, d) => {
5107
- setSidebarWidth((prev) => prev + d.width);
5108
- },
5132
+ children: [/* @__PURE__ */ jsx(TreeFrame, {
5133
+ treeOnly,
5134
+ sidebarWidth,
5135
+ onWidthChange: setSidebarWidth,
5109
5136
  children: /* @__PURE__ */ jsxs("div", {
5110
5137
  className: "h-full flex flex-col border-r theme-border bg-(--theme-panel-bg)/60",
5111
5138
  children: [/* @__PURE__ */ jsx("div", {
@@ -5139,14 +5166,17 @@ function FileExplorer({ storages, onRefresh, showUserHierarchy = false, groups =
5139
5166
  selectedPath: normalizedSelectedPath,
5140
5167
  loadingPaths,
5141
5168
  onToggleExpand: handleToggleExpand,
5142
- onSelect: handleSelectTreeItem,
5169
+ onSelect: (path, node) => {
5170
+ handleSelectTreeItem(path, node);
5171
+ if (treeOnly && node.type === "file") openPreview(node);
5172
+ },
5143
5173
  onDropToFolder: onDropFiles,
5144
5174
  onContextMenu: openNodeContextMenu,
5145
5175
  onPreviewFile: openPreview,
5146
5176
  paging: treePaging
5147
5177
  })]
5148
5178
  })
5149
- }), /* @__PURE__ */ jsx("div", {
5179
+ }), !treeOnly && /* @__PURE__ */ jsx("div", {
5150
5180
  className: "h-full flex flex-col flex-1 min-w-0",
5151
5181
  children: !selectedNode && normalizedSelectedPath ? hydrationStuck ? /* @__PURE__ */ jsx(NotFoundPreview, {
5152
5182
  path: normalizedSelectedPath,