import { jsxs, jsx, Fragment } from "react/jsx-runtime";
import * as React from "react";
import { createPortal } from "react-dom";
import { Empty } from "../Empty/empty.js";
import { useReactTable, getExpandedRowModel, getSortedRowModel, getCoreRowModel, flexRender } from "@tanstack/react-table";
import { useVirtualizer } from "@tanstack/react-virtual";
import { TableScrollProvider } from "../Field/field-context.js";
import { cva } from "class-variance-authority";
import { ArrowUp, ArrowDown, ChevronDown, X, Filter, EyeOff, ArrowUpDown, GripVertical } from "lucide-react";
import { useSensors, useSensor, PointerSensor, KeyboardSensor, pointerWithin, rectIntersection, DndContext, MeasuringStrategy, DragOverlay, useDraggable, useDroppable, useDndContext } from "@dnd-kit/core";
import { cn } from "../../lib/utils.js";
import { ICON_SIZE, ItemInlineActionButton } from "../../patterns/element-anatomy/item-anatomy.js";
import { reconstructFullRowGhost, isReorderNoop, snapToCursorModifier, dragSourceStyle, dropIndicatorColumn, dragActiveCursor, dropIndicatorRow } from "../../lib/drag-visual.js";
import { fieldDisplayTextClass, nakedCellEditableDisplayHover } from "../Field/field-wrapper.js";
import { Tooltip, TooltipTrigger, TooltipContent } from "../Tooltip/tooltip.js";
import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from "../DropdownMenu/dropdown-menu.js";
import { columnTypeDefaults } from "./column-types.js";
import { resolveCellComponent } from "./cell-registry.js";
import { DataTableInteractionLayer } from "./data-table-interaction-layer.js";
import { Checkbox } from "../Checkbox/checkbox.js";
import { RadioGroupItem } from "../RadioGroup/radio-group.js";
import * as RadioGroupPrimitive from "@radix-ui/react-radio-group";
import { useControllable } from "../../hooks/use-controllable.js";
import { Button } from "../Button/button.js";
const dataTableVariants = cva("bg-surface rounded-md overflow-hidden", {
  variants: { bordered: { true: "border border-divider", false: "" } },
  defaultVariants: { bordered: true }
});
const cellEditId = (rowId, colId) => `${rowId}__${colId}`;
const SELECT_COL_ID = "__select__";
const cellPadding = { paddingBlock: "var(--table-cell-py)", paddingInline: "var(--table-cell-px)" };
const HEADER_BG = "bg-muted";
const MIN_COLUMN_WIDTH = 80;
function columnSizeStyle(col, opts) {
  const baseSize = col.getSize();
  const minSize = col.columnDef.minSize ?? (opts.resize ? MIN_COLUMN_WIDTH : baseSize);
  const maxSize = col.columnDef.maxSize;
  if (opts.isSystemCol) {
    return { width: baseSize, minWidth: baseSize, maxWidth: maxSize };
  }
  if (opts.resize) {
    return { width: baseSize, minWidth: minSize, maxWidth: maxSize };
  }
  return { flex: `1 1 ${baseSize}px`, minWidth: baseSize, maxWidth: maxSize };
}
const SYSTEM_COL_IDS = /* @__PURE__ */ new Set([SELECT_COL_ID, "__drag__", "__actions__"]);
const isSystemColumn = (colId) => SYSTEM_COL_IDS.has(colId);
let sharedResizeObserver = null;
const roCallbacks = /* @__PURE__ */ new WeakMap();
function getSharedRO() {
  if (sharedResizeObserver) return sharedResizeObserver;
  sharedResizeObserver = new ResizeObserver((entries) => {
    entries.forEach((entry) => {
      const cb = roCallbacks.get(entry.target);
      if (cb) cb(entry);
    });
  });
  return sharedResizeObserver;
}
function observeShared(el, cb) {
  const obs = getSharedRO();
  roCallbacks.set(el, cb);
  obs.observe(el);
  return () => {
    roCallbacks.delete(el);
    obs.unobserve(el);
  };
}
function TruncateCell({ children, className }) {
  const ref = React.useRef(null);
  const [isTruncated, setIsTruncated] = React.useState(false);
  React.useEffect(() => {
    const el = ref.current;
    if (!el) return;
    const check = () => setIsTruncated(el.scrollWidth > el.clientWidth);
    check();
    return observeShared(el, check);
  }, []);
  const span = /* @__PURE__ */ jsx("span", { ref, className: cn("truncate min-w-0", className), children });
  if (!isTruncated) return span;
  return /* @__PURE__ */ jsxs(Tooltip, { children: [
    /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: span }),
    /* @__PURE__ */ jsx(TooltipContent, { children })
  ] });
}
const SortableRowCtx = React.createContext(null);
function SortableRowProvider(props) {
  return props.role === "primary" ? /* @__PURE__ */ jsx(SourceRowProvider, { ...props }) : /* @__PURE__ */ jsx(MirrorRowProvider, { ...props });
}
function SourceRowProvider({
  id,
  disabled,
  role,
  invalidDrop,
  children
}) {
  const draggable = useDraggable({ id, disabled, data: { type: "row" } });
  const droppable = useDroppable({ id, disabled, data: { type: "row" } });
  const setRefs = React.useCallback((el) => {
    draggable.setNodeRef(el);
    droppable.setNodeRef(el);
  }, [draggable.setNodeRef, droppable.setNodeRef]);
  const isDragging = draggable.isDragging;
  const style = { ...dragSourceStyle(isDragging) };
  const handleAttrs = draggable.attributes;
  const ctxValue = {
    setNodeRef: setRefs,
    role,
    style,
    attributes: {},
    isDragging,
    // row 不接 listeners(button-only),baseRowDiv `{...(extra?.listeners ?? {})}` 自動 noop
    rowListeners: void 0,
    rowAttributes: {},
    // Button activator + listener:portal'd RowDragHandle Button 走這條 ctx,
    // user 從任何 region 看見 button → 點下啟動 drag,activator rect = button DOM(24×24),
    // ghost 起點 = button 位置(table outer 左 12px),cursor 在 ghost 左前段(自然視覺)。
    handleSetActivatorNodeRef: draggable.setActivatorNodeRef,
    handleListeners: draggable.listeners,
    handleAttributes: handleAttrs,
    invalidDrop
  };
  return /* @__PURE__ */ jsx(SortableRowCtx.Provider, { value: ctxValue, children: children(ctxValue) });
}
function MirrorRowProvider({
  id,
  disabled,
  role,
  invalidDrop,
  children
}) {
  var _a;
  const droppable = useDroppable({ id, disabled, data: { type: "row" } });
  const dndCtx = useDndContext();
  const isDragging = ((_a = dndCtx.active) == null ? void 0 : _a.id) === id;
  const ctxValue = {
    setNodeRef: droppable.setNodeRef,
    role,
    style: { ...dragSourceStyle(isDragging) },
    attributes: {},
    isDragging,
    rowListeners: void 0,
    rowAttributes: {},
    handleSetActivatorNodeRef: void 0,
    handleListeners: void 0,
    handleAttributes: {},
    invalidDrop
  };
  return /* @__PURE__ */ jsx(SortableRowCtx.Provider, { value: ctxValue, children: children(ctxValue) });
}
function DraggableHeaderCell({
  id,
  disabled,
  isLocked,
  dropIndicatorSide,
  children
}) {
  const draggable = useDraggable({ id, disabled, data: { type: "column", columnId: id } });
  const droppable = useDroppable({ id, disabled, data: { type: "column", columnId: id } });
  const setRefs = React.useCallback((el) => {
    draggable.setNodeRef(el);
    droppable.setNodeRef(el);
  }, [draggable.setNodeRef, droppable.setNodeRef]);
  const isDragging = draggable.isDragging;
  const dragStyle = {
    ...dragSourceStyle(isDragging)
  };
  const childProps = children.props;
  const { role: _draggableRole, ...draggableAttrs } = draggable.attributes;
  const indicatorClass = dropIndicatorSide === "before" ? dropIndicatorColumn.pseudoBefore : dropIndicatorSide === "after" ? dropIndicatorColumn.pseudoAfter : "";
  return React.cloneElement(children, {
    ref: setRefs,
    style: { ...childProps.style ?? {}, ...dragStyle },
    "data-column-id": id,
    "data-column-locked": isLocked || void 0,
    ...disabled ? {} : { ...draggableAttrs, ...draggable.listeners },
    // 2026-05-06 v14.9 cursor canonical(對齊 Notion / Jira):
    // **idle hover NOT 顯 cursor-grab** — header click 觸發 sort,grab cursor 會誤導 user 以為「點 = 拖」;
    // **drag activation 後**(isDragging=true,過 8px activationConstraint)才顯 cursor-grabbing。
    // user 點 = sort / 長壓 = drag,兩語意分開不互踩。
    className: cn(childProps.className, isDragging && dragActiveCursor, indicatorClass)
  });
}
function RowDragHandle({ disabled, anyDragActive }) {
  const ctx = React.useContext(SortableRowCtx);
  const [rowEl, setRowEl] = React.useState(null);
  const [portalTarget, setPortalTarget] = React.useState(null);
  const [pos, setPos] = React.useState(null);
  const [buttonHovered, setButtonHovered] = React.useState(false);
  const anchorRef = React.useCallback((node) => {
    setRowEl((node == null ? void 0 : node.parentElement) ?? null);
  }, []);
  React.useLayoutEffect(() => {
    if (!rowEl || !ctx || ctx.role !== "primary") return;
    const tableEl = rowEl.closest("[data-data-table-outer]");
    setPortalTarget((tableEl == null ? void 0 : tableEl.parentElement) ?? null);
    const update = () => {
      if (!tableEl) return;
      const rRect = rowEl.getBoundingClientRect();
      const tRect = tableEl.getBoundingClientRect();
      const rowHovered = rowEl.hasAttribute("data-hovered");
      setPos({
        top: rRect.top + rRect.height / 2,
        left: tRect.left,
        // table outer 左 border line position(viewport coords)
        rowHovered
      });
    };
    update();
    const observer = new MutationObserver(update);
    observer.observe(rowEl, { attributes: true, attributeFilter: ["data-hovered"] });
    let scrollRafId = 0;
    const onScroll = () => {
      if (scrollRafId) cancelAnimationFrame(scrollRafId);
      scrollRafId = requestAnimationFrame(() => {
        scrollRafId = 0;
        update();
      });
    };
    window.addEventListener("scroll", onScroll, true);
    window.addEventListener("resize", onScroll);
    return () => {
      observer.disconnect();
      if (scrollRafId) cancelAnimationFrame(scrollRafId);
      window.removeEventListener("scroll", onScroll, true);
      window.removeEventListener("resize", onScroll);
    };
  }, [rowEl, ctx]);
  const anchor = /* @__PURE__ */ jsx(
    "span",
    {
      ref: anchorRef,
      "aria-hidden": true,
      style: { position: "absolute", top: 0, left: 0, width: 0, height: 0, pointerEvents: "none" }
    }
  );
  if (!ctx || ctx.role !== "primary" || !pos) return anchor;
  const canDrag = !disabled;
  const showInvalid = !!ctx.invalidDrop && !!ctx.isDragging;
  const visible = ctx.isDragging || !anyDragActive && (pos.rowHovered || buttonHovered);
  const handle = /* @__PURE__ */ jsx(
    Button,
    {
      ref: canDrag ? ctx.handleSetActivatorNodeRef : void 0,
      variant: "tertiary",
      iconOnly: true,
      size: "xs",
      startIcon: GripVertical,
      "aria-label": canDrag ? "拖曳重排此列" : "排序中無法拖曳",
      "aria-disabled": !canDrag || void 0,
      tabIndex: canDrag ? 0 : -1,
      onMouseEnter: () => setButtonHovered(true),
      onMouseLeave: () => setButtonHovered(false),
      style: {
        position: "fixed",
        top: pos.top,
        left: pos.left,
        transform: "translate(-50%, -50%)",
        zIndex: 50,
        // 2026-05-12 fix v2(user 抓「drag column sort 啟用時 button 不是 disable 視覺」):
        // 前 Round 4.5 加 `aria-disabled:opacity-[var(--opacity-disabled)]` 在 Button cva
        // 沒生效 — 因為 inline style `opacity` 永遠 win over Tailwind class。Fix:把 disabled
        // state opacity 也 compute 進 inline style。priority order:invisible 0 → drag 0.5 →
        // canDrag=false(sort active)disabled visual var(--opacity-disabled) 0.45 → idle 1。
        opacity: visible ? ctx.isDragging ? 0.5 : canDrag ? 1 : "var(--opacity-disabled)" : 0,
        pointerEvents: visible ? "auto" : "none",
        transition: "opacity 150ms ease"
      },
      className: cn(
        // 2026-05-12 debug fix(user 抓「hover 還是透明」)— Round 4.5 我未授權加
        // `border / shadow / hover:bg-neutral-hover` = over-design + hover override 讓
        // drag button hover bg 變 neutral-hover 跟 row hover bg 同色 → 視覺融入 row = 透明。
        // 撤回:**只保 bg-surface-raised(idle + hover + 所有 state 都同 bg)**,
        // border / shadow / hover override 全 retire(user verbatim「我有叫你加 elevation 嗎」)。
        // 對所有 state(idle / hover / aria-disabled / data-state)套同 bg-surface-raised — 跟
        // row 任何 state 視覺都有 token-level 對比(在 token 差異存在的 mode;light mode --surface-raised
        // 等於 --surface 是 design token semantic,非本 fix scope)。
        "bg-surface-raised hover:bg-surface-raised aria-disabled:bg-surface-raised",
        canDrag && !showInvalid && "cursor-grab",
        canDrag && showInvalid && "cursor-not-allowed !text-error !border-error",
        // drag 進行中 source button cursor(opacity 0.5 via style;aria-disabled visual 由 Button cva 接管)
        ctx.isDragging && "cursor-grabbing"
      ),
      ...canDrag ? ctx.handleListeners ?? {} : {},
      ...canDrag ? ctx.handleAttributes ?? {} : {}
    }
  );
  const wrapped = disabled ? /* @__PURE__ */ jsxs(Tooltip, { children: [
    /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: handle }),
    /* @__PURE__ */ jsx(TooltipContent, { children: "排序中無法拖曳,清除排序後可重排" })
  ] }) : handle;
  return /* @__PURE__ */ jsxs(Fragment, { children: [
    anchor,
    portalTarget && createPortal(wrapped, portalTarget)
  ] });
}
function DataTableInner({
  columns,
  data,
  size = "md",
  autoRowHeight = false,
  height = "400px",
  overscan = 5,
  emptyState,
  enableHover = true,
  bordered,
  estimateRowHeight,
  tableOptions,
  rowActions,
  cellErrors,
  pinnedLeftColumns,
  pinnedRightColumns,
  inlineEdit = false,
  selection: selectionProp,
  defaultSelection,
  onSelectionChange,
  selectable = false,
  isRowSelectable,
  getRowId,
  getRowAriaLabel,
  preserveSelectionOnFilter = false,
  columnVisibility: columnVisibilityProp,
  defaultColumnVisibility,
  onColumnVisibilityChange,
  enableMultiSort = true,
  onColumnFilterTrigger,
  onCellCommit,
  enableRowDrag = false,
  onRowReorder,
  enableColumnResize = false,
  onColumnResize,
  enableColumnReorder = false,
  onColumnReorder,
  experimentalSpreadsheetOverlay = false,
  experimentalActiveEditorController = false,
  spreadsheetMode = false,
  className,
  ...props
}, ref) {
  var _a, _b, _c;
  const [editingCellId, setEditingCellId] = React.useState(null);
  const [editingDraft, setEditingDraft] = React.useState(void 0);
  const exitEdit = React.useCallback(() => {
    setEditingCellId(null);
    setEditingDraft(void 0);
  }, []);
  const [selectedCellId, setSelectedCellId] = React.useState(null);
  const [rangeAnchor, setRangeAnchor] = React.useState(null);
  const [rangeFocus, setRangeFocus] = React.useState(null);
  React.useEffect(() => {
    if (!spreadsheetMode) return;
    if (selectedCellId == null && rangeAnchor == null) return;
    const handler = (e) => {
      const target = e.target;
      if (!target) return;
      if (!target.closest("[data-data-table-outer]")) {
        setSelectedCellId(null);
        setRangeAnchor(null);
        setRangeFocus(null);
      }
    };
    document.addEventListener("pointerdown", handler, true);
    return () => document.removeEventListener("pointerdown", handler, true);
  }, [spreadsheetMode, selectedCellId, rangeAnchor]);
  const commitCell = React.useCallback(
    (rowId, colId, next) => {
      onCellCommit == null ? void 0 : onCellCommit(rowId, colId, next);
      setEditingCellId(null);
      setEditingDraft(void 0);
    },
    [onCellCommit]
  );
  const isCellEditable = React.useCallback(
    // any-allow: free-form consumer meta — same rationale as L143 renderTypedValue
    (meta, row) => {
      const e = meta == null ? void 0 : meta.editable;
      if (typeof e === "function") return e(row) === true;
      return e === true;
    },
    []
  );
  const isCellDisabled = React.useCallback(
    // any-allow: free-form consumer meta — same rationale as L143 renderTypedValue
    (meta, row) => {
      const d = meta == null ? void 0 : meta.disabled;
      if (typeof d === "function") return d(row) === true;
      return d === true;
    },
    []
  );
  const canEditCell = React.useCallback(
    (meta, row) => isCellEditable(meta, row) && !isCellDisabled(meta, row),
    [isCellEditable, isCellDisabled]
  );
  const [sorting, setSorting] = React.useState(((_a = tableOptions == null ? void 0 : tableOptions.state) == null ? void 0 : _a.sorting) ?? []);
  const [columnVisibility, setColumnVisibility] = useControllable({
    value: columnVisibilityProp,
    defaultValue: defaultColumnVisibility ?? {},
    onChange: onColumnVisibilityChange
  });
  const enabled = selectable !== false;
  const mode = selectable === "single" ? "single" : "multi";
  const [selection, setSelection] = useControllable({
    value: selectionProp,
    defaultValue: defaultSelection ?? [],
    onChange: onSelectionChange
  });
  const anchorRowIdRef = React.useRef(null);
  const dsProcessedColumns = React.useMemo(() => {
    return columns.map((c) => {
      const meta = c.meta;
      if (!meta) return c;
      const cAny = c;
      const updates = {};
      if (meta.width !== void 0 && cAny.size === void 0) updates.size = meta.width;
      if (meta.minWidth !== void 0 && cAny.minSize === void 0) updates.minSize = meta.minWidth;
      if (meta.maxWidth !== void 0 && cAny.maxSize === void 0) updates.maxSize = meta.maxWidth;
      return Object.keys(updates).length > 0 ? { ...c, ...updates } : c;
    });
  }, [columns]);
  const columnsWithSelection = React.useMemo(() => {
    if (!enabled) return dsProcessedColumns;
    const selectCol = {
      id: SELECT_COL_ID,
      size: 40,
      enableSorting: false,
      enableResizing: false,
      enableHiding: false,
      // selection col 不能藏(L3 column visibility)
      header: "Select",
      // header cell 由下方自訂 render 取代
      cell: () => null
      // body cell 由下方自訂 render 取代
    };
    return [selectCol, ...dsProcessedColumns];
  }, [dsProcessedColumns, enabled]);
  const effectivePinnedLeft = React.useMemo(() => {
    const list = pinnedLeftColumns ?? [];
    const out = [...list];
    if (enabled && !out.includes(SELECT_COL_ID)) out.unshift(SELECT_COL_ID);
    return out;
  }, [pinnedLeftColumns, enabled]);
  const userColumnOrder = (_b = tableOptions == null ? void 0 : tableOptions.state) == null ? void 0 : _b.columnOrder;
  const effectiveColumnOrder = React.useMemo(() => {
    if (!userColumnOrder) return userColumnOrder;
    if (!enabled) return userColumnOrder;
    const out = [...userColumnOrder];
    if (enabled && !out.includes(SELECT_COL_ID)) out.unshift(SELECT_COL_ID);
    return out;
  }, [userColumnOrder, enabled]);
  const table = useReactTable({
    ...tableOptions,
    data,
    columns: columnsWithSelection,
    state: {
      sorting,
      columnVisibility,
      ...tableOptions == null ? void 0 : tableOptions.state,
      // columnPinning + columnOrder 在 user state 後 override,確保 __select__ 永遠左
      columnPinning: { left: effectivePinnedLeft, right: pinnedRightColumns ?? [] },
      ...effectiveColumnOrder ? { columnOrder: effectiveColumnOrder } : {}
    },
    enableMultiSort,
    // **#1 fix(2026-05-04)**:chain user `tableOptions.onSortingChange`(spread 在前被 override = 之前 bug)
    // 同 onColumnVisibilityChange:both internal setState + forward 給 user external state
    onSortingChange: (updater) => {
      var _a2;
      setSorting(updater);
      (_a2 = tableOptions == null ? void 0 : tableOptions.onSortingChange) == null ? void 0 : _a2.call(tableOptions, updater);
    },
    onColumnVisibilityChange: (updater) => {
      var _a2;
      const next = typeof updater === "function" ? updater(columnVisibility) : updater;
      setColumnVisibility(next);
      (_a2 = tableOptions == null ? void 0 : tableOptions.onColumnVisibilityChange) == null ? void 0 : _a2.call(tableOptions, updater);
    },
    getCoreRowModel: getCoreRowModel(),
    getSortedRowModel: getSortedRowModel(),
    // L4 nested rows:啟用 expanded row model(consumer 透過 tableOptions.getSubRows + state.expanded forward)
    getExpandedRowModel: getExpandedRowModel(),
    getRowId,
    // 2026-05-06 v14 column resize:`onChange` mode → drag 中 column 即時跟動 cursor(world-class
    // canonical:TanStack docs / AG Grid / Excel / Google Sheets 全部 live resize)。前 v13.2
    // 用 `onEnd` 拖完才 jump,user 報「感覺超頓像 bug」。tanstack 內部管 columnSizing state
    // (uncontrolled);`columnSizingState` 變動透過 useEffect 觀測 + 呼 callback。
    //
    // 前 v11 用 `onColumnSizingChange` 接管 updater 但忘了 setColumnSizing,導致 state 永遠不變動 →
    // column.getSize() 永遠回初始值 → drag visual 完全沒效果(user 報 "drag 沒反應")。本 v13.2 改回
    // tanstack uncontrolled state(預設行為)+ useEffect 觀測 columnSizing 變動 fire callback。
    enableColumnResizing: enableColumnResize,
    columnResizeMode: "onChange"
  });
  const columnSizingState = table.getState().columnSizing;
  const prevColumnSizingRef = React.useRef(columnSizingState);
  React.useEffect(() => {
    if (!onColumnResize) return;
    const prev = prevColumnSizingRef.current;
    Object.keys(columnSizingState).forEach((id) => {
      if (columnSizingState[id] !== prev[id]) {
        onColumnResize(id, columnSizingState[id]);
      }
    });
    prevColumnSizingRef.current = columnSizingState;
  }, [columnSizingState, onColumnResize]);
  const { rows } = table.getRowModel();
  const isEmpty = rows.length === 0;
  const hasHeightConstraint = height !== "auto";
  const isFillHeight = hasHeightConstraint && /^(100%|100vh|fill)$/.test(height);
  const VIRTUAL_THRESHOLD = 30;
  const useVirtual = hasHeightConstraint && !isEmpty && rows.length > VIRTUAL_THRESHOLD;
  const hasRowActions = !!rowActions;
  const tableRef = React.useRef(null);
  const bodyRef = React.useRef(null);
  const centerHeaderRef = React.useRef(null);
  const centerBodyRef = React.useRef(null);
  const leftHeaderRef = React.useRef(null);
  const rightHeaderRef = React.useRef(null);
  const [leftWidth, setLeftWidth] = React.useState(0);
  const [rightWidth, setRightWidth] = React.useState(0);
  const ESTIMATE_BY_SIZE = { sm: 32, md: 40, lg: 48 };
  const resolvedEstimate = estimateRowHeight ?? ESTIMATE_BY_SIZE[size] ?? 40;
  const effectiveOverscan = enableRowDrag ? Math.max(overscan, 5) : overscan;
  const activeDragIdRef = React.useRef(null);
  const virtualizer = useVirtualizer({
    count: useVirtual ? rows.length : 0,
    // V scroll 現在在 centerBodyRef(不是外層 bodyRef)
    getScrollElement: () => centerBodyRef.current,
    estimateSize: () => resolvedEstimate,
    overscan: effectiveOverscan,
    enabled: useVirtual,
    // 2026-05-14 P3 perf tune(per codex+Layer A 共識,user 拍板「全部做完」+
    // CPU-throttle-reproducible verify infra):150ms → 250ms 減少 scroll
    // start/end flip 次數 → TableScrollContext 重 cascade visible rich cell
    // tree 機會降低。對齊 TanStack Virtual `isScrollingResetDelay` API。
    isScrollingResetDelay: 250
  });
  const [bodyMaxHeight, setBodyMaxHeight] = React.useState(null);
  React.useLayoutEffect(() => {
    var _a2;
    if (!isFillHeight) {
      setBodyMaxHeight(null);
      return;
    }
    let rafId = null;
    let stableTimer = null;
    let lastValue = null;
    let pendingValue = null;
    const compute = () => {
      if (!tableRef.current) return;
      const parentEl = tableRef.current.parentElement;
      const slotH = (parentEl == null ? void 0 : parentEl.getBoundingClientRect().height) ?? tableRef.current.getBoundingClientRect().height;
      const headerEl = tableRef.current.firstElementChild;
      const headerH = (headerEl == null ? void 0 : headerEl.getBoundingClientRect().height) ?? 0;
      const next = Math.max(0, slotH - headerH);
      if (lastValue != null && Math.abs(next - lastValue) < 4) return;
      lastValue = next;
      pendingValue = next;
      if (stableTimer != null) clearTimeout(stableTimer);
      stableTimer = setTimeout(() => {
        if (pendingValue != null) setBodyMaxHeight(pendingValue);
        stableTimer = null;
      }, 100);
    };
    const scheduleCompute = () => {
      if (rafId != null) return;
      rafId = requestAnimationFrame(() => {
        rafId = null;
        compute();
      });
    };
    compute();
    const obs = new ResizeObserver(scheduleCompute);
    if ((_a2 = tableRef.current) == null ? void 0 : _a2.parentElement) obs.observe(tableRef.current.parentElement);
    return () => {
      obs.disconnect();
      if (rafId != null) cancelAnimationFrame(rafId);
      if (stableTimer != null) clearTimeout(stableTimer);
    };
  }, [isFillHeight]);
  const leftBodyRef = React.useRef(null);
  const rightBodyRef = React.useRef(null);
  const onCenterBodyScroll = React.useCallback(() => {
    const cb = centerBodyRef.current;
    if (!cb) return;
    if (centerHeaderRef.current) centerHeaderRef.current.scrollLeft = cb.scrollLeft;
    if (leftBodyRef.current) leftBodyRef.current.scrollTop = cb.scrollTop;
    if (rightBodyRef.current) rightBodyRef.current.scrollTop = cb.scrollTop;
  }, []);
  const rangeCellIds = React.useMemo(() => {
    if (!spreadsheetMode || !rangeAnchor || !rangeFocus || rangeAnchor === rangeFocus) return void 0;
    const parseCell = (id) => {
      const lastColon = id.lastIndexOf(":");
      return { rowId: id.slice(0, lastColon), colId: id.slice(lastColon + 1) };
    };
    const a = parseCell(rangeAnchor);
    const f = parseCell(rangeFocus);
    const allRows = table.getRowModel().rows.map((r) => r.id);
    const allCols = table.getVisibleLeafColumns().map((c) => c.id).filter((id) => id !== SELECT_COL_ID);
    const aRowIdx = allRows.indexOf(a.rowId);
    const fRowIdx = allRows.indexOf(f.rowId);
    const aColIdx = allCols.indexOf(a.colId);
    const fColIdx = allCols.indexOf(f.colId);
    if (aRowIdx < 0 || fRowIdx < 0 || aColIdx < 0 || fColIdx < 0) return void 0;
    const rowStart = Math.min(aRowIdx, fRowIdx);
    const rowEnd = Math.max(aRowIdx, fRowIdx);
    const colStart = Math.min(aColIdx, fColIdx);
    const colEnd = Math.max(aColIdx, fColIdx);
    const ids = [];
    for (let r = rowStart; r <= rowEnd; r++) {
      for (let c = colStart; c <= colEnd; c++) {
        ids.push(`${allRows[r]}:${allCols[c]}`);
      }
    }
    return ids;
  }, [spreadsheetMode, rangeAnchor, rangeFocus, table]);
  const rangeCellIdSet = React.useMemo(() => new Set(rangeCellIds || []), [rangeCellIds]);
  const leftCols = table.getLeftVisibleLeafColumns();
  const centerCols = table.getCenterVisibleLeafColumns();
  const rightCols = table.getRightVisibleLeafColumns();
  const hasLeft = leftCols.length > 0;
  const hasRight = rightCols.length > 0 || hasRowActions;
  const centerColsWidth = centerCols.reduce((a, c) => a + c.getSize(), 0);
  React.useEffect(() => {
    const measure = () => {
      if (leftHeaderRef.current) setLeftWidth(leftHeaderRef.current.offsetWidth);
      if (rightHeaderRef.current) setRightWidth(rightHeaderRef.current.offsetWidth);
    };
    measure();
    const obs = new ResizeObserver(measure);
    if (leftHeaderRef.current) obs.observe(leftHeaderRef.current);
    if (rightHeaderRef.current) obs.observe(rightHeaderRef.current);
    return () => obs.disconnect();
  }, [hasLeft, hasRight, rows.length]);
  const rowHeight = `h-table-row-${size}`;
  const enterLeaveHandlers = React.useMemo(() => {
    if (!enableHover) return { onMouseOver: void 0, onMouseOut: void 0 };
    const findRowIndex = (target) => {
      if (!(target instanceof HTMLElement)) return null;
      const rowEl = target.closest("[data-row-index]");
      return (rowEl == null ? void 0 : rowEl.dataset.rowIndex) ?? null;
    };
    return {
      onMouseOver: (e) => {
        var _a2;
        if (activeDragIdRef.current != null) {
          const target = e.target instanceof HTMLElement ? e.target : null;
          const rowEl = target == null ? void 0 : target.closest("[data-sortable-row-id]");
          const isSource = (rowEl == null ? void 0 : rowEl.dataset.sortableRowId) === activeDragIdRef.current;
          if (!isSource) return;
        }
        const idx = findRowIndex(e.target);
        if (idx == null) return;
        (_a2 = tableRef.current) == null ? void 0 : _a2.querySelectorAll(`[data-row-index="${idx}"]`).forEach((el) => el.dataset.hovered = "");
      },
      onMouseOut: (e) => {
        var _a2;
        const idx = findRowIndex(e.target);
        if (idx == null) return;
        const related = e.relatedTarget instanceof HTMLElement ? e.relatedTarget.closest("[data-row-index]") : null;
        if ((related == null ? void 0 : related.dataset.rowIndex) === idx) return;
        (_a2 = tableRef.current) == null ? void 0 : _a2.querySelectorAll(`[data-row-index="${idx}"]`).forEach((el) => delete el.dataset.hovered);
      }
    };
  }, [enableHover]);
  const hoverProps = (_idx) => ({});
  const renderCellContent = (cell) => {
    const meta = cell.column.columnDef.meta;
    const colType = meta == null ? void 0 : meta.type;
    const wrap = autoRowHeight && (meta == null ? void 0 : meta.wrap) === true;
    const isKnownCompound = colType === "select" || colType === "multiSelect" || colType === "person" || colType === "multiPerson" || colType === "url" || colType === "date" || colType === "time";
    const rowId = cell.row.id;
    const colId = cell.column.id;
    const editable = isCellEditable(meta, cell.row.original);
    const disabled = isCellDisabled(meta, cell.row.original);
    const isEditingThisCell = editingCellId === cellEditId(rowId, colId);
    let content;
    if (colType) {
      const Cell = resolveCellComponent(colType);
      const cellMode = experimentalActiveEditorController && isEditingThisCell ? "display" : isEditingThisCell && !disabled ? "edit" : "display";
      content = /* @__PURE__ */ jsx(
        Cell,
        {
          value: cell.getValue(),
          meta: meta ?? {},
          mode: cellMode,
          size,
          autoRowHeight,
          isEditable: editable,
          isDisabled: disabled,
          onCommit: (next) => commitCell(rowId, colId, next),
          onCommitLive: (next) => onCellCommit == null ? void 0 : onCellCommit(rowId, colId, next),
          onCancel: exitEdit,
          onRequestEdit: () => !disabled && setEditingCellId(cellEditId(rowId, colId))
        }
      );
    } else {
      content = flexRender(cell.column.columnDef.cell, cell.getContext());
    }
    const isConsumerCompound = !colType && React.isValidElement(content);
    return isEditingThisCell ? content : wrap ? /* @__PURE__ */ jsx("span", { className: "break-words min-w-0", children: content }) : isKnownCompound || isConsumerCompound ? content : /* @__PURE__ */ jsx(TruncateCell, { children: content });
  };
  const iconSize = ICON_SIZE[size];
  const dragDisabled = sorting.length > 0;
  const { allRowIds, parentMap } = React.useMemo(() => {
    const ids = [];
    const pmap = /* @__PURE__ */ new Map();
    const walk = (r, parentId) => {
      ids.push(r.id);
      pmap.set(r.id, parentId);
      const subs = r.subRows;
      if (subs && Array.isArray(subs)) subs.forEach((s) => walk(s, r.id));
    };
    rows.forEach((r) => {
      if ((r.depth ?? 0) === 0) walk(r, "");
    });
    return { allRowIds: ids, parentMap: pmap };
  }, [rows]);
  const [activeDragId, setActiveDragId] = React.useState(null);
  React.useEffect(() => {
    activeDragIdRef.current = activeDragId;
    if (enableRowDrag && useVirtual) virtualizer.measure();
  }, [activeDragId, enableRowDrag, useVirtual, virtualizer]);
  const [invalidDropActive, setInvalidDropActive] = React.useState(false);
  const invalidRef = React.useRef(false);
  invalidRef.current = invalidDropActive;
  const cellEl = (cell, _isLastInRow = false) => {
    var _a2, _b2, _c2, _d, _e, _f;
    if (enabled && cell.column.id === SELECT_COL_ID) {
      const rowId = cell.row.id;
      const rowOriginal = cell.row.original;
      const isDisabled = isRowSelectable ? !isRowSelectable(rowOriginal) : false;
      const ariaLabel = (getRowAriaLabel == null ? void 0 : getRowAriaLabel(rowOriginal)) ?? "Select row";
      const checkboxSize = size === "lg" ? "lg" : "md";
      const onCellClick = isDisabled ? void 0 : (e) => {
        e.stopPropagation();
        if (mode === "single") setSelection([rowId]);
        else toggleRow(rowId, rowOriginal, { shiftKey: e.shiftKey });
      };
      return /* @__PURE__ */ jsx(
        "div",
        {
          role: "cell",
          "data-column-id": SELECT_COL_ID,
          className: cn("flex items-center justify-center shrink-0", !isDisabled && "cursor-pointer"),
          style: { ...columnSizeStyle(cell.column, { resize: enableColumnResize, isSystemCol: isSystemColumn(cell.column.id) }), ...cellPadding },
          onClick: onCellClick,
          children: mode === "single" ? /* @__PURE__ */ jsx(
            RadioGroupItem,
            {
              size: checkboxSize,
              value: rowId,
              disabled: isDisabled,
              "aria-label": ariaLabel,
              onClick: (e) => e.stopPropagation()
            }
          ) : /* @__PURE__ */ jsx(
            Checkbox,
            {
              size: checkboxSize,
              checked: selectionSet.has(rowId),
              disabled: isDisabled,
              "aria-label": ariaLabel,
              onClick: (e) => {
                e.stopPropagation();
                if (isDisabled) return;
                e.preventDefault();
                toggleRow(rowId, rowOriginal, { shiftKey: e.shiftKey });
              },
              onKeyDown: (e) => {
                if (e.key === " " && !isDisabled) {
                  e.preventDefault();
                  toggleRow(rowId, rowOriginal, { shiftKey: e.shiftKey });
                }
              }
            }
          )
        },
        cell.id
      );
    }
    const meta = cell.column.columnDef.meta;
    const colType = meta == null ? void 0 : meta.type;
    const align = (meta == null ? void 0 : meta.align) ?? (colType ? columnTypeDefaults[colType].align : void 0);
    const cellRowId = cell.row.id;
    const cellColId = cell.column.id;
    const cellEditable = isCellEditable(meta, cell.row.original);
    const cellDisabled = isCellDisabled(meta, cell.row.original);
    const isEditingThisCell = editingCellId === cellEditId(cellRowId, cellColId);
    const cellSpreadsheetId = `${cellRowId}:${cellColId}`;
    const isSelectedCell = spreadsheetMode && selectedCellId === cellSpreadsheetId;
    const onEditableCellClick = cellEditable && !cellDisabled && colType !== "boolean" && colType !== "url" && !isEditingThisCell ? (e) => {
      if (spreadsheetMode) {
        if (e.shiftKey && rangeAnchor != null) {
          setRangeFocus(cellSpreadsheetId);
          return;
        }
        if (isSelectedCell) {
          setEditingCellId(cellEditId(cellRowId, cellColId));
          setSelectedCellId(null);
          setRangeAnchor(null);
          setRangeFocus(null);
          return;
        }
        setSelectedCellId(cellSpreadsheetId);
        setRangeAnchor(cellSpreadsheetId);
        setRangeFocus(null);
        return;
      }
      setEditingCellId(cellEditId(cellRowId, cellColId));
    } : void 0;
    const allCells = cell.row.getVisibleCells();
    const firstContentCell = allCells.find((c) => c.column.id !== SELECT_COL_ID);
    const isFirstContent = (firstContentCell == null ? void 0 : firstContentCell.id) === cell.id;
    const depth = cell.row.depth ?? 0;
    const canExpand = ((_b2 = (_a2 = cell.row).getCanExpand) == null ? void 0 : _b2.call(_a2)) ?? false;
    const isExpanded = ((_d = (_c2 = cell.row).getIsExpanded) == null ? void 0 : _d.call(_c2)) ?? false;
    const toggleExpand = (_f = (_e = cell.row).getToggleExpandedHandler) == null ? void 0 : _f.call(_e);
    const showNestedPrefix = isFirstContent && (depth > 0 || canExpand);
    const rawCellError = cellErrors == null ? void 0 : cellErrors[`${cellRowId}:${cellColId}`];
    const cellErrorMessages = (() => {
      if (isEditingThisCell) return null;
      if (rawCellError == null) return null;
      return Array.isArray(rawCellError) ? rawCellError : [rawCellError];
    })();
    const hasCellError = cellErrorMessages != null && cellErrorMessages.length > 0;
    const cellErrorId = hasCellError ? `cell-err-${cellRowId}-${cellColId}` : void 0;
    const rowHasAnyError = !!cellErrors && cell.row.getVisibleCells().some((c) => {
      const v = cellErrors[`${cell.row.id}:${c.column.id}`];
      return v != null && (Array.isArray(v) ? v.length > 0 : true);
    });
    const effectiveAutoRowForCell = autoRowHeight || rowHasAnyError;
    return /* @__PURE__ */ jsx(
      "div",
      {
        role: "cell",
        "data-row-mode": effectiveAutoRowForCell ? "auto" : "fixed",
        "data-column-id": cell.column.id,
        "data-cell-id": `${cell.row.id}:${cell.column.id}`,
        "data-range-cell": spreadsheetMode && rangeCellIdSet.has(`${cell.row.id}:${cell.column.id}`) ? "" : void 0,
        "data-cell-disabled": cellDisabled ? "true" : void 0,
        "aria-describedby": cellErrorId,
        "aria-invalid": hasCellError || void 0,
        className: cn(
          // Cell box(2026-05-05 v6 — A4 canonical: Field frame seamlessly replaces cell border):
          //   - `self-stretch`: cell 永遠填 row 高
          //   - **vertical alignment by row-mode**: autoRow=items-start(top per spec) /
          //     fixed=items-center(centered per spec)。indicator + 非 Field 內容跟 cell 走。
          //   - **editing cell**: padding=0 + 無 right divider → Field naked(`!h-full !px-[cell-px]
          //     !py-[cell-py]`)邊框與 table divider 無縫接軌,seamlessly replace cell border。
          //     Adjacent cell padding+divider 仍在,只 editing cell 自己改觀。對齊 user reminder
          //     「框框跟 cell 一樣大並取代 cell 的框且與 table 隔線無縫接軌」(2026-05-05)。
          //   - **沒有** cell 自己 box-shadow ring — focus / hover / open ring 由 Field naked 自帶
          //     state machine 提供(對齊 user「狀態樣式取決於原輸入框」reminder)
          // 字級隨 size:sm/md text-body / lg text-body-lg(fieldDisplayTextClass),對齊 Field family
          // size→font SSOT。此為非-Field content(consumer 自訂 cell / TruncateCell 純文字)的 fallback 字級;
          // typed cell 各自的 Field 控件已自帶 size→font(cell-registry 傳 size)。
          "group/cell flex text-foreground font-normal shrink-0 relative self-stretch",
          fieldDisplayTextClass(size),
          // Issue 9(2026-05-10):有 cell error → unset overflow-hidden 讓 error message
          // wrap 撐 row 高。**H1(2026-05-10)升級**:overflow-visible 條件改 `rowHasAnyError` —
          // row 內任一 cell 有 error 整 row 全 cells 都 overflow-visible(error 訊息可能多行
          // 撐高 row,row 高同步 effectiveAutoRow auto)。
          rowHasAnyError ? "overflow-visible" : "overflow-hidden",
          effectiveAutoRowForCell ? "items-start" : "items-center",
          align === "right" && "justify-end text-right",
          align === "center" && "justify-center text-center",
          // Phase 9 Issue 8 fix(2026-05-10 user 撞 + codex 重比稿 verdict ADOPT):
          // 之前 `border-r border-divider` 只 right edge → hover overlay outline:-1px 只 right
          // 邊壓 cell border,上左下 sub-pixel 不一致(user 抓「右 1px / 上左下 2px」bug)。
          // 改 `dtCellGrid` (data-table.css:96-110)用 `box-shadow: inset` 4 邊 1px divider,
          // 不佔 layout(per user verbatim「在 cell 內容起始位置不變」前提)→ 4 邊一致 grid line
          // → overlay outline:-1px 4 邊都剛好壓 cell border line。
          // Field naked edit border 仍 own(per Field SSOT)— 編輯時 Field 自帶 border 1px,
          // 跟 cell 4 邊 inset divider 視覺相疊(同 pixel)= 1 line visual,不雙線。
          inlineEdit && "dtCellGrid",
          onEditableCellClick && ["cursor-pointer", nakedCellEditableDisplayHover],
          // editable cell display hover affordance(對齊 Notion / Airtable hover-cell-shows-border canonical)
          // 2026-05-13 Q3 cell disabled SSOT(per codex Q3 verdict + user 拍板「全部馬不停蹄做完」):
          // bg `--bg-disabled` component-state token(color.spec.md:671 owner)+ cursor 抑制 click affordance。
          cellDisabled && "bg-disabled cursor-not-allowed",
          // z-10 raise inline-edit cell;portal mode 不需(layer z-3 already on top)。
          isEditingThisCell && !experimentalActiveEditorController && "z-10"
        ),
        style: {
          ...columnSizeStyle(cell.column, { resize: enableColumnResize, isSystemCol: isSystemColumn(cell.column.id) }),
          // Padding override 只在 inline-edit cell(naked Field 撐滿 cell);portal mode cell 走正常 display padding
          ...isEditingThisCell && !experimentalActiveEditorController ? {} : cellPadding,
          // Slice D Step 2(2026-05-10):flag 開時 set CSS variable 抑制 Field naked hover outline,
          // 讓 overlay layer 接管 hover ring paint(per RFC Contract 8 「one geometry owner, two paint owners」)。
          // Backward-compat:flag 關時 unset → field-wrapper default `var(--border-hover)`(既有行為)。
          ...experimentalSpreadsheetOverlay && { "--cell-hover-outline-color": "transparent" }
        },
        onClick: onEditableCellClick,
        children: hasCellError ? /* @__PURE__ */ jsxs("span", { className: "flex flex-col self-stretch w-full min-w-0 gap-1", children: [
          /* @__PURE__ */ jsxs("span", { className: "flex flex-1 min-w-0", children: [
            showNestedPrefix && /* @__PURE__ */ jsx(
              "span",
              {
                className: "flex items-center shrink-0",
                style: { paddingLeft: depth > 0 ? `calc(${depth} * var(--tree-indent-${size}, var(--tree-indent-md)))` : 0 },
                children: canExpand ? /* @__PURE__ */ jsx(
                  "button",
                  {
                    type: "button",
                    "aria-label": isExpanded ? "收合" : "展開",
                    "aria-expanded": isExpanded,
                    className: "inline-flex items-center justify-center shrink-0 w-4 h-4 mr-2 text-fg-muted hover:text-foreground rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-transform",
                    style: { transform: isExpanded ? "rotate(90deg)" : void 0 },
                    onClick: (e) => {
                      e.stopPropagation();
                      toggleExpand == null ? void 0 : toggleExpand();
                    },
                    children: /* @__PURE__ */ jsx(ChevronDown, { size: iconSize, "aria-hidden": true, style: { transform: "rotate(-90deg)" } })
                  }
                ) : /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "shrink-0 w-4 h-4 mr-2" })
              }
            ),
            /* @__PURE__ */ jsx("span", { className: cn(
              "flex-1 min-w-0 flex",
              // 2026-05-12 Round 4.5 fix(codex M31 Layer C 抓漏)— error-cell branch 也用 per-row state
              // (`effectiveAutoRowForCell`)非 global `autoRowHeight`,跟 line 1559 non-error wrapper 同 SSOT。
              // 前 Round 4 漏修此 branch → error 那格 row 內視覺仍走 global mode mismatch。
              effectiveAutoRowForCell ? "items-start" : "items-center",
              align === "right" && "justify-end"
            ), children: renderCellContent(cell) })
          ] }),
          /* @__PURE__ */ jsx("span", { id: cellErrorId, className: "text-body text-error break-words", role: "alert", children: cellErrorMessages.length === 1 ? cellErrorMessages[0] : /* @__PURE__ */ jsx("ul", { className: "list-disc list-inside flex flex-col gap-1", children: cellErrorMessages.map((m, i) => /* @__PURE__ */ jsx("li", { children: m }, i)) }) })
        ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
          showNestedPrefix && /* @__PURE__ */ jsx(
            "span",
            {
              className: "flex items-center shrink-0",
              style: { paddingLeft: depth > 0 ? `calc(${depth} * var(--tree-indent-${size}, var(--tree-indent-md)))` : 0 },
              children: canExpand ? /* @__PURE__ */ jsx(
                "button",
                {
                  type: "button",
                  "aria-label": isExpanded ? "收合" : "展開",
                  "aria-expanded": isExpanded,
                  className: "inline-flex items-center justify-center shrink-0 w-4 h-4 mr-2 text-fg-muted hover:text-foreground rounded-md focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-transform",
                  style: { transform: isExpanded ? "rotate(90deg)" : void 0 },
                  onClick: (e) => {
                    e.stopPropagation();
                    toggleExpand == null ? void 0 : toggleExpand();
                  },
                  children: /* @__PURE__ */ jsx(ChevronDown, { size: iconSize, "aria-hidden": true, style: { transform: "rotate(-90deg)" } })
                }
              ) : /* @__PURE__ */ jsx("span", { "aria-hidden": true, className: "shrink-0 w-4 h-4 mr-2" })
            }
          ),
          /* @__PURE__ */ jsx("span", { className: cn(
            "flex-1 min-w-0 self-stretch flex",
            // 2026-05-12 fix root invariant(M32 b):用 `effectiveAutoRowForCell` 而非 global
            // `autoRowHeight` — per-row error 時 row 是 auto-height,該 row 內所有 cell 都該
            // top-align(非僅 error cell)。前 v1 用 global autoRowHeight → 非 error cells in
            // error row 走 items-center → 視覺垂直置中於 tall row(user 抓 image 3 bug)。
            effectiveAutoRowForCell ? "items-start" : "items-center",
            align === "right" && "justify-end"
          ), children: renderCellContent(cell) })
        ] })
      },
      cell.id
    );
  };
  const visibleRowIdsKey = React.useMemo(() => rows.map((r) => r.id).join(","), [rows]);
  const visibleRowIdsSet = React.useMemo(() => new Set(rows.map((r) => r.id)), [visibleRowIdsKey]);
  React.useEffect(() => {
    if (!enabled || preserveSelectionOnFilter) return;
    setSelection((prev) => {
      const filtered = prev.filter((id) => visibleRowIdsSet.has(id));
      return filtered.length === prev.length ? prev : filtered;
    });
  }, [visibleRowIdsKey, enabled, preserveSelectionOnFilter, visibleRowIdsSet, setSelection]);
  const selectableVisibleIds = React.useMemo(() => {
    if (!enabled) return [];
    return rows.filter((r) => !isRowSelectable || isRowSelectable(r.original)).map((r) => r.id);
  }, [rows, enabled, isRowSelectable]);
  const selectionSet = React.useMemo(() => new Set(selection), [selection]);
  const visibleSelectedCount = selectableVisibleIds.filter((id) => selectionSet.has(id)).length;
  const headerCheckedState = selectableVisibleIds.length === 0 ? false : visibleSelectedCount === 0 ? false : visibleSelectedCount === selectableVisibleIds.length ? true : "indeterminate";
  const visibleIdToRow = React.useMemo(
    () => new Map(rows.map((r) => [r.id, r])),
    [rows]
  );
  const toggleHeaderCheckbox = React.useCallback(() => {
    if (headerCheckedState === true) {
      const visibleSet = new Set(selectableVisibleIds);
      setSelection((prev) => prev.filter((id) => !visibleSet.has(id)));
    } else {
      setSelection((prev) => Array.from(/* @__PURE__ */ new Set([...prev, ...selectableVisibleIds])));
    }
  }, [headerCheckedState, selectableVisibleIds, setSelection]);
  const toggleRow = React.useCallback((rowId, rowOriginal, opts) => {
    if (isRowSelectable && !isRowSelectable(rowOriginal)) return;
    if (mode === "single") {
      setSelection(selectionSet.has(rowId) ? [] : [rowId]);
      anchorRowIdRef.current = rowId;
      return;
    }
    const anchor = anchorRowIdRef.current;
    if ((opts == null ? void 0 : opts.shiftKey) && anchor && anchor !== rowId) {
      const visibleIds = rows.map((r) => r.id);
      const a = visibleIds.indexOf(anchor);
      const b = visibleIds.indexOf(rowId);
      if (a !== -1 && b !== -1) {
        const [from, to] = a < b ? [a, b] : [b, a];
        const rangeIds = visibleIds.slice(from, to + 1).filter((id) => {
          const row = visibleIdToRow.get(id);
          return row && (!isRowSelectable || isRowSelectable(row.original));
        });
        const willCheck = !selectionSet.has(rowId);
        setSelection((prev) => {
          const set = new Set(prev);
          rangeIds.forEach((id) => willCheck ? set.add(id) : set.delete(id));
          return Array.from(set);
        });
        return;
      }
    }
    setSelection((prev) => {
      const set = new Set(prev);
      if (set.has(rowId)) set.delete(rowId);
      else set.add(rowId);
      return Array.from(set);
    });
    anchorRowIdRef.current = rowId;
  }, [isRowSelectable, mode, selectionSet, rows, visibleIdToRow, setSelection]);
  const tableKeyboardHandler = React.useCallback(
    (e) => {
      var _a2;
      if (e.nativeEvent.isComposing || e.nativeEvent.keyCode === 229) return;
      if (spreadsheetMode && selectedCellId != null && editingCellId == null) {
        const navKeys = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Enter", "F2", "Escape"];
        if (!navKeys.includes(e.key)) return;
        const lastColon = selectedCellId.lastIndexOf(":");
        const curRowId = selectedCellId.slice(0, lastColon);
        const curColId = selectedCellId.slice(lastColon + 1);
        const allRows = table.getRowModel().rows.map((r) => r.id);
        const allCols = table.getVisibleLeafColumns().map((c) => c.id).filter((id) => id !== SELECT_COL_ID);
        const curRowIdx = allRows.indexOf(curRowId);
        const curColIdx = allCols.indexOf(curColId);
        if (curRowIdx < 0 || curColIdx < 0) return;
        let nextRowIdx = curRowIdx;
        let nextColIdx = curColIdx;
        if (e.key === "ArrowUp" && curRowIdx > 0) {
          nextRowIdx = curRowIdx - 1;
        } else if (e.key === "ArrowDown" && curRowIdx < allRows.length - 1) {
          nextRowIdx = curRowIdx + 1;
        } else if (e.key === "ArrowLeft" && curColIdx > 0) {
          nextColIdx = curColIdx - 1;
        } else if (e.key === "ArrowRight" && curColIdx < allCols.length - 1) {
          nextColIdx = curColIdx + 1;
        } else if (e.key === "Enter" || e.key === "F2") {
          const row = table.getRowModel().rowsById[curRowId];
          const colDef = table.getAllLeafColumns().find((c) => c.id === curColId);
          const meta = (_a2 = colDef == null ? void 0 : colDef.columnDef) == null ? void 0 : _a2.meta;
          if ((meta == null ? void 0 : meta.type) && meta.type !== "boolean" && meta.type !== "url" && row && canEditCell(meta, row.original)) {
            e.preventDefault();
            setEditingCellId(cellEditId(curRowId, curColId));
            setSelectedCellId(null);
            setRangeAnchor(null);
            setRangeFocus(null);
          }
          return;
        } else if (e.key === "Escape") {
          e.preventDefault();
          setSelectedCellId(null);
          setRangeAnchor(null);
          setRangeFocus(null);
          return;
        }
        if (nextRowIdx !== curRowIdx || nextColIdx !== curColIdx) {
          e.preventDefault();
          const nextCellId = `${allRows[nextRowIdx]}:${allCols[nextColIdx]}`;
          setSelectedCellId(nextCellId);
          setRangeAnchor(nextCellId);
          setRangeFocus(null);
        }
        return;
      }
      if (!enabled) return;
      if ((e.metaKey || e.ctrlKey) && e.key === "a" && mode === "multi") {
        e.preventDefault();
        setSelection((prev) => Array.from(/* @__PURE__ */ new Set([...prev, ...selectableVisibleIds])));
        return;
      }
      if (e.key === "Escape" && selection.length > 0) {
        e.preventDefault();
        setSelection([]);
        anchorRowIdRef.current = null;
        return;
      }
    },
    [
      enabled,
      mode,
      selection.length,
      selectableVisibleIds,
      setSelection,
      spreadsheetMode,
      selectedCellId,
      editingCellId,
      table,
      isCellEditable
    ]
  );
  const headerCellEl = (header, showDivider) => {
    var _a2;
    if (enabled && header.column.id === SELECT_COL_ID) {
      const isHeaderDisabled = selectableVisibleIds.length === 0 || mode !== "multi";
      return /* @__PURE__ */ jsx(
        "div",
        {
          role: "columnheader",
          className: cn("flex items-center justify-center shrink-0 select-none", !isHeaderDisabled && "cursor-pointer"),
          style: { ...columnSizeStyle(header.column, { resize: enableColumnResize, isSystemCol: isSystemColumn(header.column.id) }), ...cellPadding },
          onClick: isHeaderDisabled ? void 0 : (e) => {
            e.stopPropagation();
            toggleHeaderCheckbox();
          },
          children: mode === "multi" && /* @__PURE__ */ jsx(
            Checkbox,
            {
              size: size === "lg" ? "lg" : "md",
              checked: headerCheckedState,
              onClick: (e) => e.stopPropagation(),
              onCheckedChange: () => toggleHeaderCheckbox(),
              "aria-label": "Select all visible rows",
              disabled: selectableVisibleIds.length === 0
            }
          )
        },
        header.id
      );
    }
    const meta = header.column.columnDef.meta;
    const colType = meta == null ? void 0 : meta.type;
    const align = (meta == null ? void 0 : meta.align) ?? (colType ? columnTypeDefaults[colType].align : void 0);
    const canSort = header.column.getCanSort();
    const sortDir = header.column.getIsSorted();
    const isMultiSort = (((_a2 = table.getState().sorting) == null ? void 0 : _a2.length) ?? 0) > 1;
    const SortIcon = sortDir === "asc" ? ArrowUp : ArrowDown;
    const sortHandler = canSort ? header.column.getToggleSortingHandler() : void 0;
    return /* @__PURE__ */ jsxs(
      "div",
      {
        role: "columnheader",
        "aria-sort": sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : "none",
        className: cn(
          // **Inline action canonical**(2026-05-05 v2):header 用 `flex items-center gap-2`
          // (= 8px,inline-action.spec.md SSOT),more action 為 inline shrink-0 sibling 而非
          // absolute → hover 顯時佔位 → label 自動讓出空間給 sort + more,**不再重疊**(對齊
          // user 圖示質疑 + Notion / Airtable header layout 共識)。
          // cell padding 12px 由外層 cellPadding style 提供 → more 距 cell 右邊 = 12px。
          // header 字級也隨 size(原寫死 text-body → lg 表格 header 字偏小,跟 body 不一致)。
          // 對齊 cell wrapper + Field family size→font SSOT。色弱化由 text-fg-secondary 維持。
          "group relative flex items-center gap-2 text-fg-secondary font-normal shrink-0 overflow-hidden select-none",
          fieldDisplayTextClass(size),
          align === "right" && "justify-end",
          align === "center" && "justify-center"
        ),
        style: { ...columnSizeStyle(header.column, { resize: enableColumnResize, isSystemCol: isSystemColumn(header.column.id) }), ...cellPadding },
        children: [
          /* @__PURE__ */ jsxs(
            "div",
            {
              role: canSort ? "button" : void 0,
              tabIndex: canSort ? 0 : void 0,
              onClick: sortHandler,
              onKeyDown: canSort ? (e) => {
                if (e.key === "Enter" || e.key === " ") {
                  e.preventDefault();
                  sortHandler == null ? void 0 : sortHandler(e);
                }
              } : void 0,
              className: cn(
                "flex items-center min-w-0 flex-1 gap-1 outline-none",
                canSort && "cursor-pointer hover:text-foreground transition-colors",
                canSort && "focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
              ),
              children: [
                /* @__PURE__ */ jsx(TruncateCell, { className: cn("min-w-0", align === "right" && "text-right", align === "center" && "text-center"), children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()) }),
                canSort && sortDir && !isMultiSort && // 2026-05-18 改 per user 拍板「DataTable sort 跟 row size 變」+「做完」approval:
                // 原固定 14 違反 uiSize.spec.md Icon Tier(sm/md→16, lg→20)。改 ICON_SIZE[size]
                // 隨 DataTable size prop 變。
                /* @__PURE__ */ jsx(SortIcon, { size: ICON_SIZE[size], "aria-hidden": true, className: "shrink-0 text-fg-secondary" })
              ]
            }
          ),
          /* @__PURE__ */ jsx("div", { className: "shrink-0 hidden group-hover:inline-flex group-focus-within:inline-flex has-[[data-state=open]]:inline-flex", children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [
            /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
              ItemInlineActionButton,
              {
                icon: ChevronDown,
                size: "md",
                "aria-label": `${typeof header.column.columnDef.header === "string" ? header.column.columnDef.header : header.column.id} 欄位選單`,
                overlayTrigger: true
              }
            ) }),
            /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", children: [
              canSort && /* @__PURE__ */ jsxs(Fragment, { children: [
                /* @__PURE__ */ jsx(DropdownMenuItem, { startIcon: ArrowUp, onClick: () => header.column.toggleSorting(false, false), children: "升冪排序" }),
                /* @__PURE__ */ jsx(DropdownMenuItem, { startIcon: ArrowDown, onClick: () => header.column.toggleSorting(true, false), children: "降冪排序" }),
                sortDir && !isMultiSort && /* @__PURE__ */ jsx(DropdownMenuItem, { startIcon: X, onClick: () => header.column.clearSorting(), children: "取消排序" }),
                /* @__PURE__ */ jsx(DropdownMenuSeparator, {})
              ] }),
              onColumnFilterTrigger && /* @__PURE__ */ jsx(DropdownMenuItem, { startIcon: Filter, onClick: () => onColumnFilterTrigger(header.column.id), children: "依此欄篩選…" }),
              header.column.getCanHide() && /* @__PURE__ */ jsx(DropdownMenuItem, { startIcon: EyeOff, onClick: () => header.column.toggleVisibility(false), children: "隱藏欄位" }),
              enableColumnResize && !isSystemColumn(header.column.id) && /* @__PURE__ */ jsx(
                DropdownMenuItem,
                {
                  startIcon: ArrowUpDown,
                  onClick: () => {
                    var _a3, _b2;
                    const cells = document.querySelectorAll(
                      `[role="cell"][data-column-id="${header.column.id}"]`
                    );
                    let max = MIN_COLUMN_WIDTH;
                    cells.forEach((c) => {
                      const inner = c.firstElementChild;
                      const w = ((inner == null ? void 0 : inner.scrollWidth) ?? c.scrollWidth) + 32;
                      if (w > max) max = w;
                    });
                    (_b2 = (_a3 = header.column).resetSize) == null ? void 0 : _b2.call(_a3);
                    table.setColumnSizing((prev) => ({ ...prev, [header.column.id]: max }));
                    onColumnResize == null ? void 0 : onColumnResize(header.column.id, max);
                  },
                  children: "自動調整寬度"
                }
              )
            ] })
          ] }) }),
          (() => {
            var _a3, _b2;
            const colId = header.column.id;
            const colMeta = header.column.columnDef.meta;
            const colOptIn = (colMeta == null ? void 0 : colMeta.resizable) !== false;
            const isResizable = enableColumnResize && !isSystemColumn(colId) && colOptIn;
            const isResizing = (_b2 = (_a3 = header.column).getIsResizing) == null ? void 0 : _b2.call(_a3);
            if (!showDivider && !isResizable) return null;
            return /* @__PURE__ */ jsx(
              "span",
              {
                role: isResizable ? "separator" : void 0,
                "aria-orientation": isResizable ? "vertical" : void 0,
                "aria-label": isResizable ? "拖曳調整欄寬" : void 0,
                className: cn(
                  "group/resize absolute top-0 bottom-0 right-0 -mr-[3px] w-[7px]",
                  isResizable && "cursor-col-resize select-none"
                ),
                onPointerDownCapture: isResizable ? (e) => {
                  var _a4;
                  e.stopPropagation();
                  (_a4 = header.getResizeHandler) == null ? void 0 : _a4.call(header)(e.nativeEvent);
                } : void 0,
                onTouchStart: isResizable ? (e) => {
                  var _a4;
                  e.stopPropagation();
                  (_a4 = header.getResizeHandler) == null ? void 0 : _a4.call(header)(e.nativeEvent);
                } : void 0,
                children: showDivider && /* @__PURE__ */ jsx(
                  "span",
                  {
                    "aria-hidden": true,
                    className: cn(
                      "absolute right-[3px] w-px transition-colors",
                      isResizing ? "bg-primary" : isResizable ? "bg-divider group-hover/resize:bg-[var(--border-hover)]" : "bg-divider"
                    ),
                    style: { top: "var(--table-cell-py)", bottom: "var(--table-cell-py)" }
                  }
                )
              }
            );
          })()
        ]
      },
      header.id
    );
  };
  const leftIds = React.useMemo(() => new Set(leftCols.map((c) => c.id)), [leftCols]);
  const centerIds = React.useMemo(() => new Set(centerCols.map((c) => c.id)), [centerCols]);
  const rightIds = React.useMemo(() => new Set(rightCols.map((c) => c.id)), [rightCols]);
  const colsToIds = (cols) => cols === leftCols ? leftIds : cols === rightCols ? rightIds : centerIds;
  const getRegionHeaders = (cols) => {
    var _a2;
    const ids = colsToIds(cols);
    return ((_a2 = table.getHeaderGroups()[0]) == null ? void 0 : _a2.headers.filter((h) => ids.has(h.id))) ?? [];
  };
  const getRegionCells = (row, cols) => {
    const ids = colsToIds(cols);
    return row.getVisibleCells().filter((c) => ids.has(c.column.id));
  };
  const [dropIndicator, setDropIndicator] = React.useState(null);
  const reorderableColumnIdsRef = React.useRef([]);
  const renderHeaderRow = (cols, isRight) => {
    const headers = getRegionHeaders(cols);
    const hasVisibleChildren = headers.length > 0;
    const RowTag = hasVisibleChildren ? "div" : "div";
    const rowRole = hasVisibleChildren ? "row" : void 0;
    return /* @__PURE__ */ jsxs(RowTag, { role: rowRole, className: cn("flex items-center border-b border-divider", rowHeight, HEADER_BG), children: [
      headers.map((h, i) => {
        const showDivider = i < headers.length - 1 && !(isRight && i === headers.length - 1);
        const colId = h.column.id;
        const meta = h.column.columnDef.meta;
        const isLocked = (meta == null ? void 0 : meta.locked) === true;
        const isSystem = isSystemColumn(colId);
        const isDraggable = enableColumnReorder && !isLocked && !isSystem;
        const indicatorSide = (dropIndicator == null ? void 0 : dropIndicator.type) === "column" && dropIndicator.id === colId ? dropIndicator.side : null;
        return /* @__PURE__ */ jsx(
          DraggableHeaderCell,
          {
            id: colId,
            disabled: !isDraggable,
            isLocked,
            dropIndicatorSide: indicatorSide,
            children: headerCellEl(h, showDivider)
          },
          h.id
        );
      }),
      isRight && hasRowActions && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-end shrink-0 gap-2 invisible", "aria-hidden": "true", style: cellPadding, children: rows[0] && rowActions(rows[0].original) })
    ] });
  };
  const renderBodyRows = (cols, isCenter, isRight, regionWidth) => {
    if (isEmpty && isCenter) {
      if (emptyState && typeof emptyState !== "string") return /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center py-12", children: emptyState });
      return /* @__PURE__ */ jsx("div", { className: "flex-1 flex items-center justify-center py-12", children: /* @__PURE__ */ jsx(Empty, { description: typeof emptyState === "string" ? emptyState : "沒有資料" }) });
    }
    if (isEmpty) return null;
    const isPrimaryRegion = isCenter;
    const regionRole = isPrimaryRegion ? "primary" : "mirror";
    const rowEl = (row, idx, opts) => {
      const showBorder = bordered !== false ? !(opts == null ? void 0 : opts.isLast) : true;
      const isThisRowDragging = enableRowDrag && activeDragId === row.id;
      const useSortableWrap = enableRowDrag;
      const rowHasError = !!cellErrors && row.getVisibleCells().some((c) => {
        const key = `${row.id}:${c.column.id}`;
        const v = cellErrors[key];
        return v != null && (Array.isArray(v) ? v.length > 0 : true);
      });
      const effectiveAutoRow = autoRowHeight || rowHasError;
      const showDragHandle = enableRowDrag && (row.depth ?? 0) === 0 && isPrimaryRegion;
      const anyDragActive = activeDragId != null;
      const baseRowDiv = (extra) => /* @__PURE__ */ jsxs(
        "div",
        {
          ref: (el) => {
            var _a2;
            if (isCenter && (opts == null ? void 0 : opts.virtual) && el && !isThisRowDragging && activeDragId == null) {
              virtualizer.measureElement(el);
            }
            (_a2 = extra == null ? void 0 : extra.ref) == null ? void 0 : _a2.call(extra, el);
          },
          "data-index": isCenter && (opts == null ? void 0 : opts.virtual) ? idx : void 0,
          "data-row-index": idx,
          "data-sortable-row-id": enableRowDrag ? row.id : void 0,
          "data-row-drag-source": enableRowDrag && isPrimaryRegion ? "true" : void 0,
          role: "row",
          "aria-rowindex": idx + 2,
          className: cn(
            "group/row flex relative",
            // H1 fix(2026-05-10):effectiveAutoRow 覆 global autoRowHeight,per-row 若有 cell error
            // 自動 auto-height(撐 message)。Error 清 → 回 fixed。Other rows 不受影響。
            effectiveAutoRow ? "items-start" : "items-center",
            !effectiveAutoRow && rowHeight,
            !effectiveAutoRow && "overflow-hidden",
            (opts == null ? void 0 : opts.virtual) && "absolute w-full",
            showBorder && "border-b border-divider",
            // v15.3 hover bg canonical:hover class 永遠生效,但 onMouseOver delegate
            // 在 drag 期間只允許 source row 寫 data-hovered → 其他 row 自然不顯 bg。
            // (對齊 Linear / Jira:source 維持 active 視覺,其他 row 完全靜止)
            "transition-colors data-[hovered]:bg-neutral-hover",
            (extra == null ? void 0 : extra.isDragging) && "bg-neutral-hover"
            // **v15.3.1**:不變 cursor(對齊 Material / Carbon / Polaris / Notion canonical)。
            // 整列可拖的 affordance 由可見的 RowDragHandle Button 提供,不靠 cursor 暗示。
            // 之前 cursor-grab → drag 中 user 看到 cursor 變化反而干擾 indicator+ghost 的視覺焦點。
          ),
          style: {
            ...(opts == null ? void 0 : opts.virtual) ? { transform: `translateY(${opts.start}px)` } : {},
            ...(extra == null ? void 0 : extra.style) ?? {}
          },
          ...hoverProps(),
          ...(extra == null ? void 0 : extra.attributes) ?? {},
          ...(extra == null ? void 0 : extra.listeners) ?? {},
          children: [
            showDragHandle && /* @__PURE__ */ jsx(RowDragHandle, { disabled: dragDisabled, anyDragActive }),
            (dropIndicator == null ? void 0 : dropIndicator.type) === "row" && dropIndicator.id === row.id && dropIndicator.side === "before" && /* @__PURE__ */ jsx("div", { className: dropIndicatorRow.before, "aria-hidden": true }),
            getRegionCells(row, cols).map((cell, ci, arr) => cellEl(cell, ci === arr.length - 1 && !(isRight && hasRowActions))),
            isRight && hasRowActions && /* @__PURE__ */ jsx("div", { role: "cell", className: "flex items-center justify-end shrink-0 gap-2 flex-1", style: cellPadding, children: rowActions(row.original) }),
            (dropIndicator == null ? void 0 : dropIndicator.type) === "row" && dropIndicator.id === row.id && dropIndicator.side === "after" && /* @__PURE__ */ jsx("div", { className: dropIndicatorRow.after, "aria-hidden": true })
          ]
        },
        row.id
      );
      if (useSortableWrap) {
        const rowInvalidDrop = isThisRowDragging && invalidDropActive;
        return /* @__PURE__ */ jsx(SortableRowProvider, { id: row.id, disabled: dragDisabled, role: regionRole, invalidDrop: rowInvalidDrop, children: (ctx) => baseRowDiv({
          // mirror 也掛 setNodeRef — dnd-kit 內部以 hook instance 為單元,
          // 多 instance 同 id 時,measurement 走最後 mount 的;不影響 transform 一致性
          ref: ctx.setNodeRef,
          style: ctx.style,
          isDragging: ctx.isDragging,
          // v15.2 整列可拖:listeners + attributes spread 在 row div 上(只 primary,
          // mirror region 沒 listeners 避免 a11y 重複 announce / pointer 雙觸發)
          listeners: ctx.rowListeners,
          attributes: ctx.rowAttributes
        }) }, row.id);
      }
      return baseRowDiv();
    };
    const colsWidth = cols.reduce((a, c) => a + c.getSize(), 0);
    const containerWidth = regionWidth || colsWidth;
    if (useVirtual) {
      return /* @__PURE__ */ jsx(TableScrollProvider, { isScrolling: virtualizer.isScrolling, children: /* @__PURE__ */ jsx("div", { style: { height: virtualizer.getTotalSize(), position: "relative", minWidth: containerWidth }, children: virtualizer.getVirtualItems().map((vr) => rowEl(rows[vr.index], vr.index, { virtual: true, start: vr.start, isLast: vr.index === rows.length - 1 })) }) });
    }
    return /* @__PURE__ */ jsx("div", { style: { minWidth: containerWidth }, children: rows.map((row, i) => rowEl(row, i, { isLast: i === rows.length - 1 })) });
  };
  const tableContent = /* @__PURE__ */ jsxs(
    "div",
    {
      ref: (el) => {
        tableRef.current = el;
        if (typeof ref === "function") ref(el);
        else if (ref) ref.current = el;
      },
      "data-table-size": size,
      "data-data-table-outer": true,
      "data-active-editor-controller": experimentalActiveEditorController ? "enabled" : void 0,
      className: cn(dataTableVariants({ bordered }), isFillHeight && "flex flex-col", "outline-none focus:outline-none focus-visible:outline-none", className),
      style: isFillHeight ? { maxHeight: height } : void 0,
      role: "table",
      "aria-rowcount": rows.length + 1,
      tabIndex: enabled || spreadsheetMode ? 0 : void 0,
      onKeyDown: enabled || spreadsheetMode ? tableKeyboardHandler : void 0,
      onMouseOver: enterLeaveHandlers.onMouseOver,
      onMouseOut: enterLeaveHandlers.onMouseOut,
      ...props,
      children: [
        /* @__PURE__ */ jsxs("div", { role: "rowgroup", className: "flex", children: [
          hasLeft && /* @__PURE__ */ jsx("div", { ref: leftHeaderRef, "data-datatable-header-panel": "left", className: "shrink-0 overflow-hidden dtPanelBoundaryRight", children: renderHeaderRow(leftCols, false) }),
          /* @__PURE__ */ jsx(
            "div",
            {
              ref: centerHeaderRef,
              "data-datatable-header-panel": "center",
              className: "flex-1 min-w-0 overflow-hidden",
              children: /* @__PURE__ */ jsx("div", { style: { minWidth: centerColsWidth }, children: renderHeaderRow(centerCols, false) })
            }
          ),
          hasRight && /* @__PURE__ */ jsx("div", { ref: rightHeaderRef, "data-datatable-header-panel": "right", className: "shrink-0 overflow-hidden dtPanelBoundaryLeft", children: renderHeaderRow(rightCols, true) })
        ] }),
        /* @__PURE__ */ jsxs("div", { ref: bodyRef, className: cn("flex items-start", isFillHeight && "min-h-0 min-w-0"), children: [
          hasLeft && /* @__PURE__ */ jsx(
            "div",
            {
              ref: leftBodyRef,
              "data-datatable-panel": "left",
              className: "shrink-0 overflow-hidden dtPanelBoundaryRight",
              style: {
                width: leftWidth || void 0,
                // isFillHeight 用 JS 算的 px;固定 px(300px 等)直接套
                ...isFillHeight && bodyMaxHeight != null ? { maxHeight: bodyMaxHeight } : hasHeightConstraint ? { maxHeight: height } : {}
              },
              children: renderBodyRows(leftCols, false, false, leftWidth)
            }
          ),
          /* @__PURE__ */ jsx(
            "div",
            {
              ref: centerBodyRef,
              "data-datatable-hscroll": true,
              "data-datatable-panel": "center",
              className: "flex-1 min-w-0 overflow-x-auto overflow-y-auto",
              style: isFillHeight && bodyMaxHeight != null ? { maxHeight: bodyMaxHeight } : hasHeightConstraint ? { maxHeight: height } : void 0,
              onScroll: onCenterBodyScroll,
              children: /* @__PURE__ */ jsx("div", { style: { minWidth: centerColsWidth }, children: renderBodyRows(centerCols, true, false) })
            }
          ),
          hasRight && /* @__PURE__ */ jsx(
            "div",
            {
              ref: rightBodyRef,
              "data-datatable-panel": "right",
              className: "shrink-0 overflow-hidden dtPanelBoundaryLeft",
              style: {
                width: rightWidth || void 0,
                ...isFillHeight && bodyMaxHeight != null ? { maxHeight: bodyMaxHeight } : hasHeightConstraint ? { maxHeight: height } : {}
              },
              children: renderBodyRows(rightCols, false, true, rightWidth)
            }
          )
        ] }),
        /* @__PURE__ */ jsx(
          DataTableInteractionLayer,
          {
            enabled: experimentalSpreadsheetOverlay || spreadsheetMode,
            containerRef: tableRef,
            activeEditorCellId: editingCellId ? editingCellId.replace("__", ":") : null,
            activeEditorEnabled: experimentalActiveEditorController,
            activeEditorRender: experimentalActiveEditorController ? (cellId) => {
              var _a2;
              const lastColon = cellId.lastIndexOf(":");
              if (lastColon < 0) return null;
              const rowId = cellId.slice(0, lastColon);
              const colId = cellId.slice(lastColon + 1);
              const colDef = table.getAllLeafColumns().find((c) => c.id === colId);
              const meta = (_a2 = colDef == null ? void 0 : colDef.columnDef) == null ? void 0 : _a2.meta;
              if (!(meta == null ? void 0 : meta.type)) return null;
              const colType = meta.type;
              const Cell = resolveCellComponent(colType);
              const row = table.getRowModel().rowsById[rowId];
              if (!row) return null;
              const cellEditable = isCellEditable(meta, row.original);
              const rowValue = row.original[colId];
              const value = editingDraft !== void 0 ? editingDraft : rowValue;
              const handleEditTab = (e) => {
                var _a3;
                if (e.nativeEvent.isComposing || e.nativeEvent.keyCode === 229) return;
                if (e.key !== "Tab") return;
                e.preventDefault();
                e.stopPropagation();
                const direction = e.shiftKey ? "prev" : "next";
                const allRows = table.getRowModel().rows.map((r) => r.id);
                const allCols = table.getVisibleLeafColumns().map((c) => c.id).filter((id) => id !== SELECT_COL_ID);
                const curRowIdx = allRows.indexOf(rowId);
                const curColIdx = allCols.indexOf(colId);
                if (curRowIdx < 0 || curColIdx < 0) return;
                const NON_EDIT_TYPES = ["boolean", "url"];
                let nextRowIdx = curRowIdx;
                let nextColIdx = curColIdx;
                const totalCells = allRows.length * allCols.length;
                let safety = 0;
                while (safety < totalCells) {
                  safety++;
                  if (direction === "next") {
                    nextColIdx++;
                    if (nextColIdx >= allCols.length) {
                      nextColIdx = 0;
                      nextRowIdx++;
                    }
                    if (nextRowIdx >= allRows.length) return;
                  } else {
                    nextColIdx--;
                    if (nextColIdx < 0) {
                      nextColIdx = allCols.length - 1;
                      nextRowIdx--;
                    }
                    if (nextRowIdx < 0) return;
                  }
                  const nextRow = table.getRowModel().rowsById[allRows[nextRowIdx]];
                  const nextColDef = table.getAllLeafColumns().find((c) => c.id === allCols[nextColIdx]);
                  const nextMeta = (_a3 = nextColDef == null ? void 0 : nextColDef.columnDef) == null ? void 0 : _a3.meta;
                  if (!(nextMeta == null ? void 0 : nextMeta.type) || NON_EDIT_TYPES.includes(nextMeta.type)) continue;
                  if (!nextRow || !canEditCell(nextMeta, nextRow.original)) continue;
                  setEditingCellId(cellEditId(allRows[nextRowIdx], allCols[nextColIdx]));
                  return;
                }
              };
              return /* @__PURE__ */ jsx("div", { onKeyDownCapture: handleEditTab, style: { width: "100%", height: "100%" }, children: /* @__PURE__ */ jsx(
                Cell,
                {
                  value,
                  meta,
                  mode: "edit",
                  size,
                  autoRowHeight: false,
                  isEditable: cellEditable,
                  onCommit: (next) => commitCell(rowId, colId, next),
                  onCommitLive: (next) => onCellCommit == null ? void 0 : onCellCommit(rowId, colId, next),
                  onCancel: exitEdit,
                  onDraft: setEditingDraft
                }
              ) });
            } : void 0,
            selectedCellId: spreadsheetMode ? selectedCellId : null,
            rangeCellIds,
            cellClickEntersEdit: (cellId) => {
              var _a2;
              const lastColonIdx = cellId.lastIndexOf(":");
              if (lastColonIdx < 0) return false;
              const rowId = cellId.slice(0, lastColonIdx);
              const colId = cellId.slice(lastColonIdx + 1);
              const colDef = table.getAllLeafColumns().find((c) => c.id === colId);
              const meta = (_a2 = colDef == null ? void 0 : colDef.columnDef) == null ? void 0 : _a2.meta;
              if (!meta) return false;
              const EDITABLE_CLICK_TYPES = ["string", "number", "currency", "date", "time", "select", "multiSelect", "person", "multiPerson", "combobox"];
              if (!EDITABLE_CLICK_TYPES.includes(meta.type)) return false;
              const row = table.getRowModel().rowsById[rowId];
              if (!row) return false;
              return canEditCell(meta, row.original);
            }
          }
        )
      ]
    }
  );
  const dndSensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance: 8 } }),
    useSensor(KeyboardSensor)
  );
  const sameParentCollisionDetection = React.useCallback((args) => {
    var _a2, _b2;
    const activeId = ((_a2 = args.active) == null ? void 0 : _a2.id) != null ? String(args.active.id) : null;
    if (!activeId) {
      const pointer2 = pointerWithin(args);
      return pointer2.length > 0 ? pointer2 : rectIntersection(args);
    }
    const activeRect = (_b2 = args.active) == null ? void 0 : _b2.rect.current.initial;
    const cursor = args.pointerCoordinates;
    if (cursor && activeRect && cursor.y >= activeRect.top && cursor.y <= activeRect.bottom) {
      return [];
    }
    const activeParent = parentMap.get(activeId);
    const filtered = args.droppableContainers.filter((c) => {
      const cid = String(c.id);
      if (cid === activeId) return false;
      const cParent = parentMap.get(cid);
      if (cParent === void 0) return false;
      return cParent === activeParent;
    });
    const filteredArgs = { ...args, droppableContainers: filtered };
    const pointer = pointerWithin(filteredArgs);
    if (pointer.length > 0) return pointer;
    if (cursor) {
      const tableScope = tableRef.current ?? document;
      const liveRows = Array.from(tableScope.querySelectorAll('[role="row"][data-sortable-row-id]')).filter((el) => el.dataset.sortableRowId !== activeId).filter((el) => {
        const cParent = parentMap.get(el.dataset.sortableRowId ?? "");
        return cParent === activeParent;
      });
      for (const el of liveRows) {
        const r = el.getBoundingClientRect();
        if (cursor.y >= r.top && cursor.y <= r.bottom && cursor.x >= r.left && cursor.x <= r.right) {
          const rowId = el.dataset.sortableRowId;
          const cont = filtered.find((c) => String(c.id) === rowId);
          if (cont) return [{ id: cont.id, data: { droppableContainer: cont, value: 0 } }];
        }
      }
    }
    return rectIntersection(filteredArgs);
  }, [parentMap]);
  const [dragOverlayHtml, setDragOverlayHtml] = React.useState(null);
  const [dragOverlayWidth, setDragOverlayWidth] = React.useState(null);
  const [, setActiveDragColId] = React.useState(null);
  const handleDragStart = React.useCallback((e) => {
    var _a2, _b2, _c2, _d, _e, _f;
    const id = String(e.active.id);
    const type = ((_b2 = (_a2 = e.active.data) == null ? void 0 : _a2.current) == null ? void 0 : _b2.type) ?? "row";
    setInvalidDropActive(false);
    if (type === "row") {
      (_c2 = tableRef.current) == null ? void 0 : _c2.querySelectorAll("[data-hovered]").forEach((el) => {
        const rowId = el.dataset.sortableRowId;
        if (rowId !== id) delete el.dataset.hovered;
      });
    } else {
      (_d = tableRef.current) == null ? void 0 : _d.querySelectorAll("[data-hovered]").forEach((el) => delete el.dataset.hovered);
    }
    if (type === "column") {
      const colId = ((_f = (_e = e.active.data) == null ? void 0 : _e.current) == null ? void 0 : _f.columnId) ?? id;
      setActiveDragColId(colId);
      const headerEl = document.querySelector(`[role="columnheader"][data-column-id="${colId}"]`);
      if (headerEl) {
        const clone = headerEl.cloneNode(true);
        clone.style.position = "static";
        clone.style.transform = "none";
        clone.style.transition = "none";
        clone.style.opacity = "1";
        clone.style.zIndex = "";
        clone.style.width = `${headerEl.offsetWidth}px`;
        clone.querySelectorAll('[role="separator"][aria-orientation="vertical"]').forEach((n) => n.remove());
        setDragOverlayHtml(clone.outerHTML);
        setDragOverlayWidth(headerEl.offsetWidth);
      }
    } else {
      setActiveDragId(id);
      const ghost = reconstructFullRowGhost(id, tableRef.current);
      if (ghost) {
        setDragOverlayHtml(ghost.html);
        setDragOverlayWidth(ghost.width);
      }
    }
  }, []);
  const handleDragOver = React.useCallback((e) => {
    var _a2, _b2;
    const { active, over } = e;
    if (!active) return;
    if (!over) {
      if (!invalidRef.current) setInvalidDropActive(true);
      setDropIndicator(null);
      return;
    }
    if (invalidRef.current) setInvalidDropActive(false);
    if (active.id === over.id) {
      setDropIndicator(null);
      return;
    }
    const dragType = ((_b2 = (_a2 = active.data) == null ? void 0 : _a2.current) == null ? void 0 : _b2.type) ?? "row";
    if (dragType === "column") {
      const activeIdx = reorderableColumnIdsRef.current.indexOf(String(active.id));
      const overIdx = reorderableColumnIdsRef.current.indexOf(String(over.id));
      if (activeIdx === -1 || overIdx === -1) {
        setDropIndicator(null);
        return;
      }
      const side = activeIdx < overIdx ? "after" : "before";
      if (isReorderNoop(activeIdx, overIdx, side)) {
        setDropIndicator(null);
        return;
      }
      setDropIndicator({ id: String(over.id), side, type: "column" });
    } else {
      const activeIdx = allRowIds.indexOf(String(active.id));
      const overIdx = allRowIds.indexOf(String(over.id));
      if (activeIdx === -1 || overIdx === -1) {
        setDropIndicator(null);
        return;
      }
      const side = activeIdx < overIdx ? "after" : "before";
      if (isReorderNoop(activeIdx, overIdx, side)) {
        setDropIndicator(null);
        return;
      }
      setDropIndicator({ id: String(over.id), side, type: "row" });
    }
  }, [allRowIds, isReorderNoop]);
  const handleDragCancel = React.useCallback(() => {
    setActiveDragId(null);
    setActiveDragColId(null);
    setInvalidDropActive(false);
    setDragOverlayHtml(null);
    setDragOverlayWidth(null);
    setDropIndicator(null);
  }, []);
  const reorderableColumnIds = React.useMemo(() => {
    return table.getVisibleLeafColumns().map((c) => c.id).filter((id) => id && !isSystemColumn(id)).filter((id) => {
      var _a2;
      const meta = (_a2 = table.getColumn(id)) == null ? void 0 : _a2.columnDef.meta;
      return !(meta == null ? void 0 : meta.locked);
    });
  }, [table, columnsWithSelection, (_c = tableOptions == null ? void 0 : tableOptions.state) == null ? void 0 : _c.columnOrder]);
  React.useEffect(() => {
    reorderableColumnIdsRef.current = reorderableColumnIds;
  }, [reorderableColumnIds]);
  const handleDragEnd = React.useCallback((e) => {
    var _a2, _b2;
    const { active, over } = e;
    const type = ((_b2 = (_a2 = active.data) == null ? void 0 : _a2.current) == null ? void 0 : _b2.type) ?? "row";
    setActiveDragId(null);
    setActiveDragColId(null);
    setInvalidDropActive(false);
    setDragOverlayHtml(null);
    setDragOverlayWidth(null);
    setDropIndicator(null);
    if (!over || active.id === over.id) return;
    const sourceId = String(active.id);
    const targetId = String(over.id);
    if (type === "column") {
      const oldIdx2 = reorderableColumnIds.indexOf(sourceId);
      const newIdx2 = reorderableColumnIds.indexOf(targetId);
      if (oldIdx2 === -1 || newIdx2 === -1) return;
      const position2 = oldIdx2 < newIdx2 ? "after" : "before";
      if (isReorderNoop(oldIdx2, newIdx2, position2)) return;
      const activeRect = active.rect.current.translated ?? active.rect.current.initial;
      const overRect = over.rect;
      if (activeRect && overRect) {
        const ghostCenter = activeRect.left + activeRect.width / 2;
        const targetCenter = overRect.left + overRect.width / 2;
        if (oldIdx2 < newIdx2 && ghostCenter < targetCenter) return;
        if (oldIdx2 > newIdx2 && ghostCenter > targetCenter) return;
      }
      onColumnReorder == null ? void 0 : onColumnReorder(sourceId, targetId, position2);
      return;
    }
    if (parentMap.get(sourceId) !== parentMap.get(targetId)) return;
    const parentId = parentMap.get(sourceId);
    const siblings = allRowIds.filter((id) => parentMap.get(id) === parentId);
    const oldIdx = siblings.indexOf(sourceId);
    const newIdx = siblings.indexOf(targetId);
    if (oldIdx === -1 || newIdx === -1) return;
    const position = oldIdx < newIdx ? "after" : "before";
    if (isReorderNoop(oldIdx, newIdx, position)) return;
    onRowReorder == null ? void 0 : onRowReorder(sourceId, targetId, position);
  }, [allRowIds, parentMap, onRowReorder, onColumnReorder, reorderableColumnIds, isReorderNoop]);
  const dndCollisionDetection = React.useCallback((args) => {
    var _a2, _b2;
    const activeData = (_b2 = (_a2 = args.active) == null ? void 0 : _a2.data) == null ? void 0 : _b2.current;
    if ((activeData == null ? void 0 : activeData.type) === "column") {
      const filtered = args.droppableContainers.filter((c) => {
        var _a3, _b3;
        const cData = (_a3 = c.data) == null ? void 0 : _a3.current;
        return (cData == null ? void 0 : cData.type) === "column" && c.id !== ((_b3 = args.active) == null ? void 0 : _b3.id);
      });
      const filteredArgs = { ...args, droppableContainers: filtered };
      const pointer = pointerWithin(filteredArgs);
      return pointer.length > 0 ? pointer : rectIntersection(filteredArgs);
    }
    return sameParentCollisionDetection(args);
  }, [sameParentCollisionDetection]);
  const wrapWithDnd = (node) => {
    if (!enableRowDrag && !enableColumnReorder) return node;
    return /* @__PURE__ */ jsxs(
      DndContext,
      {
        sensors: dndSensors,
        measuring: { droppable: { strategy: MeasuringStrategy.Always } },
        collisionDetection: dndCollisionDetection,
        modifiers: [snapToCursorModifier],
        onDragStart: handleDragStart,
        onDragOver: handleDragOver,
        onDragCancel: handleDragCancel,
        onDragEnd: handleDragEnd,
        children: [
          node,
          /* @__PURE__ */ jsx(DragOverlay, { dropAnimation: null, children: dragOverlayHtml ? /* @__PURE__ */ jsx(
            "div",
            {
              style: { width: dragOverlayWidth ?? void 0 },
              className: "bg-surface-raised shadow-[var(--elevation-200)] rounded-md border border-border pointer-events-none",
              dangerouslySetInnerHTML: { __html: dragOverlayHtml }
            }
          ) : null })
        ]
      }
    );
  };
  if (enabled && mode === "single") {
    return /* @__PURE__ */ jsx(
      RadioGroupPrimitive.Root,
      {
        value: selection[0] ?? "",
        onValueChange: (v) => v && setSelection([v]),
        children: wrapWithDnd(tableContent)
      }
    );
  }
  return wrapWithDnd(tableContent);
}
const DataTable = React.forwardRef(DataTableInner);
DataTable.displayName = "DataTable";
const dataTableMeta = {
  component: "DataTable",
  family: null,
  // non-family composite / overlay / layout
  variants: {},
  sizes: {},
  states: ["default", "hover", "active", "focus-visible", "disabled"],
  tokens: {
    bg: ["bg-muted", "bg-neutral-hover", "bg-surface"],
    fg: ["text-fg-muted", "text-fg-secondary", "text-foreground"],
    ring: []
  }
};
export {
  DataTable,
  MIN_COLUMN_WIDTH,
  dataTableMeta,
  dataTableVariants
};
//# sourceMappingURL=data-table.js.map
