@flytedan/flytebot-design-system 0.7.2 → 0.8.1

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
@@ -2485,7 +2485,7 @@ function QuotaRow({ label, percent = 0, note, tone, className = "" }) {
2485
2485
  // src/components/data/SortMenu.tsx
2486
2486
  var React13 = __toESM(require("react"), 1);
2487
2487
  var import_jsx_runtime36 = require("react/jsx-runtime");
2488
- function SortMenu({ fields = [], sort, onSort, align = "right", className = "", ...rest }) {
2488
+ function SortMenu({ fields = [], sort, onSort, align = "right", size = "md", className = "", ...rest }) {
2489
2489
  const [open, setOpen] = React13.useState(false);
2490
2490
  const ref = React13.useRef(null);
2491
2491
  React13.useEffect(() => {
@@ -2531,7 +2531,7 @@ function SortMenu({ fields = [], sort, onSort, align = "right", className = "",
2531
2531
  );
2532
2532
  };
2533
2533
  return /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)("span", { className, style: { position: "relative", display: "inline-flex" }, ref, ...rest, children: [
2534
- /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(Button, { size: "sm", variant: "secondary", icon: dir === "asc" ? "sort-ascending" : "sort-descending", onClick: () => setOpen(!open), children: [
2534
+ /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(Button, { size, variant: "secondary", icon: dir === "asc" ? "sort-ascending" : "sort-descending", onClick: () => setOpen(!open), children: [
2535
2535
  cur ? cur.label : "Unsorted",
2536
2536
  /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("i", { className: "ph ph-" + (dir === "asc" ? "arrow-up" : "arrow-down"), style: { fontSize: 12, marginLeft: 6, opacity: 0.75 } })
2537
2537
  ] }),
@@ -2660,6 +2660,8 @@ function VirtualList({
2660
2660
  onNeedMore,
2661
2661
  hasMore,
2662
2662
  loading = false,
2663
+ refreshing = false,
2664
+ refreshingLabel = "Searching\u2026",
2663
2665
  keyOf,
2664
2666
  height = 400,
2665
2667
  emptyState,
@@ -2685,54 +2687,83 @@ function VirtualList({
2685
2687
  requested.current.end = items.length;
2686
2688
  onNeedMore("end");
2687
2689
  }, [wantEnd, loading, items.length, onNeedMore]);
2688
- if (!items.length && !loading && emptyState) {
2689
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: ["fd-vlist", className].filter(Boolean).join(" "), style: { height, overflow: "auto", ...style }, children: emptyState });
2690
+ const lastKeysRef = React15.useRef(null);
2691
+ const newKeys = React15.useMemo(() => {
2692
+ const prev = lastKeysRef.current;
2693
+ if (!prev) return /* @__PURE__ */ new Set();
2694
+ const fresh = /* @__PURE__ */ new Set();
2695
+ for (const item of items) {
2696
+ const k = keyOf(item);
2697
+ if (!prev.has(k)) fresh.add(k);
2698
+ }
2699
+ return fresh;
2700
+ }, [items]);
2701
+ React15.useEffect(() => {
2702
+ lastKeysRef.current = new Set(items.map(keyOf));
2703
+ }, [items]);
2704
+ if (!items.length && !loading && !refreshing && emptyState) {
2705
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: ["fd-vlist-wrap", className].filter(Boolean).join(" "), style: { height, position: "relative", ...style }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "fd-vlist fd-scrollbar", style: { height: "100%", overflow: "auto" }, children: emptyState }) });
2690
2706
  }
2691
2707
  const rows = [];
2692
2708
  for (let i = win.startIndex; i <= win.endIndex; i++) {
2693
2709
  const item = items[i];
2694
2710
  if (item === void 0) continue;
2711
+ const key = keyOf(item);
2695
2712
  rows.push(
2696
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { style: { height: itemHeight, boxSizing: "border-box" }, children: renderItem(item, i) }, keyOf(item))
2713
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
2714
+ "div",
2715
+ {
2716
+ className: "fd-vlist-row",
2717
+ style: { height: itemHeight, boxSizing: "border-box", transform: "translateY(" + i * itemHeight + "px)" },
2718
+ children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: newKeys.has(key) ? "fd-vlist-row-content fd-vlist-row-enter" : "fd-vlist-row-content", children: renderItem(item, i) })
2719
+ },
2720
+ key
2721
+ )
2697
2722
  );
2698
2723
  }
2699
- return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
2700
- "div",
2701
- {
2702
- className: ["fd-vlist", className].filter(Boolean).join(" "),
2703
- style: { height, overflowY: "auto", overflowX: "hidden", position: "relative", ...style },
2704
- onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
2705
- children: [
2706
- /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { style: { height: win.totalHeight, position: "relative" }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { style: { position: "absolute", top: win.offsetY, left: 0, right: 0 }, children: rows }) }),
2707
- loading ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { style: { position: "sticky", bottom: 0, left: 0, right: 0, display: "grid", placeItems: "center", padding: "8px 0", background: "var(--surface)" }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "fd-spinner", "aria-hidden": "true" }) }) : null
2708
- ]
2709
- }
2710
- );
2724
+ return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: ["fd-vlist-wrap", className].filter(Boolean).join(" "), style: { height, position: "relative", ...style }, children: [
2725
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
2726
+ "div",
2727
+ {
2728
+ className: "fd-vlist fd-scrollbar",
2729
+ style: { height: "100%", overflowY: "auto", overflowX: "hidden", position: "relative" },
2730
+ onScroll: (e) => setScrollTop(e.currentTarget.scrollTop),
2731
+ children: [
2732
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { style: { height: win.totalHeight, position: "relative" }, children: rows }),
2733
+ loading ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { style: { position: "sticky", bottom: 0, left: 0, right: 0, display: "grid", placeItems: "center", padding: "8px 0", background: "var(--surface)" }, children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "fd-spinner", "aria-hidden": "true" }) }) : null
2734
+ ]
2735
+ }
2736
+ ),
2737
+ refreshing ? /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("div", { className: "fd-vlist-refresh-scrim", children: /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)("div", { className: "fd-vlist-refresh-card", children: [
2738
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "fd-spinner", "aria-hidden": "true" }),
2739
+ /* @__PURE__ */ (0, import_jsx_runtime38.jsx)("span", { className: "fd-body-sm", children: refreshingLabel })
2740
+ ] }) }) : null
2741
+ ] });
2711
2742
  }
