@flytedan/flytebot-design-system 0.12.14 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -76,6 +76,7 @@ __export(index_exports, {
76
76
  FILE_VIEWER_CSV_ROW_LIMIT: () => FILE_VIEWER_CSV_ROW_LIMIT,
77
77
  FILE_VIEWER_TEXT_LIMIT: () => FILE_VIEWER_TEXT_LIMIT,
78
78
  FeatureGate: () => FeatureGate,
79
+ FileBrowser: () => FileBrowser,
79
80
  FileChip: () => FileChip,
80
81
  FileGrid: () => FileGrid,
81
82
  FileKit: () => FileKit,
@@ -1214,7 +1215,6 @@ function Topbar({
1214
1215
  }
1215
1216
  )
1216
1217
  ] }, i)) }),
1217
- /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("span", { style: { flex: 1 } }),
1218
1218
  overflowActions != null ? narrow ? /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)(import_jsx_runtime16.Fragment, { children: [
1219
1219
  /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
1220
1220
  "button",
@@ -10627,8 +10627,496 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
10627
10627
  );
10628
10628
  }
10629
10629
 
10630
- // src/kits/query.ts
10630
+ // src/components/files/FileBrowser.tsx
10631
10631
  var React48 = __toESM(require("react"), 1);
10632
+ var import_jsx_runtime79 = require("react/jsx-runtime");
10633
+ function childrenOf(folders, parentId) {
10634
+ return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
10635
+ }
10636
+ function ancestorsOf(folders, id) {
10637
+ const byId = new Map(folders.map((f) => [f.id, f]));
10638
+ const chain = [];
10639
+ let cur = id != null ? byId.get(id) : void 0;
10640
+ const seen = /* @__PURE__ */ new Set();
10641
+ while (cur && !seen.has(cur.id)) {
10642
+ seen.add(cur.id);
10643
+ chain.unshift(cur);
10644
+ cur = cur.parentId != null ? byId.get(cur.parentId) : void 0;
10645
+ }
10646
+ return chain;
10647
+ }
10648
+ function isSelfOrDescendant(folders, ancestorId, nodeId) {
10649
+ if (nodeId == null) return false;
10650
+ if (nodeId === ancestorId) return true;
10651
+ const byParent = /* @__PURE__ */ new Map();
10652
+ folders.forEach((f) => {
10653
+ const key = f.parentId ?? "";
10654
+ if (!byParent.has(key)) byParent.set(key, []);
10655
+ byParent.get(key).push(f);
10656
+ });
10657
+ const stack = [...byParent.get(ancestorId) || []];
10658
+ while (stack.length) {
10659
+ const next = stack.pop();
10660
+ if (next.id === nodeId) return true;
10661
+ stack.push(...byParent.get(next.id) || []);
10662
+ }
10663
+ return false;
10664
+ }
10665
+ function flattenDestinations(folders, excludeSubtreeOf) {
10666
+ const out = [{ id: null, name: "Home", depth: 0 }];
10667
+ const walk = (parentId, depth) => {
10668
+ childrenOf(folders, parentId).forEach((f) => {
10669
+ if (excludeSubtreeOf && isSelfOrDescendant(folders, excludeSubtreeOf, f.id)) return;
10670
+ out.push({ id: f.id, name: f.name, depth });
10671
+ walk(f.id, depth + 1);
10672
+ });
10673
+ };
10674
+ walk(null, 1);
10675
+ return out;
10676
+ }
10677
+ var DND_MIME = "application/x-fd-filebrowser";
10678
+ function writeDragPayload(e, payload) {
10679
+ const s = JSON.stringify(payload);
10680
+ e.dataTransfer.setData(DND_MIME, s);
10681
+ e.dataTransfer.setData("text/plain", s);
10682
+ e.dataTransfer.effectAllowed = "move";
10683
+ }
10684
+ function readDragPayload(e) {
10685
+ const raw = e.dataTransfer.getData(DND_MIME) || e.dataTransfer.getData("text/plain");
10686
+ if (!raw) return null;
10687
+ try {
10688
+ const parsed = JSON.parse(raw);
10689
+ if (parsed && (parsed.kind === "file" || parsed.kind === "folder") && typeof parsed.id === "string") return parsed;
10690
+ } catch {
10691
+ }
10692
+ return null;
10693
+ }
10694
+ function hasInternalPayload(e) {
10695
+ return Array.from(e.dataTransfer.types || []).indexOf(DND_MIME) !== -1;
10696
+ }
10697
+ function FolderPickerMenu({
10698
+ label,
10699
+ destinations,
10700
+ onPick
10701
+ }) {
10702
+ const [open, setOpen] = React48.useState(false);
10703
+ const zIndex = useOverlayLayer(open);
10704
+ const ref = React48.useRef(null);
10705
+ React48.useEffect(() => {
10706
+ if (!open) return;
10707
+ const away = (e) => {
10708
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
10709
+ };
10710
+ const esc = (e) => {
10711
+ if (e.key === "Escape") setOpen(false);
10712
+ };
10713
+ document.addEventListener("pointerdown", away);
10714
+ document.addEventListener("keydown", esc);
10715
+ return () => {
10716
+ document.removeEventListener("pointerdown", away);
10717
+ document.removeEventListener("keydown", esc);
10718
+ };
10719
+ }, [open]);
10720
+ return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fd-fb-menu-anchor", ref, children: [
10721
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10722
+ IconButton,
10723
+ {
10724
+ icon: "arrows-out-cardinal",
10725
+ label,
10726
+ size: "sm",
10727
+ variant: "ghost",
10728
+ "aria-haspopup": "menu",
10729
+ "aria-expanded": open,
10730
+ onClick: () => setOpen((v) => !v)
10731
+ }
10732
+ ),
10733
+ open ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(LayerProvider, { zIndex, children: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-menu", role: "menu", "aria-label": label, style: { zIndex }, children: [
10734
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-overline fd-muted fd-fb-menu-title", children: "Move to\u2026" }),
10735
+ destinations.map((d) => /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
10736
+ "button",
10737
+ {
10738
+ type: "button",
10739
+ role: "menuitem",
10740
+ className: "fd-fb-menu-item",
10741
+ style: { paddingLeft: 10 + d.depth * 14 },
10742
+ onClick: () => {
10743
+ onPick(d.id);
10744
+ setOpen(false);
10745
+ },
10746
+ children: [
10747
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("i", { className: "ph ph-" + (d.id == null ? "house-simple" : "folder-simple"), "aria-hidden": "true" }),
10748
+ d.name
10749
+ ]
10750
+ },
10751
+ d.id ?? "__root__"
10752
+ ))
10753
+ ] }) }) : null
10754
+ ] });
10755
+ }
10756
+ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropPayload, actions, className = "" }) {
10757
+ const [over, setOver] = React48.useState(false);
10758
+ const dragCounter = React48.useRef(0);
10759
+ const dropHandlers = isDropTarget ? {
10760
+ onDragEnter: (e) => {
10761
+ if (!hasInternalPayload(e)) return;
10762
+ dragCounter.current++;
10763
+ setOver(true);
10764
+ },
10765
+ onDragOver: (e) => {
10766
+ if (!hasInternalPayload(e)) return;
10767
+ e.preventDefault();
10768
+ e.dataTransfer.dropEffect = "move";
10769
+ },
10770
+ onDragLeave: () => {
10771
+ dragCounter.current = Math.max(0, dragCounter.current - 1);
10772
+ if (!dragCounter.current) setOver(false);
10773
+ },
10774
+ onDrop: (e) => {
10775
+ if (!hasInternalPayload(e)) return;
10776
+ e.preventDefault();
10777
+ dragCounter.current = 0;
10778
+ setOver(false);
10779
+ const payload = readDragPayload(e);
10780
+ if (payload && onDropPayload) onDropPayload(payload);
10781
+ }
10782
+ } : {};
10783
+ return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: ["fd-fb-row", over ? "is-drop-target" : "", className].filter(Boolean).join(" "), ...dropHandlers, children: [
10784
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
10785
+ "button",
10786
+ {
10787
+ type: "button",
10788
+ className: "fd-fb-row-main",
10789
+ onClick: onOpen,
10790
+ draggable: !!draggableId,
10791
+ onDragStart: draggableId ? (e) => writeDragPayload(e, draggableId) : void 0,
10792
+ children: [
10793
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-row-icon", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("i", { className: "ph ph-" + icon }) }),
10794
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fd-fb-row-text", children: [
10795
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-row-name", title: name, children: name }),
10796
+ meta ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-row-meta", children: meta }) : null
10797
+ ] })
10798
+ ]
10799
+ }
10800
+ ),
10801
+ actions ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-row-actions", children: actions }) : null
10802
+ ] });
10803
+ }
10804
+ function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveInto, canMove }) {
10805
+ const kids = childrenOf(folders, folder.id);
10806
+ const [expanded, setExpanded] = React48.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
10807
+ React48.useEffect(() => {
10808
+ if (currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId)) setExpanded(true);
10809
+ }, [currentFolderId, folder.id, folders]);
10810
+ const isCurrent = folder.id === currentFolderId;
10811
+ return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-tree-node", children: [
10812
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10813
+ RowShell,
10814
+ {
10815
+ icon: folder.id === currentFolderId ? "folder-open" : "folder-simple",
10816
+ name: folder.name,
10817
+ onOpen: () => onNavigate(folder.id),
10818
+ draggableId: canMove ? { kind: "folder", id: folder.id } : void 0,
10819
+ isDropTarget: canMove,
10820
+ onDropPayload: (payload) => onMoveInto(payload, folder.id),
10821
+ className: ["fd-fb-tree-row", isCurrent ? "is-current" : ""].filter(Boolean).join(" "),
10822
+ actions: kids.length ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10823
+ IconButton,
10824
+ {
10825
+ icon: expanded ? "caret-down" : "caret-right",
10826
+ label: (expanded ? "Collapse " : "Expand ") + folder.name,
10827
+ size: "sm",
10828
+ variant: "ghost",
10829
+ onClick: () => setExpanded((v) => !v)
10830
+ }
10831
+ ) : void 0
10832
+ }
10833
+ ),
10834
+ expanded && kids.length ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "fd-fb-tree-children", style: { paddingLeft: 14 }, children: kids.map((k) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10835
+ TreeNode,
10836
+ {
10837
+ folder: k,
10838
+ depth: depth + 1,
10839
+ folders,
10840
+ currentFolderId,
10841
+ onNavigate,
10842
+ onMoveInto,
10843
+ canMove
10844
+ },
10845
+ k.id
10846
+ )) }) : null
10847
+ ] });
10848
+ }
10849
+ function FileBrowser({
10850
+ folders,
10851
+ files,
10852
+ currentFolderId,
10853
+ onNavigate,
10854
+ onCreateFolder,
10855
+ onRenameFolder,
10856
+ onMoveFolder,
10857
+ onMoveFile,
10858
+ onOpenFile,
10859
+ onRemoveFile,
10860
+ onUpload,
10861
+ loading = false,
10862
+ permissions,
10863
+ className = ""
10864
+ }) {
10865
+ const perm = permissions || {};
10866
+ const canCreateFolder = perm.canCreateFolder !== false && !!onCreateFolder;
10867
+ const canRenameFolder = perm.canRenameFolder !== false && !!onRenameFolder;
10868
+ const canMove = perm.canMove !== false && (!!onMoveFolder || !!onMoveFile);
10869
+ const canUpload = perm.canUpload !== false && !!onUpload;
10870
+ const canRemoveFile = perm.canRemoveFile !== false && !!onRemoveFile;
10871
+ const [createOpen, setCreateOpen] = React48.useState(false);
10872
+ const [createName, setCreateName] = React48.useState("");
10873
+ const [renameTarget, setRenameTarget] = React48.useState(null);
10874
+ const [renameValue, setRenameValue] = React48.useState("");
10875
+ const uploadInputRef = React48.useRef(null);
10876
+ const crumbFolders = React48.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
10877
+ const subfolders = React48.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
10878
+ const currentFiles = React48.useMemo(
10879
+ () => files.filter((f) => f.folderId === currentFolderId).sort((a, b) => a.name.localeCompare(b.name)),
10880
+ [files, currentFolderId]
10881
+ );
10882
+ const isEmpty = !subfolders.length && !currentFiles.length;
10883
+ const moveIntoFolder = (payload, targetFolderId) => {
10884
+ if (payload.kind === "folder") {
10885
+ if (payload.id === targetFolderId) return;
10886
+ if (isSelfOrDescendant(folders, payload.id, targetFolderId)) return;
10887
+ onMoveFolder && onMoveFolder(payload.id, targetFolderId);
10888
+ } else {
10889
+ onMoveFile && onMoveFile(payload.id, targetFolderId);
10890
+ }
10891
+ };
10892
+ const openCreate = () => {
10893
+ setCreateName("");
10894
+ setCreateOpen(true);
10895
+ };
10896
+ const submitCreate = () => {
10897
+ const name = createName.trim();
10898
+ if (name && onCreateFolder) onCreateFolder(currentFolderId, name);
10899
+ setCreateOpen(false);
10900
+ };
10901
+ const openRename = (folder) => {
10902
+ setRenameValue(folder.name);
10903
+ setRenameTarget(folder);
10904
+ };
10905
+ const submitRename = () => {
10906
+ const name = renameValue.trim();
10907
+ if (name && renameTarget && onRenameFolder) onRenameFolder(renameTarget.id, name);
10908
+ setRenameTarget(null);
10909
+ };
10910
+ const content = /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: ["fd-fb", className].filter(Boolean).join(" "), children: [
10911
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-head", children: [
10912
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("nav", { className: "fd-fb-crumbs", "aria-label": "Folder path", children: [
10913
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10914
+ BreadcrumbCrumb,
10915
+ {
10916
+ label: "Home",
10917
+ isCurrent: currentFolderId == null,
10918
+ onClick: () => onNavigate(null),
10919
+ isDropTarget: canMove,
10920
+ onDropPayload: (p) => moveIntoFolder(p, null)
10921
+ }
10922
+ ),
10923
+ crumbFolders.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10924
+ BreadcrumbCrumb,
10925
+ {
10926
+ label: f.name,
10927
+ isCurrent: i === crumbFolders.length - 1,
10928
+ onClick: () => onNavigate(f.id),
10929
+ isDropTarget: canMove,
10930
+ onDropPayload: (p) => moveIntoFolder(p, f.id)
10931
+ },
10932
+ f.id
10933
+ ))
10934
+ ] }),
10935
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fd-fb-actions", children: [
10936
+ canCreateFolder ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", size: "sm", icon: "folder-simple-plus", onClick: openCreate, children: "New folder" }) : null,
10937
+ canUpload ? /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
10938
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", size: "sm", icon: "upload-simple", onClick: () => uploadInputRef.current && uploadInputRef.current.click(), children: "Upload" }),
10939
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10940
+ "input",
10941
+ {
10942
+ ref: uploadInputRef,
10943
+ type: "file",
10944
+ multiple: true,
10945
+ className: "fd-sr",
10946
+ "aria-label": "Upload files",
10947
+ onChange: (e) => {
10948
+ const picked = Array.from(e.target.files || []);
10949
+ if (picked.length && onUpload) onUpload(picked, currentFolderId);
10950
+ e.target.value = "";
10951
+ }
10952
+ }
10953
+ )
10954
+ ] }) : null
10955
+ ] })
10956
+ ] }),
10957
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-body", children: [
10958
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-tree", role: "region", "aria-label": "Folder tree", children: [
10959
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10960
+ RowShell,
10961
+ {
10962
+ icon: "house-simple",
10963
+ name: "Home",
10964
+ onOpen: () => onNavigate(null),
10965
+ isDropTarget: canMove,
10966
+ onDropPayload: (p) => moveIntoFolder(p, null),
10967
+ className: ["fd-fb-tree-row", currentFolderId == null ? "is-current" : ""].filter(Boolean).join(" ")
10968
+ }
10969
+ ),
10970
+ childrenOf(folders, null).map((f) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10971
+ TreeNode,
10972
+ {
10973
+ folder: f,
10974
+ depth: 0,
10975
+ folders,
10976
+ currentFolderId,
10977
+ onNavigate,
10978
+ onMoveInto: moveIntoFolder,
10979
+ canMove
10980
+ },
10981
+ f.id
10982
+ ))
10983
+ ] }),
10984
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "fd-fb-main", role: "region", "aria-label": "Folder contents", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10985
+ LoadingRegion,
10986
+ {
10987
+ state: loading ? "loading" : "ready",
10988
+ label: "Loading folder contents",
10989
+ skeleton: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-stack", style: { gap: 8, padding: 12 }, children: [
10990
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Skeleton, { h: 44, radius: "10px" }),
10991
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Skeleton, { h: 44, radius: "10px" }),
10992
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Skeleton, { h: 44, radius: "10px" })
10993
+ ] }),
10994
+ children: isEmpty ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(EmptyState, { icon: "folder-open", title: "This folder is empty", children: canUpload ? "Drop files here, or use Upload above." : "Nothing has been filed here yet." }) : /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-list", children: [
10995
+ subfolders.map((f) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
10996
+ RowShell,
10997
+ {
10998
+ icon: "folder-simple",
10999
+ name: f.name,
11000
+ onOpen: () => onNavigate(f.id),
11001
+ draggableId: canMove ? { kind: "folder", id: f.id } : void 0,
11002
+ isDropTarget: canMove,
11003
+ onDropPayload: (p) => moveIntoFolder(p, f.id),
11004
+ actions: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
11005
+ canRenameFolder ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(IconButton, { icon: "pencil-simple", label: "Rename " + f.name, size: "sm", variant: "ghost", onClick: () => openRename(f) }) : null,
11006
+ canMove ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11007
+ FolderPickerMenu,
11008
+ {
11009
+ label: "Move " + f.name,
11010
+ destinations: flattenDestinations(folders, f.id),
11011
+ onPick: (target) => moveIntoFolder({ kind: "folder", id: f.id }, target)
11012
+ }
11013
+ ) : null
11014
+ ] })
11015
+ },
11016
+ f.id
11017
+ )),
11018
+ currentFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11019
+ RowShell,
11020
+ {
11021
+ icon: iconForMime(file.mime, file.name),
11022
+ name: file.name,
11023
+ meta: file.size ? formatBytes(file.size) : void 0,
11024
+ onOpen: onOpenFile ? () => onOpenFile(file) : void 0,
11025
+ draggableId: canMove ? { kind: "file", id: file.id } : void 0,
11026
+ actions: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
11027
+ canMove ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11028
+ FolderPickerMenu,
11029
+ {
11030
+ label: "Move " + file.name,
11031
+ destinations: flattenDestinations(folders),
11032
+ onPick: (target) => onMoveFile && onMoveFile(file.id, target)
11033
+ }
11034
+ ) : null,
11035
+ canRemoveFile ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(IconButton, { icon: "trash-simple", label: "Remove " + file.name, size: "sm", variant: "ghost", onClick: () => onRemoveFile && onRemoveFile(file) }) : null
11036
+ ] })
11037
+ },
11038
+ file.id
11039
+ ))
11040
+ ] })
11041
+ }
11042
+ ) })
11043
+ ] }),
11044
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Modal, { open: createOpen, title: "New folder", onClose: () => setCreateOpen(false), footer: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
11045
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", onClick: () => setCreateOpen(false), children: "Cancel" }),
11046
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { onClick: submitCreate, disabled: !createName.trim(), children: "Create" })
11047
+ ] }), children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11048
+ "input",
11049
+ {
11050
+ className: "fd-fb-input",
11051
+ value: createName,
11052
+ onChange: (e) => setCreateName(e.target.value),
11053
+ onKeyDown: (e) => {
11054
+ if (e.key === "Enter") submitCreate();
11055
+ },
11056
+ placeholder: "Folder name",
11057
+ "aria-label": "Folder name",
11058
+ autoFocus: true
11059
+ }
11060
+ ) }),
11061
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Modal, { open: renameTarget != null, title: "Rename folder", onClose: () => setRenameTarget(null), footer: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
11062
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", onClick: () => setRenameTarget(null), children: "Cancel" }),
11063
+ /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { onClick: submitRename, disabled: !renameValue.trim(), children: "Save" })
11064
+ ] }), children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
11065
+ "input",
11066
+ {
11067
+ className: "fd-fb-input",
11068
+ value: renameValue,
11069
+ onChange: (e) => setRenameValue(e.target.value),
11070
+ onKeyDown: (e) => {
11071
+ if (e.key === "Enter") submitRename();
11072
+ },
11073
+ placeholder: "Folder name",
11074
+ "aria-label": "Folder name",
11075
+ autoFocus: true
11076
+ }
11077
+ ) })
11078
+ ] });
11079
+ if (!canUpload) return content;
11080
+ return /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Dropzone, { className: "fd-fb-dropzone", onFiles: (picked) => onUpload && onUpload(picked, currentFolderId), label: "Drop files to upload", children: content });
11081
+ }
11082
+ function BreadcrumbCrumb({
11083
+ label,
11084
+ isCurrent,
11085
+ onClick,
11086
+ isDropTarget,
11087
+ onDropPayload
11088
+ }) {
11089
+ const [over, setOver] = React48.useState(false);
11090
+ const dragCounter = React48.useRef(0);
11091
+ const dropHandlers = isDropTarget ? {
11092
+ onDragEnter: (e) => {
11093
+ if (!hasInternalPayload(e)) return;
11094
+ dragCounter.current++;
11095
+ setOver(true);
11096
+ },
11097
+ onDragOver: (e) => {
11098
+ if (!hasInternalPayload(e)) return;
11099
+ e.preventDefault();
11100
+ e.dataTransfer.dropEffect = "move";
11101
+ },
11102
+ onDragLeave: () => {
11103
+ dragCounter.current = Math.max(0, dragCounter.current - 1);
11104
+ if (!dragCounter.current) setOver(false);
11105
+ },
11106
+ onDrop: (e) => {
11107
+ if (!hasInternalPayload(e)) return;
11108
+ e.preventDefault();
11109
+ dragCounter.current = 0;
11110
+ setOver(false);
11111
+ const payload = readDragPayload(e);
11112
+ if (payload && onDropPayload) onDropPayload(payload);
11113
+ }
11114
+ } : {};
11115
+ return /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: ["fd-fb-crumb", over ? "is-drop-target" : ""].filter(Boolean).join(" "), ...dropHandlers, children: isCurrent ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-crumb-current", "aria-current": "page", children: label }) : /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("button", { type: "button", className: "fd-fb-crumb-link", onClick, children: label }) });
11116
+ }
11117
+
11118
+ // src/kits/query.ts
11119
+ var React49 = __toESM(require("react"), 1);
10632
11120
  function eqFilter(get2) {
10633
11121
  return (row, value) => Array.isArray(value) ? value.includes(get2(row)) : get2(row) === value;
10634
11122
  }
