{"version":3,"file":"virtualList.utils.mjs","names":[],"sources":["../../../../package/common/virtualList/virtualList.utils.ts"],"sourcesContent":["import type { ColumnWidthSpec, SortValue, VirtualListColumn, VirtualListSort } from './virtualList.types';\n\n// ─── Layout / windowing math ──────────────────────────────────────────────────\n\nexport function buildOffsets(\n  keys: (string | number)[],\n  heightsMap: Map<string | number, number>,\n  estimatedRowHeight: number,\n  rowGap = 0,\n): { offsets: number[]; totalHeight: number } {\n  const itemCount = keys.length;\n  const offsets = new Array<number>(itemCount + 1);\n  offsets[0] = 0;\n  for (let i = 0; i < itemCount; i++) {\n    const gap = i < itemCount - 1 ? rowGap : 0;\n    // Heights are cached by item key, not index, so a measured row keeps its\n    // height across reorders/filters instead of inheriting its neighbour's.\n    offsets[i + 1] = offsets[i] + (heightsMap.get(keys[i]) ?? estimatedRowHeight) + gap;\n  }\n  return { offsets, totalHeight: offsets[itemCount] };\n}\n\n/** Index of the row that sits at `scrollTop` — i.e. the first row whose bottom edge is below it. */\nexport function findStartIndex(offsets: number[], scrollTop: number, itemCount: number): number {\n  if (itemCount <= 0) return 0;\n  let lo = 0;\n  let hi = itemCount - 1;\n  while (lo < hi) {\n    const mid = (lo + hi) >> 1;\n    if (offsets[mid + 1] <= scrollTop) lo = mid + 1;\n    else hi = mid;\n  }\n  return lo;\n}\n\nexport function getVisibleRange(\n  offsets: number[],\n  scrollTop: number,\n  containerHeight: number,\n  overscan: number,\n  itemCount: number,\n): { startIndex: number; endIndex: number } {\n  if (itemCount === 0) return { startIndex: 0, endIndex: -1 };\n\n  const lo = findStartIndex(offsets, scrollTop, itemCount);\n\n  let end = lo;\n  while (end < itemCount && offsets[end] < scrollTop + containerHeight) end++;\n\n  return {\n    startIndex: Math.max(0, lo - overscan),\n    endIndex: Math.min(itemCount - 1, end + overscan - 1),\n  };\n}\n\n// ─── Reorder / tree-drop math ─────────────────────────────────────────────────\n\n/**\n * Decides where a dragged row should move to, applying midpoint hysteresis: a\n * move only happens once the pointer has crossed the centre of the row it is\n * over (downwards when moving down, upwards when moving up). Returns the target\n * index, or null if no move should occur. Pure for easy unit testing.\n */\nexport function resolveReorderTarget(\n  fromIndex: number,\n  overIndex: number,\n  pointerY: number,\n  rowTop: number,\n  rowHeight: number,\n): number | null {\n  if (fromIndex === -1 || fromIndex === overIndex) return null;\n  const midpoint = rowTop + rowHeight / 2;\n  const movingDown = overIndex > fromIndex;\n  if (movingDown ? pointerY >= midpoint : pointerY <= midpoint) return overIndex;\n  return null;\n}\n\n/**\n * Decides where a tree-reorder drop should land relative to the row under the\n * pointer. Group rows expose three zones (top third → `before`, middle →\n * `inside`, bottom third → `after`); leaf rows split in half (`before`/`after`)\n * since they can't receive children. Pure for easy unit testing.\n */\nexport function resolveTreeDropTarget(\n  pointerY: number,\n  rowTop: number,\n  rowHeight: number,\n  isGroup: boolean,\n): 'before' | 'inside' | 'after' {\n  if (rowHeight <= 0) return 'before';\n  const rel = (pointerY - rowTop) / rowHeight;\n  if (isGroup) {\n    if (rel < 1 / 3) return 'before';\n    if (rel > 2 / 3) return 'after';\n    return 'inside';\n  }\n  return rel < 0.5 ? 'before' : 'after';\n}\n\n/**\n * Index of the last descendant of the node at `index` in a depth-tagged flat\n * list (a contiguous run of following rows with a greater depth). Returns\n * `index` itself when the node has no children. A drop is forbidden anywhere in\n * `[index, lastDescendantIndex]` — that's the node and its own subtree.\n */\nexport function lastDescendantIndex(index: number, depths: number[]): number {\n  const d = depths[index];\n  let i = index + 1;\n  while (i < depths.length && depths[i] > d) i++;\n  return i - 1;\n}\n\n// ─── Column template ──────────────────────────────────────────────────────────\n\nexport function buildColumnTemplate(\n  columns: { key: string; width: ColumnWidthSpec }[],\n  fitWidths?: Record<string, number>,\n): string {\n  if (columns.length === 0) return '1fr';\n  return columns\n    .map((col) => {\n      switch (col.width.type) {\n        case 'fixed':\n          return `${col.width.px}px`;\n        case 'flex':\n          return `${col.width.weight ?? 1}fr`;\n        // A `fit` track is content-sized, so it resolves independently in each\n        // grid (the header and every row are separate grids) and would drift\n        // out of alignment. Once we've measured the column's natural width\n        // across all those grids, pin it to that exact px so every grid uses an\n        // identical track. Until then, fall back to content-sizing.\n        case 'fit': {\n          const w = fitWidths?.[col.key];\n          return w != null ? `${w}px` : 'fit-content(100%)';\n        }\n      }\n    })\n    .join(' ');\n}\n\n// ─── Row focus ────────────────────────────────────────────────────────────────\n\n/**\n * What counts as a control inside a row, and so what gets held out of the tab\n * order while its row is not the one being used.\n *\n * `[tabindex=\"-1\"]` is deliberately excluded. The row's own select checkbox sits\n * there and is reached with Space, and an element the consumer has already taken\n * out of the tab order stays out.\n */\nexport const ROW_FOCUSABLE_SELECTOR =\n  'a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex=\"-1\"])';\n\n/** Marks an element parked at `tabindex=\"-1\"` by the list rather than by the consumer. */\nexport const ROW_PARKED_ATTRIBUTE = 'data-vl-parked';\n\n/**\n * What `F2` steps into: the controls of a row, including the ones parked out of\n * the tab order. Without the second half it would find nothing at all in a row\n * whose controls are custom elements carrying a `tabindex`, because parking them\n * is exactly what takes them out of the selector above.\n */\nexport const ROW_ENTER_SELECTOR = `${ROW_FOCUSABLE_SELECTOR}, [${ROW_PARKED_ATTRIBUTE}]`;\n\n// ─── Column width measurement ─────────────────────────────────────────────────\n\n/**\n * The content width of one cell, in px. `scrollWidth` reports the true content\n * width even when the cell is pinned narrower than its content, which is what\n * lets a column recover from having been measured while it was still empty.\n *\n * Two corrections on top of it, both only resolved when they can apply, so the\n * hot scroll path stays cheap for a plain in-flow cell:\n *\n * - `scrollWidth` includes the left padding but drops the right one once the\n *   content overflows, so add it back.\n * - A cell whose only children are FontAwesome icons reports just its padding:\n *   their boxes overflow via `overflow: visible`, carry no intrinsic width, and\n *   so never count towards `scrollWidth` (right-aligned, they even overflow\n *   into negative offsets). Only then walk the descendants for their true span.\n *   Once a real width pins, the cell is no longer collapsed and this stops.\n */\nexport function measureCellWidth(el: HTMLElement): number {\n  const overflowing = el.scrollWidth > el.clientWidth;\n  let width = el.scrollWidth;\n  if (!overflowing && !el.firstElementChild) return width;\n\n  const cs = getComputedStyle(el);\n  const padLeft = parseFloat(cs.paddingLeft) || 0;\n  const padRight = parseFloat(cs.paddingRight) || 0;\n  if (overflowing) width += padRight;\n\n  if (el.firstElementChild && !overflowing && el.scrollWidth <= padLeft + padRight + 1) {\n    let minLeft = Infinity;\n    let maxRight = -Infinity;\n    el.querySelectorAll('*').forEach((d) => {\n      const r = d.getBoundingClientRect();\n      if (r.left < minLeft) minLeft = r.left;\n      if (r.right > maxRight) maxRight = r.right;\n    });\n    if (maxRight > minLeft) {\n      const span = Math.ceil(maxRight - minLeft) + padLeft + padRight;\n      if (span > width) width = span;\n    }\n  }\n  return width;\n}\n\n/** Attribute selector for one column's cells. Column keys are arbitrary strings. */\nexport function columnCellSelector(key: string): string {\n  return `[data-col-key=\"${key.replace(/[\"\\\\]/g, '\\\\$&')}\"]`;\n}\n\n// ─── Column resizing ──────────────────────────────────────────────────────────\n\n/** Floor for a user-resized column, unless the column raises it with `minWidth`. */\nexport const MIN_COLUMN_WIDTH = 48;\n\n/**\n * Width given to a column that gains one while the layout is already frozen,\n * i.e. one revealed from the gear menu after the first resize. Every visible\n * column has to be an exact px track for the total width (and so the horizontal\n * scroll range) to be exact, and there is nothing measured to go on yet.\n */\nexport const DEFAULT_FROZEN_WIDTH = 160;\n\n/**\n * Custom property carrying one column's width. Column keys are arbitrary\n * strings, so anything a custom property can't contain is folded to `_`; two\n * keys that differ only in those characters would share a track.\n */\nexport function columnWidthVar(key: string): string {\n  return `--vl-col-${key.replace(/[^a-zA-Z0-9_-]/g, '_')}`;\n}\n\n/** Px width for a column the user hasn't sized, at the moment the layout freezes. */\nexport function resolveFrozenWidth<T>(col: VirtualListColumn<T>): number {\n  if (col.width.type === 'fixed') return col.width.px;\n  if (col.width.type === 'fit') return DEFAULT_FROZEN_WIDTH;\n  return DEFAULT_FROZEN_WIDTH * (col.width.weight ?? 1);\n}\n\n/**\n * Grid template for a frozen layout: every column reads its own custom property,\n * so a drag can rewrite one width with a single DOM write and have the header\n * and every row follow in the same layout pass, without React rendering.\n */\nexport function buildFrozenTemplate<T>(columns: VirtualListColumn<T>[]): string {\n  if (columns.length === 0) return '1fr';\n  return columns.map((col) => `var(${columnWidthVar(col.key)}, ${DEFAULT_FROZEN_WIDTH}px)`).join(' ');\n}\n\n// ─── Column-visibility persistence ────────────────────────────────────────────\n\nexport const COLUMN_STORAGE_PREFIX = 'ahroweui:virtuallist:columns:';\n\n// We persist both the visible keys and the full set of columns that existed at\n// save time. That lets us tell \"the user hid this column\" from \"this column\n// didn't exist yet\" when columns are added later — the former stays hidden, the\n// latter falls back to its defaultHidden value.\nexport interface PersistedColumns {\n  visible: string[];\n  known: string[];\n  /** User-resized column widths in px. Absent in entries saved before resizing existed. */\n  widths?: Record<string, number>;\n}\n\nexport function loadPersistedColumns(persistKey: string | undefined): PersistedColumns | null {\n  if (!persistKey || typeof window === 'undefined') return null;\n  try {\n    const raw = window.localStorage.getItem(COLUMN_STORAGE_PREFIX + persistKey);\n    if (!raw) return null;\n    const parsed = JSON.parse(raw);\n    if (parsed && Array.isArray(parsed.visible) && Array.isArray(parsed.known)) {\n      const widths: Record<string, number> = {};\n      if (parsed.widths && typeof parsed.widths === 'object')\n        for (const [key, value] of Object.entries(parsed.widths))\n          if (typeof value === 'number' && Number.isFinite(value) && value > 0) widths[key] = value;\n      return {\n        visible: parsed.visible.filter((k: unknown): k is string => typeof k === 'string'),\n        known: parsed.known.filter((k: unknown): k is string => typeof k === 'string'),\n        widths,\n      };\n    }\n  } catch {\n    // Corrupt/unavailable storage — fall back to defaults.\n  }\n  return null;\n}\n\n/** Whether a column should start visible, honouring saved settings then `defaultHidden`. */\nexport function isColumnInitiallyVisible<T>(col: VirtualListColumn<T>, persisted: PersistedColumns | null): boolean {\n  // A locked column is always visible and can't be hidden — ignore saved state.\n  if (col.lockVisible) return true;\n  if (persisted && persisted.known.includes(col.key)) return persisted.visible.includes(col.key);\n  return !col.defaultHidden;\n}\n\n// ─── Sorting ──────────────────────────────────────────────────────────────────\n\n// `numeric` so \"Order 10\" lands after \"Order 2\" instead of before it, and\n// `sensitivity: 'base'` so case and accents don't split otherwise-equal values.\n// Built lazily and reused per locale: constructing a Collator is expensive relative\n// to the comparisons it then runs.\nconst collators = new Map<string, Intl.Collator>();\nfunction getCollator(locale: string | undefined): Intl.Collator {\n  const key = locale ?? '';\n  let collator = collators.get(key);\n  if (!collator) {\n    collator = new Intl.Collator(locale, { numeric: true, sensitivity: 'base' });\n    collators.set(key, collator);\n  }\n  return collator;\n}\n\n/** Values with no order of their own, parked at the end of the list either way. */\nexport function isEmptySortValue(value: SortValue): boolean {\n  return value == null || (typeof value === 'number' && Number.isNaN(value));\n}\n\n/**\n * Ascending comparison of two non-empty `sortValue` results. Dates and booleans\n * are normalised to numbers; anything with a string on either side goes through\n * the collator, which is also what makes a mixed-type column produce a stable\n * (if arbitrary) order rather than a random one.\n */\nexport function compareSortValues(a: SortValue, b: SortValue, locale?: string): number {\n  const na = a instanceof Date ? a.getTime() : typeof a === 'boolean' ? Number(a) : a;\n  const nb = b instanceof Date ? b.getTime() : typeof b === 'boolean' ? Number(b) : b;\n  if (typeof na === 'string' || typeof nb === 'string') return getCollator(locale).compare(String(na), String(nb));\n  return (na as number) < (nb as number) ? -1 : (na as number) > (nb as number) ? 1 : 0;\n}\n\n/** Whether a header click on this column should do anything at all. */\nexport function isColumnSortable<T>(col: VirtualListColumn<T>): boolean {\n  return col.sortValue != null || col.compare != null || col.sortable === true;\n}\n\n/** The asc → desc → unsorted cycle a header click steps through. */\nexport function nextSort(current: VirtualListSort | null, key: string): VirtualListSort | null {\n  if (!current || current.key !== key) return { key, direction: 'asc' };\n  return current.direction === 'asc' ? { key, direction: 'desc' } : null;\n}\n\n/**\n * Returns `items` reordered by `sort`, or the same array reference when nothing\n * applies: no sort, no such column, or a `sortable` column that only reports\n * (its data is sorted by the consumer). Callers rely on that identity to tell\n * \"the list sorted this\" from \"the list left it alone\".\n */\nexport function sortItems<T>(\n  items: T[],\n  sort: VirtualListSort | null | undefined,\n  columns: VirtualListColumn<T>[] | undefined,\n  locale?: string,\n): T[] {\n  if (!sort || !columns) return items;\n  const col = columns.find((c) => c.key === sort.key);\n  if (!col || (col.compare == null && col.sortValue == null)) return items;\n\n  const dir = sort.direction === 'asc' ? 1 : -1;\n  const { compare, sortValue } = col;\n  // Decorated with the source index so equal values keep their original order.\n  // Array.prototype.sort is stable per spec, but the tiebreaker also stops a\n  // `compare` that returns 0 for distinct rows from reshuffling them.\n  const decorated = items.map((item, index) => ({ item, index }));\n  decorated.sort((a, b) => {\n    let result: number;\n    if (compare) {\n      result = dir * compare(a.item, b.item);\n    } else {\n      const av = sortValue!(a.item);\n      const bv = sortValue!(b.item);\n      const aEmpty = isEmptySortValue(av);\n      const bEmpty = isEmptySortValue(bv);\n      // Deliberately not multiplied by `dir`: an empty cell belongs at the\n      // bottom of the table whichever way the column is pointing.\n      if (aEmpty || bEmpty) result = aEmpty === bEmpty ? 0 : aEmpty ? 1 : -1;\n      else result = dir * compareSortValues(av, bv, locale);\n    }\n    return result !== 0 ? result : a.index - b.index;\n  });\n  return decorated.map((d) => d.item);\n}\n"],"mappings":"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"}