{"version":3,"file":"datagrid-nav.cjs","names":[],"sources":["../../../src/inputs/datagrid/datagrid-nav.ts"],"sourcesContent":["import { type Readable, signal } from '@vielzeug/ripple';\n\n// ── Types ──────────────────────────────────────────────────────────────────────\n\n/** Position in the 2D grid model (0-based row/col indices). */\nexport type GridCellPos = { col: number; row: number };\n\n/** Handle returned by createGridNav for programmatic control. */\nexport type GridNavHandle = {\n  /** Current active cell position. Reactive signal. */\n  readonly activeCell: Readable<GridCellPos>;\n  /**\n   * Move focus to a specific cell by position.\n   * Clamps to valid grid bounds; no-ops for invalid positions.\n   */\n  focusCell(pos: GridCellPos): void;\n};\n\n// ── Selector ───────────────────────────────────────────────────────────────────\n\nconst CELL_SELECTOR = '.dg-td, .dg-th';\n\n/** Build a 2D array of focusable cells from the shadow root. One sub-array per <tr>. */\nfunction getGrid(shadow: ShadowRoot): HTMLElement[][] {\n  return Array.from(shadow.querySelectorAll<HTMLElement>('tr'))\n    .map((tr) => Array.from(tr.querySelectorAll<HTMLElement>(CELL_SELECTOR)))\n    .filter((row) => row.length > 0);\n}\n\n// ── Factory ────────────────────────────────────────────────────────────────────\n\n/**\n * Keyboard navigation for the ARIA grid pattern (roving tabindex).\n *\n * - Arrow keys move focus between cells.\n * - Home/End move to the first/last cell in the row.\n * - Ctrl+Home/Ctrl+End move to the first/last cell in the grid.\n * - Only the active cell has `tabindex=\"0\"`; all others have `\"-1\"`.\n *\n * @param table - The `.dg-table` element to attach the keydown listener to.\n * @param shadow - The shadow root containing the grid cells.\n * @returns A handle with `focusCell` and a reactive `activeCell` signal, plus a cleanup fn.\n */\nexport function createGridNav(table: HTMLElement, shadow: ShadowRoot): { cleanup: () => void; handle: GridNavHandle } {\n  const activeCell = signal<GridCellPos>({ col: 0, row: 0 });\n\n  const focusCell = (pos: GridCellPos): void => {\n    const grid = getGrid(shadow);\n    const rowIdx = Math.max(0, Math.min(pos.row, grid.length - 1));\n    const row = grid[rowIdx];\n\n    if (!row) return;\n\n    const colIdx = Math.max(0, Math.min(pos.col, row.length - 1));\n    const target = row[colIdx];\n\n    if (!target) return;\n\n    for (const r of grid) {\n      for (const cell of r) cell.setAttribute('tabindex', '-1');\n    }\n\n    target.setAttribute('tabindex', '0');\n    target.focus();\n    activeCell.value = { col: colIdx, row: rowIdx };\n  };\n\n  const handleArrow = (e: KeyboardEvent): void => {\n    const active = shadow.activeElement as HTMLElement | null;\n\n    if (!active?.matches(CELL_SELECTOR)) return;\n\n    const grid = getGrid(shadow);\n    const rowIdx = grid.findIndex((row) => row.includes(active));\n\n    if (rowIdx === -1) return;\n\n    const colIdx = grid[rowIdx].indexOf(active);\n    const lastRow = grid.length - 1;\n    const lastCol = grid[rowIdx].length - 1;\n\n    let nextRow = rowIdx;\n    let nextCol = colIdx;\n\n    if (e.key === 'ArrowRight') nextCol = colIdx + 1;\n    else if (e.key === 'ArrowLeft') nextCol = colIdx - 1;\n    else if (e.key === 'ArrowDown') nextRow = rowIdx + 1;\n    else if (e.key === 'ArrowUp') nextRow = rowIdx - 1;\n    else if (e.key === 'Home') {\n      nextCol = 0;\n\n      if (e.ctrlKey) nextRow = 0;\n    } else if (e.key === 'End') {\n      nextCol = lastCol;\n\n      if (e.ctrlKey) nextRow = lastRow;\n    } else return;\n\n    e.preventDefault();\n    focusCell({\n      col: Math.max(0, Math.min(nextCol, (grid[Math.max(0, Math.min(nextRow, lastRow))]?.length ?? 1) - 1)),\n      row: Math.max(0, Math.min(nextRow, lastRow)),\n    });\n  };\n\n  table.addEventListener('keydown', handleArrow);\n\n  return {\n    cleanup: () => table.removeEventListener('keydown', handleArrow),\n    handle: { activeCell, focusCell },\n  };\n}\n"],"mappings":"kCAoBA,IAAM,EAAgB,iBAGtB,SAAS,EAAQ,EAAqC,CACpD,OAAO,MAAM,KAAK,EAAO,iBAA8B,IAAI,CAAC,CAAC,CAC1D,IAAK,GAAO,MAAM,KAAK,EAAG,iBAA8B,CAAa,CAAC,CAAC,CAAC,CACxE,OAAQ,GAAQ,EAAI,OAAS,CAAC,CACnC,CAgBA,SAAgB,EAAc,EAAoB,EAAoE,CACpH,IAAM,GAAA,EAAa,EAAA,OAAA,CAAoB,CAAE,IAAK,EAAG,IAAK,CAAE,CAAC,EAEnD,EAAa,GAA2B,CAC5C,IAAM,EAAO,EAAQ,CAAM,EACrB,EAAS,KAAK,IAAI,EAAG,KAAK,IAAI,EAAI,IAAK,EAAK,OAAS,CAAC,CAAC,EACvD,EAAM,EAAK,GAEjB,GAAI,CAAC,EAAK,OAEV,IAAM,EAAS,KAAK,IAAI,EAAG,KAAK,IAAI,EAAI,IAAK,EAAI,OAAS,CAAC,CAAC,EACtD,EAAS,EAAI,GAEd,KAEL,KAAK,IAAM,KAAK,EACd,IAAK,IAAM,KAAQ,EAAG,EAAK,aAAa,WAAY,IAAI,EAG1D,EAAO,aAAa,WAAY,GAAG,EACnC,EAAO,MAAM,EACb,EAAW,MAAQ,CAAE,IAAK,EAAQ,IAAK,CAAO,CALY,CAM5D,EAEM,EAAe,GAA2B,CAC9C,IAAM,EAAS,EAAO,cAEtB,GAAI,CAAC,GAAQ,QAAQ,CAAa,EAAG,OAErC,IAAM,EAAO,EAAQ,CAAM,EACrB,EAAS,EAAK,UAAW,GAAQ,EAAI,SAAS,CAAM,CAAC,EAE3D,GAAI,IAAW,GAAI,OAEnB,IAAM,EAAS,EAAK,EAAO,CAAC,QAAQ,CAAM,EACpC,EAAU,EAAK,OAAS,EACxB,EAAU,EAAK,EAAO,CAAC,OAAS,EAElC,EAAU,EACV,EAAU,EAEd,GAAI,EAAE,MAAQ,aAAc,EAAU,EAAS,OAC1C,GAAI,EAAE,MAAQ,YAAa,EAAU,EAAS,OAC9C,GAAI,EAAE,MAAQ,YAAa,EAAU,EAAS,OAC9C,GAAI,EAAE,MAAQ,UAAW,EAAU,EAAS,OAC5C,GAAI,EAAE,MAAQ,OACjB,EAAU,EAEN,EAAE,UAAS,EAAU,QACpB,GAAI,EAAE,MAAQ,MACnB,EAAU,EAEN,EAAE,UAAS,EAAU,QACpB,OAEP,EAAE,eAAe,EACjB,EAAU,CACR,IAAK,KAAK,IAAI,EAAG,KAAK,IAAI,GAAU,EAAK,KAAK,IAAI,EAAG,KAAK,IAAI,EAAS,CAAO,CAAC,EAAE,EAAE,QAAU,GAAK,CAAC,CAAC,EACpG,IAAK,KAAK,IAAI,EAAG,KAAK,IAAI,EAAS,CAAO,CAAC,CAC7C,CAAC,CACH,EAIA,OAFA,EAAM,iBAAiB,UAAW,CAAW,EAEtC,CACL,YAAe,EAAM,oBAAoB,UAAW,CAAW,EAC/D,OAAQ,CAAE,aAAY,WAAU,CAClC,CACF"}