@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.js CHANGED
@@ -10418,8 +10418,496 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
10418
10418
  );
10419
10419
  }
10420
10420
 
10421
- // src/kits/query.ts
10421
+ // src/components/files/FileBrowser.tsx
10422
10422
  import * as React48 from "react";
10423
+ import { Fragment as Fragment23, jsx as jsx78, jsxs as jsxs74 } from "react/jsx-runtime";
10424
+ function childrenOf(folders, parentId) {
10425
+ return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
10426
+ }
10427
+ function ancestorsOf(folders, id) {
10428
+ const byId = new Map(folders.map((f) => [f.id, f]));
10429
+ const chain = [];
10430
+ let cur = id != null ? byId.get(id) : void 0;
10431
+ const seen = /* @__PURE__ */ new Set();
10432
+ while (cur && !seen.has(cur.id)) {
10433
+ seen.add(cur.id);
10434
+ chain.unshift(cur);
10435
+ cur = cur.parentId != null ? byId.get(cur.parentId) : void 0;
10436
+ }
10437
+ return chain;
10438
+ }
10439
+ function isSelfOrDescendant(folders, ancestorId, nodeId) {
10440
+ if (nodeId == null) return false;
10441
+ if (nodeId === ancestorId) return true;
10442
+ const byParent = /* @__PURE__ */ new Map();
10443
+ folders.forEach((f) => {
10444
+ const key = f.parentId ?? "";
10445
+ if (!byParent.has(key)) byParent.set(key, []);
10446
+ byParent.get(key).push(f);
10447
+ });
10448
+ const stack = [...byParent.get(ancestorId) || []];
10449
+ while (stack.length) {
10450
+ const next = stack.pop();
10451
+ if (next.id === nodeId) return true;
10452
+ stack.push(...byParent.get(next.id) || []);
10453
+ }
10454
+ return false;
10455
+ }
10456
+ function flattenDestinations(folders, excludeSubtreeOf) {
10457
+ const out = [{ id: null, name: "Home", depth: 0 }];
10458
+ const walk = (parentId, depth) => {
10459
+ childrenOf(folders, parentId).forEach((f) => {
10460
+ if (excludeSubtreeOf && isSelfOrDescendant(folders, excludeSubtreeOf, f.id)) return;
10461
+ out.push({ id: f.id, name: f.name, depth });
10462
+ walk(f.id, depth + 1);
10463
+ });
10464
+ };
10465
+ walk(null, 1);
10466
+ return out;
10467
+ }
10468
+ var DND_MIME = "application/x-fd-filebrowser";
10469
+ function writeDragPayload(e, payload) {
10470
+ const s = JSON.stringify(payload);
10471
+ e.dataTransfer.setData(DND_MIME, s);
10472
+ e.dataTransfer.setData("text/plain", s);
10473
+ e.dataTransfer.effectAllowed = "move";
10474
+ }
10475
+ function readDragPayload(e) {
10476
+ const raw = e.dataTransfer.getData(DND_MIME) || e.dataTransfer.getData("text/plain");
10477
+ if (!raw) return null;
10478
+ try {
10479
+ const parsed = JSON.parse(raw);
10480
+ if (parsed && (parsed.kind === "file" || parsed.kind === "folder") && typeof parsed.id === "string") return parsed;
10481
+ } catch {
10482
+ }
10483
+ return null;
10484
+ }
10485
+ function hasInternalPayload(e) {
10486
+ return Array.from(e.dataTransfer.types || []).indexOf(DND_MIME) !== -1;
10487
+ }
10488
+ function FolderPickerMenu({
10489
+ label,
10490
+ destinations,
10491
+ onPick
10492
+ }) {
10493
+ const [open, setOpen] = React48.useState(false);
10494
+ const zIndex = useOverlayLayer(open);
10495
+ const ref = React48.useRef(null);
10496
+ React48.useEffect(() => {
10497
+ if (!open) return;
10498
+ const away = (e) => {
10499
+ if (ref.current && !ref.current.contains(e.target)) setOpen(false);
10500
+ };
10501
+ const esc = (e) => {
10502
+ if (e.key === "Escape") setOpen(false);
10503
+ };
10504
+ document.addEventListener("pointerdown", away);
10505
+ document.addEventListener("keydown", esc);
10506
+ return () => {
10507
+ document.removeEventListener("pointerdown", away);
10508
+ document.removeEventListener("keydown", esc);
10509
+ };
10510
+ }, [open]);
10511
+ return /* @__PURE__ */ jsxs74("span", { className: "fd-fb-menu-anchor", ref, children: [
10512
+ /* @__PURE__ */ jsx78(
10513
+ IconButton,
10514
+ {
10515
+ icon: "arrows-out-cardinal",
10516
+ label,
10517
+ size: "sm",
10518
+ variant: "ghost",
10519
+ "aria-haspopup": "menu",
10520
+ "aria-expanded": open,
10521
+ onClick: () => setOpen((v) => !v)
10522
+ }
10523
+ ),
10524
+ open ? /* @__PURE__ */ jsx78(LayerProvider, { zIndex, children: /* @__PURE__ */ jsxs74("div", { className: "fd-fb-menu", role: "menu", "aria-label": label, style: { zIndex }, children: [
10525
+ /* @__PURE__ */ jsx78("span", { className: "fd-overline fd-muted fd-fb-menu-title", children: "Move to\u2026" }),
10526
+ destinations.map((d) => /* @__PURE__ */ jsxs74(
10527
+ "button",
10528
+ {
10529
+ type: "button",
10530
+ role: "menuitem",
10531
+ className: "fd-fb-menu-item",
10532
+ style: { paddingLeft: 10 + d.depth * 14 },
10533
+ onClick: () => {
10534
+ onPick(d.id);
10535
+ setOpen(false);
10536
+ },
10537
+ children: [
10538
+ /* @__PURE__ */ jsx78("i", { className: "ph ph-" + (d.id == null ? "house-simple" : "folder-simple"), "aria-hidden": "true" }),
10539
+ d.name
10540
+ ]
10541
+ },
10542
+ d.id ?? "__root__"
10543
+ ))
10544
+ ] }) }) : null
10545
+ ] });
10546
+ }
10547
+ function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropPayload, actions, className = "" }) {
10548
+ const [over, setOver] = React48.useState(false);
10549
+ const dragCounter = React48.useRef(0);
10550
+ const dropHandlers = isDropTarget ? {
10551
+ onDragEnter: (e) => {
10552
+ if (!hasInternalPayload(e)) return;
10553
+ dragCounter.current++;
10554
+ setOver(true);
10555
+ },
10556
+ onDragOver: (e) => {
10557
+ if (!hasInternalPayload(e)) return;
10558
+ e.preventDefault();
10559
+ e.dataTransfer.dropEffect = "move";
10560
+ },
10561
+ onDragLeave: () => {
10562
+ dragCounter.current = Math.max(0, dragCounter.current - 1);
10563
+ if (!dragCounter.current) setOver(false);
10564
+ },
10565
+ onDrop: (e) => {
10566
+ if (!hasInternalPayload(e)) return;
10567
+ e.preventDefault();
10568
+ dragCounter.current = 0;
10569
+ setOver(false);
10570
+ const payload = readDragPayload(e);
10571
+ if (payload && onDropPayload) onDropPayload(payload);
10572
+ }
10573
+ } : {};
10574
+ return /* @__PURE__ */ jsxs74("div", { className: ["fd-fb-row", over ? "is-drop-target" : "", className].filter(Boolean).join(" "), ...dropHandlers, children: [
10575
+ /* @__PURE__ */ jsxs74(
10576
+ "button",
10577
+ {
10578
+ type: "button",
10579
+ className: "fd-fb-row-main",
10580
+ onClick: onOpen,
10581
+ draggable: !!draggableId,
10582
+ onDragStart: draggableId ? (e) => writeDragPayload(e, draggableId) : void 0,
10583
+ children: [
10584
+ /* @__PURE__ */ jsx78("span", { className: "fd-fb-row-icon", "aria-hidden": "true", children: /* @__PURE__ */ jsx78("i", { className: "ph ph-" + icon }) }),
10585
+ /* @__PURE__ */ jsxs74("span", { className: "fd-fb-row-text", children: [
10586
+ /* @__PURE__ */ jsx78("span", { className: "fd-fb-row-name", title: name, children: name }),
10587
+ meta ? /* @__PURE__ */ jsx78("span", { className: "fd-fb-row-meta", children: meta }) : null
10588
+ ] })
10589
+ ]
10590
+ }
10591
+ ),
10592
+ actions ? /* @__PURE__ */ jsx78("span", { className: "fd-fb-row-actions", children: actions }) : null
10593
+ ] });
10594
+ }
10595
+ function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveInto, canMove }) {
10596
+ const kids = childrenOf(folders, folder.id);
10597
+ const [expanded, setExpanded] = React48.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
10598
+ React48.useEffect(() => {
10599
+ if (currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId)) setExpanded(true);
10600
+ }, [currentFolderId, folder.id, folders]);
10601
+ const isCurrent = folder.id === currentFolderId;
10602
+ return /* @__PURE__ */ jsxs74("div", { className: "fd-fb-tree-node", children: [
10603
+ /* @__PURE__ */ jsx78(
10604
+ RowShell,
10605
+ {
10606
+ icon: folder.id === currentFolderId ? "folder-open" : "folder-simple",
10607
+ name: folder.name,
10608
+ onOpen: () => onNavigate(folder.id),
10609
+ draggableId: canMove ? { kind: "folder", id: folder.id } : void 0,
10610
+ isDropTarget: canMove,
10611
+ onDropPayload: (payload) => onMoveInto(payload, folder.id),
10612
+ className: ["fd-fb-tree-row", isCurrent ? "is-current" : ""].filter(Boolean).join(" "),
10613
+ actions: kids.length ? /* @__PURE__ */ jsx78(
10614
+ IconButton,
10615
+ {
10616
+ icon: expanded ? "caret-down" : "caret-right",
10617
+ label: (expanded ? "Collapse " : "Expand ") + folder.name,
10618
+ size: "sm",
10619
+ variant: "ghost",
10620
+ onClick: () => setExpanded((v) => !v)
10621
+ }
10622
+ ) : void 0
10623
+ }
10624
+ ),
10625
+ expanded && kids.length ? /* @__PURE__ */ jsx78("div", { className: "fd-fb-tree-children", style: { paddingLeft: 14 }, children: kids.map((k) => /* @__PURE__ */ jsx78(
10626
+ TreeNode,
10627
+ {
10628
+ folder: k,
10629
+ depth: depth + 1,
10630
+ folders,
10631
+ currentFolderId,
10632
+ onNavigate,
10633
+ onMoveInto,
10634
+ canMove
10635
+ },
10636
+ k.id
10637
+ )) }) : null
10638
+ ] });
10639
+ }
10640
+ function FileBrowser({
10641
+ folders,
10642
+ files,
10643
+ currentFolderId,
10644
+ onNavigate,
10645
+ onCreateFolder,
10646
+ onRenameFolder,
10647
+ onMoveFolder,
10648
+ onMoveFile,
10649
+ onOpenFile,
10650
+ onRemoveFile,
10651
+ onUpload,
10652
+ loading = false,
10653
+ permissions,
10654
+ className = ""
10655
+ }) {
10656
+ const perm = permissions || {};
10657
+ const canCreateFolder = perm.canCreateFolder !== false && !!onCreateFolder;
10658
+ const canRenameFolder = perm.canRenameFolder !== false && !!onRenameFolder;
10659
+ const canMove = perm.canMove !== false && (!!onMoveFolder || !!onMoveFile);
10660
+ const canUpload = perm.canUpload !== false && !!onUpload;
10661
+ const canRemoveFile = perm.canRemoveFile !== false && !!onRemoveFile;
10662
+ const [createOpen, setCreateOpen] = React48.useState(false);
10663
+ const [createName, setCreateName] = React48.useState("");
10664
+ const [renameTarget, setRenameTarget] = React48.useState(null);
10665
+ const [renameValue, setRenameValue] = React48.useState("");
10666
+ const uploadInputRef = React48.useRef(null);
10667
+ const crumbFolders = React48.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
10668
+ const subfolders = React48.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
10669
+ const currentFiles = React48.useMemo(
10670
+ () => files.filter((f) => f.folderId === currentFolderId).sort((a, b) => a.name.localeCompare(b.name)),
10671
+ [files, currentFolderId]
10672
+ );
10673
+ const isEmpty = !subfolders.length && !currentFiles.length;
10674
+ const moveIntoFolder = (payload, targetFolderId) => {
10675
+ if (payload.kind === "folder") {
10676
+ if (payload.id === targetFolderId) return;
10677
+ if (isSelfOrDescendant(folders, payload.id, targetFolderId)) return;
10678
+ onMoveFolder && onMoveFolder(payload.id, targetFolderId);
10679
+ } else {
10680
+ onMoveFile && onMoveFile(payload.id, targetFolderId);
10681
+ }
10682
+ };
10683
+ const openCreate = () => {
10684
+ setCreateName("");
10685
+ setCreateOpen(true);
10686
+ };
10687
+ const submitCreate = () => {
10688
+ const name = createName.trim();
10689
+ if (name && onCreateFolder) onCreateFolder(currentFolderId, name);
10690
+ setCreateOpen(false);
10691
+ };
10692
+ const openRename = (folder) => {
10693
+ setRenameValue(folder.name);
10694
+ setRenameTarget(folder);
10695
+ };
10696
+ const submitRename = () => {
10697
+ const name = renameValue.trim();
10698
+ if (name && renameTarget && onRenameFolder) onRenameFolder(renameTarget.id, name);
10699
+ setRenameTarget(null);
10700
+ };
10701
+ const content = /* @__PURE__ */ jsxs74("div", { className: ["fd-fb", className].filter(Boolean).join(" "), children: [
10702
+ /* @__PURE__ */ jsxs74("div", { className: "fd-fb-head", children: [
10703
+ /* @__PURE__ */ jsxs74("nav", { className: "fd-fb-crumbs", "aria-label": "Folder path", children: [
10704
+ /* @__PURE__ */ jsx78(
10705
+ BreadcrumbCrumb,
10706
+ {
10707
+ label: "Home",
10708
+ isCurrent: currentFolderId == null,
10709
+ onClick: () => onNavigate(null),
10710
+ isDropTarget: canMove,
10711
+ onDropPayload: (p) => moveIntoFolder(p, null)
10712
+ }
10713
+ ),
10714
+ crumbFolders.map((f, i) => /* @__PURE__ */ jsx78(
10715
+ BreadcrumbCrumb,
10716
+ {
10717
+ label: f.name,
10718
+ isCurrent: i === crumbFolders.length - 1,
10719
+ onClick: () => onNavigate(f.id),
10720
+ isDropTarget: canMove,
10721
+ onDropPayload: (p) => moveIntoFolder(p, f.id)
10722
+ },
10723
+ f.id
10724
+ ))
10725
+ ] }),
10726
+ /* @__PURE__ */ jsxs74("span", { className: "fd-fb-actions", children: [
10727
+ canCreateFolder ? /* @__PURE__ */ jsx78(Button, { variant: "secondary", size: "sm", icon: "folder-simple-plus", onClick: openCreate, children: "New folder" }) : null,
10728
+ canUpload ? /* @__PURE__ */ jsxs74(Fragment23, { children: [
10729
+ /* @__PURE__ */ jsx78(Button, { variant: "secondary", size: "sm", icon: "upload-simple", onClick: () => uploadInputRef.current && uploadInputRef.current.click(), children: "Upload" }),
10730
+ /* @__PURE__ */ jsx78(
10731
+ "input",
10732
+ {
10733
+ ref: uploadInputRef,
10734
+ type: "file",
10735
+ multiple: true,
10736
+ className: "fd-sr",
10737
+ "aria-label": "Upload files",
10738
+ onChange: (e) => {
10739
+ const picked = Array.from(e.target.files || []);
10740
+ if (picked.length && onUpload) onUpload(picked, currentFolderId);
10741
+ e.target.value = "";
10742
+ }
10743
+ }
10744
+ )
10745
+ ] }) : null
10746
+ ] })
10747
+ ] }),
10748
+ /* @__PURE__ */ jsxs74("div", { className: "fd-fb-body", children: [
10749
+ /* @__PURE__ */ jsxs74("div", { className: "fd-fb-tree", role: "region", "aria-label": "Folder tree", children: [
10750
+ /* @__PURE__ */ jsx78(
10751
+ RowShell,
10752
+ {
10753
+ icon: "house-simple",
10754
+ name: "Home",
10755
+ onOpen: () => onNavigate(null),
10756
+ isDropTarget: canMove,
10757
+ onDropPayload: (p) => moveIntoFolder(p, null),
10758
+ className: ["fd-fb-tree-row", currentFolderId == null ? "is-current" : ""].filter(Boolean).join(" ")
10759
+ }
10760
+ ),
10761
+ childrenOf(folders, null).map((f) => /* @__PURE__ */ jsx78(
10762
+ TreeNode,
10763
+ {
10764
+ folder: f,
10765
+ depth: 0,
10766
+ folders,
10767
+ currentFolderId,
10768
+ onNavigate,
10769
+ onMoveInto: moveIntoFolder,
10770
+ canMove
10771
+ },
10772
+ f.id
10773
+ ))
10774
+ ] }),
10775
+ /* @__PURE__ */ jsx78("div", { className: "fd-fb-main", role: "region", "aria-label": "Folder contents", children: /* @__PURE__ */ jsx78(
10776
+ LoadingRegion,
10777
+ {
10778
+ state: loading ? "loading" : "ready",
10779
+ label: "Loading folder contents",
10780
+ skeleton: /* @__PURE__ */ jsxs74("div", { className: "fd-stack", style: { gap: 8, padding: 12 }, children: [
10781
+ /* @__PURE__ */ jsx78(Skeleton, { h: 44, radius: "10px" }),
10782
+ /* @__PURE__ */ jsx78(Skeleton, { h: 44, radius: "10px" }),
10783
+ /* @__PURE__ */ jsx78(Skeleton, { h: 44, radius: "10px" })
10784
+ ] }),
10785
+ children: isEmpty ? /* @__PURE__ */ jsx78(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__ */ jsxs74("div", { className: "fd-fb-list", children: [
10786
+ subfolders.map((f) => /* @__PURE__ */ jsx78(
10787
+ RowShell,
10788
+ {
10789
+ icon: "folder-simple",
10790
+ name: f.name,
10791
+ onOpen: () => onNavigate(f.id),
10792
+ draggableId: canMove ? { kind: "folder", id: f.id } : void 0,
10793
+ isDropTarget: canMove,
10794
+ onDropPayload: (p) => moveIntoFolder(p, f.id),
10795
+ actions: /* @__PURE__ */ jsxs74(Fragment23, { children: [
10796
+ canRenameFolder ? /* @__PURE__ */ jsx78(IconButton, { icon: "pencil-simple", label: "Rename " + f.name, size: "sm", variant: "ghost", onClick: () => openRename(f) }) : null,
10797
+ canMove ? /* @__PURE__ */ jsx78(
10798
+ FolderPickerMenu,
10799
+ {
10800
+ label: "Move " + f.name,
10801
+ destinations: flattenDestinations(folders, f.id),
10802
+ onPick: (target) => moveIntoFolder({ kind: "folder", id: f.id }, target)
10803
+ }
10804
+ ) : null
10805
+ ] })
10806
+ },
10807
+ f.id
10808
+ )),
10809
+ currentFiles.map((file) => /* @__PURE__ */ jsx78(
10810
+ RowShell,
10811
+ {
10812
+ icon: iconForMime(file.mime, file.name),
10813
+ name: file.name,
10814
+ meta: file.size ? formatBytes(file.size) : void 0,
10815
+ onOpen: onOpenFile ? () => onOpenFile(file) : void 0,
10816
+ draggableId: canMove ? { kind: "file", id: file.id } : void 0,
10817
+ actions: /* @__PURE__ */ jsxs74(Fragment23, { children: [
10818
+ canMove ? /* @__PURE__ */ jsx78(
10819
+ FolderPickerMenu,
10820
+ {
10821
+ label: "Move " + file.name,
10822
+ destinations: flattenDestinations(folders),
10823
+ onPick: (target) => onMoveFile && onMoveFile(file.id, target)
10824
+ }
10825
+ ) : null,
10826
+ canRemoveFile ? /* @__PURE__ */ jsx78(IconButton, { icon: "trash-simple", label: "Remove " + file.name, size: "sm", variant: "ghost", onClick: () => onRemoveFile && onRemoveFile(file) }) : null
10827
+ ] })
10828
+ },
10829
+ file.id
10830
+ ))
10831
+ ] })
10832
+ }
10833
+ ) })
10834
+ ] }),
10835
+ /* @__PURE__ */ jsx78(Modal, { open: createOpen, title: "New folder", onClose: () => setCreateOpen(false), footer: /* @__PURE__ */ jsxs74(Fragment23, { children: [
10836
+ /* @__PURE__ */ jsx78(Button, { variant: "secondary", onClick: () => setCreateOpen(false), children: "Cancel" }),
10837
+ /* @__PURE__ */ jsx78(Button, { onClick: submitCreate, disabled: !createName.trim(), children: "Create" })
10838
+ ] }), children: /* @__PURE__ */ jsx78(
10839
+ "input",
10840
+ {
10841
+ className: "fd-fb-input",
10842
+ value: createName,
10843
+ onChange: (e) => setCreateName(e.target.value),
10844
+ onKeyDown: (e) => {
10845
+ if (e.key === "Enter") submitCreate();
10846
+ },
10847
+ placeholder: "Folder name",
10848
+ "aria-label": "Folder name",
10849
+ autoFocus: true
10850
+ }
10851
+ ) }),
10852
+ /* @__PURE__ */ jsx78(Modal, { open: renameTarget != null, title: "Rename folder", onClose: () => setRenameTarget(null), footer: /* @__PURE__ */ jsxs74(Fragment23, { children: [
10853
+ /* @__PURE__ */ jsx78(Button, { variant: "secondary", onClick: () => setRenameTarget(null), children: "Cancel" }),
10854
+ /* @__PURE__ */ jsx78(Button, { onClick: submitRename, disabled: !renameValue.trim(), children: "Save" })
10855
+ ] }), children: /* @__PURE__ */ jsx78(
10856
+ "input",
10857
+ {
10858
+ className: "fd-fb-input",
10859
+ value: renameValue,
10860
+ onChange: (e) => setRenameValue(e.target.value),
10861
+ onKeyDown: (e) => {
10862
+ if (e.key === "Enter") submitRename();
10863
+ },
10864
+ placeholder: "Folder name",
10865
+ "aria-label": "Folder name",
10866
+ autoFocus: true
10867
+ }
10868
+ ) })
10869
+ ] });
10870
+ if (!canUpload) return content;
10871
+ return /* @__PURE__ */ jsx78(Dropzone, { className: "fd-fb-dropzone", onFiles: (picked) => onUpload && onUpload(picked, currentFolderId), label: "Drop files to upload", children: content });
10872
+ }
10873
+ function BreadcrumbCrumb({
10874
+ label,
10875
+ isCurrent,
10876
+ onClick,
10877
+ isDropTarget,
10878
+ onDropPayload
10879
+ }) {
10880
+ const [over, setOver] = React48.useState(false);
10881
+ const dragCounter = React48.useRef(0);
10882
+ const dropHandlers = isDropTarget ? {
10883
+ onDragEnter: (e) => {
10884
+ if (!hasInternalPayload(e)) return;
10885
+ dragCounter.current++;
10886
+ setOver(true);
10887
+ },
10888
+ onDragOver: (e) => {
10889
+ if (!hasInternalPayload(e)) return;
10890
+ e.preventDefault();
10891
+ e.dataTransfer.dropEffect = "move";
10892
+ },
10893
+ onDragLeave: () => {
10894
+ dragCounter.current = Math.max(0, dragCounter.current - 1);
10895
+ if (!dragCounter.current) setOver(false);
10896
+ },
10897
+ onDrop: (e) => {
10898
+ if (!hasInternalPayload(e)) return;
10899
+ e.preventDefault();
10900
+ dragCounter.current = 0;
10901
+ setOver(false);
10902
+ const payload = readDragPayload(e);
10903
+ if (payload && onDropPayload) onDropPayload(payload);
10904
+ }
10905
+ } : {};
10906
+ return /* @__PURE__ */ jsx78("span", { className: ["fd-fb-crumb", over ? "is-drop-target" : ""].filter(Boolean).join(" "), ...dropHandlers, children: isCurrent ? /* @__PURE__ */ jsx78("span", { className: "fd-fb-crumb-current", "aria-current": "page", children: label }) : /* @__PURE__ */ jsx78("button", { type: "button", className: "fd-fb-crumb-link", onClick, children: label }) });
10907
+ }
10908
+
10909
+ // src/kits/query.ts
10910
+ import * as React49 from "react";
10423
10911
  function eqFilter(get2) {
10424
10912
  return (row, value) => Array.isArray(value) ? value.includes(get2(row)) : get2(row) === value;
10425
10913
  }
@@ -10451,22 +10939,22 @@ function compare(a, b, dir) {
10451
10939
  var API = null;
10452
10940
  var PREFS = null;
10453
10941
  function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
10454
- const [query, setQuery] = React48.useState(() => {
10942
+ const [query, setQuery] = React49.useState(() => {
10455
10943
  const store = PREFS || window.PlannerPrefs;
10456
10944
  const saved = prefsKey && store ? store.getTable(prefsKey) : {};
10457
10945
  return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
10458
10946
  });
10459
- const savePref = React48.useCallback((patch2) => {
10947
+ const savePref = React49.useCallback((patch2) => {
10460
10948
  const store = PREFS || window.PlannerPrefs;
10461
10949
  if (prefsKey && store) store.setTable(prefsKey, patch2);
10462
10950
  }, [prefsKey]);
10463
- const [res, setRes] = React48.useState(null);
10464
- const [loading, setLoading] = React48.useState(true);
10465
- const seq2 = React48.useRef(0);
10951
+ const [res, setRes] = React49.useState(null);
10952
+ const [loading, setLoading] = React49.useState(true);
10953
+ const seq2 = React49.useRef(0);
10466
10954
  const depKey = (deps || []).join("|");
10467
10955
  const paramKey = JSON.stringify(params || {});
10468
10956
  const queryKey = JSON.stringify(query);
10469
- React48.useEffect(() => {
10957
+ React49.useEffect(() => {
10470
10958
  const id = ++seq2.current;
10471
10959
  setLoading(true);
10472
10960
  const t = setTimeout(() => {
@@ -10810,6 +11298,7 @@ export {
10810
11298
  FILE_VIEWER_CSV_ROW_LIMIT,
10811
11299
  FILE_VIEWER_TEXT_LIMIT,
10812
11300
  FeatureGate,
11301
+ FileBrowser,
10813
11302
  FileChip,
10814
11303
  FileGrid,
10815
11304
  FileKit,