@flytedan/flytebot-design-system 0.12.15 → 0.14.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 +558 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +80 -1
- package/dist/index.d.ts +80 -1
- package/dist/index.js +557 -9
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/styles/components.css +234 -0
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,
|
|
@@ -8672,6 +8673,30 @@ function BudgetReallocator({
|
|
|
8672
8673
|
setDraft(spend);
|
|
8673
8674
|
if (onCancel) onCancel();
|
|
8674
8675
|
};
|
|
8676
|
+
const trackRef = React39.useRef(null);
|
|
8677
|
+
const chipRef = React39.useRef(null);
|
|
8678
|
+
const [chipLeft, setChipLeft] = React39.useState(null);
|
|
8679
|
+
React39.useLayoutEffect(() => {
|
|
8680
|
+
const track = trackRef.current;
|
|
8681
|
+
if (!dirty || !track) {
|
|
8682
|
+
setChipLeft(null);
|
|
8683
|
+
return;
|
|
8684
|
+
}
|
|
8685
|
+
const measure = () => {
|
|
8686
|
+
const chip = chipRef.current;
|
|
8687
|
+
if (!chip) return;
|
|
8688
|
+
const trackWidth = track.clientWidth;
|
|
8689
|
+
const chipWidth = chip.offsetWidth;
|
|
8690
|
+
const half = chipWidth / 2;
|
|
8691
|
+
const desired = pct / 100 * trackWidth;
|
|
8692
|
+
setChipLeft(Math.min(Math.max(desired, half), Math.max(trackWidth - half, half)));
|
|
8693
|
+
};
|
|
8694
|
+
measure();
|
|
8695
|
+
if (typeof ResizeObserver !== "function") return;
|
|
8696
|
+
const ro = new ResizeObserver(measure);
|
|
8697
|
+
ro.observe(track);
|
|
8698
|
+
return () => ro.disconnect();
|
|
8699
|
+
}, [dirty, pct]);
|
|
8675
8700
|
return /* @__PURE__ */ (0, import_jsx_runtime69.jsxs)(
|
|
8676
8701
|
"div",
|
|
8677
8702
|
{
|
|
@@ -8686,9 +8711,20 @@ function BudgetReallocator({
|
|
|
8686
8711
|
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)(CsiBadge, { band: nextBand, crp: nextCrp, size: "medium", preview: true })
|
|
8687
8712
|
] }) : null
|
|
8688
8713
|
] }),
|
|
8689
|
-
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "fd-slider", children: [
|
|
8714
|
+
/* @__PURE__ */ (0, import_jsx_runtime69.jsxs)("div", { className: "fd-slider", ref: trackRef, children: [
|
|
8690
8715
|
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)("span", { className: "fd-slider-fill", style: { width: pct + "%" } }),
|
|
8691
|
-
dirty ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
|
|
8716
|
+
dirty ? /* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
|
|
8717
|
+
"span",
|
|
8718
|
+
{
|
|
8719
|
+
ref: chipRef,
|
|
8720
|
+
className: "fd-slider-chip",
|
|
8721
|
+
style: {
|
|
8722
|
+
left: chipLeft === null ? pct + "%" : chipLeft,
|
|
8723
|
+
background: delta < 0 ? "var(--danger-solid)" : "var(--ok-solid)"
|
|
8724
|
+
},
|
|
8725
|
+
children: (delta < 0 ? "\u2212$" : "+$") + Math.abs(delta).toLocaleString()
|
|
8726
|
+
}
|
|
8727
|
+
) : null,
|
|
8692
8728
|
/* @__PURE__ */ (0, import_jsx_runtime69.jsx)(
|
|
8693
8729
|
"input",
|
|
8694
8730
|
{
|
|
@@ -10626,8 +10662,520 @@ function FileViewer({ file, resolveUrl, sidePanel, sidePanelLabel = "Details", o
|
|
|
10626
10662
|
);
|
|
10627
10663
|
}
|
|
10628
10664
|
|
|
10629
|
-
// src/
|
|
10665
|
+
// src/components/files/FileBrowser.tsx
|
|
10630
10666
|
var React48 = __toESM(require("react"), 1);
|
|
10667
|
+
var import_jsx_runtime79 = require("react/jsx-runtime");
|
|
10668
|
+
function childrenOf(folders, parentId) {
|
|
10669
|
+
return folders.filter((f) => f.parentId === parentId).sort((a, b) => a.name.localeCompare(b.name));
|
|
10670
|
+
}
|
|
10671
|
+
function ancestorsOf(folders, id) {
|
|
10672
|
+
const byId = new Map(folders.map((f) => [f.id, f]));
|
|
10673
|
+
const chain = [];
|
|
10674
|
+
let cur = id != null ? byId.get(id) : void 0;
|
|
10675
|
+
const seen = /* @__PURE__ */ new Set();
|
|
10676
|
+
while (cur && !seen.has(cur.id)) {
|
|
10677
|
+
seen.add(cur.id);
|
|
10678
|
+
chain.unshift(cur);
|
|
10679
|
+
cur = cur.parentId != null ? byId.get(cur.parentId) : void 0;
|
|
10680
|
+
}
|
|
10681
|
+
return chain;
|
|
10682
|
+
}
|
|
10683
|
+
function isSelfOrDescendant(folders, ancestorId, nodeId) {
|
|
10684
|
+
if (nodeId == null) return false;
|
|
10685
|
+
if (nodeId === ancestorId) return true;
|
|
10686
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
10687
|
+
folders.forEach((f) => {
|
|
10688
|
+
const key = f.parentId ?? "";
|
|
10689
|
+
if (!byParent.has(key)) byParent.set(key, []);
|
|
10690
|
+
byParent.get(key).push(f);
|
|
10691
|
+
});
|
|
10692
|
+
const stack = [...byParent.get(ancestorId) || []];
|
|
10693
|
+
while (stack.length) {
|
|
10694
|
+
const next = stack.pop();
|
|
10695
|
+
if (next.id === nodeId) return true;
|
|
10696
|
+
stack.push(...byParent.get(next.id) || []);
|
|
10697
|
+
}
|
|
10698
|
+
return false;
|
|
10699
|
+
}
|
|
10700
|
+
function flattenDestinations(folders, excludeSubtreeOf) {
|
|
10701
|
+
const out = [{ id: null, name: "Home", depth: 0 }];
|
|
10702
|
+
const walk = (parentId, depth) => {
|
|
10703
|
+
childrenOf(folders, parentId).forEach((f) => {
|
|
10704
|
+
if (excludeSubtreeOf && isSelfOrDescendant(folders, excludeSubtreeOf, f.id)) return;
|
|
10705
|
+
out.push({ id: f.id, name: f.name, depth });
|
|
10706
|
+
walk(f.id, depth + 1);
|
|
10707
|
+
});
|
|
10708
|
+
};
|
|
10709
|
+
walk(null, 1);
|
|
10710
|
+
return out;
|
|
10711
|
+
}
|
|
10712
|
+
var DND_MIME = "application/x-fd-filebrowser";
|
|
10713
|
+
function writeDragPayload(e, payload) {
|
|
10714
|
+
const s = JSON.stringify(payload);
|
|
10715
|
+
e.dataTransfer.setData(DND_MIME, s);
|
|
10716
|
+
e.dataTransfer.setData("text/plain", s);
|
|
10717
|
+
e.dataTransfer.effectAllowed = "move";
|
|
10718
|
+
}
|
|
10719
|
+
function readDragPayload(e) {
|
|
10720
|
+
const raw = e.dataTransfer.getData(DND_MIME) || e.dataTransfer.getData("text/plain");
|
|
10721
|
+
if (!raw) return null;
|
|
10722
|
+
try {
|
|
10723
|
+
const parsed = JSON.parse(raw);
|
|
10724
|
+
if (parsed && (parsed.kind === "file" || parsed.kind === "folder") && typeof parsed.id === "string") return parsed;
|
|
10725
|
+
} catch {
|
|
10726
|
+
}
|
|
10727
|
+
return null;
|
|
10728
|
+
}
|
|
10729
|
+
function hasInternalPayload(e) {
|
|
10730
|
+
return Array.from(e.dataTransfer.types || []).indexOf(DND_MIME) !== -1;
|
|
10731
|
+
}
|
|
10732
|
+
function FolderPickerMenu({
|
|
10733
|
+
label,
|
|
10734
|
+
destinations,
|
|
10735
|
+
onPick
|
|
10736
|
+
}) {
|
|
10737
|
+
const [open, setOpen] = React48.useState(false);
|
|
10738
|
+
const zIndex = useOverlayLayer(open);
|
|
10739
|
+
const ref = React48.useRef(null);
|
|
10740
|
+
React48.useEffect(() => {
|
|
10741
|
+
if (!open) return;
|
|
10742
|
+
const away = (e) => {
|
|
10743
|
+
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
10744
|
+
};
|
|
10745
|
+
const esc = (e) => {
|
|
10746
|
+
if (e.key === "Escape") setOpen(false);
|
|
10747
|
+
};
|
|
10748
|
+
document.addEventListener("pointerdown", away);
|
|
10749
|
+
document.addEventListener("keydown", esc);
|
|
10750
|
+
return () => {
|
|
10751
|
+
document.removeEventListener("pointerdown", away);
|
|
10752
|
+
document.removeEventListener("keydown", esc);
|
|
10753
|
+
};
|
|
10754
|
+
}, [open]);
|
|
10755
|
+
return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fd-fb-menu-anchor", ref, children: [
|
|
10756
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10757
|
+
IconButton,
|
|
10758
|
+
{
|
|
10759
|
+
icon: "arrows-out-cardinal",
|
|
10760
|
+
label,
|
|
10761
|
+
size: "sm",
|
|
10762
|
+
variant: "ghost",
|
|
10763
|
+
"aria-haspopup": "menu",
|
|
10764
|
+
"aria-expanded": open,
|
|
10765
|
+
onClick: () => setOpen((v) => !v)
|
|
10766
|
+
}
|
|
10767
|
+
),
|
|
10768
|
+
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: [
|
|
10769
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-overline fd-muted fd-fb-menu-title", children: "Move to\u2026" }),
|
|
10770
|
+
destinations.map((d) => /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
|
|
10771
|
+
"button",
|
|
10772
|
+
{
|
|
10773
|
+
type: "button",
|
|
10774
|
+
role: "menuitem",
|
|
10775
|
+
className: "fd-fb-menu-item",
|
|
10776
|
+
style: { paddingLeft: 10 + d.depth * 14 },
|
|
10777
|
+
onClick: () => {
|
|
10778
|
+
onPick(d.id);
|
|
10779
|
+
setOpen(false);
|
|
10780
|
+
},
|
|
10781
|
+
children: [
|
|
10782
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("i", { className: "ph ph-" + (d.id == null ? "house-simple" : "folder-simple"), "aria-hidden": "true" }),
|
|
10783
|
+
d.name
|
|
10784
|
+
]
|
|
10785
|
+
},
|
|
10786
|
+
d.id ?? "__root__"
|
|
10787
|
+
))
|
|
10788
|
+
] }) }) : null
|
|
10789
|
+
] });
|
|
10790
|
+
}
|
|
10791
|
+
function RowShell({ icon, name, meta, onOpen, draggableId, isDropTarget, onDropPayload, actions, className = "" }) {
|
|
10792
|
+
const [over, setOver] = React48.useState(false);
|
|
10793
|
+
const dragCounter = React48.useRef(0);
|
|
10794
|
+
const dropHandlers = isDropTarget ? {
|
|
10795
|
+
onDragEnter: (e) => {
|
|
10796
|
+
if (!hasInternalPayload(e)) return;
|
|
10797
|
+
dragCounter.current++;
|
|
10798
|
+
setOver(true);
|
|
10799
|
+
},
|
|
10800
|
+
onDragOver: (e) => {
|
|
10801
|
+
if (!hasInternalPayload(e)) return;
|
|
10802
|
+
e.preventDefault();
|
|
10803
|
+
e.dataTransfer.dropEffect = "move";
|
|
10804
|
+
},
|
|
10805
|
+
onDragLeave: () => {
|
|
10806
|
+
dragCounter.current = Math.max(0, dragCounter.current - 1);
|
|
10807
|
+
if (!dragCounter.current) setOver(false);
|
|
10808
|
+
},
|
|
10809
|
+
onDrop: (e) => {
|
|
10810
|
+
if (!hasInternalPayload(e)) return;
|
|
10811
|
+
e.preventDefault();
|
|
10812
|
+
dragCounter.current = 0;
|
|
10813
|
+
setOver(false);
|
|
10814
|
+
const payload = readDragPayload(e);
|
|
10815
|
+
if (payload && onDropPayload) onDropPayload(payload);
|
|
10816
|
+
}
|
|
10817
|
+
} : {};
|
|
10818
|
+
return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: ["fd-fb-row", over ? "is-drop-target" : "", className].filter(Boolean).join(" "), ...dropHandlers, children: [
|
|
10819
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(
|
|
10820
|
+
"button",
|
|
10821
|
+
{
|
|
10822
|
+
type: "button",
|
|
10823
|
+
className: "fd-fb-row-main",
|
|
10824
|
+
onClick: onOpen,
|
|
10825
|
+
draggable: !!draggableId,
|
|
10826
|
+
onDragStart: draggableId ? (e) => writeDragPayload(e, draggableId) : void 0,
|
|
10827
|
+
children: [
|
|
10828
|
+
/* @__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 }) }),
|
|
10829
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fd-fb-row-text", children: [
|
|
10830
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-row-name", title: name, children: name }),
|
|
10831
|
+
meta ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-row-meta", children: meta }) : null
|
|
10832
|
+
] })
|
|
10833
|
+
]
|
|
10834
|
+
}
|
|
10835
|
+
),
|
|
10836
|
+
actions ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)("span", { className: "fd-fb-row-actions", children: actions }) : null
|
|
10837
|
+
] });
|
|
10838
|
+
}
|
|
10839
|
+
function TreeNode({ folder, depth, folders, currentFolderId, onNavigate, onMoveInto, canMove }) {
|
|
10840
|
+
const kids = childrenOf(folders, folder.id);
|
|
10841
|
+
const [expanded, setExpanded] = React48.useState(() => currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId));
|
|
10842
|
+
React48.useEffect(() => {
|
|
10843
|
+
if (currentFolderId != null && isSelfOrDescendant(folders, folder.id, currentFolderId)) setExpanded(true);
|
|
10844
|
+
}, [currentFolderId, folder.id, folders]);
|
|
10845
|
+
const isCurrent = folder.id === currentFolderId;
|
|
10846
|
+
return /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-tree-node", children: [
|
|
10847
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10848
|
+
RowShell,
|
|
10849
|
+
{
|
|
10850
|
+
icon: folder.id === currentFolderId ? "folder-open" : "folder-simple",
|
|
10851
|
+
name: folder.name,
|
|
10852
|
+
onOpen: () => onNavigate(folder.id),
|
|
10853
|
+
draggableId: canMove ? { kind: "folder", id: folder.id } : void 0,
|
|
10854
|
+
isDropTarget: canMove,
|
|
10855
|
+
onDropPayload: (payload) => onMoveInto(payload, folder.id),
|
|
10856
|
+
className: ["fd-fb-tree-row", isCurrent ? "is-current" : ""].filter(Boolean).join(" "),
|
|
10857
|
+
actions: kids.length ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10858
|
+
IconButton,
|
|
10859
|
+
{
|
|
10860
|
+
icon: expanded ? "caret-down" : "caret-right",
|
|
10861
|
+
label: (expanded ? "Collapse " : "Expand ") + folder.name,
|
|
10862
|
+
size: "sm",
|
|
10863
|
+
variant: "ghost",
|
|
10864
|
+
onClick: () => setExpanded((v) => !v)
|
|
10865
|
+
}
|
|
10866
|
+
) : void 0
|
|
10867
|
+
}
|
|
10868
|
+
),
|
|
10869
|
+
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)(
|
|
10870
|
+
TreeNode,
|
|
10871
|
+
{
|
|
10872
|
+
folder: k,
|
|
10873
|
+
depth: depth + 1,
|
|
10874
|
+
folders,
|
|
10875
|
+
currentFolderId,
|
|
10876
|
+
onNavigate,
|
|
10877
|
+
onMoveInto,
|
|
10878
|
+
canMove
|
|
10879
|
+
},
|
|
10880
|
+
k.id
|
|
10881
|
+
)) }) : null
|
|
10882
|
+
] });
|
|
10883
|
+
}
|
|
10884
|
+
function FileBrowser({
|
|
10885
|
+
folders,
|
|
10886
|
+
files,
|
|
10887
|
+
currentFolderId,
|
|
10888
|
+
onNavigate,
|
|
10889
|
+
onCreateFolder,
|
|
10890
|
+
onRenameFolder,
|
|
10891
|
+
onMoveFolder,
|
|
10892
|
+
onMoveFile,
|
|
10893
|
+
onOpenFile,
|
|
10894
|
+
onRemoveFile,
|
|
10895
|
+
onDeleteFolder,
|
|
10896
|
+
onUpload,
|
|
10897
|
+
loading = false,
|
|
10898
|
+
permissions,
|
|
10899
|
+
className = ""
|
|
10900
|
+
}) {
|
|
10901
|
+
const perm = permissions || {};
|
|
10902
|
+
const canCreateFolder = perm.canCreateFolder !== false && !!onCreateFolder;
|
|
10903
|
+
const canRenameFolder = perm.canRenameFolder !== false && !!onRenameFolder;
|
|
10904
|
+
const canMove = perm.canMove !== false && (!!onMoveFolder || !!onMoveFile);
|
|
10905
|
+
const canUpload = perm.canUpload !== false && !!onUpload;
|
|
10906
|
+
const canRemoveFile = perm.canRemoveFile !== false && !!onRemoveFile;
|
|
10907
|
+
const canDeleteFolder = perm.canDeleteFolder !== false && !!onDeleteFolder;
|
|
10908
|
+
const [createOpen, setCreateOpen] = React48.useState(false);
|
|
10909
|
+
const [createName, setCreateName] = React48.useState("");
|
|
10910
|
+
const [renameTarget, setRenameTarget] = React48.useState(null);
|
|
10911
|
+
const [renameValue, setRenameValue] = React48.useState("");
|
|
10912
|
+
const [deleteTarget, setDeleteTarget] = React48.useState(null);
|
|
10913
|
+
const uploadInputRef = React48.useRef(null);
|
|
10914
|
+
const crumbFolders = React48.useMemo(() => ancestorsOf(folders, currentFolderId), [folders, currentFolderId]);
|
|
10915
|
+
const subfolders = React48.useMemo(() => childrenOf(folders, currentFolderId), [folders, currentFolderId]);
|
|
10916
|
+
const currentFiles = React48.useMemo(
|
|
10917
|
+
() => files.filter((f) => f.folderId === currentFolderId).sort((a, b) => a.name.localeCompare(b.name)),
|
|
10918
|
+
[files, currentFolderId]
|
|
10919
|
+
);
|
|
10920
|
+
const isEmpty = !subfolders.length && !currentFiles.length;
|
|
10921
|
+
const moveIntoFolder = (payload, targetFolderId) => {
|
|
10922
|
+
if (payload.kind === "folder") {
|
|
10923
|
+
if (payload.id === targetFolderId) return;
|
|
10924
|
+
if (isSelfOrDescendant(folders, payload.id, targetFolderId)) return;
|
|
10925
|
+
onMoveFolder && onMoveFolder(payload.id, targetFolderId);
|
|
10926
|
+
} else {
|
|
10927
|
+
onMoveFile && onMoveFile(payload.id, targetFolderId);
|
|
10928
|
+
}
|
|
10929
|
+
};
|
|
10930
|
+
const openCreate = () => {
|
|
10931
|
+
setCreateName("");
|
|
10932
|
+
setCreateOpen(true);
|
|
10933
|
+
};
|
|
10934
|
+
const submitCreate = () => {
|
|
10935
|
+
const name = createName.trim();
|
|
10936
|
+
if (name && onCreateFolder) onCreateFolder(currentFolderId, name);
|
|
10937
|
+
setCreateOpen(false);
|
|
10938
|
+
};
|
|
10939
|
+
const openRename = (folder) => {
|
|
10940
|
+
setRenameValue(folder.name);
|
|
10941
|
+
setRenameTarget(folder);
|
|
10942
|
+
};
|
|
10943
|
+
const submitRename = () => {
|
|
10944
|
+
const name = renameValue.trim();
|
|
10945
|
+
if (name && renameTarget && onRenameFolder) onRenameFolder(renameTarget.id, name);
|
|
10946
|
+
setRenameTarget(null);
|
|
10947
|
+
};
|
|
10948
|
+
const openDelete = (folder) => {
|
|
10949
|
+
setDeleteTarget(folder);
|
|
10950
|
+
};
|
|
10951
|
+
const confirmDelete = () => {
|
|
10952
|
+
if (deleteTarget && onDeleteFolder) onDeleteFolder(deleteTarget);
|
|
10953
|
+
setDeleteTarget(null);
|
|
10954
|
+
};
|
|
10955
|
+
const content = /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: ["fd-fb", className].filter(Boolean).join(" "), children: [
|
|
10956
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-head", children: [
|
|
10957
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("nav", { className: "fd-fb-crumbs", "aria-label": "Folder path", children: [
|
|
10958
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10959
|
+
BreadcrumbCrumb,
|
|
10960
|
+
{
|
|
10961
|
+
label: "Home",
|
|
10962
|
+
isCurrent: currentFolderId == null,
|
|
10963
|
+
onClick: () => onNavigate(null),
|
|
10964
|
+
isDropTarget: canMove,
|
|
10965
|
+
onDropPayload: (p) => moveIntoFolder(p, null)
|
|
10966
|
+
}
|
|
10967
|
+
),
|
|
10968
|
+
crumbFolders.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10969
|
+
BreadcrumbCrumb,
|
|
10970
|
+
{
|
|
10971
|
+
label: f.name,
|
|
10972
|
+
isCurrent: i === crumbFolders.length - 1,
|
|
10973
|
+
onClick: () => onNavigate(f.id),
|
|
10974
|
+
isDropTarget: canMove,
|
|
10975
|
+
onDropPayload: (p) => moveIntoFolder(p, f.id)
|
|
10976
|
+
},
|
|
10977
|
+
f.id
|
|
10978
|
+
))
|
|
10979
|
+
] }),
|
|
10980
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("span", { className: "fd-fb-actions", children: [
|
|
10981
|
+
canCreateFolder ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", size: "sm", icon: "folder-simple-plus", onClick: openCreate, children: "New folder" }) : null,
|
|
10982
|
+
canUpload ? /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
|
|
10983
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", size: "sm", icon: "upload-simple", onClick: () => uploadInputRef.current && uploadInputRef.current.click(), children: "Upload" }),
|
|
10984
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
10985
|
+
"input",
|
|
10986
|
+
{
|
|
10987
|
+
ref: uploadInputRef,
|
|
10988
|
+
type: "file",
|
|
10989
|
+
multiple: true,
|
|
10990
|
+
className: "fd-sr",
|
|
10991
|
+
"aria-label": "Upload files",
|
|
10992
|
+
onChange: (e) => {
|
|
10993
|
+
const picked = Array.from(e.target.files || []);
|
|
10994
|
+
if (picked.length && onUpload) onUpload(picked, currentFolderId);
|
|
10995
|
+
e.target.value = "";
|
|
10996
|
+
}
|
|
10997
|
+
}
|
|
10998
|
+
)
|
|
10999
|
+
] }) : null
|
|
11000
|
+
] })
|
|
11001
|
+
] }),
|
|
11002
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-body", children: [
|
|
11003
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-fb-tree", role: "region", "aria-label": "Folder tree", children: [
|
|
11004
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
11005
|
+
RowShell,
|
|
11006
|
+
{
|
|
11007
|
+
icon: "house-simple",
|
|
11008
|
+
name: "Home",
|
|
11009
|
+
onOpen: () => onNavigate(null),
|
|
11010
|
+
isDropTarget: canMove,
|
|
11011
|
+
onDropPayload: (p) => moveIntoFolder(p, null),
|
|
11012
|
+
className: ["fd-fb-tree-row", currentFolderId == null ? "is-current" : ""].filter(Boolean).join(" ")
|
|
11013
|
+
}
|
|
11014
|
+
),
|
|
11015
|
+
childrenOf(folders, null).map((f) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
11016
|
+
TreeNode,
|
|
11017
|
+
{
|
|
11018
|
+
folder: f,
|
|
11019
|
+
depth: 0,
|
|
11020
|
+
folders,
|
|
11021
|
+
currentFolderId,
|
|
11022
|
+
onNavigate,
|
|
11023
|
+
onMoveInto: moveIntoFolder,
|
|
11024
|
+
canMove
|
|
11025
|
+
},
|
|
11026
|
+
f.id
|
|
11027
|
+
))
|
|
11028
|
+
] }),
|
|
11029
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)("div", { className: "fd-fb-main", role: "region", "aria-label": "Folder contents", children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
11030
|
+
LoadingRegion,
|
|
11031
|
+
{
|
|
11032
|
+
state: loading ? "loading" : "ready",
|
|
11033
|
+
label: "Loading folder contents",
|
|
11034
|
+
skeleton: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)("div", { className: "fd-stack", style: { gap: 8, padding: 12 }, children: [
|
|
11035
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Skeleton, { h: 44, radius: "10px" }),
|
|
11036
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Skeleton, { h: 44, radius: "10px" }),
|
|
11037
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Skeleton, { h: 44, radius: "10px" })
|
|
11038
|
+
] }),
|
|
11039
|
+
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: [
|
|
11040
|
+
subfolders.map((f) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
11041
|
+
RowShell,
|
|
11042
|
+
{
|
|
11043
|
+
icon: "folder-simple",
|
|
11044
|
+
name: f.name,
|
|
11045
|
+
onOpen: () => onNavigate(f.id),
|
|
11046
|
+
draggableId: canMove ? { kind: "folder", id: f.id } : void 0,
|
|
11047
|
+
isDropTarget: canMove,
|
|
11048
|
+
onDropPayload: (p) => moveIntoFolder(p, f.id),
|
|
11049
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
|
|
11050
|
+
canRenameFolder ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(IconButton, { icon: "pencil-simple", label: "Rename " + f.name, size: "sm", variant: "ghost", onClick: () => openRename(f) }) : null,
|
|
11051
|
+
canMove ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
11052
|
+
FolderPickerMenu,
|
|
11053
|
+
{
|
|
11054
|
+
label: "Move " + f.name,
|
|
11055
|
+
destinations: flattenDestinations(folders, f.id),
|
|
11056
|
+
onPick: (target) => moveIntoFolder({ kind: "folder", id: f.id }, target)
|
|
11057
|
+
}
|
|
11058
|
+
) : null,
|
|
11059
|
+
canDeleteFolder ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(IconButton, { icon: "trash-simple", label: "Delete " + f.name, size: "sm", variant: "ghost", onClick: () => openDelete(f) }) : null
|
|
11060
|
+
] })
|
|
11061
|
+
},
|
|
11062
|
+
f.id
|
|
11063
|
+
)),
|
|
11064
|
+
currentFiles.map((file) => /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
11065
|
+
RowShell,
|
|
11066
|
+
{
|
|
11067
|
+
icon: iconForMime(file.mime, file.name),
|
|
11068
|
+
name: file.name,
|
|
11069
|
+
meta: file.size ? formatBytes(file.size) : void 0,
|
|
11070
|
+
onOpen: onOpenFile ? () => onOpenFile(file) : void 0,
|
|
11071
|
+
draggableId: canMove ? { kind: "file", id: file.id } : void 0,
|
|
11072
|
+
actions: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
|
|
11073
|
+
canMove ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
11074
|
+
FolderPickerMenu,
|
|
11075
|
+
{
|
|
11076
|
+
label: "Move " + file.name,
|
|
11077
|
+
destinations: flattenDestinations(folders),
|
|
11078
|
+
onPick: (target) => onMoveFile && onMoveFile(file.id, target)
|
|
11079
|
+
}
|
|
11080
|
+
) : null,
|
|
11081
|
+
canRemoveFile ? /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(IconButton, { icon: "trash-simple", label: "Remove " + file.name, size: "sm", variant: "ghost", onClick: () => onRemoveFile && onRemoveFile(file) }) : null
|
|
11082
|
+
] })
|
|
11083
|
+
},
|
|
11084
|
+
file.id
|
|
11085
|
+
))
|
|
11086
|
+
] })
|
|
11087
|
+
}
|
|
11088
|
+
) })
|
|
11089
|
+
] }),
|
|
11090
|
+
/* @__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: [
|
|
11091
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", onClick: () => setCreateOpen(false), children: "Cancel" }),
|
|
11092
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { onClick: submitCreate, disabled: !createName.trim(), children: "Create" })
|
|
11093
|
+
] }), children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
11094
|
+
"input",
|
|
11095
|
+
{
|
|
11096
|
+
className: "fd-fb-input",
|
|
11097
|
+
value: createName,
|
|
11098
|
+
onChange: (e) => setCreateName(e.target.value),
|
|
11099
|
+
onKeyDown: (e) => {
|
|
11100
|
+
if (e.key === "Enter") submitCreate();
|
|
11101
|
+
},
|
|
11102
|
+
placeholder: "Folder name",
|
|
11103
|
+
"aria-label": "Folder name",
|
|
11104
|
+
autoFocus: true
|
|
11105
|
+
}
|
|
11106
|
+
) }),
|
|
11107
|
+
/* @__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: [
|
|
11108
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", onClick: () => setRenameTarget(null), children: "Cancel" }),
|
|
11109
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { onClick: submitRename, disabled: !renameValue.trim(), children: "Save" })
|
|
11110
|
+
] }), children: /* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
11111
|
+
"input",
|
|
11112
|
+
{
|
|
11113
|
+
className: "fd-fb-input",
|
|
11114
|
+
value: renameValue,
|
|
11115
|
+
onChange: (e) => setRenameValue(e.target.value),
|
|
11116
|
+
onKeyDown: (e) => {
|
|
11117
|
+
if (e.key === "Enter") submitRename();
|
|
11118
|
+
},
|
|
11119
|
+
placeholder: "Folder name",
|
|
11120
|
+
"aria-label": "Folder name",
|
|
11121
|
+
autoFocus: true
|
|
11122
|
+
}
|
|
11123
|
+
) }),
|
|
11124
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(
|
|
11125
|
+
Modal,
|
|
11126
|
+
{
|
|
11127
|
+
open: deleteTarget != null,
|
|
11128
|
+
title: deleteTarget ? "Delete \u201C" + deleteTarget.name + "\u201D?" : "Delete folder?",
|
|
11129
|
+
description: "This can't be undone. The folder must be empty.",
|
|
11130
|
+
onClose: () => setDeleteTarget(null),
|
|
11131
|
+
footer: /* @__PURE__ */ (0, import_jsx_runtime79.jsxs)(import_jsx_runtime79.Fragment, { children: [
|
|
11132
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "secondary", onClick: () => setDeleteTarget(null), children: "Cancel" }),
|
|
11133
|
+
/* @__PURE__ */ (0, import_jsx_runtime79.jsx)(Button, { variant: "danger", onClick: confirmDelete, children: "Delete" })
|
|
11134
|
+
] })
|
|
11135
|
+
}
|
|
11136
|
+
)
|
|
11137
|
+
] });
|
|
11138
|
+
if (!canUpload) return content;
|
|
11139
|
+
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 });
|
|
11140
|
+
}
|
|
11141
|
+
function BreadcrumbCrumb({
|
|
11142
|
+
label,
|
|
11143
|
+
isCurrent,
|
|
11144
|
+
onClick,
|
|
11145
|
+
isDropTarget,
|
|
11146
|
+
onDropPayload
|
|
11147
|
+
}) {
|
|
11148
|
+
const [over, setOver] = React48.useState(false);
|
|
11149
|
+
const dragCounter = React48.useRef(0);
|
|
11150
|
+
const dropHandlers = isDropTarget ? {
|
|
11151
|
+
onDragEnter: (e) => {
|
|
11152
|
+
if (!hasInternalPayload(e)) return;
|
|
11153
|
+
dragCounter.current++;
|
|
11154
|
+
setOver(true);
|
|
11155
|
+
},
|
|
11156
|
+
onDragOver: (e) => {
|
|
11157
|
+
if (!hasInternalPayload(e)) return;
|
|
11158
|
+
e.preventDefault();
|
|
11159
|
+
e.dataTransfer.dropEffect = "move";
|
|
11160
|
+
},
|
|
11161
|
+
onDragLeave: () => {
|
|
11162
|
+
dragCounter.current = Math.max(0, dragCounter.current - 1);
|
|
11163
|
+
if (!dragCounter.current) setOver(false);
|
|
11164
|
+
},
|
|
11165
|
+
onDrop: (e) => {
|
|
11166
|
+
if (!hasInternalPayload(e)) return;
|
|
11167
|
+
e.preventDefault();
|
|
11168
|
+
dragCounter.current = 0;
|
|
11169
|
+
setOver(false);
|
|
11170
|
+
const payload = readDragPayload(e);
|
|
11171
|
+
if (payload && onDropPayload) onDropPayload(payload);
|
|
11172
|
+
}
|
|
11173
|
+
} : {};
|
|
11174
|
+
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 }) });
|
|
11175
|
+
}
|
|
11176
|
+
|
|
11177
|
+
// src/kits/query.ts
|
|
11178
|
+
var React49 = __toESM(require("react"), 1);
|
|
10631
11179
|
function eqFilter(get2) {
|
|
10632
11180
|
return (row, value) => Array.isArray(value) ? value.includes(get2(row)) : get2(row) === value;
|
|
10633
11181
|
}
|
|
@@ -10659,22 +11207,22 @@ function compare(a, b, dir) {
|
|
|
10659
11207
|
var API = null;
|
|
10660
11208
|
var PREFS = null;
|
|
10661
11209
|
function useServerTable({ endpoint, params, defaults, deps, prefsKey }) {
|
|
10662
|
-
const [query, setQuery] =
|
|
11210
|
+
const [query, setQuery] = React49.useState(() => {
|
|
10663
11211
|
const store = PREFS || window.PlannerPrefs;
|
|
10664
11212
|
const saved = prefsKey && store ? store.getTable(prefsKey) : {};
|
|
10665
11213
|
return { ...DEFAULTS, ...defaults || {}, ...saved.pageSize ? { pageSize: saved.pageSize } : {}, ...saved.sort ? { sort: saved.sort, dir: saved.dir || "desc" } : {} };
|
|
10666
11214
|
});
|
|
10667
|
-
const savePref =
|
|
11215
|
+
const savePref = React49.useCallback((patch2) => {
|
|
10668
11216
|
const store = PREFS || window.PlannerPrefs;
|
|
10669
11217
|
if (prefsKey && store) store.setTable(prefsKey, patch2);
|
|
10670
11218
|
}, [prefsKey]);
|
|
10671
|
-
const [res, setRes] =
|
|
10672
|
-
const [loading, setLoading] =
|
|
10673
|
-
const seq2 =
|
|
11219
|
+
const [res, setRes] = React49.useState(null);
|
|
11220
|
+
const [loading, setLoading] = React49.useState(true);
|
|
11221
|
+
const seq2 = React49.useRef(0);
|
|
10674
11222
|
const depKey = (deps || []).join("|");
|
|
10675
11223
|
const paramKey = JSON.stringify(params || {});
|
|
10676
11224
|
const queryKey = JSON.stringify(query);
|
|
10677
|
-
|
|
11225
|
+
React49.useEffect(() => {
|
|
10678
11226
|
const id = ++seq2.current;
|
|
10679
11227
|
setLoading(true);
|
|
10680
11228
|
const t = setTimeout(() => {
|
|
@@ -11019,6 +11567,7 @@ function createVersionStore(initialState, options) {
|
|
|
11019
11567
|
FILE_VIEWER_CSV_ROW_LIMIT,
|
|
11020
11568
|
FILE_VIEWER_TEXT_LIMIT,
|
|
11021
11569
|
FeatureGate,
|
|
11570
|
+
FileBrowser,
|
|
11022
11571
|
FileChip,
|
|
11023
11572
|
FileGrid,
|
|
11024
11573
|
FileKit,
|