@@ -10660,22 +11148,22 @@ function compare(a, b, dir) {
10660
11148
  var API = null;
10661
11149
  var PREFS = null;
10662
11150
  function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
10663
- const [query, setQuery] = React48.useState(() => {
11151
+ const [query, setQuery] = React49.useState(() => {
10664
11152
  const store = PREFS || window.PlannerPrefs;
10665
11153
  const saved = prefsKey && store ? store.getTable(prefsKey) : {};
10666
11154
  return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
10667
11155
  });
10668
- const savePref = React48.useCallback((patch2) => {
11156
+ const savePref = React49.useCallback((patch2) => {
10669
11157
  const store = PREFS || window.PlannerPrefs;
10670
11158
  if (prefsKey && store) store.setTable(prefsKey, patch2);
10671
11159
  }, [prefsKey]);
10672
- const [res, setRes] = React48.useState(null);
10673
- const [loading, setLoading] = React48.useState(true);
10674
- const seq2 = React48.useRef(0);
11160
+ const [res, setRes] = React49.useState(null);
11161
+ const [loading, setLoading] = React49.useState(true);
11162
+ const seq2 = React49.useRef(0);
10675
11163
  const depKey = (deps || []).join("|");
10676
11164
  const paramKey = JSON.stringify(params || {});
10677
11165
  const queryKey = JSON.stringify(query);
10678
- React48.useEffect(() => {
11166
+ React49.useEffect(() => {
10679
11167
  const id = ++seq2.current;
10680
11168
  setLoading(true);
10681
11169
  const t = setTimeout(() => {
@@ -11020,6 +11508,7 @@ function createVersionStore(initialState, options) {
11020
11508
  FILE_VIEWER_CSV_ROW_LIMIT,
11021
11509
  FILE_VIEWER_TEXT_LIMIT,
11022
11510
  FeatureGate,
11511
+ FileBrowser,
11023
11512
  FileChip,
11024
11513
  FileGrid,
11025
11514
  FileKit,