2712
2743
 
2713
2744
  // src/components/data/EntityRow.tsx
2714
2745
  var import_jsx_runtime39 = require("react/jsx-runtime");
2715
- function EntityRow({ title, meta, action, draggable = false, onDragStart, onDragEnd, style, className = "" }) {
2746
+ function EntityRow({ title, meta, action, draggable = false, onPointerDown, style, className = "" }) {
2716
2747
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)(
2717
2748
  "div",
2718
2749
  {
2719
- draggable,
2720
- onDragStart,
2721
- onDragEnd,
2750
+ onPointerDown,
2722
2751
  className: ["fd-erow", className].filter(Boolean).join(" "),
2723
2752
  style: {
2724
2753
  display: "flex",
2725
2754
  alignItems: "center",
2726
- gap: 8,
2755
+ gap: 10,
2727
2756
  height: "100%",
2728
- padding: "0 8px",
2729
- borderRadius: 6,
2757
+ padding: "0 14px",
2730
2758
  cursor: draggable ? "grab" : void 0,
2759
+ // Own the gesture on touch too — without this, a touch-drag on a draggable row
2760
+ // starts the page scrolling instead of (or as well as) the pointer drag.
2761
+ touchAction: draggable ? "none" : void 0,
2731
2762
  ...style
2732
2763
  },
2733
2764
  children: [
2734
- draggable ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("i", { className: "ph ph-dots-six-vertical", "aria-hidden": "true", style: { fontSize: 13, flex: "none", color: "var(--text-muted)" } }) : null,
2735
- /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 1 }, children: [
2765
+ draggable ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("i", { className: "ph ph-dots-six-vertical", "aria-hidden": "true", style: { fontSize: 14, flex: "none", color: "var(--text-muted)" } }) : null,
2766
+ /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("span", { style: { flex: 1, minWidth: 0, display: "flex", flexDirection: "column", gap: 2 }, children: [
2736
2767
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600, color: "var(--text)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: title }),
2737
2768
  meta ? /* @__PURE__ */ (0, import_jsx_runtime39.jsx)("span", { style: { fontSize: "var(--overline-size)", color: "var(--text-muted)", whiteSpace: "nowrap", overflow: "hidden", textOverflow: "ellipsis" }, children: meta }) : null
2738
2769
  ] }),
@@ -2753,6 +2784,7 @@ function EntityRow({ title, meta, action, draggable = false, onDragStart, onDrag
2753
2784
 
2754
2785
  // src/components/data/TransferList.tsx
2755
2786
  var React16 = __toESM(require("react"), 1);
2787
+ var import_react_dom4 = require("react-dom");
2756
2788
 
2757
2789
  // src/components/forms/Input.tsx
2758
2790
  var import_jsx_runtime40 = require("react/jsx-runtime");
@@ -2849,6 +2881,7 @@ function SearchField({
2849
2881
  // src/components/data/TransferList.tsx
2850
2882
  var import_jsx_runtime42 = require("react/jsx-runtime");
2851
2883
  var emptyHasMore = {};
2884
+ var DRAG_THRESHOLD_PX = 5;
2852
2885
  function TransferList({
2853
2886
  left,
2854
2887
  right,
@@ -2860,97 +2893,199 @@ function TransferList({
2860
2893
  listHeight = 440,
2861
2894
  className = ""
2862
2895
  }) {
2863
- const [dragging, setDragging] = React16.useState(null);
2896
+ const [drag, setDrag] = React16.useState(null);
2864
2897
  const [dragOverSide, setDragOverSide] = React16.useState(null);
2865
- const startDrag = (e, item, from) => {
2866
- const key = keyOf(item);
2867
- setDragging({ key, from });
2868
- e.dataTransfer.effectAllowed = "move";
2869
- e.dataTransfer.setData("text/plain", String(key));
2870
- };
2871
- const endDrag = () => {
2872
- setDragging(null);
2873
- setDragOverSide(null);
2874
- };
2875
- const findItem = (side, key) => (side === "left" ? left.items : right.items).find((it) => keyOf(it) === key);
2876
- const dropOnSide = (e, side) => {
2898
+ const dragRef = React16.useRef(null);
2899
+ const armedRef = React16.useRef(null);
2900
+ const sideRefs = React16.useRef({ left: null, right: null });
2901
+ const hitTestSide = React16.useCallback((x, y) => {
2902
+ for (const side of ["left", "right"]) {
2903
+ const el = sideRefs.current[side];
2904
+ if (!el) continue;
2905
+ const r = el.getBoundingClientRect();
2906
+ if (x >= r.left && x <= r.right && y >= r.top && y <= r.bottom) return side;
2907
+ }
2908
+ return null;
2909
+ }, []);
2910
+ const findItem = React16.useCallback(
2911
+ (side, key) => (side === "left" ? left.items : right.items).find((it) => keyOf(it) === key),
2912
+ [left.items, right.items, keyOf]
2913
+ );
2914
+ const beginDrag = (e, item, from) => {
2915
+ if (e.button !== 0) return;
2916
+ if (e.target.closest && e.target.closest("button")) return;
2877
2917
  e.preventDefault();
2878
- setDragOverSide(null);
2879
- if (!dragging || dragging.from === side) return;
2880
- const item = findItem(dragging.from, dragging.key);
2881
- if (item !== void 0) onMove(item, dragging.from, side);
2882
- setDragging(null);
2918
+ const rect = e.currentTarget.getBoundingClientRect();
2919
+ armedRef.current = {
2920
+ item,
2921
+ from,
2922
+ key: keyOf(item),
2923
+ pointerId: e.pointerId,
2924
+ startX: e.clientX,
2925
+ startY: e.clientY,
2926
+ offsetX: e.clientX - rect.left,
2927
+ offsetY: e.clientY - rect.top,
2928
+ width: rect.width,
2929
+ height: rect.height
2930
+ };
2883
2931
  };
2884
- const renderSide = (side, cfg, opposite) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2885
- "div",
2886
- {
2887
- style: {
2888
- flex: 1,
2889
- minWidth: 0,
2890
- display: "flex",
2891
- flexDirection: "column",
2892
- gap: 8,
2893
- padding: 10,
2894
- borderRadius: 10,
2895
- border: "1px solid " + (dragOverSide === side ? "var(--brand)" : "var(--border)"),
2896
- background: "var(--surface)"
2897
- },
2898
- onDragOver: (e) => {
2899
- if (!dragging || dragging.from === side) return;
2900
- e.preventDefault();
2901
- e.dataTransfer.dropEffect = "move";
2902
- if (dragOverSide !== side) setDragOverSide(side);
2903
- },
2904
- onDragLeave: (e) => {
2905
- if (e.currentTarget.contains(e.relatedTarget)) return;
2906
- setDragOverSide((s) => s === side ? null : s);
2907
- },
2908
- onDrop: (e) => dropOnSide(e, side),
2909
- children: [
2910
- cfg.label ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "fd-overline fd-muted", children: [
2911
- cfg.label,
2912
- cfg.total != null ? " (" + cfg.total.toLocaleString() + ")" : ""
2913
- ] }) : null,
2914
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "fd-row", style: { gap: 8 }, children: [
2915
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SearchField, { value: cfg.search, onChange: cfg.onSearchChange, placeholder: "Search", "aria-label": (cfg.label || side) + " search", style: { flex: 1 } }),
2916
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SortMenu, { fields: cfg.sortFields, sort: cfg.sort, onSort: cfg.onSort, align: side === "left" ? "left" : "right" })
2917
- ] }),
2918
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2919
- VirtualList,
2920
- {
2921
- items: cfg.items,
2922
- itemHeight,
2923
- height: listHeight,
2924
- keyOf,
2925
- loading: cfg.loading,
2926
- hasMore: cfg.hasMore || emptyHasMore,
2927
- onNeedMore: cfg.onNeedMore,
2928
- emptyState: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(EmptyState, { icon: "tray", title: "Nothing here" }),
2929
- renderItem: (item) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
2930
- EntityRow,
2931
- {
2932
- title: renderLabel(item),
2933
- meta: renderMeta ? renderMeta(item) : void 0,
2934
- draggable: true,
2935
- onDragStart: (e) => startDrag(e, item, side),
2936
- onDragEnd: endDrag,
2937
- style: { opacity: dragging && dragging.from === side && dragging.key === keyOf(item) ? 0.4 : 1 },
2938
- action: {
2939
- icon: side === "left" ? "plus" : "minus",
2940
- label: (side === "left" ? "Move to " : "Move from ") + (side === "left" ? right.label || "the other list" : left.label || "the other list"),
2941
- tone: side === "left" ? "add" : "remove",
2942
- onClick: () => onMove(item, side, opposite)
2932
+ React16.useEffect(() => {
2933
+ const onMovePointer = (e) => {
2934
+ const armed = armedRef.current;
2935
+ if (!armed || e.pointerId !== armed.pointerId) return;
2936
+ if (!dragRef.current) {
2937
+ const dx = e.clientX - armed.startX;
2938
+ const dy = e.clientY - armed.startY;
2939
+ if (Math.hypot(dx, dy) < DRAG_THRESHOLD_PX) return;
2940
+ const started = {
2941
+ item: armed.item,
2942
+ from: armed.from,
2943
+ key: armed.key,
2944
+ pointerId: armed.pointerId,
2945
+ offsetX: armed.offsetX,
2946
+ offsetY: armed.offsetY,
2947
+ width: armed.width,
2948
+ height: armed.height,
2949
+ x: e.clientX,
2950
+ y: e.clientY
2951
+ };
2952
+ dragRef.current = started;
2953
+ setDrag(started);
2954
+ } else {
2955
+ dragRef.current = { ...dragRef.current, x: e.clientX, y: e.clientY };
2956
+ setDrag(dragRef.current);
2957
+ }
2958
+ const hit = hitTestSide(e.clientX, e.clientY);
2959
+ setDragOverSide(hit && hit !== armed.from ? hit : null);
2960
+ };
2961
+ const endDrag = (e) => {
2962
+ const armed = armedRef.current;
2963
+ if (!armed || e.pointerId !== armed.pointerId) return;
2964
+ const wasDragging = dragRef.current;
2965
+ armedRef.current = null;
2966
+ dragRef.current = null;
2967
+ setDrag(null);
2968
+ setDragOverSide(null);
2969
+ if (!wasDragging) return;
2970
+ const hit = hitTestSide(e.clientX, e.clientY);
2971
+ if (hit && hit !== armed.from) {
2972
+ const item = findItem(armed.from, armed.key);
2973
+ if (item !== void 0) onMove(item, armed.from, hit);
2974
+ }
2975
+ };
2976
+ window.addEventListener("pointermove", onMovePointer);
2977
+ window.addEventListener("pointerup", endDrag);
2978
+ window.addEventListener("pointercancel", endDrag);
2979
+ return () => {
2980
+ window.removeEventListener("pointermove", onMovePointer);
2981
+ window.removeEventListener("pointerup", endDrag);
2982
+ window.removeEventListener("pointercancel", endDrag);
2983
+ };
2984
+ }, [hitTestSide, findItem, onMove]);
2985
+ React16.useEffect(() => {
2986
+ if (!drag || typeof document === "undefined") return;
2987
+ const prev = document.body.style.userSelect;
2988
+ document.body.style.userSelect = "none";
2989
+ return () => {
2990
+ document.body.style.userSelect = prev;
2991
+ };
2992
+ }, [drag]);
2993
+ const renderSide = (side, cfg, opposite) => {
2994
+ const showOverlay = drag != null && dragOverSide === side;
2995
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
2996
+ "div",
2997
+ {
2998
+ ref: (el) => {
2999
+ sideRefs.current[side] = el;
3000
+ },
3001
+ style: {
3002
+ flex: 1,
3003
+ minWidth: 0,
3004
+ display: "flex",
3005
+ flexDirection: "column",
3006
+ gap: 8,
3007
+ padding: 10,
3008
+ borderRadius: 10,
3009
+ border: "1px solid " + (dragOverSide === side ? "var(--brand)" : "var(--border)"),
3010
+ background: "var(--surface)",
3011
+ position: "relative"
3012
+ },
3013
+ children: [
3014
+ cfg.label ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "fd-overline fd-muted", children: [
3015
+ cfg.label,
3016
+ cfg.total != null ? " (" + cfg.total.toLocaleString() + ")" : ""
3017
+ ] }) : null,
3018
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "fd-row", style: { gap: 8 }, children: [
3019
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SearchField, { value: cfg.search, onChange: cfg.onSearchChange, placeholder: "Search", "aria-label": (cfg.label || side) + " search", style: { flex: 1 } }),
3020
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SortMenu, { fields: cfg.sortFields, sort: cfg.sort, onSort: cfg.onSort, align: side === "left" ? "left" : "right" })
3021
+ ] }),
3022
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
3023
+ VirtualList,
3024
+ {
3025
+ items: cfg.items,
3026
+ itemHeight,
3027
+ height: listHeight,
3028
+ keyOf,
3029
+ loading: cfg.loading,
3030
+ refreshing: cfg.refreshing,
3031
+ hasMore: cfg.hasMore || emptyHasMore,
3032
+ onNeedMore: cfg.onNeedMore,
3033
+ emptyState: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(EmptyState, { icon: "tray", title: "Nothing here" }),
3034
+ renderItem: (item) => {
3035
+ const isBeingDragged = drag != null && drag.from === side && drag.key === keyOf(item);
3036
+ if (isBeingDragged) {
3037
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "fd-erow-placeholder", "aria-hidden": "true" });
2943
3038
  }
3039
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
3040
+ EntityRow,
3041
+ {
3042
+ title: renderLabel(item),
3043
+ meta: renderMeta ? renderMeta(item) : void 0,
3044
+ draggable: true,
3045
+ onPointerDown: (e) => beginDrag(e, item, side),
3046
+ action: {
3047
+ icon: side === "left" ? "plus" : "minus",
3048
+ label: (side === "left" ? "Move to " : "Move from ") + (side === "left" ? right.label || "the other list" : left.label || "the other list"),
3049
+ tone: side === "left" ? "add" : "remove",
3050
+ onClick: () => onMove(item, side, opposite)
3051
+ }
3052
+ }
3053
+ );
2944
3054
  }
2945
- )
2946
- }
2947
- )
2948
- ]
2949
- }
2950
- );
3055
+ }
3056
+ ),
3057
+ showOverlay ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "fd-tlist-drop-overlay", "aria-hidden": "true", children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "fd-tlist-drop-card", children: [
3058
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("i", { className: "ph ph-tray-arrow-down", style: { fontSize: 18 }, "aria-hidden": "true" }),
3059
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "fd-body-sm", style: { fontWeight: 600 }, children: cfg.dropLabel || "Drop here" + (cfg.label ? " \u2014 " + cfg.label : "") })
3060
+ ] }) }) : null
3061
+ ]
3062
+ }
3063
+ );
3064
+ };
2951
3065
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: ["fd-tlist", className].filter(Boolean).join(" "), style: { display: "flex", gap: 16, alignItems: "flex-start" }, children: [
2952
3066
  renderSide("left", left, "right"),
2953
- renderSide("right", right, "left")
3067
+ renderSide("right", right, "left"),
3068
+ drag ? (0, import_react_dom4.createPortal)(
3069
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
3070
+ "div",
3071
+ {
3072
+ "aria-hidden": "true",
3073
+ className: "fd-erow-ghost",
3074
+ style: {
3075
+ position: "fixed",
3076
+ left: 0,
3077
+ top: 0,
3078
+ width: drag.width,
3079
+ height: drag.height,
3080
+ transform: `translate(${drag.x - drag.offsetX}px, ${drag.y - drag.offsetY}px) rotate(-3deg)`,
3081
+ pointerEvents: "none",
3082
+ zIndex: 1e3
3083
+ },
3084
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(EntityRow, { title: renderLabel(drag.item), meta: renderMeta ? renderMeta(drag.item) : void 0, draggable: true, className: "fd-erow-dragging" })
3085
+ }
3086
+ ),
3087
+ document.body
3088
+ ) : null
2954
3089
  ] });
