{"version":3,"file":"useHeaderNavigation.mjs","names":[],"sources":["../../../../package/common/virtualList/useHeaderNavigation.ts"],"sourcesContent":["import React, { useRef, useState } from 'react';\n\ninterface StationProps {\n  tabIndex: number;\n  ref: (el: HTMLElement | null) => void;\n  onFocus: () => void;\n}\n\ninterface UseHeaderNavigationResult {\n  getStationProps: (index: number) => StationProps;\n  /** True when the key moved the focus, so the caller stops handling it. */\n  handleNavigationKey: (event: React.KeyboardEvent, index: number) => boolean;\n}\n\n/**\n * Roving tab stop across the header: the select-all cell, every column header,\n * and the column menu are stations, and arrow keys move between them.\n *\n * Without it each of those is its own tab stop, so a table with eight sortable,\n * resizable columns takes seventeen presses of Tab before the data. The stops\n * also belong together: they all act on the same header, which is exactly the\n * case the roving pattern exists for.\n */\nexport function useHeaderNavigation(stationCount: number): UseHeaderNavigationResult {\n  const [activeIndex, setActiveIndex] = useState(0);\n  const stationsRef = useRef<(HTMLElement | null)[]>([]);\n\n  // Hiding a column can shrink the header past the active station. Clamping\n  // during render rather than in an effect keeps the header from painting a\n  // single frame with no tab stop at all.\n  const active = Math.min(activeIndex, Math.max(0, stationCount - 1));\n  if (active !== activeIndex) setActiveIndex(active);\n\n  function focusStation(index: number) {\n    const next = Math.max(0, Math.min(index, stationCount - 1));\n    setActiveIndex(next);\n    stationsRef.current[next]?.focus();\n  }\n\n  function getStationProps(index: number): StationProps {\n    return {\n      tabIndex: index === active ? 0 : -1,\n      ref: (el: HTMLElement | null) => {\n        stationsRef.current[index] = el;\n      },\n      // Also fires for a click, which keeps the tab stop where the user last\n      // was rather than snapping back to the first column.\n      onFocus: () => setActiveIndex(index),\n    };\n  }\n\n  function handleNavigationKey(event: React.KeyboardEvent, index: number): boolean {\n    // Alt is the resize modifier, so those combinations belong to the station.\n    if (event.altKey || event.ctrlKey || event.metaKey) return false;\n    switch (event.key) {\n      case 'ArrowRight':\n        focusStation(index + 1);\n        break;\n      case 'ArrowLeft':\n        focusStation(index - 1);\n        break;\n      case 'Home':\n        focusStation(0);\n        break;\n      case 'End':\n        focusStation(stationCount - 1);\n        break;\n      default:\n        return false;\n    }\n    event.preventDefault();\n    event.stopPropagation();\n    return true;\n  }\n\n  return { getStationProps, handleNavigationKey };\n}\n"],"mappings":"0DAuBA,SAAgB,EAAoB,EAAiD,CACnF,GAAM,CAAC,EAAa,GAAkB,EAAS,CAAC,EAC1C,EAAc,EAA+B,CAAC,CAAC,EAK/C,EAAS,KAAK,IAAI,EAAa,KAAK,IAAI,EAAG,EAAe,CAAC,CAAC,EAC9D,IAAW,GAAa,EAAe,CAAM,EAEjD,SAAS,EAAa,EAAe,CACnC,IAAM,EAAO,KAAK,IAAI,EAAG,KAAK,IAAI,EAAO,EAAe,CAAC,CAAC,EAC1D,EAAe,CAAI,EACnB,EAAY,QAAQ,EAAK,EAAE,MAAM,CACnC,CAEA,SAAS,EAAgB,EAA6B,CACpD,MAAO,CACL,SAAU,IAAU,EAAS,EAAI,GACjC,IAAM,GAA2B,CAC/B,EAAY,QAAQ,GAAS,CAC/B,EAGA,YAAe,EAAe,CAAK,CACrC,CACF,CAEA,SAAS,EAAoB,EAA4B,EAAwB,CAE/E,GAAI,EAAM,QAAU,EAAM,SAAW,EAAM,QAAS,MAAO,GAC3D,OAAQ,EAAM,IAAd,CACE,IAAK,aACH,EAAa,EAAQ,CAAC,EACtB,MACF,IAAK,YACH,EAAa,EAAQ,CAAC,EACtB,MACF,IAAK,OACH,EAAa,CAAC,EACd,MACF,IAAK,MACH,EAAa,EAAe,CAAC,EAC7B,MACF,QACE,MAAO,EACX,CAGA,OAFA,EAAM,eAAe,EACrB,EAAM,gBAAgB,EACf,EACT,CAEA,MAAO,CAAE,kBAAiB,qBAAoB,CAChD"}