{"version":3,"file":"useSelection.mjs","names":[],"sources":["../../../../package/common/virtualList/useSelection.ts"],"sourcesContent":["import { useState, useMemo } from 'react';\n\ninterface UseSelectionArgs<T> {\n  items: T[];\n  getKey: (item: T, index: number) => string | number;\n  multiSelect: boolean;\n  /** Controlled selected keys. When provided, internal state is ignored. */\n  selectedKeys?: Set<string | number>;\n  onSelectionChange?: (keys: Set<string | number>) => void;\n}\n\ninterface UseSelectionResult {\n  effectiveSelectedKeys: Set<string | number>;\n  /** Toggle one row's membership in the selection. */\n  handleRowSelect: (key: string | number) => void;\n  /** Select all rows, or clear if all are already selected. */\n  handleSelectAll: () => void;\n  allSelected: boolean;\n  someSelected: boolean;\n}\n\n/**\n * Multi-row selection, controlled or uncontrolled. `allSelected`/`someSelected`\n * (for the header checkbox) are O(n) scans, so they're only computed when\n * `multiSelect` is on.\n */\nexport function useSelection<T>({\n  items,\n  getKey,\n  multiSelect,\n  selectedKeys,\n  onSelectionChange,\n}: UseSelectionArgs<T>): UseSelectionResult {\n  const isControlled = selectedKeys !== undefined;\n  const [internalSelectedKeys, setInternalSelectedKeys] = useState<Set<string | number>>(new Set());\n  const effectiveSelectedKeys = isControlled ? selectedKeys! : internalSelectedKeys;\n\n  function applySelection(next: Set<string | number>) {\n    if (!isControlled) setInternalSelectedKeys(next);\n    onSelectionChange?.(next);\n  }\n\n  function handleRowSelect(key: string | number) {\n    const next = new Set(effectiveSelectedKeys);\n    if (next.has(key)) next.delete(key);\n    else next.add(key);\n    applySelection(next);\n  }\n\n  function handleSelectAll() {\n    const allKeys = items.map((item, i) => getKey(item, i));\n    const allChosen = allKeys.every((k) => effectiveSelectedKeys.has(k));\n    applySelection(allChosen ? new Set() : new Set(allKeys));\n  }\n\n  // O(n) header-checkbox state. The component re-renders every scroll frame\n  // (scrollTop is state), but these depend only on the items and the selection —\n  // so memoise to keep two full-list scans off the scroll path.\n  const { allSelected, someSelected } = useMemo(() => {\n    if (!multiSelect || items.length === 0) return { allSelected: false, someSelected: false };\n    const all = items.every((item, i) => effectiveSelectedKeys.has(getKey(item, i)));\n    const some = !all && items.some((item, i) => effectiveSelectedKeys.has(getKey(item, i)));\n    return { allSelected: all, someSelected: some };\n  }, [multiSelect, items, effectiveSelectedKeys, getKey]);\n\n  return { effectiveSelectedKeys, handleRowSelect, handleSelectAll, allSelected, someSelected };\n}\n"],"mappings":"2DA0BA,SAAgB,EAAgB,CAC9B,QACA,SACA,cACA,eACA,qBAC0C,CAC1C,IAAM,EAAe,IAAiB,IAAA,GAChC,CAAC,EAAsB,GAA2B,EAA+B,IAAI,GAAK,EAC1F,EAAwB,EAAe,EAAgB,EAE7D,SAAS,EAAe,EAA4B,CAC7C,GAAc,EAAwB,CAAI,EAC/C,IAAoB,CAAI,CAC1B,CAEA,SAAS,EAAgB,EAAsB,CAC7C,IAAM,EAAO,IAAI,IAAI,CAAqB,EACtC,EAAK,IAAI,CAAG,EAAG,EAAK,OAAO,CAAG,EAC7B,EAAK,IAAI,CAAG,EACjB,EAAe,CAAI,CACrB,CAEA,SAAS,GAAkB,CACzB,IAAM,EAAU,EAAM,KAAK,EAAM,IAAM,EAAO,EAAM,CAAC,CAAC,EAEtD,EADkB,EAAQ,MAAO,GAAM,EAAsB,IAAI,CAAC,CACnD,EAAY,IAAI,IAAQ,IAAI,IAAI,CAAO,CAAC,CACzD,CAKA,GAAM,CAAE,cAAa,gBAAiB,MAAc,CAClD,GAAI,CAAC,GAAe,EAAM,SAAW,EAAG,MAAO,CAAE,YAAa,GAAO,aAAc,EAAM,EACzF,IAAM,EAAM,EAAM,OAAO,EAAM,IAAM,EAAsB,IAAI,EAAO,EAAM,CAAC,CAAC,CAAC,EAE/E,MAAO,CAAE,YAAa,EAAK,aADd,CAAC,GAAO,EAAM,MAAM,EAAM,IAAM,EAAsB,IAAI,EAAO,EAAM,CAAC,CAAC,CAAC,CACzC,CAChD,EAAG,CAAC,EAAa,EAAO,EAAuB,CAAM,CAAC,EAEtD,MAAO,CAAE,wBAAuB,kBAAiB,kBAAiB,cAAa,cAAa,CAC9F"}