{"version":3,"file":"boostgrid.umd.cjs","sources":["../src/icons.ts","../src/dom.ts","../src/options.ts","../src/render/header.ts","../src/render/group.ts","../src/render/tree.ts","../src/render/cell-select.ts","../src/render/body.ts","../src/render/footer.ts","../src/render/toolbar.ts","../src/render/pagination.ts","../src/render/virtual.ts","../src/render/edit.ts","../src/state.ts","../src/core.ts","../src/index.ts"],"sourcesContent":["import type { IconSet } from \"./types.js\";\n\nexport const bootstrapIcons: IconSet = {\n  sortAsc: \"bi bi-caret-up-fill\",\n  sortDesc: \"bi bi-caret-down-fill\",\n  sortNone: \"bi bi-chevron-expand\",\n  search: \"bi bi-search\",\n  refresh: \"bi bi-arrow-clockwise\",\n  columns: \"bi bi-list-columns\",\n};\n\nexport const fontAwesomeIcons: IconSet = {\n  sortAsc: \"fa fa-sort-up\",\n  sortDesc: \"fa fa-sort-down\",\n  sortNone: \"fa fa-sort\",\n  search: \"fa fa-search\",\n  refresh: \"fa fa-refresh\",\n  columns: \"fa fa-th-list\",\n};\n","// Tiny DOM helpers — replace jQuery usage in the core. No external deps.\n\nexport function $<T extends Element = HTMLElement>(sel: string, root: ParentNode = document): T | null {\n  return root.querySelector<T>(sel);\n}\n\nexport function $$<T extends Element = HTMLElement>(sel: string, root: ParentNode = document): T[] {\n  return Array.from(root.querySelectorAll<T>(sel));\n}\n\nexport function el<K extends keyof HTMLElementTagNameMap>(\n  tag: K,\n  attrs: Record<string, string | boolean | null | undefined> = {},\n  ...children: (Node | string)[]\n): HTMLElementTagNameMap[K] {\n  const node = document.createElement(tag);\n  for (const [k, v] of Object.entries(attrs)) {\n    if (v == null || v === false) continue;\n    if (k === \"class\") node.className = String(v);\n    else if (k === \"html\") node.innerHTML = String(v);\n    else if (v === true) node.setAttribute(k, \"\");\n    else node.setAttribute(k, String(v));\n  }\n  for (const c of children) {\n    node.append(typeof c === \"string\" ? document.createTextNode(c) : c);\n  }\n  return node;\n}\n\n/**\n * Delegated event listener. Returns an unsubscribe function.\n * One listener per grid root replaces v1's per-row bindings.\n */\nexport function delegate(\n  root: Element,\n  type: string,\n  selector: string,\n  handler: (e: Event, target: HTMLElement) => void,\n): () => void {\n  const listener = (e: Event) => {\n    const target = e.target as Element | null;\n    if (!target) return;\n    const match = target.closest(selector) as HTMLElement | null;\n    if (match && root.contains(match)) handler(e, match);\n  };\n  root.addEventListener(type, listener);\n  return () => root.removeEventListener(type, listener);\n}\n\nexport function debounce<T extends (...a: never[]) => void>(fn: T, ms: number): (...a: Parameters<T>) => void {\n  let h: ReturnType<typeof setTimeout> | undefined;\n  return (...args: Parameters<T>) => {\n    if (h) clearTimeout(h);\n    h = setTimeout(() => fn(...args), ms);\n  };\n}\n\n/** Read all data-* attributes on an element, kebab → camelCase, with primitive coercion. */\nexport function readData(elm: Element): Record<string, unknown> {\n  const out: Record<string, unknown> = {};\n  for (const attr of Array.from(elm.attributes)) {\n    if (!attr.name.startsWith(\"data-\")) continue;\n    const key = attr.name\n      .slice(5)\n      .replace(/-([a-z])/g, (_, c: string) => c.toUpperCase());\n    out[key] = coerce(attr.value);\n  }\n  return out;\n}\n\nfunction coerce(v: string): unknown {\n  if (v === \"\" || v === \"true\") return true;\n  if (v === \"false\") return false;\n  if (v === \"null\") return null;\n  if (/^-?\\d+(\\.\\d+)?$/.test(v)) return Number(v);\n  if (v.startsWith(\"[\") || v.startsWith(\"{\")) {\n    try { return JSON.parse(v); } catch { /* fall through */ }\n  }\n  return v;\n}\n\nexport function clearChildren(node: Element): void {\n  while (node.firstChild) node.removeChild(node.firstChild);\n}\n","import type {\n  BoostgridOptions, Column, Converter, Row, Align, SortOrder, EditType, EditOption,\n} from \"./types.js\";\nimport { bootstrapIcons } from \"./icons.js\";\nimport { readData } from \"./dom.js\";\n\nconst numericConverter: Converter = {\n  from: (v) => Number(v),\n  to: (v) => (v == null ? \"\" : String(v)),\n};\nconst stringConverter: Converter = {\n  from: (v) => v,\n  to: (v) => (v == null ? \"\" : String(v)),\n};\n\nexport const defaults: BoostgridOptions<Row> = {\n  navigation: 3,\n  padding: 2,\n  columnSelection: true,\n  rowCount: [10, 25, 50, -1],\n  selection: false,\n  multiSelect: false,\n  rowSelect: false,\n  keepSelection: false,\n  highlightRows: false,\n  sorting: true,\n  multiSort: false,\n  caseSensitive: false,\n  searchSettings: { delay: 200, characters: 1 },\n  virtualScroll: false,\n  rowHeight: 38,\n  overscan: 5,\n  ajax: false,\n  url: \"\",\n  ajaxSettings: { method: \"POST\" },\n  requestHandler: (r) => r,\n  responseHandler: (r) => r,\n  converters: {\n    numeric: numericConverter,\n    string: stringConverter,\n  },\n  formatters: {},\n  footer: false,\n  footerFormatters: {},\n  footerCallback: null,\n  editable: false,\n  onCellEdit: null,\n  groupBy: null,\n  groupExpanded: \"all\",\n  groupAggregators: {},\n  groupSubtotalsOnTop: false,\n  columnReorder: true,\n  onColumnReorder: null,\n  columnResize: true,\n  onColumnResize: null,\n  treeReparent: false,\n  onReparent: null,\n  treeMode: false,\n  treeParentField: \"parentId\",\n  treeIdField: null,\n  treeIndentPx: 24,\n  treeColumn: null,\n  treeExpanded: \"all\",\n  rowDetail: null,\n  rowDetailExpanded: \"none\",\n  bulkActions: null,\n  loadingSkeleton: true,\n  cellSelection: false,\n  stickyHeader: false,\n  truncatedTooltips: true,\n  locale: null,\n  performanceMarks: false,\n  stateSave: false,\n  stateKey: null,\n  labels: {\n    all: \"All\",\n    infos: \"Showing {start} to {end} of {total} entries\",\n    loading: \"Loading…\",\n    noResults: \"No results found.\",\n    refresh: \"Refresh\",\n    search: \"Search\",\n    columns: \"Columns\",\n    resetColumns: \"Reset to defaults\",\n    searchColumns: \"Search columns\",\n    dragToReorder: \"Drag to reorder\",\n    resizeColumn: \"Resize {column}\",\n    rowDetailsHeader: \"Row details\",\n    showDetails: \"Show details\",\n    hideDetails: \"Hide details\",\n    bulkActions: \"Bulk actions\",\n    bulkSelected: \"{n} selected\",\n    bulkClear: \"Clear\",\n    treeExpand: \"Expand\",\n    treeCollapse: \"Collapse\",\n  },\n  icons: bootstrapIcons,\n  statusMapping: { 0: \"table-success\", 1: \"table-info\", 2: \"table-warning\", 3: \"table-danger\" },\n};\n\nexport function mergeOptions<TRow extends Row = Row>(\n  user: Partial<BoostgridOptions<TRow>> | undefined,\n  fromTable: Partial<BoostgridOptions<TRow>>,\n): BoostgridOptions<TRow> {\n  // The narrow defaults are valid for any TRow because the only fields the\n  // generic flows through are user-supplied dictionaries (`formatters`,\n  // `footerFormatters`, `footerCallback`); their built-in defaults are\n  // empty `{}` / `null` that satisfy the typed shape.\n  const base = defaults as unknown as BoostgridOptions<TRow>;\n  return {\n    ...base,\n    ...fromTable,\n    ...(user ?? {}),\n    searchSettings: { ...base.searchSettings, ...fromTable.searchSettings, ...user?.searchSettings },\n    ajaxSettings: { ...base.ajaxSettings, ...fromTable.ajaxSettings, ...user?.ajaxSettings },\n    converters: { ...base.converters, ...fromTable.converters, ...user?.converters },\n    formatters: { ...base.formatters, ...fromTable.formatters, ...user?.formatters },\n    footerFormatters: {\n      ...base.footerFormatters,\n      ...fromTable.footerFormatters,\n      ...user?.footerFormatters,\n    },\n    groupAggregators: {\n      ...base.groupAggregators,\n      ...fromTable.groupAggregators,\n      ...user?.groupAggregators,\n    },\n    labels: { ...base.labels, ...fromTable.labels, ...user?.labels },\n    icons: { ...base.icons, ...fromTable.icons, ...user?.icons },\n    statusMapping: { ...base.statusMapping, ...fromTable.statusMapping, ...user?.statusMapping },\n  };\n}\n\n/**\n * Parse columns from the table's <thead><tr>. Mirrors v1's data-* contract:\n * data-column-id, data-identifier, data-type, data-align, data-header-align,\n * data-css-class, data-header-css-class, data-formatter, data-order (asc|desc),\n * data-searchable, data-sortable, data-visible, data-width.\n */\nexport function parseColumns<TRow extends Row = Row>(\n  table: HTMLTableElement,\n  options: BoostgridOptions<TRow>,\n): Column<TRow>[] {\n  const headRow = table.querySelector(\"thead > tr\");\n  if (!headRow) return [];\n  const cols: Column<TRow>[] = [];\n  let firstSortClaimed = false;\n  let firstIdentifierClaimed = false;\n  for (const th of Array.from(headRow.children)) {\n    const d = readData(th);\n    const id = String(d.columnId ?? \"\");\n    if (!id) continue;\n    const type = String(d.type ?? \"string\");\n    const converter = options.converters[String(d.converter ?? type)] ?? options.converters.string;\n    const formatterKey = d.formatter ? String(d.formatter) : null;\n    const formatter = formatterKey ? options.formatters[formatterKey] ?? null : null;\n    const footerFormatterKey = d.footerFormatter ? String(d.footerFormatter) : null;\n    const footerFormatter = footerFormatterKey\n      ? options.footerFormatters[footerFormatterKey] ?? null\n      : null;\n    const isIdentifier = !firstIdentifierClaimed && d.identifier === true;\n    if (isIdentifier) firstIdentifierClaimed = true;\n    let order: SortOrder | null = null;\n    if (!firstSortClaimed && (d.order === \"asc\" || d.order === \"desc\")) {\n      order = d.order;\n      if (!options.multiSort) firstSortClaimed = true;\n    }\n    const widthRaw = d.width;\n    const width =\n      typeof widthRaw === \"number\" ? `${widthRaw}px` :\n      typeof widthRaw === \"string\" ? widthRaw :\n      null;\n    // Identifier columns are NEVER editable, even if data-editable=\"true\" was set.\n    const editableFromData = d.editable === true || (d.editable !== false && options.editable);\n    const editable = !isIdentifier && !!editableFromData;\n    const editType: EditType =\n      d.editType === \"number\" || d.editType === \"select\" || d.editType === \"text\"\n        ? d.editType\n        : (type === \"numeric\" ? \"number\" : \"text\");\n    const editOptions = Array.isArray(d.editOptions) ? (d.editOptions as EditOption[]) : [];\n\n    const frozen: \"left\" | \"right\" | null =\n      d.frozen === \"right\" ? \"right\" :\n      d.frozen === \"left\" || d.frozen === true ? \"left\" :\n      null;\n    const treeColumn = d.treeColumn === true;\n\n    const reorderable = d.reorderable !== false;\n    const resizable = d.resizable !== false;\n    const minWidthRaw = d.minWidth;\n    const maxWidthRaw = d.maxWidth;\n    const minWidth = typeof minWidthRaw === \"number\"\n      ? minWidthRaw\n      : typeof minWidthRaw === \"string\" && /^\\d+(\\.\\d+)?$/.test(minWidthRaw)\n        ? Number(minWidthRaw)\n        : 40;\n    const maxWidth = typeof maxWidthRaw === \"number\"\n      ? maxWidthRaw\n      : typeof maxWidthRaw === \"string\" && /^\\d+(\\.\\d+)?$/.test(maxWidthRaw)\n        ? Number(maxWidthRaw)\n        : Infinity;\n\n    cols.push({\n      id,\n      text: (th.textContent ?? \"\").trim(),\n      identifier: isIdentifier,\n      type,\n      converter,\n      align: (d.align as Align) ?? \"left\",\n      headerAlign: (d.headerAlign as Align) ?? \"left\",\n      cssClass: String(d.cssClass ?? \"\"),\n      headerCssClass: String(d.headerCssClass ?? \"\"),\n      formatter,\n      footerFormatter,\n      editable,\n      editType,\n      editOptions,\n      frozen,\n      treeColumn,\n      order,\n      searchable: d.searchable !== false,\n      sortable: d.sortable !== false && options.sorting,\n      visible: d.visible !== false,\n      width,\n      reorderable,\n      resizable,\n      minWidth,\n      maxWidth,\n    });\n  }\n  return cols;\n}\n\n/**\n * Read the existing tbody rows into row objects keyed by column id.\n * Used when the user provides static markup rather than ajax/data.\n */\nexport function parseRowsFromTable<TRow extends Row = Row>(\n  table: HTMLTableElement,\n  columns: Column<TRow>[],\n): TRow[] {\n  const rows: Row[] = [];\n  const trs = table.querySelectorAll(\"tbody > tr\");\n  trs.forEach((tr) => {\n    const row: Row = {};\n    Array.from(tr.children).forEach((td, i) => {\n      const col = columns[i];\n      if (!col) return;\n      row[col.id] = col.converter.from((td.textContent ?? \"\").trim());\n    });\n    rows.push(row);\n  });\n  // Caller-supplied TRow constrains the row shape; we only know fields\n  // declared as columns, so the remaining shape is the user's responsibility.\n  return rows as TRow[];\n}\n","import type { Boostgrid } from \"../core.js\";\r\nimport type { Row } from \"../types.js\";\r\nimport { $, clearChildren, el } from \"../dom.js\";\r\n\r\nconst ALIGN: Record<string, string> = {\r\n  left: \"text-start\",\r\n  center: \"text-center\",\r\n  right: \"text-end\",\r\n};\r\n\r\n/** px width assumed when a frozen column has no explicit `width`. */\r\nconst DEFAULT_FROZEN_WIDTH_PX = 120;\r\n\r\n/** Width of the selection cell in px (matches `width: 2.5rem;` at the BS5 16px root). */\r\nconst SELECTION_WIDTH_PX = 40;\r\n\r\n/** Width of the row-detail chevron cell in px (matches `width: 2.5rem;`). */\r\nconst DETAIL_WIDTH_PX = 40;\r\n\r\n/**\r\n * Try to interpret a column's `width` as a pixel value for sticky-left math.\r\n * Accepts \"120\", \"120px\", or null/falsy. Anything else (em/%, rem, etc.)\r\n * is reported as null so callers can fall back to a default.\r\n */\r\nexport function widthAsPx(width: string | null): number | null {\r\n  if (!width) return null;\r\n  const m = /^\\s*(\\d+(?:\\.\\d+)?)\\s*(?:px)?\\s*$/i.exec(width);\r\n  return m ? Number(m[1]) : null;\r\n}\r\n\r\n/**\r\n * Precomputed sticky-position offsets for one render pass.\r\n * `left[i]` and `right[i]` are the px offsets to apply to the i-th visible\r\n * column (whose `frozen` is \"left\" or \"right\" respectively). Cells that\r\n * aren't frozen on that side may still read these arrays harmlessly — the\r\n * caller gates on `col.frozen` before using the value.\r\n */\r\nexport interface FrozenOffsets {\r\n  left: number[];\r\n  right: number[];\r\n}\r\n\r\n/**\r\n * Compute both offset arrays in a single O(n) pass. Selection + row-detail\r\n * cell widths are added to the leading-left baseline so the first\r\n * frozen-left column sticks past them.\r\n */\r\nexport function computeFrozenOffsets<TRow extends Row>(\r\n  grid: Boostgrid<TRow>,\r\n  visibleCols: ReadonlyArray<{ frozen: \"left\" | \"right\" | null; width: string | null }>,\r\n): FrozenOffsets {\r\n  const baseLeft = (grid.options.selection ? SELECTION_WIDTH_PX : 0)\r\n    + (grid.options.rowDetail ? DETAIL_WIDTH_PX : 0);\r\n  const left = new Array<number>(visibleCols.length);\r\n  const right = new Array<number>(visibleCols.length);\r\n  let leftAcc = baseLeft;\r\n  for (let i = 0; i < visibleCols.length; i++) {\r\n    left[i] = leftAcc;\r\n    const col = visibleCols[i];\r\n    if (col.frozen === \"left\") leftAcc += widthAsPx(col.width) ?? DEFAULT_FROZEN_WIDTH_PX;\r\n  }\r\n  let rightAcc = 0;\r\n  for (let i = visibleCols.length - 1; i >= 0; i--) {\r\n    right[i] = rightAcc;\r\n    const col = visibleCols[i];\r\n    if (col.frozen === \"right\") rightAcc += widthAsPx(col.width) ?? DEFAULT_FROZEN_WIDTH_PX;\r\n  }\r\n  return { left, right };\r\n}\r\n\r\nexport function renderHeader<TRow extends Row = Row>(grid: Boostgrid<TRow>): void {\r\n  const thead = $(\"thead\", grid.element) ?? grid.element.appendChild(document.createElement(\"thead\"));\r\n  clearChildren(thead);\r\n  const tr = el(\"tr\");\r\n\r\n  const hasFrozen = grid.columns.some((c) => c.visible && c.frozen === \"left\");\r\n  if (grid.options.selection) {\r\n    const selectCellClasses = [\"bg-select-cell\"];\r\n    if (hasFrozen) selectCellClasses.push(\"boostgrid-frozen\");\r\n    const th = el(\"th\", { class: selectCellClasses.join(\" \"), style: \"width: 2.5rem; left: 0;\" });\r\n    if (grid.options.multiSelect) {\r\n      const cb = el(\"input\", { type: \"checkbox\", class: \"form-check-input bg-select-all\" });\r\n      th.appendChild(cb);\r\n    }\r\n    tr.appendChild(th);\r\n  }\r\n  if (grid.options.rowDetail) {\r\n    const detailClasses = [\"bg-detail-cell\"];\r\n    if (hasFrozen) detailClasses.push(\"boostgrid-frozen\");\r\n    const left = grid.options.selection ? SELECTION_WIDTH_PX : 0;\r\n    const th = el(\"th\", {\r\n      class: detailClasses.join(\" \"),\r\n      style: `width: 2.5rem;${hasFrozen ? ` left: ${left}px;` : \"\"}`,\r\n      \"aria-label\": grid.options.labels.rowDetailsHeader,\r\n    });\r\n    tr.appendChild(th);\r\n  }\r\n\r\n  const visibleCols = grid.columns.filter((c) => c.visible);\r\n  const offsets = computeFrozenOffsets(grid, visibleCols);\r\n\r\n  let visibleIndex = 0;\r\n  for (const col of visibleCols) {\r\n    const classes = [\r\n      \"boostgrid-th\",\r\n      ALIGN[col.headerAlign] ?? \"\",\r\n      col.headerCssClass,\r\n      col.frozen ? \"boostgrid-frozen\" : \"\",\r\n    ].filter(Boolean).join(\" \");\r\n    const styleParts: string[] = [];\r\n    if (col.width) styleParts.push(`width: ${col.width};`);\r\n    if (col.frozen === \"left\") {\r\n      styleParts.push(`left: ${offsets.left[visibleIndex]}px;`);\r\n    } else if (col.frozen === \"right\") {\r\n      styleParts.push(`right: ${offsets.right[visibleIndex]}px;`);\r\n    }\r\n    const th = el(\"th\", {\r\n      class: classes,\r\n      \"data-column-id\": col.id,\r\n      \"data-frozen-side\": col.frozen ?? null,\r\n      style: styleParts.length ? styleParts.join(\" \") : null,\r\n    });\r\n    visibleIndex++;\r\n    if (col.sortable) {\r\n      const link = el(\"a\", {\r\n        href: \"javascript:void(0);\",\r\n        class: \"boostgrid-sort\",\r\n        \"data-bg-action\": \"sort\",\r\n        \"data-bg-value\": col.id,\r\n      });\r\n      link.appendChild(document.createTextNode(col.text));\r\n      const order = grid.sortDictionary[col.id];\r\n      const iconCls =\r\n        order === \"asc\" ? grid.options.icons.sortAsc :\r\n        order === \"desc\" ? grid.options.icons.sortDesc :\r\n        grid.options.icons.sortNone;\r\n      const icon = el(\"i\", { class: `${iconCls} ms-1`, \"aria-hidden\": \"true\" });\r\n      link.appendChild(icon);\r\n      th.appendChild(link);\r\n    } else {\r\n      th.appendChild(document.createTextNode(col.text));\r\n    }\r\n    if (grid.options.columnReorder && col.reorderable) {\r\n      th.setAttribute(\"draggable\", \"true\");\r\n      th.classList.add(\"boostgrid-th--draggable\");\r\n    }\r\n    if (grid.options.columnResize && col.resizable) {\r\n      const grip = el(\"span\", {\r\n        class: \"boostgrid-resize-grip\",\r\n        \"data-bg-grip\": col.id,\r\n        role: \"separator\",\r\n        \"aria-orientation\": \"vertical\",\r\n        \"aria-label\": grid.options.labels.resizeColumn.replace(\"{column}\", col.text || col.id),\r\n      });\r\n      th.appendChild(grip);\r\n    }\r\n    tr.appendChild(th);\r\n  }\r\n  thead.appendChild(tr);\r\n}\r\n","import type { Boostgrid } from \"../core.js\";\r\nimport type { Column, GroupBy, GroupContext, Row } from \"../types.js\";\r\nimport { el } from \"../dom.js\";\r\n\r\nconst ALIGN: Record<string, string> = {\r\n  left: \"text-start\",\r\n  center: \"text-center\",\r\n  right: \"text-end\",\r\n};\r\n\r\n/** Path separator used to flatten group paths into stable Set keys. */\r\nexport const GROUP_PATH_SEP = \"//\";\r\n\r\n/** Stringify a single group key consistently — primitives compare by string. */\r\nexport function groupKeyToken(key: unknown): string {\r\n  return String(key == null ? \"\" : key);\r\n}\r\n\r\n/** Convert a path of raw keys to its canonical string form. */\r\nexport function groupPathToString(path: readonly unknown[]): string {\r\n  return path.map(groupKeyToken).join(GROUP_PATH_SEP);\r\n}\r\n\r\n/** Normalize the `groupBy` option to a deduped, non-empty string array.\r\n *  Returns `null` when grouping is disabled. */\r\nexport function normalizeGroupBy(g: GroupBy): readonly string[] | null {\r\n  if (g == null) return null;\r\n  const arr = typeof g === \"string\" ? [g] : g;\r\n  if (arr.length === 0) return null;\r\n  const seen = new Set<string>();\r\n  const out: string[] = [];\r\n  for (const k of arr) {\r\n    if (!seen.has(k)) { seen.add(k); out.push(k); }\r\n  }\r\n  return out;\r\n}\r\n\r\n/** Aggregator key resolution. `colId@N` wins over the bare `colId` at depth N. */\r\nexport function resolveAggregator<TRow extends Row>(\r\n  map: Record<string, (col: Column<TRow>, ctx: GroupContext<TRow>) => string>,\r\n  colId: string,\r\n  depth: number,\r\n): ((col: Column<TRow>, ctx: GroupContext<TRow>) => string) | undefined {\r\n  return map[`${colId}@${depth}`] ?? map[colId];\r\n}\r\n\r\n/**\r\n * Render directive emitted by {@link walkGroups}. body.ts iterates the\r\n * flat list and emits matching `<tr>`s. Keeping this flat (instead of a\r\n * nested tree) makes the body renderer a simple loop and lets us emit\r\n * footer rows AFTER child rows without re-walking.\r\n */\r\nexport type GroupDirective<TRow extends Row = Row> =\r\n  | { type: \"header\"; ctx: GroupContext<TRow>; expanded: boolean }\r\n  | { type: \"row\"; row: TRow }\r\n  | { type: \"footer\"; ctx: GroupContext<TRow> };\r\n\r\ninterface WalkOpts {\r\n  groupBy: readonly string[];\r\n  isCollapsed: (path: readonly unknown[]) => boolean;\r\n}\r\n\r\n/**\r\n * Walk the slice and produce a flat list of render directives. Sorting is\r\n * upstream (see `applySort` in core), so adjacency-based bucketing produces\r\n * correct groups in O(n) per level. Total work is O(n × levels).\r\n *\r\n * The walker stops descending into a collapsed branch — its rows simply\r\n * don't appear in the output. The branch's footer row is also skipped so\r\n * collapsed groups read cleanly.\r\n */\r\nexport function walkGroups<TRow extends Row = Row>(\r\n  grid: Boostgrid<TRow>,\r\n  slice: TRow[],\r\n): GroupDirective<TRow>[] {\r\n  const groupBy = normalizeGroupBy(grid.options.groupBy);\r\n  if (!groupBy) return [];\r\n  const out: GroupDirective<TRow>[] = [];\r\n  const opts: WalkOpts = {\r\n    groupBy,\r\n    // Delegate to grid.isGroupExpanded so `groupExpanded: \"none\"` and\r\n    // per-path overrides in `groupExpanded: Record<…>` are honored without\r\n    // re-encoding that logic here.\r\n    isCollapsed: (path) => !grid.isGroupExpanded(groupPathToString(path)),\r\n  };\r\n  walkLevel(grid, slice, [], 0, opts, out);\r\n  return out;\r\n}\r\n\r\nfunction walkLevel<TRow extends Row>(\r\n  grid: Boostgrid<TRow>,\r\n  rows: TRow[],\r\n  parentPath: readonly unknown[],\r\n  depth: number,\r\n  opts: WalkOpts,\r\n  out: GroupDirective<TRow>[],\r\n): void {\r\n  const colId = opts.groupBy[depth];\r\n  const column = grid.columns.find((c) => c.id === colId);\r\n  if (!column) {\r\n    // Unknown column at this depth — emit raw rows so the slice is still rendered.\r\n    for (const row of rows) out.push({ type: \"row\", row });\r\n    return;\r\n  }\r\n  // Adjacency-based bucketing within this level (sort upstream).\r\n  let bucketKey: unknown = Symbol(\"init\");\r\n  let bucket: TRow[] = [];\r\n  let siblingIndex = 0;\r\n  const flush = () => {\r\n    if (bucket.length === 0) return;\r\n    const path = [...parentPath, bucketKey];\r\n    const ctx: GroupContext<TRow> = {\r\n      groupKey: bucketKey,\r\n      groupLabel: column.converter.to(bucketKey),\r\n      groupPath: path,\r\n      depth,\r\n      rows: bucket,\r\n      index: siblingIndex++,\r\n    };\r\n    const collapsed = opts.isCollapsed(path);\r\n    out.push({ type: \"header\", ctx, expanded: !collapsed });\r\n    if (!collapsed) {\r\n      // When `groupSubtotalsOnTop` is on, the footer sits between the\r\n      // header and the bucket's rows so the subtotal reads first. Default\r\n      // remains \"rows then footer\" so existing layouts keep their feel.\r\n      if (grid.options.groupSubtotalsOnTop) out.push({ type: \"footer\", ctx });\r\n      const last = depth === opts.groupBy.length - 1;\r\n      if (last) {\r\n        for (const row of bucket) out.push({ type: \"row\", row });\r\n      } else {\r\n        walkLevel(grid, bucket, path, depth + 1, opts, out);\r\n      }\r\n      if (!grid.options.groupSubtotalsOnTop) out.push({ type: \"footer\", ctx });\r\n    }\r\n    bucket = [];\r\n  };\r\n  for (const row of rows) {\r\n    const key = (row as Record<string, unknown>)[colId];\r\n    if (bucket.length === 0) {\r\n      bucketKey = key;\r\n      bucket.push(row);\r\n    } else if (sameKey(bucketKey, key)) {\r\n      bucket.push(row);\r\n    } else {\r\n      flush();\r\n      bucketKey = key;\r\n      bucket.push(row);\r\n    }\r\n  }\r\n  flush();\r\n}\r\n\r\n/**\r\n * Build the group header `<tr>` — a colspan'd cell with caret + label\r\n * + count. `depth` drives the inline indent style; CSS picks up\r\n * `data-depth` for background tinting.\r\n */\r\nexport function renderGroupHeader<TRow extends Row = Row>(\r\n  _grid: Boostgrid<TRow>,\r\n  ctx: GroupContext<TRow>,\r\n  colSpan: number,\r\n  expanded: boolean,\r\n): HTMLTableRowElement {\r\n  const path = groupPathToString(ctx.groupPath);\r\n  const tr = el(\"tr\", {\r\n    class: `boostgrid-group-row${expanded ? \"\" : \" boostgrid-group-row--collapsed\"}`,\r\n    \"data-bg-action\": \"toggle-group\",\r\n    \"data-bg-value\": path,\r\n    \"data-depth\": String(ctx.depth),\r\n  });\r\n  const td = el(\"td\", {\r\n    colspan: String(colSpan),\r\n    class: \"boostgrid-group-cell\",\r\n    style: `--bg-group-depth: ${ctx.depth};`,\r\n  });\r\n  const caret = el(\"i\", {\r\n    class: expanded ? \"bi bi-caret-down-fill me-2\" : \"bi bi-caret-right-fill me-2\",\r\n    \"aria-hidden\": \"true\",\r\n  });\r\n  td.appendChild(caret);\r\n  td.appendChild(document.createTextNode(`${ctx.groupLabel} (${ctx.rows.length})`));\r\n  tr.appendChild(td);\r\n  return tr;\r\n}\r\n\r\n/**\r\n * Build the optional group footer `<tr>` — one cell per visible column\r\n * (plus the leading selection cell if applicable), populated by\r\n * `groupAggregators`. Returns `null` when no aggregator produced a value.\r\n */\r\nexport function renderGroupFooter<TRow extends Row = Row>(\r\n  grid: Boostgrid<TRow>,\r\n  ctx: GroupContext<TRow>,\r\n  visibleCols: Column<TRow>[],\r\n): HTMLTableRowElement | null {\r\n  const aggregators = grid.options.groupAggregators;\r\n  let any = false;\r\n  const tr = el(\"tr\", {\r\n    class: \"boostgrid-group-footer\",\r\n    \"data-depth\": String(ctx.depth),\r\n  });\r\n  if (grid.options.selection) tr.appendChild(el(\"td\", { class: \"bg-select-cell\" }));\r\n\r\n  for (const col of visibleCols) {\r\n    const aggr = resolveAggregator(aggregators, col.id, ctx.depth);\r\n    const classes = [ALIGN[col.align] ?? \"\", col.cssClass, \"boostgrid-group-aggregate\"]\r\n      .filter(Boolean).join(\" \");\r\n    const td = el(\"td\", { class: classes, \"data-column-id\": col.id });\r\n    if (aggr) {\r\n      const out = aggr(col, ctx);\r\n      if (out) {\r\n        td.innerHTML = out;\r\n        any = true;\r\n      }\r\n    }\r\n    tr.appendChild(td);\r\n  }\r\n  return any ? tr : null;\r\n}\r\n\r\nfunction sameKey(a: unknown, b: unknown): boolean {\r\n  return a === b || (a == null && b == null) || String(a) === String(b);\r\n}\r\n","import type { Boostgrid } from \"../core.js\";\nimport type { Row } from \"../types.js\";\n\n/**\n * Internal node shape. `id` is whatever the row holds in the\n * id field — typically string or number. Children are kept in\n * insertion order (sorted upstream by `applySort`).\n */\nexport interface TreeNode<TRow extends Row = Row> {\n  id: string | number;\n  row: TRow;\n  parentId: string | number | null;\n  depth: number;\n  children: TreeNode<TRow>[];\n}\n\nexport interface BuildTreeResult<TRow extends Row = Row> {\n  /** Roots in original (sorted) order. */\n  roots: TreeNode<TRow>[];\n  /** Lookup table by id, including all descendants. */\n  byId: Map<string | number, TreeNode<TRow>>;\n  /** Set of row ids that the search predicate matched, when search is active. */\n  matchedIds: Set<string | number>;\n}\n\ninterface BuildOpts<TRow extends Row> {\n  rows: TRow[];\n  idField: string;\n  parentField: string;\n  /** Optional search predicate; when provided, ancestors of matches stay visible. */\n  matchFn?: (row: TRow) => boolean;\n}\n\n/**\n * Normalize a raw id-or-parent-id value to a comparable token. Empty strings\n * are treated as \"no value\" (root marker); numeric strings are coerced to\n * numbers so a row with `id: 1` matches `parentId: \"1\"` (which is what the\n * default string converter yields for `<td>1</td>`). Returns `null` when\n * the value should be ignored (missing parent / no row id).\n */\nfunction normalizeId(v: unknown): string | number | null {\n  if (v == null) return null;\n  if (typeof v === \"number\") return Number.isFinite(v) ? v : null;\n  if (typeof v === \"string\") {\n    const t = v.trim();\n    if (t === \"\") return null;\n    if (/^-?\\d+(\\.\\d+)?$/.test(t)) return Number(t);\n    return t;\n  }\n  return null;\n}\n\n/**\n * Build a tree from a flat (already filtered/sorted) row list using the\n * adjacency-list shape. Detects cycles defensively (warns and breaks the\n * cycle by treating the offending node as a root). Orphans (parent id\n * pointing nowhere) are also promoted to roots with a warning.\n *\n * Ids and parent ids are normalized through {@link normalizeId} so a row\n * with `id: 1` matches a child carrying `parentId: \"1\"` — the common case\n * when parents come back numeric and children round-tripped through string\n * cells.\n */\nexport function buildTree<TRow extends Row>(opts: BuildOpts<TRow>): BuildTreeResult<TRow> {\n  const { rows, idField, parentField, matchFn } = opts;\n  const byId = new Map<string | number, TreeNode<TRow>>();\n  const matchedIds = new Set<string | number>();\n\n  // First pass: create every node. Don't link parents yet — we need the full\n  // map first so we can detect orphans deterministically.\n  for (const row of rows) {\n    const rawId = (row as Record<string, unknown>)[idField];\n    const id = normalizeId(rawId);\n    if (id == null) continue; // rows without id can't appear in a tree\n    if (byId.has(id)) {\n      // eslint-disable-next-line no-console\n      console.warn(`Boostgrid: duplicate tree id ${String(id)} — last wins.`);\n    }\n    byId.set(id, { id, row, parentId: null, depth: 0, children: [] });\n    if (matchFn && matchFn(row)) matchedIds.add(id);\n  }\n\n  const roots: TreeNode<TRow>[] = [];\n\n  // Second pass: link children to parents. Maintain row-order.\n  for (const row of rows) {\n    const rawId = (row as Record<string, unknown>)[idField];\n    const id = normalizeId(rawId);\n    if (id == null) continue;\n    const node = byId.get(id);\n    if (!node) continue;\n    const parentRaw = (row as Record<string, unknown>)[parentField];\n    const parentId = normalizeId(parentRaw);\n    if (parentId == null) {\n      roots.push(node);\n      continue;\n    }\n    const parent = byId.get(parentId);\n    if (!parent) {\n      // Orphan — promote to root.\n      // eslint-disable-next-line no-console\n      console.warn(\n        `Boostgrid: tree row ${String(id)} references unknown parent ${String(parentId)} — promoted to root.`,\n      );\n      roots.push(node);\n      continue;\n    }\n    if (wouldCycle(parent, id, byId)) {\n      // eslint-disable-next-line no-console\n      console.warn(\n        `Boostgrid: tree cycle detected at id ${String(id)} — promoted to root.`,\n      );\n      roots.push(node);\n      continue;\n    }\n    node.parentId = parentId;\n    parent.children.push(node);\n  }\n\n  // Third pass: assign depths via BFS from roots.\n  for (const root of roots) {\n    root.depth = 0;\n    const queue: TreeNode<TRow>[] = [root];\n    while (queue.length) {\n      const n = queue.shift()!;\n      for (const c of n.children) {\n        c.depth = n.depth + 1;\n        queue.push(c);\n      }\n    }\n  }\n\n  // If a search predicate was used, expand ancestor chain of every match.\n  // We modify nothing here; ancestor inclusion happens during walkTree below.\n\n  return { roots, byId, matchedIds };\n}\n\n/** True if linking `childId` under `parent` would create a cycle. Walks\n *  ancestors of `parent` looking for `childId`. */\nfunction wouldCycle<TRow extends Row>(\n  parent: TreeNode<TRow>,\n  childId: string | number,\n  byId: Map<string | number, TreeNode<TRow>>,\n): boolean {\n  const seen = new Set<string | number>();\n  let cur: TreeNode<TRow> | undefined = parent;\n  while (cur) {\n    if (cur.id === childId) return true;\n    if (seen.has(cur.id)) return true; // safety net for already-corrupt graphs\n    seen.add(cur.id);\n    if (cur.parentId == null) return false;\n    cur = byId.get(cur.parentId);\n  }\n  return false;\n}\n\n/**\n * DFS-walk the tree and produce a flat list of (node, depth) tuples in\n * render order. Hidden subtrees are pruned. When `matchedIds` is non-empty\n * (search active), nodes are visible if (a) they match, or (b) any\n * descendant matches — and ALL ancestors of any match are auto-expanded\n * for this render pass without mutating user expand state.\n */\nexport function walkTree<TRow extends Row>(\n  grid: Boostgrid<TRow>,\n  result: BuildTreeResult<TRow>,\n): { node: TreeNode<TRow>; visible: boolean }[] {\n  const out: { node: TreeNode<TRow>; visible: boolean }[] = [];\n  const searching = result.matchedIds.size > 0;\n\n  // Pre-compute \"subtree contains a match\" for visibility filtering.\n  // Short-circuit when an ancestor was already walked: if 100 leaves match\n  // a deep subtree, the chain to the root used to be re-walked 100 times.\n  const subtreeMatches = new Set<string | number>();\n  if (searching) {\n    const markUp = (id: string | number) => {\n      let cur = result.byId.get(id);\n      while (cur) {\n        if (subtreeMatches.has(cur.id)) break; // already walked from a sibling\n        subtreeMatches.add(cur.id);\n        if (cur.parentId == null) break;\n        cur = result.byId.get(cur.parentId);\n      }\n    };\n    for (const id of result.matchedIds) markUp(id);\n  }\n\n  const isExpanded = (id: string | number): boolean => {\n    if (searching && subtreeMatches.has(id)) return true; // auto-expand ancestor chain\n    return grid.isTreeExpanded(id);\n  };\n\n  const dfs = (node: TreeNode<TRow>) => {\n    if (searching && !subtreeMatches.has(node.id)) return; // pruned\n    out.push({ node, visible: true });\n    if (!isExpanded(node.id)) return;\n    for (const child of node.children) dfs(child);\n  };\n\n  for (const root of result.roots) dfs(root);\n  return out;\n}\n\n/**\n * Resolve which column id should carry the caret. Priority order:\n *  1. `options.treeColumn` if set and the column exists+visible\n *  2. First column with `treeColumn: true` (and visible)\n *  3. First visible non-frozen column\n *  4. First visible column\n */\nexport function resolveTreeColumnId<TRow extends Row>(\n  grid: Boostgrid<TRow>,\n): string | null {\n  const visible = grid.columns.filter((c) => c.visible);\n  if (visible.length === 0) return null;\n  const opt = grid.options.treeColumn;\n  if (opt) {\n    const found = visible.find((c) => c.id === opt);\n    if (found) return found.id;\n  }\n  const flagged = visible.find((c) => c.treeColumn);\n  if (flagged) return flagged.id;\n  const nonFrozen = visible.find((c) => c.frozen !== \"left\");\n  if (nonFrozen) return nonFrozen.id;\n  return visible[0].id;\n}\n","import type { Boostgrid } from \"../core.js\";\r\nimport type { Row } from \"../types.js\";\r\n\r\n/**\r\n * Inclusive rectangle of (rowIndex, colIndex) pairs into `currentRows` and\r\n * the visible-columns list. Both axes are normalized so `start <= end`.\r\n */\r\nexport interface CellRange {\r\n  startRow: number;\r\n  endRow: number;\r\n  startCol: number;\r\n  endCol: number;\r\n}\r\n\r\n/** Per-grid live state for cell selection. */\r\nexport interface CellSelectState {\r\n  range: CellRange | null;\r\n  anchorRow: number;\r\n  anchorCol: number;\r\n  isDragging: boolean;\r\n}\r\n\r\nconst SELECTED_CLASS = \"boostgrid-cell-selected\";\r\n\r\n/**\r\n * Mount the cell-selection listeners. Returns a cleanup fn that the\r\n * caller pushes into `grid.cleanupFns`.\r\n *\r\n * The listeners run on the wrapper container so they survive body\r\n * re-renders without needing to re-bind.\r\n */\r\nexport function mountCellSelection<TRow extends Row = Row>(grid: Boostgrid<TRow>): () => void {\r\n  const root = grid.element.parentElement; // .boostgrid wrapper\r\n  if (!root) return () => { /* nothing mounted */ };\r\n\r\n  const state: CellSelectState = {\r\n    range: null,\r\n    anchorRow: 0,\r\n    anchorCol: 0,\r\n    isDragging: false,\r\n  };\r\n  // Attach state to the grid so renderBody (or any caller) can re-paint\r\n  // the highlight after a render replaces the DOM nodes.\r\n  (grid as unknown as { cellSelectState?: CellSelectState }).cellSelectState = state;\r\n\r\n  const findCellCoords = (target: EventTarget | null): { row: number; col: number } | null => {\r\n    const td = (target as Element | null)?.closest?.(\"td\") as HTMLTableCellElement | null;\r\n    if (!td) return null;\r\n    const tr = td.parentElement as HTMLTableRowElement | null;\r\n    if (!tr || !tr.parentElement) return null;\r\n    if (tr.classList.contains(\"boostgrid-skeleton-row\")) return null;\r\n    if (tr.classList.contains(\"boostgrid-detail-row\")) return null;\r\n    if (tr.classList.contains(\"boostgrid-group-row\")) return null;\r\n    if (tr.classList.contains(\"boostgrid-group-footer\")) return null;\r\n    const tbody = tr.parentElement;\r\n    // Row index = position among non-pseudo rows in the tbody.\r\n    let row = -1;\r\n    let i = 0;\r\n    for (const child of Array.from(tbody.children)) {\r\n      if (\r\n        child.classList.contains(\"boostgrid-skeleton-row\") ||\r\n        child.classList.contains(\"boostgrid-detail-row\") ||\r\n        child.classList.contains(\"boostgrid-group-row\") ||\r\n        child.classList.contains(\"boostgrid-group-footer\") ||\r\n        child.classList.contains(\"boostgrid-pad\")\r\n      ) continue;\r\n      if (child === tr) { row = i; break; }\r\n      i++;\r\n    }\r\n    if (row === -1) return null;\r\n    // Col index: count <td>'s with data-column-id up to this cell.\r\n    let col = -1;\r\n    let j = 0;\r\n    for (const cell of Array.from(tr.children)) {\r\n      if (!(cell as HTMLElement).hasAttribute(\"data-column-id\")) continue;\r\n      if (cell === td) { col = j; break; }\r\n      j++;\r\n    }\r\n    if (col === -1) return null;\r\n    return { row, col };\r\n  };\r\n\r\n  const onMouseDown = (ev: MouseEvent) => {\r\n    if (ev.button !== 0) return; // only left-click\r\n    const coords = findCellCoords(ev.target);\r\n    if (!coords) return;\r\n    state.anchorRow = coords.row;\r\n    state.anchorCol = coords.col;\r\n    state.range = {\r\n      startRow: coords.row, endRow: coords.row,\r\n      startCol: coords.col, endCol: coords.col,\r\n    };\r\n    state.isDragging = true;\r\n    paint(grid, state.range);\r\n    // Without preventDefault here, the browser's native text-selection\r\n    // would fight the drag. preventDefault keeps the user inside our\r\n    // rectangle gesture.\r\n    ev.preventDefault();\r\n  };\r\n\r\n  const onMouseMove = (ev: MouseEvent) => {\r\n    if (!state.isDragging) return;\r\n    const coords = findCellCoords(ev.target);\r\n    if (!coords) return;\r\n    state.range = normalize(state.anchorRow, state.anchorCol, coords.row, coords.col);\r\n    paint(grid, state.range);\r\n  };\r\n\r\n  const onMouseUp = () => {\r\n    state.isDragging = false;\r\n  };\r\n\r\n  // Shift-click extends the existing range without tearing down the anchor.\r\n  const onClick = (ev: MouseEvent) => {\r\n    if (!ev.shiftKey || !state.range) return;\r\n    const coords = findCellCoords(ev.target);\r\n    if (!coords) return;\r\n    state.range = normalize(state.anchorRow, state.anchorCol, coords.row, coords.col);\r\n    paint(grid, state.range);\r\n  };\r\n\r\n  // Document-level copy + escape so the user doesn't have to focus a cell first.\r\n  const onCopy = (ev: ClipboardEvent) => {\r\n    if (!state.range) return;\r\n    if (!root.contains(document.activeElement) && document.activeElement !== document.body) {\r\n      // Only steal copy when the grid has focus context (user is inside it).\r\n      return;\r\n    }\r\n    const tsv = rangeToTsv(grid, state.range);\r\n    if (ev.clipboardData) {\r\n      ev.clipboardData.setData(\"text/plain\", tsv);\r\n      ev.preventDefault();\r\n    }\r\n  };\r\n\r\n  const onKeyDown = (ev: KeyboardEvent) => {\r\n    if (ev.key !== \"Escape\") return;\r\n    if (!state.range) return;\r\n    state.range = null;\r\n    paint(grid, null);\r\n  };\r\n\r\n  root.addEventListener(\"mousedown\", onMouseDown);\r\n  root.addEventListener(\"mousemove\", onMouseMove);\r\n  root.addEventListener(\"click\", onClick);\r\n  document.addEventListener(\"mouseup\", onMouseUp);\r\n  document.addEventListener(\"copy\", onCopy);\r\n  document.addEventListener(\"keydown\", onKeyDown);\r\n\r\n  return () => {\r\n    root.removeEventListener(\"mousedown\", onMouseDown);\r\n    root.removeEventListener(\"mousemove\", onMouseMove);\r\n    root.removeEventListener(\"click\", onClick);\r\n    document.removeEventListener(\"mouseup\", onMouseUp);\r\n    document.removeEventListener(\"copy\", onCopy);\r\n    document.removeEventListener(\"keydown\", onKeyDown);\r\n    paint(grid, null);\r\n    delete (grid as unknown as { cellSelectState?: CellSelectState }).cellSelectState;\r\n  };\r\n}\r\n\r\nfunction normalize(ar: number, ac: number, fr: number, fc: number): CellRange {\r\n  return {\r\n    startRow: Math.min(ar, fr),\r\n    endRow:   Math.max(ar, fr),\r\n    startCol: Math.min(ac, fc),\r\n    endCol:   Math.max(ac, fc),\r\n  };\r\n}\r\n\r\n/** Walk the tbody and toggle the selected class on cells in the rectangle. */\r\nexport function paint<TRow extends Row>(grid: Boostgrid<TRow>, range: CellRange | null): void {\r\n  const tbody = grid.element.querySelector(\"tbody\");\r\n  if (!tbody) return;\r\n  // Clear previous painting first (cheap — class set is small).\r\n  tbody.querySelectorAll(`.${SELECTED_CLASS}`).forEach((el) => el.classList.remove(SELECTED_CLASS));\r\n  if (!range) return;\r\n  // Real data rows are everything that ISN'T a pseudo-row. We can't\r\n  // gate on `data-row-id` because tables without an identifier column\r\n  // (cell-selection demos, anonymous tabular data) won't carry one.\r\n  const dataRows = Array.from(tbody.children).filter((tr) => {\r\n    return !tr.classList.contains(\"boostgrid-detail-row\")\r\n      && !tr.classList.contains(\"boostgrid-group-row\")\r\n      && !tr.classList.contains(\"boostgrid-group-footer\")\r\n      && !tr.classList.contains(\"boostgrid-skeleton-row\")\r\n      && !tr.classList.contains(\"boostgrid-pad\");\r\n  });\r\n  for (let r = range.startRow; r <= range.endRow; r++) {\r\n    const tr = dataRows[r];\r\n    if (!tr) continue;\r\n    const cells = Array.from(tr.children).filter((c) => (c as HTMLElement).hasAttribute(\"data-column-id\"));\r\n    for (let c = range.startCol; c <= range.endCol; c++) {\r\n      const td = cells[c];\r\n      if (td) td.classList.add(SELECTED_CLASS);\r\n    }\r\n  }\r\n}\r\n\r\n/** Build a tab-separated string for the rectangle. Uses the column's\r\n *  converter (no formatter HTML) — same convention as CSV export. */\r\nexport function rangeToTsv<TRow extends Row>(\r\n  grid: Boostgrid<TRow>,\r\n  range: CellRange,\r\n): string {\r\n  const visible = grid.columns.filter((c) => c.visible);\r\n  const rows = grid.currentRows.slice(range.startRow, range.endRow + 1);\r\n  const lines: string[] = [];\r\n  for (const row of rows) {\r\n    const cells: string[] = [];\r\n    for (let c = range.startCol; c <= range.endCol; c++) {\r\n      const col = visible[c];\r\n      if (!col) continue;\r\n      const v = (row as Record<string, unknown>)[col.id];\r\n      // Strip tabs/newlines so the TSV stays one-row-per-line.\r\n      const s = col.converter.to(v).replace(/[\\t\\r\\n]+/g, \" \");\r\n      cells.push(s);\r\n    }\r\n    lines.push(cells.join(\"\\t\"));\r\n  }\r\n  return lines.join(\"\\n\");\r\n}\r\n","import type { Boostgrid } from \"../core.js\";\r\nimport type { Column, Row } from \"../types.js\";\r\nimport { $, clearChildren, el } from \"../dom.js\";\r\nimport { walkGroups, renderGroupHeader, renderGroupFooter } from \"./group.js\";\r\nimport { computeFrozenOffsets, type FrozenOffsets } from \"./header.js\";\r\nimport { buildTree, walkTree, resolveTreeColumnId, type TreeNode } from \"./tree.js\";\r\nimport { paint as paintCellSelection, type CellSelectState } from \"./cell-select.js\";\r\n\r\n/** Per-row hierarchy hint passed from the tree walker to buildRow. */\r\ninterface TreeRowMeta<TRow extends Row> {\r\n  node: TreeNode<TRow>;\r\n  treeColumnId: string;\r\n  indentPx: number;\r\n  isExpanded: boolean;\r\n}\r\n\r\n/**\r\n * Cell-paint closure resolved once per column at the top of `renderBody`,\r\n * then invoked per-cell with no further branching. Replaces the inner\r\n * `col.formatter ? td.innerHTML = … : td.textContent = …` branch that\r\n * previously fired N rows × M cols times per render.\r\n *\r\n * Note: tree-mode caret cells have their own composition path inside\r\n * `buildRow` and don't go through this array.\r\n */\r\ntype CellPaint<TRow extends Row> = (td: HTMLTableCellElement, row: TRow) => void;\r\n\r\nfunction makeCellPaint<TRow extends Row>(visibleCols: Column<TRow>[]): CellPaint<TRow>[] {\r\n  return visibleCols.map((col) =>\r\n    col.formatter\r\n      ? (td: HTMLTableCellElement, row: TRow) => { td.innerHTML = col.formatter!(col, row); }\r\n      : (td: HTMLTableCellElement, row: TRow) => { td.textContent = col.converter.to(row[col.id]); },\r\n  );\r\n}\r\n\r\nconst ALIGN: Record<string, string> = {\r\n  left: \"text-start\",\r\n  center: \"text-center\",\r\n  right: \"text-end\",\r\n};\r\n\r\n/**\r\n * Renders the current page of rows. Uses a DocumentFragment so the\r\n * <tbody> sees a single mutation per render — much cheaper than per-row\r\n * appendChild and avoids parsing HTML strings.\r\n */\r\nexport function renderBody<TRow extends Row = Row>(grid: Boostgrid<TRow>): void {\r\n  const tbody = $(\"tbody\", grid.element) ?? grid.element.appendChild(document.createElement(\"tbody\"));\r\n\r\n  // Virtual scroll pad-only fast path: when the visible slice is unchanged\r\n  // but the pad heights moved (e.g. ajax delivered more rows while the\r\n  // user was scrolled at the top), mutate the pad <tr>s in place instead\r\n  // of rebuilding the entire body. This MUST run before clearChildren so\r\n  // the pad rows still exist in the DOM. Cell-selection range stays valid\r\n  // because the data <tr>s keep their identity.\r\n  if (grid.options.virtualScroll && grid.virtualWindow) {\r\n    const win = grid.virtualWindow;\r\n    const prev = grid.lastRenderedVirtualWindow;\r\n    if (\r\n      prev\r\n      && prev.start === win.start\r\n      && prev.end === win.end\r\n      && (prev.padTop !== win.padTop || prev.padBottom !== win.padBottom)\r\n      && tbody.children.length > 0\r\n    ) {\r\n      const first = tbody.firstElementChild as HTMLElement | null;\r\n      const last = tbody.lastElementChild as HTMLElement | null;\r\n      if (first?.classList.contains(\"boostgrid-pad\")) first.style.height = `${win.padTop}px`;\r\n      if (last && last !== first && last.classList.contains(\"boostgrid-pad\")) {\r\n        last.style.height = `${win.padBottom}px`;\r\n      }\r\n      grid.lastRenderedVirtualWindow = { ...win };\r\n      return;\r\n    }\r\n  }\r\n  const visibleCols = grid.columns.filter((c) => c.visible);\r\n  const leadingCells = (grid.options.selection ? 1 : 0) + (grid.options.rowDetail ? 1 : 0);\r\n  const colSpan = visibleCols.length + leadingCells;\r\n  // Precompute frozen-side offsets once per render. Without this, every\r\n  // cell in every row would re-walk the visible-columns array via\r\n  // frozenLeftPx / frozenRightPx — quadratic in column count.\r\n  const offsets = computeFrozenOffsets(grid, visibleCols);\r\n  // Resolve the cell-paint closure once per column. The inner per-cell\r\n  // loop in `buildRow` then calls paint[i](td, row) with zero branching.\r\n  const paint = makeCellPaint(visibleCols);\r\n\r\n  // Virtual scroll element pool — reuse <tr>s in place when the windowed\r\n  // slice changes. Avoids the create+append+GC cost of rebuilding the\r\n  // visible slice on every scroll tick. Only the flat windowed-list shape\r\n  // goes through here; tree mode and groupBy take precedence as before\r\n  // and continue to render via the fragment path below.\r\n  if (\r\n    grid.options.virtualScroll\r\n    && grid.virtualWindow\r\n    && grid.currentRows.length > 0\r\n    && !grid.options.treeMode\r\n    && !grid.options.groupBy\r\n  ) {\r\n    grid.commitActiveEdit?.();\r\n    const win = grid.virtualWindow;\r\n    const { topPad, bottomPad } = ensurePadRows(tbody, colSpan);\r\n    // Collect existing pool rows; discard any tbody child that isn't a\r\n    // pad and isn't a pool row (raw HTML rows on first mount, leftovers\r\n    // from a prior tree/group render, \"no results\" row).\r\n    const pool: HTMLTableRowElement[] = [];\r\n    for (const child of Array.from(tbody.children)) {\r\n      if (child === topPad || child === bottomPad) continue;\r\n      if (child.classList.contains(\"boostgrid-pool-row\")) {\r\n        pool.push(child as HTMLTableRowElement);\r\n      } else {\r\n        child.remove();\r\n      }\r\n    }\r\n    const needed = win.end - win.start;\r\n    while (pool.length < needed) {\r\n      const tr = bareRow(grid, visibleCols, offsets);\r\n      tbody.insertBefore(tr, bottomPad);\r\n      pool.push(tr);\r\n    }\r\n    while (pool.length > needed) {\r\n      pool.pop()!.remove();\r\n    }\r\n    for (let i = 0; i < needed; i++) {\r\n      rebindRow(grid, pool[i], grid.currentRows[win.start + i], visibleCols, paint);\r\n    }\r\n    topPad.style.height = `${win.padTop}px`;\r\n    bottomPad.style.height = `${win.padBottom}px`;\r\n    grid.lastRenderedVirtualWindow = { ...win };\r\n    grid.rerenderSelectionState();\r\n    const css = (grid as unknown as { cellSelectState?: CellSelectState }).cellSelectState;\r\n    if (css?.range) paintCellSelection(grid, css.range);\r\n    return;\r\n  }\r\n\r\n  clearChildren(tbody);\r\n\r\n  if (grid.currentRows.length === 0) {\r\n    const tr = el(\"tr\");\r\n    const td = el(\"td\", { class: \"boostgrid-no-results text-center text-muted py-4\", colspan: String(colSpan) });\r\n    td.textContent = grid.options.labels.noResults;\r\n    tr.appendChild(td);\r\n    tbody.appendChild(tr);\r\n    return;\r\n  }\r\n\r\n  // Tree mode: build the tree from the current slice (already filtered+sorted),\r\n  // then DFS-render visible nodes with depth-driven indent + caret.\r\n  if (grid.options.treeMode) {\r\n    const treeColumnId = resolveTreeColumnId(grid);\r\n    if (treeColumnId && grid.identifier) {\r\n      const idField = grid.options.treeIdField ?? grid.identifier;\r\n      const parentField = grid.options.treeParentField;\r\n      const search = grid.getSearchPhrase();\r\n      const re = search\r\n        ? new RegExp(escapeRegExp(search), grid.options.caseSensitive ? \"\" : \"i\")\r\n        : null;\r\n      const matchFn = re\r\n        ? (row: TRow) => {\r\n            for (const col of visibleCols) {\r\n              if (!col.searchable) continue;\r\n              if (re.test(col.converter.to(row[col.id]))) return true;\r\n            }\r\n            return false;\r\n          }\r\n        : undefined;\r\n      const tree = buildTree<TRow>({\r\n        rows: grid.currentRows,\r\n        idField,\r\n        parentField,\r\n        matchFn,\r\n      });\r\n      const flat = walkTree(grid, tree);\r\n      const indent = grid.options.treeIndentPx;\r\n      const frag = document.createDocumentFragment();\r\n      for (const { node } of flat) {\r\n        const meta: TreeRowMeta<TRow> = {\r\n          node,\r\n          treeColumnId,\r\n          indentPx: indent,\r\n          isExpanded: grid.isTreeExpanded(node.id),\r\n        };\r\n        frag.appendChild(buildRow(grid, node.row, visibleCols, offsets, paint, meta));\r\n      }\r\n      tbody.appendChild(frag);\r\n      return;\r\n    }\r\n  }\r\n\r\n  // Row grouping: bucket the slice by groupBy column(s), emitting nested\r\n  // headers and optional aggregator footers per level. Collapsed branches\r\n  // skip both their member rows AND their footers.\r\n  if (grid.options.groupBy && !grid.options.treeMode) {\r\n    const directives = walkGroups(grid, grid.currentRows);\r\n    if (directives.length > 0) {\r\n      const frag = document.createDocumentFragment();\r\n      for (const d of directives) {\r\n        if (d.type === \"header\") {\r\n          frag.appendChild(renderGroupHeader(grid, d.ctx, colSpan, d.expanded));\r\n        } else if (d.type === \"row\") {\r\n          frag.appendChild(buildRow(grid, d.row, visibleCols, offsets, paint));\r\n        } else {\r\n          const footer = renderGroupFooter(grid, d.ctx, visibleCols);\r\n          if (footer) frag.appendChild(footer);\r\n        }\r\n      }\r\n      tbody.appendChild(frag);\r\n      return;\r\n    }\r\n  }\r\n\r\n  const frag = document.createDocumentFragment();\r\n  for (const row of grid.currentRows) {\r\n    frag.appendChild(buildRow(grid, row, visibleCols, offsets, paint));\r\n    const detail = buildDetailRow(grid, row, visibleCols, colSpan);\r\n    if (detail) frag.appendChild(detail);\r\n  }\r\n  tbody.appendChild(frag);\r\n  // Re-apply any active cell-selection rectangle — the render replaced\r\n  // every <td>, so the highlight class was wiped.\r\n  const css = (grid as unknown as { cellSelectState?: CellSelectState }).cellSelectState;\r\n  if (css?.range) paintCellSelection(grid, css.range);\r\n}\r\n\r\n/**\r\n * Build the detail-panel `<tr>` for a row when `rowDetail` is set and the\r\n * row is currently expanded. Returns `null` when the panel should not\r\n * render. The panel cell is colspan'd across all visible columns + the\r\n * leading affordance cells.\r\n */\r\nfunction buildDetailRow<TRow extends Row>(\r\n  grid: Boostgrid<TRow>,\r\n  row: TRow,\r\n  _visibleCols: Column<TRow>[],\r\n  colSpan: number,\r\n): HTMLTableRowElement | null {\r\n  if (!grid.options.rowDetail || !grid.identifier) return null;\r\n  const id = row[grid.identifier] as string | number;\r\n  if (id == null) return null;\r\n  if (!grid.isRowDetailExpanded(id)) return null;\r\n  const content = grid.options.rowDetail(row);\r\n  if (content == null) return null;\r\n  const tr = el(\"tr\", {\r\n    class: \"boostgrid-detail-row\",\r\n    \"data-row-id\": String(id),\r\n  });\r\n  const td = el(\"td\", { colspan: String(colSpan), class: \"boostgrid-detail-cell\" });\r\n  if (typeof content === \"string\") {\r\n    td.innerHTML = content;\r\n  } else {\r\n    td.appendChild(content);\r\n  }\r\n  tr.appendChild(td);\r\n  return tr;\r\n}\r\n\r\nfunction padRow(colSpan: number, height: number): HTMLTableRowElement {\r\n  const tr = el(\"tr\", { class: \"boostgrid-pad\", \"aria-hidden\": \"true\", style: `height: ${height}px;` });\r\n  // Single colspan'd <td> so the row has no visible cells.\r\n  tr.appendChild(el(\"td\", { colspan: String(colSpan), style: \"padding: 0; border: 0;\" }));\r\n  return tr;\r\n}\r\n\r\n/**\r\n * Render `count` shimmer-style placeholder rows into the grid's tbody —\r\n * one cell per visible column plus the leading affordance cells. Used\r\n * during ajax fetches so the user sees structure (not just a blank\r\n * \"Loading...\") while bytes are in flight. Cleared on the next\r\n * `renderBody()` call.\r\n */\r\nexport function renderSkeleton<TRow extends Row = Row>(\r\n  grid: Boostgrid<TRow>,\r\n  count: number,\r\n): void {\r\n  const tbody = $(\"tbody\", grid.element) ?? grid.element.appendChild(document.createElement(\"tbody\"));\r\n  clearChildren(tbody);\r\n  const visibleCols = grid.columns.filter((c) => c.visible);\r\n  const frag = document.createDocumentFragment();\r\n  for (let i = 0; i < count; i++) {\r\n    const tr = el(\"tr\", { class: \"boostgrid-skeleton-row\", \"aria-hidden\": \"true\" });\r\n    if (grid.options.selection) tr.appendChild(el(\"td\", { class: \"bg-select-cell\" }));\r\n    if (grid.options.rowDetail) tr.appendChild(el(\"td\", { class: \"bg-detail-cell\" }));\r\n    for (const col of visibleCols) {\r\n      const td = el(\"td\", { \"data-column-id\": col.id });\r\n      td.appendChild(el(\"span\", { class: \"boostgrid-skeleton-bar\", \"aria-hidden\": \"true\" }));\r\n      tr.appendChild(td);\r\n    }\r\n    frag.appendChild(tr);\r\n  }\r\n  tbody.appendChild(frag);\r\n}\r\n\r\nfunction buildRow<TRow extends Row>(\r\n  grid: Boostgrid<TRow>,\r\n  row: TRow,\r\n  visibleCols: Column<TRow>[],\r\n  offsets: FrozenOffsets,\r\n  paint: CellPaint<TRow>[],\r\n  treeMeta?: TreeRowMeta<TRow>,\r\n): HTMLTableRowElement {\r\n  const idCol = grid.identifier;\r\n  const rowId = idCol ? String(row[idCol] ?? \"\") : \"\";\r\n  const trAttrs: Record<string, string | null> = { \"data-row-id\": rowId || null };\r\n  if (treeMeta) {\r\n    trAttrs.class = `boostgrid-tree-row${treeMeta.isExpanded ? \" boostgrid-tree-row--expanded\" : \"\"}`;\r\n    trAttrs[\"data-tree-depth\"] = String(treeMeta.node.depth);\r\n    if (grid.options.treeReparent && rowId) {\r\n      trAttrs.draggable = \"true\";\r\n    }\r\n  }\r\n  const tr = el(\"tr\", trAttrs);\r\n\r\n  const hasFrozen = visibleCols.some((c) => c.frozen === \"left\");\r\n  if (grid.options.selection) {\r\n    const cls = hasFrozen ? \"bg-select-cell boostgrid-frozen\" : \"bg-select-cell\";\r\n    const td = el(\"td\", { class: cls, style: hasFrozen ? \"left: 0;\" : null });\r\n    const cb = el(\"input\", {\r\n      type: grid.options.multiSelect ? \"checkbox\" : \"radio\",\r\n      class: \"form-check-input bg-select-row\",\r\n      name: \"bg-select\",\r\n    });\r\n    td.appendChild(cb);\r\n    tr.appendChild(td);\r\n  }\r\n  if (grid.options.rowDetail) {\r\n    const cls = hasFrozen\r\n      ? \"bg-detail-cell boostgrid-frozen\"\r\n      : \"bg-detail-cell\";\r\n    const left = grid.options.selection ? 40 : 0;\r\n    const td = el(\"td\", {\r\n      class: cls,\r\n      style: hasFrozen ? `left: ${left}px;` : null,\r\n    });\r\n    if (rowId) {\r\n      const expanded = grid.isRowDetailExpanded(\r\n        /^-?\\d+(\\.\\d+)?$/.test(rowId) ? Number(rowId) : rowId,\r\n      );\r\n      const caret = el(\"span\", {\r\n        class: `boostgrid-detail-caret${expanded ? \" boostgrid-detail-caret--open\" : \"\"}`,\r\n        \"data-bg-action\": \"toggle-detail\",\r\n        \"data-bg-value\": rowId,\r\n        role: \"button\",\r\n        \"aria-label\": expanded ? grid.options.labels.hideDetails : grid.options.labels.showDetails,\r\n      });\r\n      const icon = el(\"i\", {\r\n        class: expanded ? \"bi bi-chevron-down\" : \"bi bi-chevron-right\",\r\n        \"aria-hidden\": \"true\",\r\n      });\r\n      caret.appendChild(icon);\r\n      td.appendChild(caret);\r\n    }\r\n    tr.appendChild(td);\r\n  }\r\n\r\n  let visibleIndex = 0;\r\n  for (const col of visibleCols) {\r\n    const classes = [\r\n      ALIGN[col.align] ?? \"\",\r\n      col.cssClass,\r\n      col.frozen ? \"boostgrid-frozen\" : \"\",\r\n    ].filter(Boolean).join(\" \");\r\n    const styleParts: string[] = [];\r\n    if (col.frozen === \"left\") {\r\n      styleParts.push(`left: ${offsets.left[visibleIndex]}px;`);\r\n    } else if (col.frozen === \"right\") {\r\n      styleParts.push(`right: ${offsets.right[visibleIndex]}px;`);\r\n    }\r\n    const isTreeCell = treeMeta && col.id === treeMeta.treeColumnId;\r\n    if (isTreeCell) {\r\n      const indent = treeMeta!.node.depth * treeMeta!.indentPx;\r\n      // Use padding-left so the caret + content stay inside the cell box.\r\n      styleParts.push(`padding-left: ${indent + 8}px;`);\r\n    }\r\n    const td = el(\"td\", {\r\n      class: classes,\r\n      \"data-column-id\": col.id,\r\n      \"data-editable\": col.editable ? \"true\" : null,\r\n      \"data-frozen-side\": col.frozen ?? null,\r\n      style: styleParts.length ? styleParts.join(\" \") : null,\r\n    });\r\n    visibleIndex++;\r\n    if (isTreeCell) {\r\n      // Caret cell: clickable when the node has children, inert spacer otherwise.\r\n      const node = treeMeta!.node;\r\n      if (node.children.length > 0) {\r\n        const caret = el(\"span\", {\r\n          class: `boostgrid-tree-caret${treeMeta!.isExpanded ? \" boostgrid-tree-caret--open\" : \"\"}`,\r\n          \"data-bg-action\": \"toggle-tree\",\r\n          \"data-bg-value\": String(node.id),\r\n          role: \"button\",\r\n          \"aria-label\": treeMeta!.isExpanded ? grid.options.labels.treeCollapse : grid.options.labels.treeExpand,\r\n        });\r\n        const icon = el(\"i\", {\r\n          class: treeMeta!.isExpanded ? \"bi bi-caret-down-fill\" : \"bi bi-caret-right-fill\",\r\n          \"aria-hidden\": \"true\",\r\n        });\r\n        caret.appendChild(icon);\r\n        td.appendChild(caret);\r\n      } else {\r\n        td.appendChild(el(\"span\", { class: \"boostgrid-tree-leaf\", \"aria-hidden\": \"true\" }));\r\n      }\r\n      const content = el(\"span\", { class: \"boostgrid-tree-label\" });\r\n      if (col.formatter) content.innerHTML = col.formatter(col, row);\r\n      else content.textContent = col.converter.to(row[col.id]);\r\n      td.appendChild(content);\r\n    } else {\r\n      // Pre-resolved paint closure — no per-cell branching. visibleIndex\r\n      // was incremented above; the array is 0-indexed so we use -1.\r\n      paint[visibleIndex - 1](td, row);\r\n    }\r\n    tr.appendChild(td);\r\n  }\r\n  return tr;\r\n}\r\n\r\n/**\r\n * Build an empty <tr> with all structural cells in place but no row data\r\n * bound — used by the virtual-scroll pool path. Leading select / detail\r\n * cells are added when those features are on; cells start in default\r\n * (unselected / collapsed) state and get rebound per row by `rebindRow`.\r\n * Frozen offsets and column-derived classes/attributes are written once\r\n * here and never touched again — they're row-invariant.\r\n */\r\nfunction bareRow<TRow extends Row>(\r\n  grid: Boostgrid<TRow>,\r\n  visibleCols: Column<TRow>[],\r\n  offsets: FrozenOffsets,\r\n): HTMLTableRowElement {\r\n  // Marker class — pool-path-only. Lets the pool tell apart its own\r\n  // recyclable <tr>s from any pre-existing rows in <tbody> (raw HTML,\r\n  // \"no results\" row, leftovers from a prior tree/group render).\r\n  const tr = el(\"tr\", { class: \"boostgrid-pool-row\" });\r\n  const hasFrozen = visibleCols.some((c) => c.frozen === \"left\");\r\n  if (grid.options.selection) {\r\n    const cls = hasFrozen ? \"bg-select-cell boostgrid-frozen\" : \"bg-select-cell\";\r\n    const td = el(\"td\", { class: cls, style: hasFrozen ? \"left: 0;\" : null });\r\n    const cb = el(\"input\", {\r\n      type: grid.options.multiSelect ? \"checkbox\" : \"radio\",\r\n      class: \"form-check-input bg-select-row\",\r\n      name: \"bg-select\",\r\n    });\r\n    td.appendChild(cb);\r\n    tr.appendChild(td);\r\n  }\r\n  if (grid.options.rowDetail) {\r\n    const cls = hasFrozen ? \"bg-detail-cell boostgrid-frozen\" : \"bg-detail-cell\";\r\n    const left = grid.options.selection ? 40 : 0;\r\n    const td = el(\"td\", {\r\n      class: cls,\r\n      style: hasFrozen ? `left: ${left}px;` : null,\r\n    });\r\n    const caret = el(\"span\", {\r\n      class: \"boostgrid-detail-caret\",\r\n      \"data-bg-action\": \"toggle-detail\",\r\n      role: \"button\",\r\n    });\r\n    const icon = el(\"i\", { class: \"bi bi-chevron-right\", \"aria-hidden\": \"true\" });\r\n    caret.appendChild(icon);\r\n    td.appendChild(caret);\r\n    tr.appendChild(td);\r\n  }\r\n  let visibleIndex = 0;\r\n  for (const col of visibleCols) {\r\n    const classes = [\r\n      ALIGN[col.align] ?? \"\",\r\n      col.cssClass,\r\n      col.frozen ? \"boostgrid-frozen\" : \"\",\r\n    ].filter(Boolean).join(\" \");\r\n    const styleParts: string[] = [];\r\n    if (col.frozen === \"left\") styleParts.push(`left: ${offsets.left[visibleIndex]}px;`);\r\n    else if (col.frozen === \"right\") styleParts.push(`right: ${offsets.right[visibleIndex]}px;`);\r\n    const td = el(\"td\", {\r\n      class: classes,\r\n      \"data-column-id\": col.id,\r\n      \"data-editable\": col.editable ? \"true\" : null,\r\n      \"data-frozen-side\": col.frozen ?? null,\r\n      style: styleParts.length ? styleParts.join(\" \") : null,\r\n    });\r\n    tr.appendChild(td);\r\n    visibleIndex++;\r\n  }\r\n  return tr;\r\n}\r\n\r\n/**\r\n * Rebind a pooled <tr> to a new row payload. Updates data-row-id, the\r\n * leading detail-caret expanded state (selection checkbox state is\r\n * applied later by `refreshSelectionVisuals`), and writes new content\r\n * into each data cell via the pre-resolved paint closures.\r\n */\r\nfunction rebindRow<TRow extends Row>(\r\n  grid: Boostgrid<TRow>,\r\n  tr: HTMLTableRowElement,\r\n  row: TRow,\r\n  visibleCols: Column<TRow>[],\r\n  paint: CellPaint<TRow>[],\r\n): void {\r\n  const idCol = grid.identifier;\r\n  const rowId = idCol ? String(row[idCol] ?? \"\") : \"\";\r\n  if (rowId) tr.dataset.rowId = rowId;\r\n  else delete tr.dataset.rowId;\r\n  let cellOffset = 0;\r\n  if (grid.options.selection) cellOffset++;\r\n  if (grid.options.rowDetail) {\r\n    const caret = tr.children[cellOffset]?.querySelector<HTMLElement>(\".boostgrid-detail-caret\");\r\n    if (caret) {\r\n      const idValue = /^-?\\d+(\\.\\d+)?$/.test(rowId) ? Number(rowId) : rowId;\r\n      const expanded = !!rowId && grid.isRowDetailExpanded(idValue);\r\n      caret.className = `boostgrid-detail-caret${expanded ? \" boostgrid-detail-caret--open\" : \"\"}`;\r\n      if (rowId) caret.setAttribute(\"data-bg-value\", rowId);\r\n      else caret.removeAttribute(\"data-bg-value\");\r\n      caret.setAttribute(\r\n        \"aria-label\",\r\n        expanded ? grid.options.labels.hideDetails : grid.options.labels.showDetails,\r\n      );\r\n      const icon = caret.querySelector(\"i\");\r\n      if (icon) icon.className = expanded ? \"bi bi-chevron-down\" : \"bi bi-chevron-right\";\r\n    }\r\n    cellOffset++;\r\n  }\r\n  for (let i = 0; i < visibleCols.length; i++) {\r\n    const td = tr.children[cellOffset + i] as HTMLTableCellElement | undefined;\r\n    if (td) paint[i](td, row);\r\n  }\r\n}\r\n\r\n/**\r\n * Ensure the tbody has a top and bottom pad row, creating either if\r\n * missing. Used by the virtual-scroll pool path so recycled data <tr>s\r\n * can be inserted between them without worrying about first-render\r\n * state.\r\n */\r\nfunction ensurePadRows(tbody: HTMLElement, colSpan: number): {\r\n  topPad: HTMLTableRowElement;\r\n  bottomPad: HTMLTableRowElement;\r\n} {\r\n  let topPad = tbody.firstElementChild as HTMLTableRowElement | null;\r\n  if (!topPad || !topPad.classList.contains(\"boostgrid-pad\")) {\r\n    topPad = padRow(colSpan, 0);\r\n    tbody.insertBefore(topPad, tbody.firstChild);\r\n  }\r\n  let bottomPad = tbody.lastElementChild as HTMLTableRowElement | null;\r\n  if (!bottomPad || !bottomPad.classList.contains(\"boostgrid-pad\") || bottomPad === topPad) {\r\n    bottomPad = padRow(colSpan, 0);\r\n    tbody.appendChild(bottomPad);\r\n  }\r\n  return { topPad, bottomPad };\r\n}\r\n\r\nfunction escapeRegExp(s: string): string {\r\n  return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n}\r\n","import type { Boostgrid } from \"../core.js\";\r\nimport type { FooterContext, Row } from \"../types.js\";\r\nimport { $, clearChildren, el } from \"../dom.js\";\r\nimport { computeFrozenOffsets } from \"./header.js\";\r\n\r\nconst ALIGN: Record<string, string> = {\r\n  left: \"text-start\",\r\n  center: \"text-center\",\r\n  right: \"text-end\",\r\n};\r\n\r\n/**\r\n * Renders <tfoot>. Three opt-in signals:\r\n *   1. options.footer === true            (explicit)\r\n *   2. options.footerCallback is set       (user wants a draw hook)\r\n *   3. any column has footerFormatter set  (declarative per-column totals)\r\n *   4. <tfoot> already exists in markup    (user authored static footer cells)\r\n *\r\n * If none of the above are true the existing <tfoot> (if any) is left\r\n * untouched — a defensive default that lets users keep static footer\r\n * markup without paying for a re-render every draw.\r\n *\r\n * Order of operations per draw:\r\n *   a. Locate or create <tfoot>.\r\n *   b. Build a fresh <tr> with one cell per visible column (+ leading\r\n *      empty cell when selection is on, mirroring renderHeader).\r\n *   c. For columns with a footerFormatter, set the cell's innerHTML.\r\n *   d. Replace the previous <tr> in <tfoot> with the new one.\r\n *   e. Run footerCallback last so it can override anything formatters wrote.\r\n */\r\nexport function renderFooter<TRow extends Row = Row>(grid: Boostgrid<TRow>): void {\r\n  const opts = grid.options;\r\n  const hasFormatters = grid.columns.some((c) => c.footerFormatter);\r\n  const hasMarkup = !!grid.element.querySelector(\":scope > tfoot\");\r\n  const enabled = opts.footer || !!opts.footerCallback || hasFormatters || hasMarkup;\r\n  if (!enabled) return;\r\n\r\n  let tfoot = $<HTMLTableSectionElement>(\":scope > tfoot\", grid.element);\r\n  let auto = false;\r\n  if (!tfoot) {\r\n    tfoot = grid.element.appendChild(document.createElement(\"tfoot\"));\r\n    auto = true;\r\n  }\r\n  if (auto) tfoot.dataset.boostgridAuto = \"true\";\r\n\r\n  const visibleCols = grid.columns.filter((c) => c.visible);\r\n  const offsets = computeFrozenOffsets(grid, visibleCols);\r\n  const tr = el(\"tr\");\r\n\r\n  if (opts.selection) {\r\n    const hasFrozen = visibleCols.some((c) => c.frozen === \"left\");\r\n    const cls = hasFrozen ? \"bg-select-cell boostgrid-frozen\" : \"bg-select-cell\";\r\n    tr.appendChild(el(\"th\", { class: cls, style: hasFrozen ? \"left: 0;\" : null }));\r\n  }\r\n\r\n  let visibleIndex = 0;\r\n  for (const col of visibleCols) {\r\n    const classes = [\r\n      \"boostgrid-tf\",\r\n      ALIGN[col.align] ?? \"\",\r\n      col.cssClass,\r\n      col.frozen === \"left\" ? \"boostgrid-frozen\" : \"\",\r\n    ].filter(Boolean).join(\" \");\r\n    const styleParts: string[] = [];\r\n    if (col.width) styleParts.push(`width: ${col.width};`);\r\n    if (col.frozen === \"left\") {\r\n      styleParts.push(`left: ${offsets.left[visibleIndex]}px;`);\r\n    }\r\n    const th = el(\"th\", {\r\n      class: classes,\r\n      \"data-column-id\": col.id,\r\n      style: styleParts.length ? styleParts.join(\" \") : null,\r\n    });\r\n    visibleIndex++;\r\n    if (col.footerFormatter) {\r\n      th.innerHTML = col.footerFormatter(col, buildContext(grid));\r\n    }\r\n    tr.appendChild(th);\r\n  }\r\n\r\n  clearChildren(tfoot);\r\n  tfoot.appendChild(tr);\r\n\r\n  if (opts.footerCallback) {\r\n    opts.footerCallback(tr as HTMLTableRowElement, buildContext(grid));\r\n  }\r\n}\r\n\r\n/**\r\n * Snapshot the grid's view state for footer formatters and the table-level\r\n * callback. Arrays are *copies* — formatters that mutate them won't poison\r\n * the grid's internal state.\r\n */\r\nexport function buildContext<TRow extends Row = Row>(grid: Boostgrid<TRow>): FooterContext<TRow> {\r\n  const rowsPerPage = grid.getRowCount();\r\n  const currentRows = grid.currentRows.slice();\r\n  const start = currentRows.length === 0 ? 0 : (grid.current - 1) * (rowsPerPage === -1 ? 0 : rowsPerPage) + 1;\r\n  const end = currentRows.length === 0 ? 0 : start + currentRows.length - 1;\r\n  const idCol = grid.identifier;\r\n  const selectedIds = new Set(grid.getSelectedRows());\r\n  const filteredRows = grid.getFilteredRows();\r\n  const allRows = grid.getAllRows();\r\n  const selectedRows = idCol\r\n    ? allRows.filter((r) => selectedIds.has(r[idCol]))\r\n    : [];\r\n  return {\r\n    currentRows,\r\n    filteredRows,\r\n    allRows,\r\n    selectedRows,\r\n    start,\r\n    end,\r\n    pageIndex: grid.current,\r\n    totalPages: grid.totalPages || 1,\r\n  };\r\n}\r\n","import type { Boostgrid } from \"../core.js\";\r\nimport type { Row } from \"../types.js\";\r\nimport { el } from \"../dom.js\";\r\n\r\n/**\r\n * One-shot guard: warn (once per page load, not per grid) when the top\r\n * toolbar is about to render dropdowns but Bootstrap's JS bundle isn't\r\n * loaded on the host page. Without `bootstrap.Dropdown` the page-size\r\n * picker and column-visibility menu look correct but never open — a\r\n * silent failure that's costly to debug if you don't know it's a host\r\n * dependency. Skipped in non-browser contexts (SSR, vitest workers\r\n * that don't define `window`).\r\n */\r\nlet _bootstrapJsWarned = false;\r\nfunction warnIfBootstrapJsMissing(): void {\r\n  if (_bootstrapJsWarned) return;\r\n  if (typeof window === \"undefined\") return;\r\n  const w = window as Window & { bootstrap?: { Dropdown?: unknown } };\r\n  if (w.bootstrap && w.bootstrap.Dropdown) return;\r\n  _bootstrapJsWarned = true;\r\n  // eslint-disable-next-line no-console\r\n  console.warn(\r\n    \"[boostgrid] Bootstrap's JS bundle is not loaded on this page \" +\r\n    \"(window.bootstrap.Dropdown is missing). The page-size picker and \" +\r\n    \"column-visibility menu will render but won't open. Add \" +\r\n    \"https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js \" +\r\n    \"to your host page. See the docs Getting started section for the full \" +\r\n    \"host-page dependency list (Bootstrap CSS/JS + an icon font).\",\r\n  );\r\n}\r\n\r\n/**\r\n * Toolbar shell: search input, row-count dropdown, column-toggle dropdown,\r\n * pagination + info row. Mounted above and/or below the table according\r\n * to options.navigation.\r\n */\r\nexport function renderToolbar<TRow extends Row = Row>(grid: Boostgrid<TRow>, position: \"top\" | \"bottom\"): HTMLDivElement {\r\n  const bar = el(\"div\", { class: `boostgrid-toolbar boostgrid-toolbar-${position} d-flex flex-wrap align-items-center gap-2 my-2` });\r\n\r\n  // top toolbar gets controls; bottom only gets pagination + info\r\n  if (position === \"top\") {\r\n    warnIfBootstrapJsMissing();\r\n    bar.appendChild(buildSearch(grid));\r\n    bar.appendChild(buildRowCountDropdown(grid));\r\n    if (grid.options.columnSelection) bar.appendChild(buildColumnDropdown(grid));\r\n    bar.appendChild(el(\"button\", {\r\n      type: \"button\",\r\n      class: \"btn btn-outline-secondary btn-sm\",\r\n      \"data-bg-action\": \"refresh\",\r\n      title: grid.options.labels.refresh,\r\n    }, iconEl(grid.options.icons.refresh)));\r\n  }\r\n\r\n  // info + pagination shared\r\n  const tail = el(\"div\", { class: \"ms-auto d-flex align-items-center gap-3\" });\r\n  tail.appendChild(el(\"div\", { class: \"bg-infos small text-muted\" }));\r\n  tail.appendChild(el(\"nav\", { class: \"bg-pagination\", \"aria-label\": \"pagination\" }));\r\n  bar.appendChild(tail);\r\n\r\n  return bar;\r\n}\r\n\r\nfunction buildSearch<TRow extends Row>(grid: Boostgrid<TRow>): HTMLElement {\r\n  const wrap = el(\"div\", { class: \"bg-search input-group input-group-sm\", style: \"width: 16rem;\" });\r\n  wrap.appendChild(el(\"span\", { class: \"input-group-text\" }, iconEl(grid.options.icons.search)));\r\n  wrap.appendChild(el(\"input\", {\r\n    type: \"search\",\r\n    class: \"form-control\",\r\n    placeholder: grid.options.labels.search,\r\n    \"aria-label\": grid.options.labels.search,\r\n  }));\r\n  return wrap;\r\n}\r\n\r\nfunction buildRowCountDropdown<TRow extends Row>(grid: Boostgrid<TRow>): HTMLElement {\r\n  const counts = Array.isArray(grid.options.rowCount) ? grid.options.rowCount : [grid.options.rowCount];\r\n  const wrap = el(\"div\", { class: \"dropdown\" });\r\n  const btn = el(\"button\", {\r\n    class: \"btn btn-outline-secondary btn-sm dropdown-toggle\",\r\n    type: \"button\",\r\n    \"data-bs-toggle\": \"dropdown\",\r\n    \"aria-expanded\": \"false\",\r\n  });\r\n  btn.textContent = String(grid.getRowCount() === -1 ? grid.options.labels.all : grid.getRowCount());\r\n  wrap.appendChild(btn);\r\n  const ul = el(\"ul\", { class: \"dropdown-menu\" });\r\n  for (const c of counts) {\r\n    const li = el(\"li\");\r\n    const a = el(\"a\", {\r\n      class: \"dropdown-item\",\r\n      href: \"javascript:void(0);\",\r\n      \"data-bg-action\": \"rows-per-page\",\r\n      \"data-bg-value\": String(c),\r\n    });\r\n    a.textContent = c === -1 ? grid.options.labels.all : String(c);\r\n    li.appendChild(a);\r\n    ul.appendChild(li);\r\n  }\r\n  wrap.appendChild(ul);\r\n  return wrap;\r\n}\r\n\r\nfunction buildColumnDropdown<TRow extends Row>(grid: Boostgrid<TRow>): HTMLElement {\r\n  const wrap = el(\"div\", { class: \"dropdown boostgrid-columns-panel\" });\r\n  const btn = el(\"button\", {\r\n    class: \"btn btn-outline-secondary btn-sm dropdown-toggle\",\r\n    type: \"button\",\r\n    \"data-bs-toggle\": \"dropdown\",\r\n    \"data-bs-auto-close\": \"outside\",\r\n    \"aria-expanded\": \"false\",\r\n    title: grid.options.labels.columns,\r\n  }, iconEl(grid.options.icons.columns));\r\n  wrap.appendChild(btn);\r\n  const menu = el(\"div\", {\r\n    class: \"dropdown-menu dropdown-menu-end p-2 boostgrid-columns-menu\",\r\n    style: \"min-width: 16rem;\",\r\n  });\r\n\r\n  // Search input — filters the list locally via a delegated input handler\r\n  // (data-bg-action=\"filter-columns\") that toggles a hidden class per item.\r\n  const search = el(\"input\", {\r\n    type: \"search\",\r\n    class: \"form-control form-control-sm mb-2\",\r\n    placeholder: grid.options.labels.searchColumns,\r\n    \"data-bg-action\": \"filter-columns\",\r\n    \"aria-label\": grid.options.labels.searchColumns,\r\n  });\r\n  menu.appendChild(search);\r\n\r\n  const list = el(\"div\", { class: \"boostgrid-columns-list\" });\r\n  for (const col of grid.columns) {\r\n    const item = el(\"div\", {\r\n      class: \"boostgrid-columns-item d-flex align-items-center gap-2 px-1 py-1\",\r\n      \"data-column-id\": col.id,\r\n      draggable: grid.options.columnReorder && col.reorderable ? \"true\" : null,\r\n    });\r\n    if (grid.options.columnReorder && col.reorderable) {\r\n      const handle = el(\"span\", {\r\n        class: \"boostgrid-columns-handle text-muted\",\r\n        \"aria-hidden\": \"true\",\r\n        title: grid.options.labels.dragToReorder,\r\n      });\r\n      handle.appendChild(el(\"i\", { class: \"bi bi-grip-vertical\" }));\r\n      item.appendChild(handle);\r\n    }\r\n    const cb = el(\"input\", {\r\n      type: \"checkbox\",\r\n      class: \"form-check-input m-0\",\r\n      \"data-bg-action\": \"toggle-column\",\r\n      \"data-bg-value\": col.id,\r\n      id: `bg-col-${col.id}`,\r\n    });\r\n    if (col.visible) cb.setAttribute(\"checked\", \"\");\r\n    item.appendChild(cb);\r\n    const label = el(\"label\", {\r\n      class: \"form-check-label flex-grow-1\",\r\n      for: `bg-col-${col.id}`,\r\n    });\r\n    label.textContent = col.text || col.id;\r\n    item.appendChild(label);\r\n    list.appendChild(item);\r\n  }\r\n  menu.appendChild(list);\r\n\r\n  menu.appendChild(el(\"div\", { class: \"dropdown-divider\" }));\r\n  const reset = el(\"button\", {\r\n    type: \"button\",\r\n    class: \"btn btn-link btn-sm p-0 px-1\",\r\n    \"data-bg-action\": \"reset-columns\",\r\n  });\r\n  reset.textContent = grid.options.labels.resetColumns;\r\n  menu.appendChild(reset);\r\n\r\n  wrap.appendChild(menu);\r\n  return wrap;\r\n}\r\n\r\nfunction iconEl(cls: string): HTMLElement {\r\n  return el(\"i\", { class: cls, \"aria-hidden\": \"true\" });\r\n}\r\n","import type { Boostgrid } from \"../core.js\";\r\nimport type { Row } from \"../types.js\";\r\nimport { $, clearChildren, el } from \"../dom.js\";\r\n\r\nexport function renderPagination<TRow extends Row = Row>(bar: HTMLElement, grid: Boostgrid<TRow>): void {\r\n  const slot = $(\".bg-pagination\", bar);\r\n  if (!slot) return;\r\n  clearChildren(slot);\r\n  if (grid.totalPages <= 1) return;\r\n\r\n  const ul = el(\"ul\", { class: \"pagination pagination-sm m-0\" });\r\n  ul.appendChild(pageItem(\"«\", grid.current === 1, grid.current === 1, \"1\"));\r\n  ul.appendChild(pageItem(\"‹\", grid.current === 1, grid.current === 1, String(grid.current - 1)));\r\n\r\n  const padding = grid.options.padding;\r\n  const start = Math.max(1, grid.current - padding);\r\n  const end = Math.min(grid.totalPages, grid.current + padding);\r\n  for (let i = start; i <= end; i++) {\r\n    ul.appendChild(pageItem(String(i), false, i === grid.current, String(i)));\r\n  }\r\n  ul.appendChild(pageItem(\"›\", grid.current === grid.totalPages, grid.current === grid.totalPages, String(grid.current + 1)));\r\n  ul.appendChild(pageItem(\"»\", grid.current === grid.totalPages, grid.current === grid.totalPages, String(grid.totalPages)));\r\n  slot.appendChild(ul);\r\n}\r\n\r\nfunction pageItem(label: string, disabled: boolean, active: boolean, value: string): HTMLLIElement {\r\n  const li = el(\"li\", { class: `page-item ${disabled ? \"disabled\" : \"\"} ${active ? \"active\" : \"\"}` });\r\n  const a = el(\"a\", {\r\n    class: \"page-link\",\r\n    href: \"javascript:void(0);\",\r\n    \"data-bg-action\": \"page\",\r\n    \"data-bg-value\": value,\r\n  });\r\n  a.textContent = label;\r\n  li.appendChild(a);\r\n  return li;\r\n}\r\n\r\nexport function renderInfos<TRow extends Row = Row>(bar: HTMLElement, grid: Boostgrid<TRow>): void {\r\n  const slot = $(\".bg-infos\", bar);\r\n  if (!slot) return;\r\n  const start = grid.total === 0 ? 0 : (grid.current - 1) * Math.max(0, grid.getRowCount()) + 1;\r\n  const end = grid.getRowCount() === -1 ? grid.total : Math.min(grid.total, start + grid.getRowCount() - 1);\r\n  // Locale-aware digit grouping: `Intl.NumberFormat` gives us thousands\r\n  // separators (or whatever the locale uses) for free.\r\n  const fmt = numberFormatter(grid.options.locale);\r\n  slot.textContent = grid.options.labels.infos\r\n    .replace(\"{start}\", fmt.format(start))\r\n    .replace(\"{end}\", fmt.format(end))\r\n    .replace(\"{total}\", fmt.format(grid.total));\r\n}\r\n\r\n/** Cached `Intl.NumberFormat` keyed by locale — building one is hundreds of\r\n *  microseconds, so we don't want a fresh instance on every render. */\r\nconst numberFormatterCache = new Map<string, Intl.NumberFormat>();\r\nfunction numberFormatter(locale: string | null): Intl.NumberFormat {\r\n  const key = locale ?? \"\";\r\n  let f = numberFormatterCache.get(key);\r\n  if (!f) {\r\n    f = new Intl.NumberFormat(locale ?? undefined);\r\n    numberFormatterCache.set(key, f);\r\n  }\r\n  return f;\r\n}\r\n","import type { Boostgrid } from \"../core.js\";\nimport type { Row } from \"../types.js\";\n\n/**\n * Virtual scroll — windows the visible row slice based on scroll position.\n *\n * Strategy: classic windowing with two pad <tr>s above and below the rendered\n * slice. The <tbody> becomes the y-scroll viewport; on every scroll we\n * recompute `[firstVisible, lastVisible]` from `scrollTop / rowHeight`,\n * widen by `overscan` on each side, and re-call renderBody. A\n * `requestAnimationFrame` coalescer prevents thrash during fast scrolls.\n *\n * Lifecycle hooks live on the grid via private-but-public fields\n * (`virtualWindow` and the slice helpers below) so renderBody can read the\n * current window without circular-imports.\n */\n\nexport interface VirtualWindow {\n  /** Row index of the first rendered row (inclusive). */\n  start: number;\n  /** Row index of the last rendered row (exclusive). */\n  end: number;\n  /** Pre-pad height in px (start * rowHeight). */\n  padTop: number;\n  /** Post-pad height in px ((total - end) * rowHeight). */\n  padBottom: number;\n}\n\nexport function mountVirtualScroll<TRow extends Row = Row>(grid: Boostgrid<TRow>): () => void {\n  const opts = grid.options;\n  if (!opts.virtualScroll) return () => { /* noop */ };\n\n  // The wrapper gets the marker class so SCSS can apply tbody { display: block; height: ... }\n  const wrapper = grid.element.parentElement;\n  wrapper?.classList.add(\"boostgrid--virtual\");\n\n  let frame = 0;\n\n  const onScroll = () => {\n    if (frame) return;\n    frame = requestAnimationFrame(() => {\n      frame = 0;\n      // Capture the previous window before recomputing so we can skip\n      // the render entirely when the scrolled-to position covers the\n      // same slice (small scrolls within overscan).\n      const prev = grid.virtualWindow;\n      refreshVirtualWindow(grid);\n      const cur = grid.virtualWindow;\n      if (prev && cur && prev.start === cur.start && prev.end === cur.end) return;\n      grid.rerenderBody();\n    });\n  };\n\n  // The scroll listener attaches to <tbody> if it exists yet; otherwise it\n  // re-binds in refreshVirtualWindow once renderBody has created one. We\n  // track the bound element so we can unbind cleanly on destroy.\n  let boundTo: HTMLElement | null = null;\n  const ensureBound = () => {\n    const t = grid.element.querySelector<HTMLTableSectionElement>(\":scope > tbody\");\n    if (t && t !== boundTo) {\n      if (boundTo) boundTo.removeEventListener(\"scroll\", onScroll);\n      boundTo = t;\n      t.addEventListener(\"scroll\", onScroll, { passive: true });\n    }\n  };\n  // Cache the binder so refreshVirtualWindow can call it lazily.\n  grid.ensureVirtualBinding = ensureBound;\n  ensureBound();\n\n  return () => {\n    if (frame) cancelAnimationFrame(frame);\n    if (boundTo) boundTo.removeEventListener(\"scroll\", onScroll);\n    wrapper?.classList.remove(\"boostgrid--virtual\");\n    grid.virtualWindow = null;\n    grid.lastRenderedVirtualWindow = null;\n    grid.ensureVirtualBinding = null;\n  };\n}\n\n/**\n * Recompute the window from the current tbody scrollTop. Safe to call any\n * time the underlying dataset (filtered) changes — e.g. after sort/search.\n * Falls back to a sensible default viewport when the tbody hasn't been\n * laid out yet (initial mount, jsdom, hidden parents).\n */\nexport function refreshVirtualWindow<TRow extends Row = Row>(grid: Boostgrid<TRow>): void {\n  const opts = grid.options;\n  if (!opts.virtualScroll) return;\n  const tbody = grid.element.querySelector<HTMLTableSectionElement>(\":scope > tbody\");\n  const defaultViewport = 480;\n  const scrollTop = tbody?.scrollTop ?? 0;\n  const viewportH = tbody?.clientHeight || defaultViewport;\n  const visible = Math.max(1, Math.ceil(viewportH / opts.rowHeight));\n  const firstVisible = Math.floor(scrollTop / opts.rowHeight);\n  recomputeWindow(grid, firstVisible, visible);\n}\n\n/**\n * Compute the windowed slice for the given first-visible-row index. Mutates\n * `grid.virtualWindow` so the next renderBody pass picks it up. The window\n * always covers the visible viewport plus `overscan` on each side, clamped\n * to the dataset.\n */\nexport function recomputeWindow<TRow extends Row = Row>(\n  grid: Boostgrid<TRow>,\n  firstVisible: number,\n  visibleCount: number,\n): void {\n  const opts = grid.options;\n  // Read .length directly off the internal arrays — avoids the .slice()\n  // allocation that getFilteredRows() / getCurrentRows() would do per\n  // scroll event.\n  const total = grid.filtered.length || grid.currentRows.length;\n  const overscan = Math.max(0, opts.overscan);\n  const start = Math.max(0, firstVisible - overscan);\n  const end = Math.min(total, firstVisible + visibleCount + overscan);\n  const rowHeight = opts.rowHeight;\n  grid.virtualWindow = {\n    start,\n    end,\n    padTop: start * rowHeight,\n    padBottom: Math.max(0, (total - end) * rowHeight),\n  };\n}\n","import type { Boostgrid } from \"../core.js\";\r\nimport type { Column, EditCommit, Row } from \"../types.js\";\r\n\r\n/**\r\n * Cell-level edit-on-dblclick. One delegated `dblclick` listener on the\r\n * grid's root catches `<td data-editable=\"true\">` events, swaps an\r\n * `<input>` (or `<select>`) into the cell, focuses it, and commits on\r\n * Enter or blur, cancels on Escape. Concurrent edits are serialized:\r\n * opening a second cell commits the first.\r\n *\r\n * The mounted listener is no-op'd when no column is editable, so grids\r\n * that don't use the feature pay zero runtime cost beyond the tiny check.\r\n */\r\nexport function mountCellEdit<TRow extends Row = Row>(grid: Boostgrid<TRow>): () => void {\r\n  if (!grid.columns.some((c) => c.editable)) return () => { /* noop */ };\r\n\r\n  let active: ActiveEditor<TRow> | null = null;\r\n\r\n  const openIfApplicable = (target: Element | null) => {\r\n    const td = target?.closest(\"td[data-editable]\") as HTMLTableCellElement | null;\r\n    if (!td || td.querySelector(\"[data-edit-input]\")) return;\r\n    const tr = td.closest(\"tr\");\r\n    const colId = td.getAttribute(\"data-column-id\");\r\n    const rowId = tr?.getAttribute(\"data-row-id\");\r\n    if (!colId || rowId == null) return;\r\n\r\n    const column = grid.columns.find((c) => c.id === colId);\r\n    const row = grid.currentRows.find((r) => grid.identifier && String(r[grid.identifier]) === rowId);\r\n    if (!column || !row || !column.editable) return;\r\n\r\n    if (active) commit(active, /* silent */ true);\r\n    active = openEditor(grid, td, column, row, () => {\r\n      active = null;\r\n      grid.commitActiveEdit = null;\r\n    });\r\n    grid.commitActiveEdit = () => { if (active) commit(active, /* silent */ true); };\r\n  };\r\n\r\n  const onDblClick = (e: Event) => openIfApplicable(e.target as Element | null);\r\n\r\n  grid.element.addEventListener(\"dblclick\", onDblClick);\r\n\r\n  return () => {\r\n    if (active) commit(active, /* silent */ true);\r\n    grid.commitActiveEdit = null;\r\n    grid.element.removeEventListener(\"dblclick\", onDblClick);\r\n  };\r\n}\r\n\r\ninterface ActiveEditor<TRow extends Row> {\r\n  td: HTMLTableCellElement;\r\n  input: HTMLInputElement | HTMLSelectElement;\r\n  column: Column<TRow>;\r\n  row: TRow;\r\n  oldValue: unknown;\r\n  oldHtml: string;\r\n  grid: Boostgrid<TRow>;\r\n  closed: boolean;\r\n  onClose: () => void;\r\n  /** Removes the keydown/blur listeners attached to `input`. Called from\r\n   *  commit/cancel before swapping the input out of the DOM so a delayed\r\n   *  blur from focus-stealing UA behavior cannot re-trigger commit. */\r\n  detach: () => void;\r\n}\r\n\r\nfunction openEditor<TRow extends Row>(\r\n  grid: Boostgrid<TRow>,\r\n  td: HTMLTableCellElement,\r\n  column: Column<TRow>,\r\n  row: TRow,\r\n  onClose: () => void,\r\n): ActiveEditor<TRow> {\r\n  const oldValue = row[column.id];\r\n  const oldHtml = td.innerHTML;\r\n\r\n  let input: HTMLInputElement | HTMLSelectElement;\r\n  if (column.editType === \"select\") {\r\n    const select = document.createElement(\"select\");\r\n    select.className = \"form-select form-select-sm boostgrid-edit-input\";\r\n    select.setAttribute(\"data-edit-input\", \"true\");\r\n    for (const opt of column.editOptions) {\r\n      const o = document.createElement(\"option\");\r\n      o.value = String(opt.value);\r\n      o.textContent = opt.label;\r\n      if (String(opt.value) === String(oldValue)) o.selected = true;\r\n      select.appendChild(o);\r\n    }\r\n    input = select;\r\n  } else {\r\n    const inp = document.createElement(\"input\");\r\n    inp.type = column.editType === \"number\" ? \"number\" : \"text\";\r\n    inp.className = \"form-control form-control-sm boostgrid-edit-input\";\r\n    inp.setAttribute(\"data-edit-input\", \"true\");\r\n    inp.value = oldValue == null ? \"\" : String(oldValue);\r\n    input = inp;\r\n  }\r\n\r\n  td.innerHTML = \"\";\r\n  td.appendChild(input);\r\n  // Focus & select on next microtask so the swap is visually immediate\r\n  Promise.resolve().then(() => {\r\n    input.focus();\r\n    if (input instanceof HTMLInputElement) input.select();\r\n  });\r\n\r\n  const editor: ActiveEditor<TRow> = {\r\n    td, input, column, row, oldValue, oldHtml, grid, closed: false, onClose,\r\n    detach: () => { /* assigned below */ },\r\n  };\r\n\r\n  const onKey = (e: Event) => {\r\n    const ke = e as KeyboardEvent;\r\n    if (ke.key === \"Enter\") { e.preventDefault(); commit(editor, false); }\r\n    else if (ke.key === \"Escape\") { e.preventDefault(); cancel(editor); }\r\n  };\r\n  const onBlur = () => commit(editor, false);\r\n  input.addEventListener(\"keydown\", onKey);\r\n  input.addEventListener(\"blur\", onBlur);\r\n  editor.detach = () => {\r\n    input.removeEventListener(\"keydown\", onKey);\r\n    input.removeEventListener(\"blur\", onBlur);\r\n  };\r\n\r\n  return editor;\r\n}\r\n\r\nfunction commit<TRow extends Row>(editor: ActiveEditor<TRow>, silent: boolean): void {\r\n  if (editor.closed) return;\r\n  editor.closed = true;\r\n  // Detach BEFORE swapping the input out — innerHTML/textContent\r\n  // mutations dispatch a synthetic blur on the now-removed input which\r\n  // would re-enter commit if the listener were still wired up.\r\n  editor.detach();\r\n  const raw = editor.input.value;\r\n  const newValue = editor.column.editType === \"number\" ? Number(raw) : raw;\r\n\r\n  // Mutate the row in place — currentRows/filtered/sorted hold the same refs.\r\n  (editor.row as Record<string, unknown>)[editor.column.id] = newValue;\r\n  paintCell(editor.td, editor.column, editor.row);\r\n\r\n  if (!silent && editor.grid.options.onCellEdit) {\r\n    const commitInfo: EditCommit<TRow> = {\r\n      row: editor.row,\r\n      column: editor.column,\r\n      oldValue: editor.oldValue,\r\n      newValue,\r\n      revert: () => {\r\n        (editor.row as Record<string, unknown>)[editor.column.id] = editor.oldValue;\r\n        paintCell(editor.td, editor.column, editor.row);\r\n      },\r\n    };\r\n    void editor.grid.options.onCellEdit(commitInfo);\r\n  }\r\n  editor.onClose();\r\n}\r\n\r\nfunction cancel<TRow extends Row>(editor: ActiveEditor<TRow>): void {\r\n  if (editor.closed) return;\r\n  editor.closed = true;\r\n  editor.detach();\r\n  editor.td.innerHTML = editor.oldHtml;\r\n  editor.onClose();\r\n}\r\n\r\nfunction paintCell<TRow extends Row>(td: HTMLTableCellElement, col: Column<TRow>, row: TRow): void {\r\n  if (col.formatter) {\r\n    td.innerHTML = col.formatter(col, row);\r\n  } else {\r\n    td.textContent = col.converter.to(row[col.id]);\r\n  }\r\n}\r\n","import type { Boostgrid } from \"./core.js\";\nimport type { BoostgridState, Row } from \"./types.js\";\n\n/**\n * Persistent view state, keyed by table id (or a hash of column ids when no\n * id is set). Defensive against environments without `localStorage` —\n * Safari private mode, SSR, sandboxed iframes — by silently no-opping.\n *\n * Schema is versioned (`v: 1`); a future breaking change should bump the\n * version and discard older payloads on read.\n */\n\nconst VERSION = 3 as const;\n\nfunction safeStorage(): Storage | null {\n  try {\n    if (typeof localStorage === \"undefined\") return null;\n    // Trigger an access to surface \"access denied\" early\n    const k = \"__boostgrid_probe__\";\n    localStorage.setItem(k, \"1\");\n    localStorage.removeItem(k);\n    return localStorage;\n  } catch {\n    return null;\n  }\n}\n\nexport function defaultStateKey<TRow extends Row = Row>(grid: Boostgrid<TRow>): string {\n  const id = grid.element.id;\n  if (id) return `boostgrid:${id}`;\n  // Fall back to a stable hash of the column id list\n  const sig = grid.columns.map((c) => c.id).join(\"|\");\n  return `boostgrid:${djb2(sig)}`;\n}\n\nfunction resolveKey<TRow extends Row>(grid: Boostgrid<TRow>): string {\n  return grid.options.stateKey ?? defaultStateKey(grid);\n}\n\n/** Trailing-edge debounce window (ms) for `saveState`. Picked to be long\n *  enough to coalesce the burst of state changes during column-resize\n *  drag (60Hz pointermove) and short enough to feel instant for a tab\n *  close after a discrete action like sort/page. */\nconst SAVE_DEBOUNCE_MS = 200;\n\n/** One pending-save timer per grid. WeakMap so the entry is GC'd alongside\n *  the grid instance — no leak when grids are destroyed without an\n *  explicit flush. */\nconst saveTimers = new WeakMap<Boostgrid<Row>, ReturnType<typeof setTimeout>>();\n\n/** Synchronous serializer — invoked by the debounced `saveState` and by\n *  `flushSaveState` on `destroy()`. */\nfunction writeState<TRow extends Row = Row>(grid: Boostgrid<TRow>): void {\n  if (!grid.options.stateSave) return;\n  const store = safeStorage();\n  if (!store) return;\n  const state: BoostgridState = {\n    v: VERSION,\n    current: grid.getCurrentPage(),\n    rowsPerPage: grid.getRowCount(),\n    searchPhrase: grid.getSearchPhrase(),\n    sortDictionary: grid.getSortDictionary(),\n    columnVisibility: Object.fromEntries(\n      grid.columns.map((c) => [c.id, c.visible]),\n    ),\n    selected: grid.options.keepSelection ? grid.getSelectedRows() : [],\n    collapsedGroups: Array.from(grid.collapsedGroupPaths),\n    expandedTreeNodes: Array.from(grid.expandedTreeNodes) as Array<string | number>,\n    columnOrder: grid.columns.map((c) => c.id),\n    columnWidths: Object.fromEntries(\n      grid.columns\n        .filter((c) => typeof c.width === \"string\" && /px$/.test(c.width))\n        .map((c) => [c.id, c.width as string]),\n    ),\n  };\n  try {\n    store.setItem(resolveKey(grid), JSON.stringify(state));\n  } catch {\n    /* quota exceeded, etc. — silent */\n  }\n}\n\nexport function saveState<TRow extends Row = Row>(grid: Boostgrid<TRow>): void {\n  if (!grid.options.stateSave) return;\n  const key = grid as unknown as Boostgrid<Row>;\n  const existing = saveTimers.get(key);\n  if (existing) clearTimeout(existing);\n  saveTimers.set(\n    key,\n    setTimeout(() => {\n      saveTimers.delete(key);\n      writeState(grid);\n    }, SAVE_DEBOUNCE_MS),\n  );\n}\n\n/**\n * Synchronously flush any pending debounced save. Called from `destroy()`\n * so the user's last interaction (which may have happened mid-debounce)\n * still lands in localStorage instead of being silently dropped.\n */\nexport function flushSaveState<TRow extends Row = Row>(grid: Boostgrid<TRow>): void {\n  const key = grid as unknown as Boostgrid<Row>;\n  const t = saveTimers.get(key);\n  if (t) {\n    clearTimeout(t);\n    saveTimers.delete(key);\n    writeState(grid);\n  }\n}\n\n/**\n * Read persisted state and apply it to the grid IN PLACE. Returns true if a\n * payload was found and applied (so the caller can skip its own initial\n * sort/page setup), or false otherwise.\n *\n * Invariants enforced:\n *  - Only restores columns that still exist on the table.\n *  - Discards payloads with a missing or wrong `v` field.\n *  - Sanity-checks `current` and `rowsPerPage` to numbers.\n */\nexport function restoreState<TRow extends Row = Row>(grid: Boostgrid<TRow>): boolean {\n  if (!grid.options.stateSave) return false;\n  const store = safeStorage();\n  if (!store) return false;\n  const raw = store.getItem(resolveKey(grid));\n  if (!raw) return false;\n  let parsed: unknown;\n  try { parsed = JSON.parse(raw); } catch { return false; }\n  // Cast to a broader shape — older payloads carry `v: 1` which the\n  // current `BoostgridState` literal type doesn't allow.\n  const state = parsed as (Omit<Partial<BoostgridState>, \"v\"> & { v?: number }) | null;\n  if (!state) return false;\n  // Accept v:1 (pre-Round 4), v:2 (Round 4), and v:3 (Round 5) payloads.\n  // Older versions simply lack newer fields; everything else applies cleanly.\n  const v = state.v;\n  if (v !== 1 && v !== 2 && v !== 3) return false;\n\n  if (typeof state.rowsPerPage === \"number\") {\n    grid.applyRestoredRowsPerPage(state.rowsPerPage);\n  }\n  if (typeof state.current === \"number\" && state.current >= 1) {\n    grid.current = state.current;\n  }\n  if (typeof state.searchPhrase === \"string\") {\n    grid.searchPhrase = state.searchPhrase;\n  }\n  if (state.sortDictionary && typeof state.sortDictionary === \"object\") {\n    grid.sortDictionary = { ...state.sortDictionary };\n  }\n  if (state.columnVisibility && typeof state.columnVisibility === \"object\") {\n    for (const col of grid.columns) {\n      const cv = (state.columnVisibility as Record<string, unknown>)[col.id];\n      if (typeof cv === \"boolean\") col.visible = cv;\n    }\n  }\n  if (Array.isArray(state.selected) && grid.options.keepSelection) {\n    grid.applyRestoredSelection(state.selected);\n  }\n  // v:2+ fields. Each is independently optional so partial payloads\n  // (e.g. someone hand-edited localStorage) still apply non-broken parts.\n  if (v === 2 || v === 3) {\n    if (Array.isArray(state.collapsedGroups)) {\n      grid.collapsedGroupPaths = new Set(\n        state.collapsedGroups.filter((s): s is string => typeof s === \"string\"),\n      );\n    }\n    if (Array.isArray(state.expandedTreeNodes)) {\n      const ids = state.expandedTreeNodes.filter(\n        (x): x is string | number => typeof x === \"string\" || typeof x === \"number\",\n      );\n      grid.expandedTreeNodes = new Set(ids);\n    }\n  }\n  // v:3-only fields — column reorder + drag-resize persistence.\n  if (v === 3) {\n    if (Array.isArray(state.columnOrder)) {\n      const stored = state.columnOrder.filter((s): s is string => typeof s === \"string\");\n      const live = grid.columns.map((c) => c.id);\n      // Apply only when id sets match exactly — silently fall back to\n      // authored order if a column was added or removed in code.\n      if (\n        stored.length === live.length &&\n        stored.every((id) => live.includes(id))\n      ) {\n        const byId = new Map(grid.columns.map((c) => [c.id, c]));\n        grid.columns = stored.map((id) => byId.get(id)!);\n      }\n    }\n    if (state.columnWidths && typeof state.columnWidths === \"object\") {\n      const widths = state.columnWidths as Record<string, unknown>;\n      for (const col of grid.columns) {\n        const w = widths[col.id];\n        if (typeof w === \"string\" && /^\\d+(\\.\\d+)?px$/.test(w)) {\n          col.width = w;\n        }\n      }\n    }\n  }\n  return true;\n}\n\nexport function clearState<TRow extends Row = Row>(grid: Boostgrid<TRow>): void {\n  const store = safeStorage();\n  if (!store) return;\n  try { store.removeItem(resolveKey(grid)); } catch { /* silent */ }\n}\n\n/** Tiny string hash; collision risk is acceptable since the column-id\n *  signature is the only fallback when no table id exists. */\nfunction djb2(s: string): string {\n  let h = 5381;\n  for (let i = 0; i < s.length; i++) h = ((h << 5) + h + s.charCodeAt(i)) | 0;\n  return (h >>> 0).toString(36);\n}\n","import type {\r\n  AjaxRequest, AjaxResponse, BoostgridOptions, Column, EventName, Listener, Row, SortDictionary,\r\n} from \"./types.js\";\r\nimport { mergeOptions, parseColumns, parseRowsFromTable } from \"./options.js\";\r\nimport { $, $$, debounce, delegate, el, readData } from \"./dom.js\";\r\nimport { renderHeader } from \"./render/header.js\";\r\nimport { renderBody, renderSkeleton } from \"./render/body.js\";\r\nimport { resolveTreeColumnId } from \"./render/tree.js\";\r\nimport { renderFooter } from \"./render/footer.js\";\r\nimport { renderToolbar } from \"./render/toolbar.js\";\r\nimport { renderPagination, renderInfos } from \"./render/pagination.js\";\r\nimport { mountVirtualScroll, refreshVirtualWindow, type VirtualWindow } from \"./render/virtual.js\";\r\nimport { mountCellEdit } from \"./render/edit.js\";\r\nimport { mountCellSelection } from \"./render/cell-select.js\";\r\nimport { saveState, restoreState, clearState, flushSaveState } from \"./state.js\";\r\n\r\n/** Normalize a tree id-or-parent-id (mirrors `normalizeId` in tree.ts so\r\n *  the reparent path matches what the tree builder produces). */\r\nfunction normalizeTreeId(v: unknown): string | number | null {\r\n  if (v == null) return null;\r\n  if (typeof v === \"number\") return Number.isFinite(v) ? v : null;\r\n  if (typeof v === \"string\") {\r\n    const t = v.trim();\r\n    if (t === \"\") return null;\r\n    if (/^-?\\d+(\\.\\d+)?$/.test(t)) return Number(t);\r\n    return t;\r\n  }\r\n  return null;\r\n}\r\n\r\n/**\r\n * Boostgrid v2 — vanilla, BS5-native data grid.\r\n *\r\n * Performance notes:\r\n *  - Single delegated click listener (data-bg-action) replaces v1's per-row bindings.\r\n *  - Derived view (filtered + sorted + paged) memoized via dirty flags so paging\r\n *    doesn't re-run sort, sorting doesn't re-run filter, etc.\r\n *  - Selection lookups use a Map<id, Row> keyed by the identifier column for O(1) hits.\r\n *  - Search input is debounced (default 200ms).\r\n *  - Body renders via DocumentFragment + cloneNode of a row template.\r\n */\r\nexport class Boostgrid<TRow extends Row = Row> {\r\n  readonly element: HTMLTableElement;\r\n  readonly options: BoostgridOptions<TRow>;\r\n  columns: Column<TRow>[] = [];\r\n  identifier: string | null = null;\r\n\r\n  private rows: TRow[] = [];\r\n  private rowIndex: Map<unknown, TRow> = new Map();\r\n  private selected: Set<unknown> = new Set();\r\n\r\n  current = 1;\r\n  private rowsPerPage: number;\r\n  searchPhrase = \"\";\r\n  /** Cached compiled regex for `searchPhrase`. Invalidated whenever\r\n   *  `executeSearch` mutates the phrase. Avoids re-compiling per row\r\n   *  inside `applyFilter`. */\r\n  private searchRegex: RegExp | null = null;\r\n  sortDictionary: SortDictionary = {};\r\n  /** Cached comparator built from `sortDictionary`. Cleared by `sort()` /\r\n   *  the action handler whenever the dictionary mutates. Avoids paying\r\n   *  `Object.entries` + closure allocation per `applySort` call. */\r\n  private sortComparator: ((a: TRow, b: TRow) => number) | null = null;\r\n\r\n  /** Filtered (pre-sort, pre-page) view. Public so render/virtual.ts can\r\n   *  read its `.length` without paying for a `.slice()` allocation per\r\n   *  scroll event via the public getter. Treat as read-only. */\r\n  filtered: TRow[] = [];\r\n  private sorted: TRow[] = [];\r\n  /** rows on the current page after filter+sort */\r\n  currentRows: TRow[] = [];\r\n  total = 0;\r\n  totalPages = 0;\r\n  /**\r\n   * Active virtual-scroll window. `null` when virtualScroll is off.\r\n   * Exposed publicly so `render/virtual.ts` can mutate it without\r\n   * a circular import.\r\n   */\r\n  virtualWindow: VirtualWindow | null = null;\r\n  /**\r\n   * Snapshot of the window the previous virtual-scroll renderBody pass\r\n   * actually painted. Public so `render/body.ts` can compare and short-\r\n   * circuit a full rebuild when only the pad heights changed. Cleared\r\n   * by `destroy()` and on virtual-scroll teardown.\r\n   */\r\n  lastRenderedVirtualWindow: VirtualWindow | null = null;\r\n  /** Hook the virtual-scroll module sets to lazy-bind its scroll listener\r\n   *  to <tbody> after renderBody creates it. Null when virtualScroll is off. */\r\n  ensureVirtualBinding: (() => void) | null = null;\r\n  /** Hook `render/edit.ts` sets to allow the virtual-scroll pool path to\r\n   *  commit any in-flight cell edit before recycling the row that owns it.\r\n   *  Null when no edit is currently open. */\r\n  commitActiveEdit: (() => void) | null = null;\r\n  /** Collapsed group paths. Path strings are joined with `//`\r\n   *  (see `groupPathToString` in render/group.ts) so multi-level\r\n   *  collapse targets one branch precisely. Public so `render/group.ts`\r\n   *  can read it during the walk without a back-reference. */\r\n  collapsedGroupPaths: Set<string> = new Set();\r\n  /** Tree expand state — set of row ids the user has explicitly expanded\r\n   *  (when `treeExpanded: \"none\"`) or NOT collapsed (when `\"all\"`).\r\n   *  Public so `render/tree.ts` can read it. */\r\n  expandedTreeNodes: Set<string | number> = new Set();\r\n  /** Row-detail expand state — set of row ids whose detail panel should\r\n   *  render. Tracking mode mirrors the tree-expanded model: `\"expanded\"`\r\n   *  means the set lists rows that ARE expanded (default `\"none\"`);\r\n   *  `\"collapsed\"` means it lists rows that are collapsed despite the\r\n   *  default `\"all\"`. */\r\n  expandedRowDetails: Set<string | number> = new Set();\r\n  rowDetailTracking: \"expanded\" | \"collapsed\" = \"expanded\";\r\n  /** When true, `expandedTreeNodes` records which roots/branches are\r\n   *  expanded; when false (the default for `treeExpanded: \"all\"`), it\r\n   *  records which are *collapsed*. Computed once at construction. */\r\n  treeExpandTracking: \"expanded\" | \"collapsed\" = \"collapsed\";\r\n\r\n  /** Transient: the id of the column currently being drag-reordered. Reset\r\n   *  to null on drop / dragend / window blur. */\r\n  private dragColumnId: string | null = null;\r\n\r\n  /** Snapshot of authored column order/visibility/width, captured at attach\r\n   *  time. Powers `resetColumnState()`. */\r\n  private columnsBaseline: Array<{ id: string; visible: boolean; width: string | null }> = [];\r\n\r\n  private dirtyFilter = true;\r\n  private dirtySort = true;\r\n\r\n  private listeners: Map<EventName, Set<Listener>> = new Map();\r\n  private cleanupFns: Array<() => void> = [];\r\n  private rootContainer: HTMLDivElement;\r\n  private toolbarTop: HTMLDivElement | null = null;\r\n  private toolbarBottom: HTMLDivElement | null = null;\r\n  private bulkBar: HTMLDivElement | null = null;\r\n  /** Public so `attach()` can detect a stale registry entry and replace it. */\r\n  destroyed = false;\r\n  private debouncedSearch: (phrase: string) => void;\r\n\r\n  /** Monotonic counter so an out-of-order ajax response (later request,\r\n   *  earlier landing) cannot overwrite a fresher one. Each `fetchAjax`\r\n   *  bumps this and bails if its captured value no longer matches. */\r\n  private ajaxRequestId = 0;\r\n  /** AbortController for the in-flight ajax request; lets us cancel the\r\n   *  network slot when a newer call starts. Null when no fetch is pending. */\r\n  private ajaxAbort: AbortController | null = null;\r\n\r\n  constructor(table: HTMLTableElement, options?: Partial<BoostgridOptions<TRow>>) {\r\n    this.element = table;\r\n    const fromTable = readData(table) as Partial<BoostgridOptions<TRow>>;\r\n    this.options = mergeOptions<TRow>(options, fromTable);\r\n\r\n    const rc = this.options.rowCount;\r\n    // Virtual scroll manages the slice itself, so force \"All\" on the page-size\r\n    // axis. The row-count dropdown is suppressed in this mode (see toolbar).\r\n    this.rowsPerPage = this.options.virtualScroll\r\n      ? -1\r\n      : Array.isArray(rc) ? Number(rc[0]) : Number(rc);\r\n\r\n    this.columns = parseColumns<TRow>(table, this.options);\r\n    // Snapshot the columns as authored so \"Reset to defaults\" can revert\r\n    // user reorder + visibility + width changes back to the markup state.\r\n    this.columnsBaseline = this.columns.map((c) => ({\r\n      id: c.id,\r\n      visible: c.visible,\r\n      width: c.width,\r\n    }));\r\n    const idCol = this.columns.find((c) => c.identifier);\r\n    this.identifier = idCol ? idCol.id : null;\r\n\r\n    this.columns.forEach((c) => {\r\n      if (c.order) this.sortDictionary[c.id] = c.order;\r\n    });\r\n\r\n    // Seed collapsed groups from options.groupExpanded. \"all\" → none collapsed,\r\n    // \"none\" → seeded lazily on first walk, Record<path,bool> → falsy paths.\r\n    const ge = this.options.groupExpanded;\r\n    if (ge && typeof ge === \"object\") {\r\n      for (const [k, v] of Object.entries(ge)) {\r\n        if (!v) this.collapsedGroupPaths.add(k);\r\n      }\r\n    }\r\n    // Seed tree-expand state. \"all\" tracks collapses (default open),\r\n    // \"none\" tracks expansions (default closed), Record bootstraps either.\r\n    const te = this.options.treeExpanded;\r\n    this.treeExpandTracking = te === \"none\" ? \"expanded\" : \"collapsed\";\r\n    if (te && typeof te === \"object\") {\r\n      for (const [k, v] of Object.entries(te)) {\r\n        // If tracking expansions, store keys that are TRUE; if collapses, FALSE.\r\n        const wantStored = this.treeExpandTracking === \"expanded\" ? !!v : !v;\r\n        if (wantStored) this.expandedTreeNodes.add(k);\r\n      }\r\n    }\r\n    // Same model for row-detail expand state. Default \"none\" → closed.\r\n    const rde = this.options.rowDetailExpanded;\r\n    this.rowDetailTracking = rde === \"all\" ? \"collapsed\" : \"expanded\";\r\n    if (rde && typeof rde === \"object\") {\r\n      for (const [k, v] of Object.entries(rde)) {\r\n        const wantStored = this.rowDetailTracking === \"expanded\" ? !!v : !v;\r\n        if (wantStored) this.expandedRowDetails.add(k);\r\n      }\r\n    }\r\n    // Mutual exclusivity: tree mode wins if both are set.\r\n    if (this.options.treeMode && this.options.groupBy) {\r\n      // eslint-disable-next-line no-console\r\n      console.warn(\r\n        \"Boostgrid: treeMode and groupBy cannot both be active; treeMode wins.\",\r\n      );\r\n    }\r\n\r\n    // wrap the table inside a container we own (toolbars/pagination live here)\r\n    this.rootContainer = el(\"div\", { class: \"boostgrid\" });\r\n    table.parentNode?.insertBefore(this.rootContainer, table);\r\n    this.rootContainer.appendChild(table);\r\n    table.classList.add(\"boostgrid-table\");\r\n    if (!table.classList.contains(\"table\")) table.classList.add(\"table\");\r\n    if (this.options.stickyHeader) {\r\n      this.rootContainer.classList.add(\"boostgrid--sticky-head\");\r\n    }\r\n\r\n    this.rows = parseRowsFromTable<TRow>(table, this.columns);\r\n    this.reindex();\r\n\r\n    this.debouncedSearch = debounce((phrase: string) => {\r\n      this.executeSearch(phrase);\r\n    }, this.options.searchSettings.delay);\r\n\r\n    this.mountChrome();\r\n    this.bindDelegatedEvents();\r\n    // Restore persisted view state BEFORE the first render so the initial\r\n    // paint is already in the saved page/sort/search/visibility state.\r\n    restoreState(this);\r\n    this.invalidate(\"filter\");\r\n    // Virtual scroll listener must mount AFTER the table is in the DOM but\r\n    // BEFORE the first loadData, so its initial window is in place when\r\n    // renderBody runs.\r\n    if (this.options.virtualScroll) {\r\n      this.cleanupFns.push(mountVirtualScroll(this));\r\n    }\r\n    // Cell-edit listener: only attaches if any column is editable.\r\n    this.cleanupFns.push(mountCellEdit(this));\r\n    // Cell-selection listener: spreadsheet-style range select + Ctrl+C copy.\r\n    if (this.options.cellSelection) {\r\n      this.cleanupFns.push(mountCellSelection(this));\r\n    }\r\n    // Frozen-columns shadow: track horizontal scroll on the .table-responsive\r\n    // ancestor (if any) so the rightmost frozen cell can show a drop-shadow\r\n    // only while content is scrolled. No-op when no .table-responsive parent\r\n    // exists — the frozen columns still work, just without the shadow affordance.\r\n    this.cleanupFns.push(mountScrollShadow(this));\r\n    this.loadData();\r\n    this.emit(\"initialized\");\r\n  }\r\n\r\n  /** Public hook used by the virtual-scroll module to repaint the body\r\n   *  without re-running filter/sort. */\r\n  rerenderBody(): void {\r\n    renderBody(this);\r\n  }\r\n\r\n  /** Internal hook used by the virtual-scroll pool path to re-evaluate\r\n   *  row-selection visuals (table-active class + checkbox state) across\r\n   *  visible rows after a recycle pass. Not part of the documented public\r\n   *  API. */\r\n  rerenderSelectionState(): void {\r\n    this.refreshSelectionVisuals();\r\n  }\r\n\r\n  // Hooks used by state.ts to apply restored values without exposing\r\n  // private fields. Kept public for the helpers but not part of the\r\n  // documented user-facing API.\r\n  applyRestoredRowsPerPage(n: number): void {\r\n    if (!Number.isFinite(n)) return;\r\n    this.rowsPerPage = n;\r\n  }\r\n  applyRestoredSelection(ids: unknown[]): void {\r\n    if (!this.options.keepSelection || !this.identifier) return;\r\n    this.selected.clear();\r\n    for (const id of ids) this.selected.add(id);\r\n  }\r\n\r\n  // -------- public API --------\r\n\r\n  append(rows: TRow[]): this {\r\n    if (this.options.ajax) return this;\r\n    rows.forEach((r) => {\r\n      this.rows.push(r);\r\n      if (this.identifier) this.rowIndex.set(r[this.identifier], r);\r\n    });\r\n    this.invalidate(\"filter\");\r\n    this.loadData();\r\n    this.emit(\"appended\", rows);\r\n    return this;\r\n  }\r\n\r\n  clear(): this {\r\n    if (this.options.ajax) return this;\r\n    const removed = this.rows.slice();\r\n    this.rows = [];\r\n    this.rowIndex.clear();\r\n    this.selected.clear();\r\n    this.current = 1;\r\n    this.invalidate(\"filter\");\r\n    this.loadData();\r\n    this.emit(\"cleared\", removed);\r\n    return this;\r\n  }\r\n\r\n  remove(rowIds?: unknown[]): this {\r\n    if (!this.identifier) return this;\r\n    const ids = rowIds ?? Array.from(this.selected);\r\n    const removed: TRow[] = [];\r\n    for (const id of ids) {\r\n      const row = this.rowIndex.get(id);\r\n      if (!row) continue;\r\n      this.rows.splice(this.rows.indexOf(row), 1);\r\n      this.rowIndex.delete(id);\r\n      this.selected.delete(id);\r\n      removed.push(row);\r\n    }\r\n    this.current = 1;\r\n    this.invalidate(\"filter\");\r\n    this.loadData();\r\n    this.emit(\"removed\", removed);\r\n    return this;\r\n  }\r\n\r\n  search(phrase?: string): this {\r\n    this.executeSearch(phrase ?? \"\");\r\n    return this;\r\n  }\r\n\r\n  sort(dictionary?: SortDictionary): this {\r\n    this.sortDictionary = dictionary ? { ...dictionary } : {};\r\n    this.sortComparator = null;\r\n    this.invalidate(\"sort\");\r\n    this.renderHeader();\r\n    this.loadData();\r\n    this.emit(\"sorted\", this.sortDictionary);\r\n    return this;\r\n  }\r\n\r\n  /** Scroll the virtual viewport so the row at `index` is in view. The\r\n   *  index is into the current filtered + sorted dataset (the same view\r\n   *  that `getCurrentRows()` exposes). Clamped to `[0, total - 1]`.\r\n   *  No-op when `virtualScroll` is off or the dataset is empty.\r\n   *  Synchronously refreshes the virtual window and re-renders, so the\r\n   *  target row is in the DOM after the call returns. Returns `this`. */\r\n  scrollToRow(index: number): this {\r\n    if (!this.options.virtualScroll) return this;\r\n    const total = this.filtered.length || this.currentRows.length;\r\n    if (total === 0) return this;\r\n    const clamped = Math.max(0, Math.min(index, total - 1));\r\n    const tbody = this.element.querySelector<HTMLTableSectionElement>(\":scope > tbody\");\r\n    if (tbody) tbody.scrollTop = clamped * this.options.rowHeight;\r\n    refreshVirtualWindow(this);\r\n    this.rerenderBody();\r\n    return this;\r\n  }\r\n\r\n  select(rowIds?: unknown[]): this {\r\n    if (!this.options.selection || !this.identifier) return this;\r\n    // Distinguish \"select all visible rows\" (caller passed nothing) from\r\n    // \"select these specific rows\" — the former gets the full-table\r\n    // refresh; the latter takes the fast diffed path that only touches\r\n    // the changed rows' <tr>s.\r\n    const fullRefresh = rowIds == null;\r\n    const ids = rowIds ?? this.currentRows.map((r) => r[this.identifier!]);\r\n    const newly: TRow[] = [];\r\n    const changedIds: unknown[] = [];\r\n    for (const id of ids) {\r\n      if (this.selected.has(id)) continue;\r\n      if (!this.options.multiSelect && this.selected.size >= 1) break;\r\n      const row = this.rowIndex.get(id);\r\n      if (row) {\r\n        this.selected.add(id);\r\n        newly.push(row);\r\n        changedIds.push(id);\r\n      }\r\n    }\r\n    if (newly.length) {\r\n      if (fullRefresh) this.refreshSelectionVisuals();\r\n      else this.refreshSelectionForIds(changedIds);\r\n      saveState(this);\r\n      this.emit(\"selected\", newly);\r\n    }\r\n    return this;\r\n  }\r\n\r\n  deselect(rowIds?: unknown[]): this {\r\n    if (!this.options.selection || !this.identifier) return this;\r\n    const fullRefresh = rowIds == null;\r\n    const ids = rowIds ?? Array.from(this.selected);\r\n    const removed: TRow[] = [];\r\n    const changedIds: unknown[] = [];\r\n    for (const id of ids) {\r\n      if (!this.selected.delete(id)) continue;\r\n      const row = this.rowIndex.get(id);\r\n      if (row) {\r\n        removed.push(row);\r\n        changedIds.push(id);\r\n      }\r\n    }\r\n    if (removed.length) {\r\n      if (fullRefresh) this.refreshSelectionVisuals();\r\n      else this.refreshSelectionForIds(changedIds);\r\n      saveState(this);\r\n      this.emit(\"deselected\", removed);\r\n    }\r\n    return this;\r\n  }\r\n\r\n  reload(): this {\r\n    this.current = 1;\r\n    this.invalidate(\"filter\");\r\n    this.loadData();\r\n    return this;\r\n  }\r\n\r\n  destroy(): this {\r\n    if (this.destroyed) return this;\r\n    this.destroyed = true;\r\n    // Flush any pending debounced state save BEFORE we tear DOM down —\r\n    // a user's last interaction in the debounce window must still land\r\n    // in localStorage.\r\n    flushSaveState(this);\r\n    // Bump the request id so any in-flight ajax response is silently\r\n    // dropped instead of trying to render into a torn-down DOM. Abort\r\n    // the actual fetch too, when supported.\r\n    this.ajaxRequestId++;\r\n    if (this.ajaxAbort) { this.ajaxAbort.abort(); this.ajaxAbort = null; }\r\n    this.lastRenderedVirtualWindow = null;\r\n    this.cleanupFns.forEach((fn) => fn());\r\n    this.cleanupFns = [];\r\n    this.toolbarTop?.remove();\r\n    this.toolbarBottom?.remove();\r\n    this.bulkBar?.remove();\r\n    // Strip the auto-created <tfoot>. User-authored markup (no marker) stays.\r\n    const tfoot = this.element.querySelector<HTMLTableSectionElement>(\":scope > tfoot\");\r\n    if (tfoot && tfoot.dataset.boostgridAuto === \"true\") tfoot.remove();\r\n    // Unwrap: move the table back to where the wrapper currently sits, then\r\n    // drop the wrapper. Guarded so a partially-detached tree (e.g. when\r\n    // React's reconciler is mid-unmount) doesn't throw.\r\n    const parent = this.rootContainer.parentNode;\r\n    if (parent && this.element.parentNode === this.rootContainer) {\r\n      parent.insertBefore(this.element, this.rootContainer);\r\n    }\r\n    this.rootContainer.remove();\r\n    // Clear the convenience handle. `attach()`'s WeakMap entry is still\r\n    // there but the `existing.destroyed` guard makes it safe to ignore.\r\n    delete (this.element as HTMLTableElement & { boostgridInstance?: unknown }).boostgridInstance;\r\n    return this;\r\n  }\r\n\r\n  // -------- getters --------\r\n\r\n  getColumnSettings(): Column<TRow>[] { return this.columns.slice(); }\r\n  getCurrentPage(): number { return this.current; }\r\n  getCurrentRows(): TRow[] { return this.currentRows.slice(); }\r\n  getRowCount(): number { return this.rowsPerPage; }\r\n  getSearchPhrase(): string { return this.searchPhrase; }\r\n  getSelectedRows(): unknown[] { return Array.from(this.selected); }\r\n  getSortDictionary(): SortDictionary { return { ...this.sortDictionary }; }\r\n  getTotalPageCount(): number { return this.totalPages; }\r\n  getTotalRowCount(): number { return this.total; }\r\n  /** Rows after search/filter (across all pages). Snapshot copy. */\r\n  getFilteredRows(): TRow[] { return this.filtered.slice(); }\r\n  /** Unfiltered dataset. Snapshot copy. */\r\n  getAllRows(): TRow[] { return this.rows.slice(); }\r\n  /**\r\n   * Returns the footer cell for the given column id, or null if no footer\r\n   * is rendered or the column id doesn't match. Analog of DataTables'\r\n   * `column().footer()`.\r\n   */\r\n  /**\r\n   * Wipe persisted state for this table (no-op if `stateSave` is off or\r\n   * `localStorage` isn't available). Doesn't reset the live grid — call\r\n   * `reload()` afterwards if you want the in-memory state to roll back too.\r\n   */\r\n  clearSavedState(): this {\r\n    clearState(this);\r\n    return this;\r\n  }\r\n\r\n  /**\r\n   * Synchronously flush any pending debounced state save. State writes\r\n   * are debounced (~200ms) so a burst of mutations during a column\r\n   * resize drag doesn't pay 60Hz of `JSON.stringify`. Call this from a\r\n   * `beforeunload` handler, before reading `localStorage` in a test, or\r\n   * any other moment you need the latest state on disk *now*.\r\n   * `destroy()` flushes automatically, so this is only needed when the\r\n   * grid stays alive.\r\n   */\r\n  flushState(): this {\r\n    flushSaveState(this);\r\n    return this;\r\n  }\r\n\r\n  getFooterCell(columnId: string): HTMLTableCellElement | null {\r\n    const tfoot = this.element.querySelector(\":scope > tfoot\");\r\n    if (!tfoot) return null;\r\n    return tfoot.querySelector<HTMLTableCellElement>(\r\n      `[data-column-id=\"${cssEscape(columnId)}\"]`,\r\n    );\r\n  }\r\n\r\n  // -------- events --------\r\n\r\n  on(name: EventName, fn: Listener): this {\r\n    if (!this.listeners.has(name)) this.listeners.set(name, new Set());\r\n    this.listeners.get(name)!.add(fn);\r\n    return this;\r\n  }\r\n  off(name: EventName, fn: Listener): this {\r\n    this.listeners.get(name)?.delete(fn);\r\n    return this;\r\n  }\r\n  private emit(name: EventName, ...args: unknown[]): void {\r\n    this.listeners.get(name)?.forEach((fn) => fn(...args));\r\n    this.element.dispatchEvent(new CustomEvent(`boostgrid:${name}`, { detail: args, bubbles: true }));\r\n  }\r\n\r\n  // -------- internal --------\r\n\r\n  setRowsPerPage(n: number): void {\r\n    this.rowsPerPage = n;\r\n    this.current = 1;\r\n    this.invalidate(\"page\");\r\n    this.loadData();\r\n  }\r\n\r\n  goToPage(page: number): void {\r\n    if (page < 1 || page > this.totalPages) return;\r\n    this.current = page;\r\n    this.invalidate(\"page\");\r\n    this.loadData();\r\n  }\r\n\r\n  toggleSelectRow(id: unknown): void {\r\n    if (this.selected.has(id)) this.deselect([id]);\r\n    else this.select([id]);\r\n  }\r\n\r\n  /**\r\n   * True if the given group is currently rendered expanded. `pathOrKey` is\r\n   * either a single key (single-level grouping, back-compat) or a `//`-joined\r\n   * path string (multi-level — e.g. `\"active//us\"`). Honors\r\n   * `options.groupExpanded` for groups never toggled by the user.\r\n   */\r\n  isGroupExpanded(pathOrKey: unknown): boolean {\r\n    const k = String(pathOrKey == null ? \"\" : pathOrKey);\r\n    if (this.collapsedGroupPaths.has(k)) return false;\r\n    const ge = this.options.groupExpanded;\r\n    if (ge === \"none\") return false;\r\n    return true;\r\n  }\r\n\r\n  /** Flip the expand state for a group path and re-render the body. */\r\n  toggleGroup(pathOrKey: unknown): void {\r\n    const k = String(pathOrKey == null ? \"\" : pathOrKey);\r\n    if (this.collapsedGroupPaths.has(k)) this.collapsedGroupPaths.delete(k);\r\n    else this.collapsedGroupPaths.add(k);\r\n    saveState(this);\r\n    renderBody(this);\r\n  }\r\n\r\n  // -------- tree-mode public API --------\r\n\r\n  /** True if the given tree node is currently rendered expanded. */\r\n  isTreeExpanded(id: string | number): boolean {\r\n    if (this.treeExpandTracking === \"expanded\") {\r\n      return this.expandedTreeNodes.has(id);\r\n    }\r\n    return !this.expandedTreeNodes.has(id);\r\n  }\r\n\r\n  /** Flip the expand state for a tree node and re-render the body. */\r\n  toggleTreeNode(id: string | number): void {\r\n    if (this.treeExpandTracking === \"expanded\") {\r\n      if (this.expandedTreeNodes.has(id)) this.expandedTreeNodes.delete(id);\r\n      else this.expandedTreeNodes.add(id);\r\n    } else {\r\n      if (this.expandedTreeNodes.has(id)) this.expandedTreeNodes.delete(id);\r\n      else this.expandedTreeNodes.add(id);\r\n    }\r\n    saveState(this);\r\n    renderBody(this);\r\n  }\r\n\r\n  /** Expand every tree node. */\r\n  expandAllTree(): void {\r\n    this.expandedTreeNodes.clear();\r\n    this.treeExpandTracking = \"collapsed\";\r\n    saveState(this);\r\n    renderBody(this);\r\n  }\r\n\r\n  /**\r\n   * The id of the column that carries the tree caret (and ought to receive\r\n   * indentation in a tree-aware export). Returns `null` when not in tree\r\n   * mode. Same priority order as the renderer: explicit option →\r\n   * `treeColumn: true` flag → first non-frozen visible → first visible.\r\n   */\r\n  getTreeColumnId(): string | null {\r\n    if (!this.options.treeMode) return null;\r\n    return resolveTreeColumnId(this);\r\n  }\r\n\r\n  /**\r\n   * Walk the parent chain of a tree row and return its ancestors in\r\n   * root-first order (ie. `[root, …, parent]`, **excluding the row itself**).\r\n   * Returns `null` outside tree mode and an empty array for unknown ids.\r\n   */\r\n  getTreeAncestors(id: string | number): TRow[] | null {\r\n    if (!this.options.treeMode || !this.identifier) return null;\r\n    const idField = this.options.treeIdField ?? this.identifier;\r\n    const parentField = this.options.treeParentField;\r\n    const startRow = this.findTreeRowById(id, idField);\r\n    if (!startRow) return [];\r\n    const chain: TRow[] = [];\r\n    const seen = new Set<string | number>();\r\n    let cur: string | number | null = normalizeTreeId(\r\n      (startRow as Record<string, unknown>)[parentField],\r\n    );\r\n    while (cur != null) {\r\n      if (seen.has(cur)) break; // cycle guard\r\n      seen.add(cur);\r\n      const row = this.findTreeRowById(cur, idField);\r\n      if (!row) break;\r\n      chain.unshift(row);\r\n      cur = normalizeTreeId((row as Record<string, unknown>)[parentField]);\r\n    }\r\n    return chain;\r\n  }\r\n\r\n  /**\r\n   * Resolve a tree row's depth (0 for roots) by walking ancestors via\r\n   * `treeParentField`. Returns `null` when the id is unknown or when\r\n   * `treeMode` is off. Useful for export plugins that want to indent or\r\n   * path-annotate rows.\r\n   */\r\n  getTreeDepth(id: string | number): number | null {\r\n    if (!this.options.treeMode || !this.identifier) return null;\r\n    const idField = this.options.treeIdField ?? this.identifier;\r\n    const parentField = this.options.treeParentField;\r\n    if (!this.findTreeRowById(id, idField)) return null;\r\n    let depth = 0;\r\n    const seen = new Set<string | number>();\r\n    let cur: string | number | null = id;\r\n    while (cur != null) {\r\n      if (seen.has(cur)) return depth; // cycle guard\r\n      seen.add(cur);\r\n      const row = this.findTreeRowById(cur, idField);\r\n      if (!row) return depth;\r\n      const parent = normalizeTreeId((row as Record<string, unknown>)[parentField]);\r\n      if (parent == null) return depth;\r\n      depth++;\r\n      cur = parent;\r\n    }\r\n    return depth;\r\n  }\r\n\r\n  /** Collapse every tree node. */\r\n  collapseAllTree(): void {\r\n    this.expandedTreeNodes.clear();\r\n    this.treeExpandTracking = \"expanded\";\r\n    saveState(this);\r\n    renderBody(this);\r\n  }\r\n\r\n  // -------- row detail public API --------\r\n\r\n  /** True if the given row's detail panel is currently rendered. */\r\n  isRowDetailExpanded(id: string | number): boolean {\r\n    if (this.rowDetailTracking === \"expanded\") {\r\n      return this.expandedRowDetails.has(id);\r\n    }\r\n    return !this.expandedRowDetails.has(id);\r\n  }\r\n\r\n  /** Flip a row's detail-panel state and re-render the body. */\r\n  toggleRowDetail(id: string | number): void {\r\n    if (this.expandedRowDetails.has(id)) this.expandedRowDetails.delete(id);\r\n    else this.expandedRowDetails.add(id);\r\n    renderBody(this);\r\n  }\r\n\r\n  /** Open every row's detail panel. */\r\n  expandAllRowDetails(): void {\r\n    this.expandedRowDetails.clear();\r\n    this.rowDetailTracking = \"collapsed\";\r\n    renderBody(this);\r\n  }\r\n\r\n  /** Close every row's detail panel. */\r\n  collapseAllRowDetails(): void {\r\n    this.expandedRowDetails.clear();\r\n    this.rowDetailTracking = \"expanded\";\r\n    renderBody(this);\r\n  }\r\n\r\n  /**\r\n   * Reparent a tree row programmatically. Returns `true` on a real move,\r\n   * `false` on no-op or guard-rejected (cycle, missing row, etc.). Mutates\r\n   * `child[treeParentField]` in place and re-renders. Use `null` for\r\n   * `newParentId` to make the row a root.\r\n   */\r\n  reparentTreeNode(childId: string | number, newParentId: string | number | null): boolean {\r\n    if (!this.options.treeMode || !this.identifier) return false;\r\n    const idField = this.options.treeIdField ?? this.identifier;\r\n    const parentField = this.options.treeParentField;\r\n    const childRow = this.findTreeRowById(childId, idField);\r\n    if (!childRow) return false;\r\n    const oldParentId = normalizeTreeId(\r\n      (childRow as Record<string, unknown>)[parentField],\r\n    );\r\n    const targetParentId = newParentId == null ? null : normalizeTreeId(newParentId);\r\n    if (oldParentId === targetParentId) return false;\r\n    if (childId === targetParentId) return false; // self-parent\r\n    if (targetParentId != null && this.wouldCreateTreeCycle(childId, targetParentId, idField, parentField)) {\r\n      return false;\r\n    }\r\n    const oldParentRow = oldParentId == null ? null : this.findTreeRowById(oldParentId, idField);\r\n    const newParentRow = targetParentId == null ? null : this.findTreeRowById(targetParentId, idField);\r\n    if (targetParentId != null && newParentRow == null) return false; // unknown target\r\n    (childRow as Record<string, unknown>)[parentField] = targetParentId;\r\n    // Auto-expand the new parent so the moved subtree is visible.\r\n    if (targetParentId != null) {\r\n      if (this.treeExpandTracking === \"expanded\") {\r\n        this.expandedTreeNodes.add(targetParentId);\r\n      } else {\r\n        this.expandedTreeNodes.delete(targetParentId);\r\n      }\r\n    }\r\n    this.invalidate(\"filter\");\r\n    this.loadData();\r\n    saveState(this);\r\n    void Promise.resolve(\r\n      this.options.onReparent?.(childRow, newParentRow, oldParentRow),\r\n    );\r\n    return true;\r\n  }\r\n\r\n  private findTreeRowById(id: string | number, idField: string): TRow | null {\r\n    for (const r of this.rows) {\r\n      const rid = normalizeTreeId((r as Record<string, unknown>)[idField]);\r\n      if (rid === id) return r;\r\n    }\r\n    return null;\r\n  }\r\n\r\n  /** True if making `parentId` the parent of `childId` would create a\r\n   *  cycle — i.e. `parentId` is `childId` itself or a descendant. */\r\n  private wouldCreateTreeCycle(\r\n    childId: string | number,\r\n    parentId: string | number,\r\n    idField: string,\r\n    parentField: string,\r\n  ): boolean {\r\n    const seen = new Set<string | number>();\r\n    let cursor: string | number | null = parentId;\r\n    while (cursor != null) {\r\n      if (cursor === childId) return true;\r\n      if (seen.has(cursor)) return true;\r\n      seen.add(cursor);\r\n      const row = this.findTreeRowById(cursor, idField);\r\n      if (!row) return false;\r\n      cursor = normalizeTreeId((row as Record<string, unknown>)[parentField]);\r\n    }\r\n    return false;\r\n  }\r\n\r\n  private reindex(): void {\r\n    this.rowIndex.clear();\r\n    if (!this.identifier) return;\r\n    for (const r of this.rows) this.rowIndex.set(r[this.identifier], r);\r\n  }\r\n\r\n  private invalidate(level: \"filter\" | \"sort\" | \"page\"): void {\r\n    if (level === \"filter\") this.dirtyFilter = this.dirtySort = true;\r\n    else if (level === \"sort\") this.dirtySort = true;\r\n    // \"page\" is recomputed every loadData() — no flag needed\r\n  }\r\n\r\n  private executeSearch(phrase: string): void {\r\n    if (phrase === this.searchPhrase) return;\r\n    this.searchPhrase = phrase;\r\n    this.searchRegex = null;\r\n    this.current = 1;\r\n    this.invalidate(\"filter\");\r\n    const input = this.searchInput();\r\n    if (input && input.value !== phrase) input.value = phrase;\r\n    this.loadData();\r\n    this.emit(\"searched\", phrase);\r\n  }\r\n\r\n  private loadData(): void {\r\n    if (this.options.ajax) {\r\n      this.fetchAjax();\r\n      return;\r\n    }\r\n    if (this.dirtyFilter) {\r\n      this.filtered = this.applyFilter(this.rows);\r\n      this.dirtyFilter = false;\r\n    }\r\n    if (this.dirtySort) {\r\n      this.sorted = this.applySort(this.filtered);\r\n      this.dirtySort = false;\r\n    }\r\n    this.total = this.sorted.length;\r\n    if (this.rowsPerPage === -1) {\r\n      this.totalPages = 1;\r\n      this.current = 1;\r\n      this.currentRows = this.sorted;\r\n    } else {\r\n      this.totalPages = Math.max(1, Math.ceil(this.total / this.rowsPerPage));\r\n      if (this.current > this.totalPages) this.current = this.totalPages;\r\n      const start = (this.current - 1) * this.rowsPerPage;\r\n      this.currentRows = this.sorted.slice(start, start + this.rowsPerPage);\r\n    }\r\n    if (!this.options.keepSelection) this.selected.clear();\r\n\r\n    // Virtual scroll: refresh the window from the latest dataset slice\r\n    // BEFORE renderBody reads it. Then ensure the scroll listener is bound\r\n    // to the (possibly fresh) <tbody> AFTER the first paint.\r\n    if (this.options.virtualScroll) refreshVirtualWindow(this);\r\n    this.renderBody();\r\n    if (this.ensureVirtualBinding) this.ensureVirtualBinding();\r\n    this.renderFooter();\r\n    this.renderInfo();\r\n    this.renderPagination();\r\n    this.refreshSelectionVisuals();\r\n    saveState(this);\r\n    this.emit(\"loaded\");\r\n  }\r\n\r\n  private async fetchAjax(): Promise<void> {\r\n    const url = typeof this.options.url === \"function\" ? this.options.url() : this.options.url;\r\n    if (!url) throw new Error(\"Boostgrid: ajax mode requires a non-empty url.\");\r\n    const reqBody: AjaxRequest = {\r\n      current: this.current,\r\n      rowCount: this.rowsPerPage,\r\n      sort: this.sortDictionary,\r\n      searchPhrase: this.searchPhrase,\r\n    };\r\n    // Surface grouping + tree state so server adapters can return the\r\n    // matching slice / shape. Only include the fields when their feature\r\n    // is on, so existing endpoints see the same payload as before.\r\n    const gb = this.options.groupBy;\r\n    if (gb != null) {\r\n      const ids = typeof gb === \"string\" ? [gb] : Array.from(gb);\r\n      if (ids.length > 0) {\r\n        reqBody.groupBy = ids;\r\n        if (this.collapsedGroupPaths.size > 0) {\r\n          reqBody.collapsedGroups = Array.from(this.collapsedGroupPaths);\r\n        }\r\n      }\r\n    }\r\n    if (this.options.treeMode) {\r\n      reqBody.treeMode = true;\r\n      if (this.expandedTreeNodes.size > 0) {\r\n        reqBody.expandedTreeNodes = Array.from(this.expandedTreeNodes);\r\n      }\r\n    }\r\n    const transformed = this.options.requestHandler(reqBody);\r\n    this.emit(\"load\");\r\n    // Skeleton rows: animated placeholders fill the tbody until the fetch\r\n    // resolves and the real rows replace them.\r\n    const sk = this.options.loadingSkeleton;\r\n    if (sk !== false) {\r\n      const count = typeof sk === \"number\" ? Math.max(1, sk) :\r\n        this.rowsPerPage > 0 ? Math.min(this.rowsPerPage, 10) : 5;\r\n      renderSkeleton(this, count);\r\n    }\r\n    // Race-condition guard: bump the request id, abort any pending older\r\n    // request, and capture the new id locally so we can detect being\r\n    // superseded after the await.\r\n    const myId = ++this.ajaxRequestId;\r\n    if (this.ajaxAbort) this.ajaxAbort.abort();\r\n    const ctrl = typeof AbortController !== \"undefined\" ? new AbortController() : null;\r\n    this.ajaxAbort = ctrl;\r\n    let res: Response;\r\n    try {\r\n      res = await fetch(url, {\r\n        method: this.options.ajaxSettings.method,\r\n        headers: { \"Content-Type\": \"application/json\" },\r\n        body: JSON.stringify(transformed),\r\n        signal: ctrl ? ctrl.signal : undefined,\r\n      });\r\n    } catch (err) {\r\n      // A newer call aborted us — silently drop. Anything else rethrows so\r\n      // callers / global handlers see real network failures.\r\n      if (myId !== this.ajaxRequestId) return;\r\n      throw err;\r\n    }\r\n    if (myId !== this.ajaxRequestId) return;\r\n    const json = (await res.json()) as AjaxResponse;\r\n    if (myId !== this.ajaxRequestId) return;\r\n    if (this.ajaxAbort === ctrl) this.ajaxAbort = null;\r\n    const final = this.options.responseHandler(json);\r\n    this.rows = final.rows as TRow[];\r\n    this.reindex();\r\n    this.total = final.total;\r\n    this.totalPages = Math.max(1, Math.ceil(final.total / Math.max(1, this.rowsPerPage)));\r\n    this.currentRows = final.rows as TRow[];\r\n    // In ajax mode the server returns one page at a time, so filtered/all\r\n    // collapse to the same view as currentRows. Footer formatters that\r\n    // need true cross-page totals must compute server-side and inject via\r\n    // a custom column or footerCallback.\r\n    this.filtered = final.rows as TRow[];\r\n    this.sorted = final.rows as TRow[];\r\n    this.renderBody();\r\n    this.renderFooter();\r\n    this.renderInfo();\r\n    this.renderPagination();\r\n    saveState(this);\r\n    this.emit(\"loaded\");\r\n  }\r\n\r\n  private applyFilter(rows: TRow[]): TRow[] {\r\n    // Tree mode keeps every row through filtering so buildTree() can\r\n    // include ancestors of matched leaves. The actual match-vs-prune\r\n    // happens inside render/tree.ts's walkTree().\r\n    if (this.options.treeMode) return rows.slice();\r\n    if (!this.searchPhrase) return rows.slice();\r\n    // Compile the search regex once per phrase, reuse across rows + columns.\r\n    let re = this.searchRegex;\r\n    if (!re) {\r\n      const flags = this.options.caseSensitive ? \"\" : \"i\";\r\n      re = new RegExp(escapeRegExp(this.searchPhrase), flags);\r\n      this.searchRegex = re;\r\n    }\r\n    const cols = this.columns.filter((c) => c.searchable && c.visible);\r\n    return rows.filter((row) => {\r\n      for (const col of cols) {\r\n        if (re!.test(col.converter.to(row[col.id]))) return true;\r\n      }\r\n      return false;\r\n    });\r\n  }\r\n\r\n  private applySort(rows: TRow[]): TRow[] {\r\n    let cmp = this.sortComparator;\r\n    if (!cmp) {\r\n      const entries = Object.entries(this.sortDictionary);\r\n      if (entries.length === 0) return rows.slice();\r\n      // Pre-compile the comparator once. Each subsequent applySort with\r\n      // the same sortDictionary reuses this closure rather than rebuilding\r\n      // `entries` and the multi-key fallback chain.\r\n      cmp = (a: TRow, b: TRow) => {\r\n        for (let i = 0; i < entries.length; i++) {\r\n          const [col, dir] = entries[i];\r\n          const c = compare(a[col], b[col]);\r\n          if (c !== 0) return dir === \"asc\" ? c : -c;\r\n        }\r\n        return 0;\r\n      };\r\n      this.sortComparator = cmp;\r\n    }\r\n    return rows.slice().sort(cmp);\r\n  }\r\n\r\n  // -------- rendering glue --------\r\n\r\n  private mountChrome(): void {\r\n    this.mountToolbars();\r\n    this.renderHeader();\r\n  }\r\n\r\n  /** Mount toolbars + bulk bar around the table. Called once at construction\r\n   *  and again from {@link redrawChrome} when callers swap labels at runtime. */\r\n  private mountToolbars(): void {\r\n    const top = !!(this.options.navigation & 1);\r\n    const bottom = !!(this.options.navigation & 2);\r\n    if (top) {\r\n      this.toolbarTop = renderToolbar(this, \"top\");\r\n      this.rootContainer.insertBefore(this.toolbarTop, this.element);\r\n    }\r\n    if (this.options.bulkActions) {\r\n      this.bulkBar = el(\"div\", {\r\n        class: \"boostgrid-bulkbar\",\r\n        style: \"display: none;\",\r\n        role: \"toolbar\",\r\n        \"aria-label\": this.options.labels.bulkActions,\r\n      });\r\n      this.rootContainer.insertBefore(this.bulkBar, this.element);\r\n    }\r\n    if (bottom) {\r\n      this.toolbarBottom = renderToolbar(this, \"bottom\");\r\n      this.rootContainer.appendChild(this.toolbarBottom);\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Re-render the toolbars + bulk bar after mutating `options.labels`,\r\n   * `options.locale`, or `options.bulkActions` at runtime. Skips the\r\n   * `<thead>` so per-column markup metadata (data-identifier, data-tree-column,\r\n   * etc.) is preserved across the call. Delegated listeners on the wrapper\r\n   * survive the swap automatically.\r\n   */\r\n  redrawChrome(): void {\r\n    this.toolbarTop?.remove();\r\n    this.toolbarBottom?.remove();\r\n    this.bulkBar?.remove();\r\n    this.toolbarTop = null;\r\n    this.toolbarBottom = null;\r\n    this.bulkBar = null;\r\n    this.mountToolbars();\r\n    this.renderInfo();\r\n    this.renderPagination();\r\n    this.renderBulkBar();\r\n  }\r\n\r\n  /** Refresh the sticky bulk-action bar based on current selection. Idempotent\r\n   *  — called from selection mutators. No-ops when `bulkActions` is null. */\r\n  private renderBulkBar(): void {\r\n    if (!this.bulkBar || !this.options.bulkActions) return;\r\n    const selectedIds = Array.from(this.selected);\r\n    if (selectedIds.length === 0) {\r\n      this.bulkBar.style.display = \"none\";\r\n      this.bulkBar.replaceChildren();\r\n      return;\r\n    }\r\n    const rows = this.getSelectedRowObjects();\r\n    const userContent = this.options.bulkActions(rows);\r\n    this.bulkBar.replaceChildren();\r\n    this.bulkBar.style.display = \"\";\r\n    const counter = el(\"span\", { class: \"boostgrid-bulkbar-count\" });\r\n    counter.textContent = this.options.labels.bulkSelected.replace(\r\n      \"{n}\",\r\n      String(selectedIds.length),\r\n    );\r\n    this.bulkBar.appendChild(counter);\r\n    const slot = el(\"div\", { class: \"boostgrid-bulkbar-slot\" });\r\n    if (typeof userContent === \"string\") slot.innerHTML = userContent;\r\n    else slot.appendChild(userContent);\r\n    this.bulkBar.appendChild(slot);\r\n    const clear = el(\"button\", {\r\n      type: \"button\",\r\n      class: \"btn btn-sm btn-link ms-auto\",\r\n      \"data-bg-action\": \"bulk-clear\",\r\n    });\r\n    clear.textContent = this.options.labels.bulkClear;\r\n    this.bulkBar.appendChild(clear);\r\n  }\r\n\r\n  /** Resolve currently-selected ids to row objects via the row index. */\r\n  private getSelectedRowObjects(): TRow[] {\r\n    const out: TRow[] = [];\r\n    for (const id of this.selected) {\r\n      const r = this.rowIndex.get(id);\r\n      if (r) out.push(r);\r\n    }\r\n    return out;\r\n  }\r\n\r\n  private renderHeader(): void { this.mark(\"header\", () => renderHeader(this)); }\r\n  private renderBody(): void   { this.mark(\"body\",   () => renderBody(this)); }\r\n  private renderFooter(): void { this.mark(\"footer\", () => renderFooter(this)); }\r\n\r\n  /**\r\n   * Wrap a render phase with `performance.mark()` + `performance.measure()`\r\n   * entries when `performanceMarks` is on. The measure name is namespaced\r\n   * by table id so multiple grids on the same page surface as distinct\r\n   * Timings entries in DevTools. No-ops (zero overhead) by default.\r\n   */\r\n  private mark<T>(phase: string, fn: () => T): T {\r\n    if (!this.options.performanceMarks || typeof performance === \"undefined\" || !performance.mark) {\r\n      return fn();\r\n    }\r\n    const id = this.element.id || \"boostgrid\";\r\n    const start = `boostgrid:${id}:${phase}:start`;\r\n    const end = `boostgrid:${id}:${phase}:end`;\r\n    performance.mark(start);\r\n    try { return fn(); }\r\n    finally {\r\n      performance.mark(end);\r\n      try { performance.measure(`boostgrid:${id}:${phase}`, start, end); }\r\n      catch { /* mark cleared between calls — silent */ }\r\n    }\r\n  }\r\n  private renderInfo(): void {\r\n    [this.toolbarTop, this.toolbarBottom].forEach((bar) => {\r\n      if (bar) renderInfos(bar, this);\r\n    });\r\n  }\r\n  private renderPagination(): void {\r\n    [this.toolbarTop, this.toolbarBottom].forEach((bar) => {\r\n      if (bar) renderPagination(bar, this);\r\n    });\r\n  }\r\n\r\n  private searchInput(): HTMLInputElement | null {\r\n    return $<HTMLInputElement>(\".bg-search input\", this.rootContainer);\r\n  }\r\n\r\n  private refreshSelectionVisuals(): void {\r\n    // Bulk-action bar reflects selection regardless of `options.selection`,\r\n    // so refresh it before the early return below.\r\n    this.renderBulkBar();\r\n    if (!this.identifier || !this.options.selection) return;\r\n    const idCol = this.identifier;\r\n    const rows = $$(\"tbody > tr\", this.element);\r\n    rows.forEach((tr) => {\r\n      const id = (tr as HTMLElement).dataset.rowId;\r\n      if (id == null) return;\r\n      // O(1) lookup via the row index instead of a per-row .find() walk\r\n      // over currentRows. Identifier values may be numeric, so coerce\r\n      // both sides to string for the keyed lookup.\r\n      let match = this.rowIndex.get(id) as TRow | undefined;\r\n      if (!match && /^-?\\d+(\\.\\d+)?$/.test(id)) match = this.rowIndex.get(Number(id));\r\n      if (!match) return;\r\n      const isSelected = this.selected.has(match[idCol]);\r\n      tr.classList.toggle(\"table-active\", isSelected);\r\n      const cb = $<HTMLInputElement>(\"input.bg-select-row\", tr);\r\n      if (cb) cb.checked = isSelected;\r\n    });\r\n    const head = $<HTMLInputElement>(\"thead input.bg-select-all\", this.element);\r\n    if (head) {\r\n      const all = this.currentRows.length > 0\r\n        && this.currentRows.every((r) => this.selected.has(r[idCol]));\r\n      head.checked = all;\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Fast path: only touch the rows whose selection state actually changed.\r\n   * Avoids walking every visible <tr> for single-row toggle events. The\r\n   * caller passes the ids it just added to / removed from `this.selected`.\r\n   * The header select-all checkbox is cheap (an `every()` over currentRows)\r\n   * and is re-evaluated on every call so it stays in sync.\r\n   */\r\n  private refreshSelectionForIds(changedIds: unknown[]): void {\r\n    this.renderBulkBar();\r\n    if (!this.identifier || !this.options.selection) return;\r\n    const idCol = this.identifier;\r\n    const tbody = this.element.querySelector<HTMLTableSectionElement>(\":scope > tbody\");\r\n    if (tbody) {\r\n      for (const id of changedIds) {\r\n        const sel = `tr[data-row-id=\"${cssEscape(String(id))}\"]`;\r\n        const tr = tbody.querySelector<HTMLTableRowElement>(sel);\r\n        if (!tr) continue;\r\n        const isSelected = this.selected.has(id);\r\n        tr.classList.toggle(\"table-active\", isSelected);\r\n        const cb = tr.querySelector<HTMLInputElement>(\"input.bg-select-row\");\r\n        if (cb) cb.checked = isSelected;\r\n      }\r\n    }\r\n    const head = $<HTMLInputElement>(\"thead input.bg-select-all\", this.element);\r\n    if (head) {\r\n      const all = this.currentRows.length > 0\r\n        && this.currentRows.every((r) => this.selected.has(r[idCol]));\r\n      head.checked = all;\r\n    }\r\n  }\r\n\r\n  private bindDelegatedEvents(): void {\r\n    // Single delegated click listener handles every action chip in the grid.\r\n    this.cleanupFns.push(delegate(this.rootContainer, \"click\", \"[data-bg-action]\", (e, t) => {\r\n      const action = t.dataset.bgAction;\r\n      const value = t.dataset.bgValue;\r\n      switch (action) {\r\n        case \"page\": {\r\n          const p = Number(value);\r\n          if (!Number.isNaN(p)) this.goToPage(p);\r\n          break;\r\n        }\r\n        case \"rows-per-page\": {\r\n          this.setRowsPerPage(Number(value));\r\n          break;\r\n        }\r\n        case \"sort\": {\r\n          if (!value) return;\r\n          const col = this.columns.find((c) => c.id === value);\r\n          if (!col?.sortable) return;\r\n          const cur = this.sortDictionary[value];\r\n          const next: SortDictionary = this.options.multiSort ? { ...this.sortDictionary } : {};\r\n          next[value] = cur === \"asc\" ? \"desc\" : \"asc\";\r\n          if (cur === \"desc\") delete next[value];\r\n          this.sort(next);\r\n          break;\r\n        }\r\n        case \"toggle-column\": {\r\n          const col = this.columns.find((c) => c.id === value);\r\n          if (col) {\r\n            col.visible = !col.visible;\r\n            // Diffed fast path for non-frozen columns: flip `hidden` on\r\n            // every existing cell in this column. The header / body /\r\n            // footer DOM stays put; only one boolean attribute moves.\r\n            // Frozen columns can't take this path because hiding one\r\n            // shifts the sticky offsets of its siblings — the offset\r\n            // cache needs the full re-render to recompute.\r\n            //\r\n            // Caveat: rows whose colspan reads visible-column count\r\n            // (group headers, \"no results\", master/detail panels) keep\r\n            // their old colspan until the next full render. The visual\r\n            // overshoot is one column wide and harmless; loadData() and\r\n            // any user action that triggers a re-render restores it.\r\n            if (col.frozen) {\r\n              this.renderHeader();\r\n              this.renderBody();\r\n              this.renderFooter();\r\n            } else {\r\n              const sel = `[data-column-id=\"${cssEscape(value!)}\"]`;\r\n              const cells = this.element.querySelectorAll<HTMLElement>(sel);\r\n              const hide = !col.visible;\r\n              for (const cell of cells) cell.hidden = hide;\r\n            }\r\n            saveState(this);\r\n          }\r\n          break;\r\n        }\r\n        case \"reset-columns\": {\r\n          this.resetColumnState();\r\n          break;\r\n        }\r\n        case \"refresh\": {\r\n          this.reload();\r\n          break;\r\n        }\r\n        case \"toggle-group\": {\r\n          if (value != null) this.toggleGroup(value);\r\n          break;\r\n        }\r\n        case \"toggle-tree\": {\r\n          if (value != null) {\r\n            // Coerce numeric ids back to numbers — readData stores them as strings.\r\n            const id = /^-?\\d+(\\.\\d+)?$/.test(value) ? Number(value) : value;\r\n            this.toggleTreeNode(id);\r\n          }\r\n          break;\r\n        }\r\n        case \"toggle-detail\": {\r\n          if (value != null) {\r\n            const id = /^-?\\d+(\\.\\d+)?$/.test(value) ? Number(value) : value;\r\n            this.toggleRowDetail(id);\r\n          }\r\n          break;\r\n        }\r\n        case \"bulk-clear\": {\r\n          this.deselect();\r\n          break;\r\n        }\r\n      }\r\n      // Cancel the default action for anchors / buttons (hash jump,\r\n      // `javascript:void(0)` navigation, stray form submits). BUT NOT for\r\n      // checkbox / radio controls — the column-visibility menu uses\r\n      // `<input type=\"checkbox\" data-bg-action=\"toggle-column\">`, and\r\n      // calling preventDefault on its click reverts the native toggle and\r\n      // suppresses the change event, leaving the box stuck at its initial\r\n      // checked state even though the column correctly shows/hides.\r\n      const isToggleInput = t instanceof HTMLInputElement\r\n        && (t.type === \"checkbox\" || t.type === \"radio\");\r\n      if (!isToggleInput) e.preventDefault();\r\n    }));\r\n\r\n    // Selection: row checkbox + row click (if rowSelect)\r\n    this.cleanupFns.push(delegate(this.rootContainer, \"click\", \"input.bg-select-row\", (_, t) => {\r\n      const tr = t.closest(\"tr\");\r\n      const id = tr?.getAttribute(\"data-row-id\");\r\n      if (id == null) return;\r\n      this.toggleSelectRowByDatasetId(id);\r\n    }));\r\n    this.cleanupFns.push(delegate(this.rootContainer, \"click\", \"input.bg-select-all\", (_, t) => {\r\n      const checked = (t as HTMLInputElement).checked;\r\n      if (checked) this.select();\r\n      else this.deselect();\r\n    }));\r\n    this.cleanupFns.push(delegate(this.rootContainer, \"click\", \"tbody > tr\", (e, tr) => {\r\n      if (!this.options.rowSelect) return;\r\n      const target = e.target as Element;\r\n      if (target.closest(\"input,a,button,label\")) return;\r\n      const id = tr.getAttribute(\"data-row-id\");\r\n      if (id == null) return;\r\n      this.toggleSelectRowByDatasetId(id);\r\n    }));\r\n\r\n    // Search input\r\n    this.cleanupFns.push(delegate(this.rootContainer, \"input\", \".bg-search input\", (e) => {\r\n      const v = (e.target as HTMLInputElement).value;\r\n      if (v.length === 0 || v.length >= this.options.searchSettings.characters) {\r\n        this.debouncedSearch(v);\r\n      }\r\n    }));\r\n\r\n    // Truncated-text tooltips: lazily attach a `title` attribute when the\r\n    // user hovers a body cell whose content overflows. Skips cells that\r\n    // already carry a title (formatter authors win) and cells with an\r\n    // explicit `data-bg-no-tooltip`.\r\n    if (this.options.truncatedTooltips) {\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"mouseover\", \"tbody > tr > td\", (_e, td) => {\r\n          if (td.hasAttribute(\"title\")) return;\r\n          if (td.hasAttribute(\"data-bg-no-tooltip\")) return;\r\n          // scrollWidth > clientWidth → content is being clipped.\r\n          if (td.scrollWidth - td.clientWidth <= 1) return;\r\n          // Use textContent so we mirror what the user actually sees,\r\n          // not the formatter's HTML.\r\n          const text = (td.textContent ?? \"\").trim();\r\n          if (text) td.setAttribute(\"title\", text);\r\n        }),\r\n      );\r\n    }\r\n\r\n    // Column-visibility panel: filter-columns input filters the items\r\n    // locally, no re-render needed. We toggle a `d-none` class per item.\r\n    this.cleanupFns.push(\r\n      delegate(this.rootContainer, \"input\", \"[data-bg-action=\\\"filter-columns\\\"]\", (e, t) => {\r\n        const q = (t as HTMLInputElement).value.trim().toLowerCase();\r\n        const list = t.closest(\".boostgrid-columns-menu\")?.querySelector(\".boostgrid-columns-list\");\r\n        if (!list) return;\r\n        // Build the id->column map once instead of doing .find() per item.\r\n        const byId = new Map<string, Column<TRow>>();\r\n        for (const c of this.columns) byId.set(c.id, c);\r\n        list.querySelectorAll<HTMLElement>(\".boostgrid-columns-item\").forEach((item) => {\r\n          const id = item.getAttribute(\"data-column-id\") ?? \"\";\r\n          const col = byId.get(id);\r\n          const text = (col?.text ?? id).toLowerCase();\r\n          const match = !q || text.includes(q) || id.toLowerCase().includes(q);\r\n          item.classList.toggle(\"d-none\", !match);\r\n        });\r\n        e.preventDefault();\r\n      }),\r\n    );\r\n\r\n    // Tree drag-to-reparent: opt-in (treeReparent: true). Uses the same\r\n    // delegated DnD pattern as column reorder, but on body <tr>'s carrying\r\n    // data-row-id. Cycle/self guards live in reparentTreeNode().\r\n    if (this.options.treeReparent) {\r\n      let dragRowId: string | null = null;\r\n      const clearMarks = () => {\r\n        this.element.querySelectorAll<HTMLElement>(\"tbody > tr[data-drop-target]\").forEach((tr) => {\r\n          tr.removeAttribute(\"data-drop-target\");\r\n        });\r\n      };\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"dragstart\", \"tbody > tr[data-row-id][draggable=\\\"true\\\"]\", (e, t) => {\r\n          const id = t.getAttribute(\"data-row-id\");\r\n          if (!id) return;\r\n          dragRowId = id;\r\n          const dt = (e as DragEvent).dataTransfer;\r\n          if (dt) {\r\n            dt.effectAllowed = \"move\";\r\n            try { dt.setData(\"text/plain\", id); } catch { /* jsdom no-op */ }\r\n          }\r\n        }),\r\n      );\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"dragover\", \"tbody > tr[data-row-id]\", (e, t) => {\r\n          if (!dragRowId) return;\r\n          const targetId = t.getAttribute(\"data-row-id\");\r\n          if (!targetId || targetId === dragRowId) return;\r\n          clearMarks();\r\n          t.setAttribute(\"data-drop-target\", \"true\");\r\n          e.preventDefault();\r\n          const dt = (e as DragEvent).dataTransfer;\r\n          if (dt) dt.dropEffect = \"move\";\r\n        }),\r\n      );\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"dragleave\", \"tbody > tr[data-row-id]\", (_, t) => {\r\n          t.removeAttribute(\"data-drop-target\");\r\n        }),\r\n      );\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"drop\", \"tbody > tr[data-row-id]\", (e, t) => {\r\n          const dragId = dragRowId;\r\n          dragRowId = null;\r\n          clearMarks();\r\n          if (!dragId) return;\r\n          const targetId = t.getAttribute(\"data-row-id\");\r\n          if (!targetId || targetId === dragId) return;\r\n          // Coerce numeric ids back so they match the row's typed id.\r\n          const childKey = /^-?\\d+(\\.\\d+)?$/.test(dragId) ? Number(dragId) : dragId;\r\n          const parentKey = /^-?\\d+(\\.\\d+)?$/.test(targetId) ? Number(targetId) : targetId;\r\n          if (this.reparentTreeNode(childKey, parentKey)) {\r\n            e.preventDefault();\r\n          }\r\n        }),\r\n      );\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"dragend\", \"tbody > tr[data-row-id]\", () => {\r\n          dragRowId = null;\r\n          clearMarks();\r\n        }),\r\n      );\r\n    }\r\n\r\n    // Column-visibility panel: drag a row's grip handle to reorder.\r\n    // Reuses the same reorderColumn() method as the header drag, so the\r\n    // frozen-group constraint is honored.\r\n    if (this.options.columnReorder) {\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"dragstart\", \".boostgrid-columns-item[draggable=\\\"true\\\"]\", (e, t) => {\r\n          const id = t.getAttribute(\"data-column-id\");\r\n          if (!id) return;\r\n          this.dragColumnId = id;\r\n          t.setAttribute(\"data-dragging\", \"true\");\r\n          const dt = (e as DragEvent).dataTransfer;\r\n          if (dt) {\r\n            dt.effectAllowed = \"move\";\r\n            try { dt.setData(\"text/plain\", id); } catch { /* jsdom no-op */ }\r\n          }\r\n        }),\r\n      );\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"dragover\", \".boostgrid-columns-item\", (e, t) => {\r\n          if (!this.dragColumnId) return;\r\n          const targetId = t.getAttribute(\"data-column-id\");\r\n          if (!targetId || targetId === this.dragColumnId) return;\r\n          const rect = t.getBoundingClientRect();\r\n          const before = (e as DragEvent).clientY < rect.top + rect.height / 2;\r\n          t.setAttribute(\"data-drop-side\", before ? \"before\" : \"after\");\r\n          e.preventDefault();\r\n        }),\r\n      );\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"dragleave\", \".boostgrid-columns-item\", (_, t) => {\r\n          t.removeAttribute(\"data-drop-side\");\r\n        }),\r\n      );\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"drop\", \".boostgrid-columns-item\", (e, t) => {\r\n          const dragId = this.dragColumnId;\r\n          this.dragColumnId = null;\r\n          this.element.querySelectorAll<HTMLElement>(\".boostgrid-columns-item[data-drop-side]\").forEach((it) => {\r\n            it.removeAttribute(\"data-drop-side\");\r\n          });\r\n          if (!dragId) return;\r\n          const targetId = t.getAttribute(\"data-column-id\");\r\n          if (!targetId || targetId === dragId) return;\r\n          const rect = t.getBoundingClientRect();\r\n          const before = (e as DragEvent).clientY < rect.top + rect.height / 2;\r\n          if (this.reorderColumn(dragId, targetId, before ? \"before\" : \"after\")) {\r\n            e.preventDefault();\r\n          }\r\n        }),\r\n      );\r\n    }\r\n\r\n    // Column reorder: HTML5 DnD on header cells. Single delegated listener\r\n    // for each phase mirrors the click-action pattern. Drop is constrained\r\n    // within each frozen group (left / unfrozen / right) so sticky stacking\r\n    // stays sensible.\r\n    if (this.options.columnReorder) {\r\n      const clearDropMarks = () => {\r\n        this.element.querySelectorAll<HTMLElement>(\"thead th[data-drop-side]\").forEach((th) => {\r\n          th.removeAttribute(\"data-drop-side\");\r\n        });\r\n      };\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"dragstart\", \"thead > tr > th[draggable=\\\"true\\\"]\", (e, t) => {\r\n          // Ignore drags that began on the resize grip — that's a separate gesture.\r\n          const tgt = e.target as HTMLElement;\r\n          if (tgt.classList.contains(\"boostgrid-resize-grip\")) {\r\n            e.preventDefault();\r\n            return;\r\n          }\r\n          const id = t.getAttribute(\"data-column-id\");\r\n          if (!id) return;\r\n          this.dragColumnId = id;\r\n          t.setAttribute(\"data-dragging\", \"true\");\r\n          const dt = (e as DragEvent).dataTransfer;\r\n          if (dt) {\r\n            dt.effectAllowed = \"move\";\r\n            // Some browsers (Firefox) need a payload to start a drag at all.\r\n            try { dt.setData(\"text/plain\", id); } catch { /* jsdom no-op */ }\r\n          }\r\n        }),\r\n      );\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"dragover\", \"thead > tr > th\", (e, t) => {\r\n          if (!this.dragColumnId) return;\r\n          const targetId = t.getAttribute(\"data-column-id\");\r\n          if (!targetId || targetId === this.dragColumnId) return;\r\n          const dragCol = this.columns.find((c) => c.id === this.dragColumnId);\r\n          const targetCol = this.columns.find((c) => c.id === targetId);\r\n          if (!dragCol || !targetCol) return;\r\n          // Frozen-group constraint — if sides differ, cap at the appropriate\r\n          // side's edge. We still allow the dragover so the cursor doesn't\r\n          // turn into \"no entry\"; we just bias the side marker.\r\n          const rect = t.getBoundingClientRect();\r\n          const x = (e as DragEvent).clientX;\r\n          const before = x < rect.left + rect.width / 2;\r\n          clearDropMarks();\r\n          t.setAttribute(\"data-drop-side\", before ? \"before\" : \"after\");\r\n          e.preventDefault();\r\n          const dt = (e as DragEvent).dataTransfer;\r\n          if (dt) dt.dropEffect = \"move\";\r\n        }),\r\n      );\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"dragleave\", \"thead > tr > th\", (_, t) => {\r\n          t.removeAttribute(\"data-drop-side\");\r\n        }),\r\n      );\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"drop\", \"thead > tr > th\", (e, t) => {\r\n          const dragId = this.dragColumnId;\r\n          this.dragColumnId = null;\r\n          clearDropMarks();\r\n          this.element.querySelectorAll<HTMLElement>(\"thead th[data-dragging]\").forEach((th) => {\r\n            th.removeAttribute(\"data-dragging\");\r\n          });\r\n          if (!dragId) return;\r\n          const targetId = t.getAttribute(\"data-column-id\");\r\n          if (!targetId || targetId === dragId) return;\r\n          const rect = t.getBoundingClientRect();\r\n          const before = (e as DragEvent).clientX < rect.left + rect.width / 2;\r\n          if (this.reorderColumn(dragId, targetId, before ? \"before\" : \"after\")) {\r\n            e.preventDefault();\r\n          }\r\n        }),\r\n      );\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"dragend\", \"thead > tr > th\", () => {\r\n          this.dragColumnId = null;\r\n          clearDropMarks();\r\n          this.element.querySelectorAll<HTMLElement>(\"thead th[data-dragging]\").forEach((th) => {\r\n            th.removeAttribute(\"data-dragging\");\r\n          });\r\n        }),\r\n      );\r\n    }\r\n\r\n    // Column resize: delegated mousedown on the grip starts a drag tracked\r\n    // on the document until mouseup. Live updates skip a full re-render —\r\n    // we only mutate the column's <th> + matching <td> widths.\r\n    if (this.options.columnResize) {\r\n      this.cleanupFns.push(\r\n        delegate(this.rootContainer, \"mousedown\", \".boostgrid-resize-grip\", (e, t) => {\r\n          const id = t.getAttribute(\"data-bg-grip\");\r\n          if (!id) return;\r\n          const col = this.columns.find((c) => c.id === id);\r\n          if (!col || !col.resizable) return;\r\n          this.beginResize(col, t as HTMLElement, e as MouseEvent);\r\n          e.preventDefault();\r\n        }),\r\n      );\r\n    }\r\n  }\r\n\r\n  /**\r\n   * Restore column order, visibility, and widths to the values captured\r\n   * at attach time (the \"as authored\" baseline). Persists the reset.\r\n   */\r\n  resetColumnState(): void {\r\n    const byId = new Map(this.columns.map((c) => [c.id, c]));\r\n    const restored: Column<TRow>[] = [];\r\n    for (const snap of this.columnsBaseline) {\r\n      const c = byId.get(snap.id);\r\n      if (!c) continue;\r\n      c.visible = snap.visible;\r\n      c.width = snap.width;\r\n      restored.push(c);\r\n    }\r\n    // Preserve any columns that weren't in the baseline (defensive — should\r\n    // never happen in normal use).\r\n    for (const c of this.columns) {\r\n      if (!restored.includes(c)) restored.push(c);\r\n    }\r\n    this.columns = restored;\r\n    this.renderHeader();\r\n    renderBody(this);\r\n    this.renderFooter();\r\n    saveState(this);\r\n  }\r\n\r\n  /**\r\n   * Splice `dragId` into a new position relative to `targetId`. Returns\r\n   * `true` when the order changed. Frozen-group constrained: a left-frozen\r\n   * column dropped onto a non-frozen target snaps to the end of the\r\n   * left-frozen run; same idea for the right-frozen group. Identifier\r\n   * columns reorder freely (the user can opt out per-column via\r\n   * `reorderable: false`).\r\n   */\r\n  reorderColumn(dragId: string, targetId: string, side: \"before\" | \"after\"): boolean {\r\n    if (dragId === targetId) return false;\r\n    const dragIdx = this.columns.findIndex((c) => c.id === dragId);\r\n    if (dragIdx === -1) return false;\r\n    const dragCol = this.columns[dragIdx];\r\n    if (!dragCol.reorderable) return false;\r\n    // Snap-to-edge: if the target sits in a different frozen group, snap to\r\n    // the boundary of the dragged column's own group instead of jumping\r\n    // across. Index of last left-frozen + count of right-frozen are the\r\n    // anchor points.\r\n    const next = [...this.columns];\r\n    next.splice(dragIdx, 1);\r\n    let insertAt: number;\r\n    const targetIdx = next.findIndex((c) => c.id === targetId);\r\n    if (targetIdx === -1) return false;\r\n    const targetCol = next[targetIdx];\r\n    if (dragCol.frozen === targetCol.frozen) {\r\n      insertAt = side === \"before\" ? targetIdx : targetIdx + 1;\r\n    } else {\r\n      // Snap to end of the dragged column's frozen group.\r\n      if (dragCol.frozen === \"left\") {\r\n        insertAt = next.findIndex((c) => c.frozen !== \"left\");\r\n        if (insertAt === -1) insertAt = next.length;\r\n      } else if (dragCol.frozen === \"right\") {\r\n        insertAt = next.findIndex((c) => c.frozen === \"right\");\r\n        if (insertAt === -1) insertAt = next.length;\r\n      } else {\r\n        // Non-frozen dragged onto a frozen target: settle just outside the\r\n        // frozen group on whichever side the target lives.\r\n        if (targetCol.frozen === \"left\") {\r\n          insertAt = next.findIndex((c) => c.frozen !== \"left\");\r\n          if (insertAt === -1) insertAt = next.length;\r\n        } else {\r\n          insertAt = next.findIndex((c) => c.frozen === \"right\");\r\n          if (insertAt === -1) insertAt = next.length;\r\n        }\r\n      }\r\n    }\r\n    next.splice(insertAt, 0, dragCol);\r\n    if (next.every((c, i) => c === this.columns[i])) return false;\r\n    this.columns = next;\r\n    this.renderHeader();\r\n    renderBody(this);\r\n    this.renderFooter();\r\n    saveState(this);\r\n    this.options.onColumnReorder?.(this.columns.map((c) => c.id));\r\n    return true;\r\n  }\r\n\r\n  /** Start a drag-resize on the given column. Tracked on the document so\r\n   *  the user can drag past the grid bounds. Cleans up its own listeners\r\n   *  on mouseup; does not push to the persistent cleanupFns list. */\r\n  private beginResize(\r\n    col: Column<TRow>,\r\n    grip: HTMLElement,\r\n    ev: MouseEvent,\r\n  ): void {\r\n    const th = grip.closest<HTMLTableCellElement>(\"th\");\r\n    if (!th) return;\r\n    const rect = th.getBoundingClientRect();\r\n    const startX = ev.clientX;\r\n    const startWidth = rect.width;\r\n    grip.setAttribute(\"data-active\", \"true\");\r\n    th.setAttribute(\"data-resizing\", \"true\");\r\n    document.body.style.cursor = \"col-resize\";\r\n\r\n    // Pre-resolve matching <td>s once at drag start. Rows can't change\r\n    // while the drag is in flight (no other inputs are processing), so\r\n    // re-querying every mousemove was pure waste. Walk manually rather\r\n    // than using a CSS selector because column ids may contain characters\r\n    // that need escaping.\r\n    const matchingTds: HTMLTableCellElement[] = [];\r\n    const allTds = this.element.querySelectorAll<HTMLTableCellElement>(\"tbody > tr > td\");\r\n    for (const td of allTds) {\r\n      if (td.getAttribute(\"data-column-id\") === col.id) matchingTds.push(td);\r\n    }\r\n\r\n    const apply = (px: number): number => {\r\n      const clamped = Math.max(col.minWidth, Math.min(col.maxWidth, px));\r\n      const wpx = `${clamped}px`;\r\n      th.style.width = wpx;\r\n      for (const td of matchingTds) td.style.width = wpx;\r\n      return clamped;\r\n    };\r\n\r\n    let last = startWidth;\r\n    // rAF-throttle the move handler — high-frame-rate pointers fire\r\n    // mousemove 120-240 Hz, but we only need one width write per\r\n    // animation frame. Coalesce: the latest cursor X wins.\r\n    let pendingX: number | null = null;\r\n    let frame = 0;\r\n    const flushMove = () => {\r\n      frame = 0;\r\n      if (pendingX == null) return;\r\n      last = apply(startWidth + (pendingX - startX));\r\n      pendingX = null;\r\n    };\r\n    const onMove = (e: MouseEvent) => {\r\n      pendingX = e.clientX;\r\n      if (!frame) frame = requestAnimationFrame(flushMove);\r\n    };\r\n    const onUp = () => {\r\n      if (frame) { cancelAnimationFrame(frame); frame = 0; }\r\n      // Apply any pending coalesced move synchronously so `last` reflects\r\n      // the final cursor position, not the previous frame's.\r\n      if (pendingX != null) last = apply(startWidth + (pendingX - startX));\r\n      document.removeEventListener(\"mousemove\", onMove);\r\n      document.removeEventListener(\"mouseup\", onUp);\r\n      grip.removeAttribute(\"data-active\");\r\n      th.removeAttribute(\"data-resizing\");\r\n      document.body.style.cursor = \"\";\r\n      const finalPx = Math.round(last);\r\n      col.width = `${finalPx}px`;\r\n      // Single re-render so frozen offsets / footer cells catch up.\r\n      this.renderHeader();\r\n      renderBody(this);\r\n      this.renderFooter();\r\n      saveState(this);\r\n      this.options.onColumnResize?.(col.id, finalPx);\r\n    };\r\n    document.addEventListener(\"mousemove\", onMove);\r\n    document.addEventListener(\"mouseup\", onUp);\r\n  }\r\n\r\n  private toggleSelectRowByDatasetId(datasetId: string): void {\r\n    if (!this.identifier) return;\r\n    const idCol = this.identifier;\r\n    const row = this.currentRows.find((r) => String(r[idCol]) === datasetId);\r\n    if (!row) return;\r\n    this.toggleSelectRow(row[idCol]);\r\n  }\r\n}\r\n\r\nfunction compare(a: unknown, b: unknown): number {\r\n  if (a == null && b == null) return 0;\r\n  if (a == null) return -1;\r\n  if (b == null) return 1;\r\n  if (typeof a === \"number\" && typeof b === \"number\") return a - b;\r\n  return String(a).localeCompare(String(b), undefined, { numeric: true, sensitivity: \"base\" });\r\n}\r\n\r\nfunction escapeRegExp(s: string): string {\r\n  return s.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\r\n}\r\n\r\n/**\r\n * Track horizontal scroll on the nearest `.table-responsive` ancestor so the\r\n * grid wrapper gets a `boostgrid--scrolled-x` class when scrolled past the\r\n * leading edge. The class drives the rightmost-frozen-cell drop-shadow CSS.\r\n * Returns a no-op cleanup if no scroll parent or no frozen column exists.\r\n */\r\nfunction mountScrollShadow<TRow extends Row>(grid: Boostgrid<TRow>): () => void {\r\n  if (!grid.columns.some((c) => c.frozen === \"left\")) return () => undefined;\r\n  const wrapper = grid.element.parentElement; // .boostgrid\r\n  const scrollParent = wrapper?.closest<HTMLElement>(\".table-responsive\");\r\n  if (!wrapper || !scrollParent) return () => undefined;\r\n\r\n  const update = () => {\r\n    if (scrollParent.scrollLeft > 0) wrapper.classList.add(\"boostgrid--scrolled-x\");\r\n    else wrapper.classList.remove(\"boostgrid--scrolled-x\");\r\n  };\r\n  update();\r\n  scrollParent.addEventListener(\"scroll\", update, { passive: true });\r\n  return () => {\r\n    scrollParent.removeEventListener(\"scroll\", update);\r\n    wrapper.classList.remove(\"boostgrid--scrolled-x\");\r\n  };\r\n}\r\n\r\n/** Minimal attribute-selector escape for column ids. CSS.escape is widely\r\n *  available but tsdom-friendly fallback covers the characters that actually\r\n *  appear in identifier-style column ids. */\r\nfunction cssEscape(s: string): string {\r\n  if (typeof CSS !== \"undefined\" && typeof CSS.escape === \"function\") return CSS.escape(s);\r\n  return s.replace(/[\"\\\\]/g, \"\\\\$&\");\r\n}\r\n","import { Boostgrid } from \"./core.js\";\nimport type { BoostgridOptions, Row } from \"./types.js\";\nimport \"./styles/boostgrid.scss\";\n\nexport { Boostgrid };\nexport * from \"./types.js\";\nexport { bootstrapIcons, fontAwesomeIcons } from \"./icons.js\";\n\nconst REGISTRY = new WeakMap<HTMLTableElement, Boostgrid<Row>>();\n\nexport function attach<TRow extends Row = Row>(\n  target: string | HTMLTableElement | NodeListOf<HTMLTableElement> | HTMLTableElement[],\n  options?: Partial<BoostgridOptions<TRow>>,\n): Boostgrid<TRow>[] {\n  const tables = resolve(target);\n  return tables.map((t) => {\n    const existing = REGISTRY.get(t);\n    // A destroyed instance still sits in the WeakMap until its element is\n    // garbage-collected. Treat it as absent so re-attaching the same table\n    // (e.g. example demos that swap options at runtime) creates a fresh grid.\n    if (existing && !existing.destroyed) {\n      // Warn when a caller passes options against a table that's already\n      // attached (most commonly: `data-toggle=\"boostgrid\"` auto-init ran at\n      // DOMContentLoaded with defaults, and a later script calls `attach()`\n      // expecting its options to apply). The returned instance is the\n      // pre-existing one — caller's options are silently dropped without\n      // this warning. Set in dev console:\n      //   grid.options.someOpt = ... ; grid.reload();\n      // ...or remove the auto-init attribute.\n      if (options && Object.keys(options).length > 0) {\n        const id = (t as HTMLTableElement).id || \"(no id)\";\n        // eslint-disable-next-line no-console\n        console.warn(\n          `[boostgrid] attach() was called with options against an already-attached table (#${id}). ` +\n          `The pre-existing instance is being returned and your options were not applied. ` +\n          `If the table has data-toggle=\"boostgrid\", auto-init ran first with defaults — ` +\n          `remove the attribute and call attach(target, options) explicitly to configure it.`,\n        );\n      }\n      return existing as unknown as Boostgrid<TRow>;\n    }\n    const g = new Boostgrid<TRow>(t, options);\n    REGISTRY.set(t, g as unknown as Boostgrid<Row>);\n    // Convenience handle for inline scripts / framework wrappers.\n    (t as HTMLTableElement & { boostgridInstance?: Boostgrid<Row> }).boostgridInstance = g as unknown as Boostgrid<Row>;\n    return g;\n  });\n}\n\nexport function instance<TRow extends Row = Row>(table: HTMLTableElement): Boostgrid<TRow> | undefined {\n  return REGISTRY.get(table) as unknown as Boostgrid<TRow> | undefined;\n}\n\nfunction resolve(t: string | HTMLTableElement | NodeListOf<HTMLTableElement> | HTMLTableElement[]): HTMLTableElement[] {\n  if (typeof t === \"string\") return Array.from(document.querySelectorAll<HTMLTableElement>(t));\n  if (t instanceof HTMLTableElement) return [t];\n  return Array.from(t);\n}\n\n// Auto-init: any <table data-toggle=\"boostgrid\"> on DOMContentLoaded.\nif (typeof document !== \"undefined\") {\n  const init = () => attach('table[data-toggle=\"boostgrid\"]');\n  if (document.readyState === \"loading\") {\n    document.addEventListener(\"DOMContentLoaded\", init, { once: true });\n  } else {\n    init();\n  }\n}\n"],"names":["bootstrapIcons","fontAwesomeIcons","$","sel","root","$$","el","tag","attrs","children","node","k","v","c","delegate","type","selector","handler","listener","e","target","match","debounce","fn","ms","h","args","readData","elm","out","attr","key","_","coerce","clearChildren","defaults","r","mergeOptions","user","fromTable","base","parseColumns","table","options","headRow","cols","firstSortClaimed","firstIdentifierClaimed","th","d","id","converter","formatterKey","formatter","footerFormatterKey","footerFormatter","isIdentifier","order","widthRaw","width","editableFromData","editable","editType","editOptions","frozen","treeColumn","reorderable","resizable","minWidthRaw","maxWidthRaw","minWidth","maxWidth","parseRowsFromTable","columns","rows","tr","row","td","i","col","ALIGN","DEFAULT_FROZEN_WIDTH_PX","SELECTION_WIDTH_PX","DETAIL_WIDTH_PX","widthAsPx","m","computeFrozenOffsets","grid","visibleCols","baseLeft","left","right","leftAcc","rightAcc","renderHeader","thead","hasFrozen","selectCellClasses","cb","detailClasses","offsets","visibleIndex","classes","styleParts","link","iconCls","icon","grip","GROUP_PATH_SEP","groupKeyToken","groupPathToString","path","normalizeGroupBy","g","arr","seen","resolveAggregator","map","colId","depth","walkGroups","slice","groupBy","walkLevel","parentPath","opts","column","bucketKey","bucket","siblingIndex","flush","ctx","collapsed","sameKey","renderGroupHeader","_grid","colSpan","expanded","caret","renderGroupFooter","aggregators","any","aggr","a","b","normalizeId","buildTree","idField","parentField","matchFn","byId","matchedIds","rawId","roots","parentRaw","parentId","parent","wouldCycle","queue","n","childId","cur","walkTree","result","searching","subtreeMatches","markUp","isExpanded","dfs","child","resolveTreeColumnId","visible","opt","found","flagged","nonFrozen","SELECTED_CLASS","mountCellSelection","state","findCellCoords","_a","tbody","j","cell","onMouseDown","ev","coords","paint","onMouseMove","normalize","onMouseUp","onClick","onCopy","tsv","rangeToTsv","onKeyDown","ar","ac","fr","fc","range","dataRows","cells","lines","s","makeCellPaint","renderBody","win","prev","first","last","leadingCells","topPad","bottomPad","ensurePadRows","pool","needed","bareRow","rebindRow","css","paintCellSelection","treeColumnId","search","re","escapeRegExp","tree","flat","indent","frag","meta","buildRow","directives","footer","detail","buildDetailRow","_visibleCols","content","padRow","height","renderSkeleton","count","treeMeta","idCol","rowId","trAttrs","cls","isTreeCell","cellOffset","idValue","renderFooter","hasFormatters","hasMarkup","tfoot","auto","buildContext","rowsPerPage","currentRows","start","end","selectedIds","filteredRows","allRows","selectedRows","_bootstrapJsWarned","warnIfBootstrapJsMissing","w","renderToolbar","position","bar","buildSearch","buildRowCountDropdown","buildColumnDropdown","iconEl","tail","wrap","counts","btn","ul","li","menu","list","item","handle","label","reset","renderPagination","slot","pageItem","padding","disabled","active","value","renderInfos","fmt","numberFormatter","numberFormatterCache","locale","f","mountVirtualScroll","wrapper","frame","onScroll","refreshVirtualWindow","boundTo","ensureBound","t","defaultViewport","scrollTop","viewportH","firstVisible","recomputeWindow","visibleCount","total","overscan","rowHeight","mountCellEdit","openIfApplicable","commit","openEditor","onDblClick","onClose","oldValue","oldHtml","input","select","o","inp","editor","onKey","ke","cancel","onBlur","silent","raw","newValue","paintCell","commitInfo","VERSION","safeStorage","defaultStateKey","sig","djb2","resolveKey","SAVE_DEBOUNCE_MS","saveTimers","writeState","store","saveState","existing","flushSaveState","restoreState","parsed","cv","ids","x","stored","live","widths","clearState","normalizeTreeId","Boostgrid","rc","ge","te","rde","phrase","mountScrollShadow","removed","rowIds","dictionary","index","clamped","fullRefresh","newly","changedIds","_b","_c","columnId","cssEscape","name","page","pathOrKey","startRow","chain","newParentId","childRow","oldParentId","targetParentId","oldParentRow","newParentRow","cursor","level","url","reqBody","gb","transformed","sk","myId","ctrl","res","err","json","final","flags","cmp","entries","dir","compare","top","bottom","userContent","counter","clear","phase","isSelected","head","all","action","p","next","hide","_e","text","q","dragRowId","clearMarks","dt","targetId","dragId","childKey","parentKey","rect","before","it","clearDropMarks","dragCol","targetCol","restored","snap","side","dragIdx","insertAt","targetIdx","startX","startWidth","matchingTds","allTds","apply","px","wpx","pendingX","flushMove","onMove","onUp","finalPx","datasetId","scrollParent","update","REGISTRY","attach","resolve","instance","init"],"mappings":"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"}