@flytedan/flytebot-design-system 0.12.15 → 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,
@@ -10626,8 +10627,496 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
10626
10627
  );
10627
10628
  }
10628
10629
 
10629
- // src/kits/query.ts
10630
+ // src/components/files/FileBrowser.tsx
10630
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);
10631
11120
  function eqFilter(get2) {
10632
11121
  return (row, value) => Array.isArray(value) ? value.includes(get2(row)) : get2(row) === value;
10633
11122
  }
@@ -10659,22 +11148,22 @@ function compare(a, b, dir) {
10659
11148
  var API = null;
10660
11149
  var PREFS = null;
10661
11150
  function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
10662
- const [query, setQuery] = React48.useState(() => {
11151
+ const [query, setQuery] = React49.useState(() => {
10663
11152
  const store = PREFS || window.PlannerPrefs;
10664
11153
  const saved = prefsKey && store ? store.getTable(prefsKey) : {};
10665
11154
  return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
10666
11155
  });
10667
- const savePref = React48.useCallback((patch2) => {
11156
+ const savePref = React49.useCallback((patch2) => {
10668
11157
  const store = PREFS || window.PlannerPrefs;
10669
11158
  if (prefsKey && store) store.setTable(prefsKey, patch2);
10670
11159
  }, [prefsKey]);
10671
- const [res, setRes] = React48.useState(null);
10672
- const [loading, setLoading] = React48.useState(true);
10673
- 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);
10674
11163
  const depKey = (deps || []).join("|");
10675
11164
  const paramKey = JSON.stringify(params || {});
10676
11165
  const queryKey = JSON.stringify(query);
10677
- React48.useEffect(() => {
11166
+ React49.useEffect(() => {
10678
11167
  const id = ++seq2.current;
10679
11168
  setLoading(true);
10680
11169
  const t = setTimeout(() => {
@@ -11019,6 +11508,7 @@ function createVersionStore(initialState, options) {
11019
11508
  FILE_VIEWER_CSV_ROW_LIMIT,
11020
11509
  FILE_VIEWER_TEXT_LIMIT,
11021
11510
  FeatureGate,
11511
+ FileBrowser,
11022
11512
  FileChip,
11023
11513
  FileGrid,
11024
11514
  FileKit,