2955
3090
  }
2956
3091
 
@@ -3160,7 +3295,7 @@ function NumberInput({
3160
3295
 
3161
3296
  // src/components/forms/Select.tsx
3162
3297
  var React19 = __toESM(require("react"), 1);
3163
- var import_react_dom4 = require("react-dom");
3298
+ var import_react_dom5 = require("react-dom");
3164
3299
  var import_jsx_runtime48 = require("react/jsx-runtime");
3165
3300
  var norm = (o) => typeof o === "string" ? { value: o, label: o } : o;
3166
3301
  function usePopPos(open, ref, estH, estW) {
@@ -3366,7 +3501,7 @@ function Select({
3366
3501
  ) : null,
3367
3502
  loading ? /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "fd-spinner", style: { color: "var(--text-muted)" }, "aria-label": "Loading options" }) : /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("span", { className: "fd-select-caret", onClick: toggle, children: /* @__PURE__ */ (0, import_jsx_runtime48.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true" }) })
3368
3503
  ] }),
3369
- open && pos ? (0, import_react_dom4.createPortal)(
3504
+ open && pos ? (0, import_react_dom5.createPortal)(
3370
3505
  /* @__PURE__ */ (0, import_jsx_runtime48.jsxs)(
3371
3506
  "div",
3372
3507
  {
@@ -3463,7 +3598,7 @@ function Select({
3463
3598
 
3464
3599
  // src/components/forms/DatePicker.tsx
3465
3600
  var React20 = __toESM(require("react"), 1);
3466
- var import_react_dom5 = require("react-dom");
3601
+ var import_react_dom6 = require("react-dom");
3467
3602
  var import_jsx_runtime49 = require("react/jsx-runtime");
3468
3603
  var MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
3469
3604
  var DOW = ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa"];
@@ -3692,7 +3827,7 @@ function DatePicker({ label, help, error, required = false, disabled = false, ra
3692
3827
  ),
3693
3828
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("span", { className: "fd-select-caret", children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true" }) })
3694
3829
  ] }),
3695
- open && pos ? (0, import_react_dom5.createPortal)(
3830
+ open && pos ? (0, import_react_dom6.createPortal)(
3696
3831
  /* @__PURE__ */ (0, import_jsx_runtime49.jsx)("div", { className: "fd-pop" + (pos.up ? " is-up" : ""), ref: popRef, style: popStyle2(pos, { width: "max-content", minWidth: 0, zIndex: 130 }), children: /* @__PURE__ */ (0, import_jsx_runtime49.jsx)(
3697
3832
  Calendar,
3698
3833
  {
@@ -3716,7 +3851,7 @@ function DatePicker({ label, help, error, required = false, disabled = false, ra
3716
3851
 
3717
3852
  // src/components/forms/TimePicker.tsx
3718
3853
  var React21 = __toESM(require("react"), 1);
3719
- var import_react_dom6 = require("react-dom");
3854
+ var import_react_dom7 = require("react-dom");
3720
3855
  var import_jsx_runtime50 = require("react/jsx-runtime");
3721
3856
  var pad = (n) => String(n).padStart(2, "0");
3722
3857
  function usePopPos3(open, ref, estH, estW) {
@@ -3895,7 +4030,7 @@ function TimePicker({ label, help, error, required = false, disabled = false, va
3895
4030
  ),
3896
4031
  /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("span", { className: "fd-select-caret", children: /* @__PURE__ */ (0, import_jsx_runtime50.jsx)("i", { className: "ph ph-caret-down", "aria-hidden": "true" }) })
3897
4032
  ] }),
3898
- open && pos ? (0, import_react_dom6.createPortal)(
4033
+ open && pos ? (0, import_react_dom7.createPortal)(
3899
4034
  /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "fd-pop" + (pos.up ? " is-up" : ""), ref: popRef, style: popStyle3(pos, { width: 262, minWidth: 0, zIndex: 130 }), children: [
3900
4035
  /* @__PURE__ */ (0, import_jsx_runtime50.jsx)(ClockFace, { value: value || "09:00", onChange: (v) => onChange && onChange({ target: { value: v } }) }),
3901
4036
  /* @__PURE__ */ (0, import_jsx_runtime50.jsxs)("div", { className: "fd-cal-foot", style: { margin: "0 14px 12px", paddingTop: 10 }, children: [
@@ -4357,7 +4492,7 @@ function FileTile({ file, onOpen, onRemove, maxHeight = 200, className = "" }) {
4357
4492
  function FileStrip({ files = [], size = 68, onOpen, onRemove, onRetry, className = "" }) {
4358
4493
  const list = files.filter(Boolean);
4359
4494
  if (!list.length) return null;
4360
- return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { className: ["fd-filestrip", className].filter(Boolean).join(" "), style: { "--fd-cell": size + "px" }, children: list.map((f) => /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(FileCell, { file: f, onOpen, onRemove, onRetry }, f.id || f.name)) });
4495
+ return /* @__PURE__ */ (0, import_jsx_runtime54.jsx)("div", { className: ["fd-filestrip", "fd-scrollbar", className].filter(Boolean).join(" "), style: { "--fd-cell": size + "px" }, children: list.map((f) => /* @__PURE__ */ (0, import_jsx_runtime54.jsx)(FileCell, { file: f, onOpen, onRemove, onRetry }, f.id || f.name)) });
4361
4496
  }
4362
4497
  var shortName = (name, keep = 5) => {
4363
4498
  const s = String(name || "file");
@@ -7355,7 +7490,7 @@ function RoadmapTimeline({ onOpenFlag, title = "Roadmap", lede }) {
7355
7490
 
7356
7491
  // src/components/platform/ComingSoon.tsx
7357
7492
  var React32 = __toESM(require("react"), 1);
7358
- var import_react_dom7 = require("react-dom");
7493
+ var import_react_dom8 = require("react-dom");
7359
7494
  var import_jsx_runtime60 = require("react/jsx-runtime");
7360
7495
  var BYPASS_STORE = "fd.soon.bypass.v1";
7361
7496
  function readBypassed() {
@@ -7596,7 +7731,7 @@ function InlineSoon({ label, detail, backend, eta, effort, onRoadmap, allowed, o
7596
7731
  children: /* @__PURE__ */ (0, import_jsx_runtime60.jsx)("i", { className: "ph ph-traffic-cone", "aria-hidden": "true" })
7597
7732
  }
7598
7733
  ),
7599
- open && pos ? (0, import_react_dom7.createPortal)(
7734
+ open && pos ? (0, import_react_dom8.createPortal)(
7600
7735
  /* @__PURE__ */ (0, import_jsx_runtime60.jsx)(
7601
7736
  "div",
7602
7737
  {