@ahrowe/ui 0.36.0 → 0.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/README.md +3 -2
  2. package/dist/cjs-types/common/hooks/index.d.ts +13 -0
  3. package/dist/cjs-types/common/hooks/useHotkeys.d.ts +45 -0
  4. package/dist/cjs-types/common/hooks/useSwipeDismiss.d.ts +6 -2
  5. package/dist/cjs-types/common/loading/index.d.ts +0 -1
  6. package/dist/cjs-types/common/menu/index.d.ts +1 -0
  7. package/dist/cjs-types/common/menu/menu.types.d.ts +9 -3
  8. package/dist/cjs-types/common/menu/menuHotkeys.d.ts +20 -0
  9. package/dist/cjs-types/common/popover/usePopoverPosition.d.ts +1 -0
  10. package/dist/cjs-types/common/sticky/sticky.d.ts +1 -1
  11. package/dist/cjs-types/common/sticky/sticky.types.d.ts +15 -0
  12. package/dist/cjs-types/common/sticky/stickyStack.d.ts +5 -0
  13. package/dist/cjs-types/common/utils/scrollAncestors.d.ts +10 -0
  14. package/dist/cjs-types/common/virtualList/scroller.d.ts +33 -0
  15. package/dist/cjs-types/common/virtualList/useRowDrag.d.ts +6 -1
  16. package/dist/cjs-types/common/virtualList/useRowNavigation.d.ts +11 -1
  17. package/dist/cjs-types/common/virtualList/useVirtualWindow.d.ts +22 -3
  18. package/dist/cjs-types/common/virtualList/virtualList.d.ts +1 -1
  19. package/dist/cjs-types/common/virtualList/virtualList.types.d.ts +43 -3
  20. package/dist/cjs-types/index.d.ts +1 -0
  21. package/dist/esm/common/dropdown/dropdown.module.mjs.map +1 -1
  22. package/dist/esm/common/hooks/useHotkeys.mjs +2 -0
  23. package/dist/esm/common/hooks/useHotkeys.mjs.map +1 -0
  24. package/dist/esm/common/hooks/useSwipeDismiss.mjs +1 -1
  25. package/dist/esm/common/hooks/useSwipeDismiss.mjs.map +1 -1
  26. package/dist/esm/common/input/input.mjs +1 -1
  27. package/dist/esm/common/input/input.mjs.map +1 -1
  28. package/dist/esm/common/input/input.module.mjs +1 -1
  29. package/dist/esm/common/input/input.module.mjs.map +1 -1
  30. package/dist/esm/common/menu/menu.mjs +1 -1
  31. package/dist/esm/common/menu/menu.mjs.map +1 -1
  32. package/dist/esm/common/menu/menu.types.mjs.map +1 -1
  33. package/dist/esm/common/menu/menuHotkeys.mjs +2 -0
  34. package/dist/esm/common/menu/menuHotkeys.mjs.map +1 -0
  35. package/dist/esm/common/multiSelect/multiSelect.mjs +1 -1
  36. package/dist/esm/common/multiSelect/multiSelect.mjs.map +1 -1
  37. package/dist/esm/common/multiSelect/multiSelect.module.mjs +1 -1
  38. package/dist/esm/common/multiSelect/multiSelect.module.mjs.map +1 -1
  39. package/dist/esm/common/otpInput/otpInput.module.mjs.map +1 -1
  40. package/dist/esm/common/popover/usePopoverPosition.mjs +1 -1
  41. package/dist/esm/common/popover/usePopoverPosition.mjs.map +1 -1
  42. package/dist/esm/common/sticky/sticky.mjs +1 -1
  43. package/dist/esm/common/sticky/sticky.mjs.map +1 -1
  44. package/dist/esm/common/sticky/stickyStack.mjs +1 -1
  45. package/dist/esm/common/sticky/stickyStack.mjs.map +1 -1
  46. package/dist/esm/common/utils/scrollAncestors.mjs +1 -1
  47. package/dist/esm/common/utils/scrollAncestors.mjs.map +1 -1
  48. package/dist/esm/common/virtualList/scroller.mjs +2 -0
  49. package/dist/esm/common/virtualList/scroller.mjs.map +1 -0
  50. package/dist/esm/common/virtualList/useRowDrag.mjs +1 -1
  51. package/dist/esm/common/virtualList/useRowDrag.mjs.map +1 -1
  52. package/dist/esm/common/virtualList/useRowNavigation.mjs +1 -1
  53. package/dist/esm/common/virtualList/useRowNavigation.mjs.map +1 -1
  54. package/dist/esm/common/virtualList/useVirtualWindow.mjs +1 -1
  55. package/dist/esm/common/virtualList/useVirtualWindow.mjs.map +1 -1
  56. package/dist/esm/common/virtualList/virtualList.mjs +1 -1
  57. package/dist/esm/common/virtualList/virtualList.mjs.map +1 -1
  58. package/dist/esm/common/virtualList/virtualList.module.mjs +1 -1
  59. package/dist/esm/common/virtualList/virtualList.module.mjs.map +1 -1
  60. package/dist/esm/index.mjs +1 -1
  61. package/dist/index.cjs +3 -3
  62. package/dist/index.cjs.map +1 -1
  63. package/dist/style.css +1 -1
  64. package/dist/types/common/hooks/index.d.ts +13 -0
  65. package/dist/types/common/hooks/useHotkeys.d.ts +45 -0
  66. package/dist/types/common/hooks/useSwipeDismiss.d.ts +6 -2
  67. package/dist/types/common/loading/index.d.ts +0 -1
  68. package/dist/types/common/menu/index.d.ts +1 -0
  69. package/dist/types/common/menu/menu.types.d.ts +9 -3
  70. package/dist/types/common/menu/menuHotkeys.d.ts +20 -0
  71. package/dist/types/common/popover/usePopoverPosition.d.ts +1 -0
  72. package/dist/types/common/sticky/sticky.d.ts +1 -1
  73. package/dist/types/common/sticky/sticky.types.d.ts +15 -0
  74. package/dist/types/common/sticky/stickyStack.d.ts +5 -0
  75. package/dist/types/common/utils/scrollAncestors.d.ts +10 -0
  76. package/dist/types/common/virtualList/scroller.d.ts +33 -0
  77. package/dist/types/common/virtualList/useRowDrag.d.ts +6 -1
  78. package/dist/types/common/virtualList/useRowNavigation.d.ts +11 -1
  79. package/dist/types/common/virtualList/useVirtualWindow.d.ts +22 -3
  80. package/dist/types/common/virtualList/virtualList.d.ts +1 -1
  81. package/dist/types/common/virtualList/virtualList.types.d.ts +43 -3
  82. package/dist/types/index.d.ts +1 -0
  83. package/docs/CLAUDE.md +3 -2
  84. package/docs/Hooks.md +232 -0
  85. package/docs/Loading.md +56 -15
  86. package/docs/Menu.md +25 -3
  87. package/docs/Popover.md +2 -0
  88. package/docs/Sticky.md +22 -1
  89. package/docs/VirtualList.md +44 -4
  90. package/package.json +1 -1
  91. package/dist/cjs-types/common/loading/iconLoading/iconLoading.d.ts +0 -5
  92. package/dist/cjs-types/common/loading/iconLoading/index.d.ts +0 -2
  93. package/dist/esm/common/loading/iconLoading/iconLoading.mjs +0 -2
  94. package/dist/esm/common/loading/iconLoading/iconLoading.mjs.map +0 -1
  95. package/dist/esm/common/loading/iconLoading/iconLoading.module.mjs +0 -2
  96. package/dist/esm/common/loading/iconLoading/iconLoading.module.mjs.map +0 -1
  97. package/dist/esm/common/loading/iconLoading/index.mjs +0 -2
  98. package/dist/esm/common/loading/iconLoading/index.mjs.map +0 -1
  99. package/dist/types/common/loading/iconLoading/iconLoading.d.ts +0 -5
  100. package/dist/types/common/loading/iconLoading/index.d.ts +0 -2
@@ -1 +1 @@
1
- {"version":3,"file":"useRowNavigation.mjs","names":[],"sources":["../../../../package/common/virtualList/useRowNavigation.ts"],"sourcesContent":["import React, { useRef, useState } from 'react';\n\nimport { findStartIndex, ROW_ENTER_SELECTOR } from './virtualList.utils';\nimport type { VirtualListScrollOptions } from './virtualList.types';\n\ninterface UseRowNavigationArgs {\n enabled: boolean;\n rowCount: number;\n /** Row tops, from the windowing engine, used for paging and visibility. */\n offsets: number[];\n rootRef: React.RefObject<HTMLDivElement | null>;\n headerRef: React.RefObject<HTMLDivElement | null>;\n scrollToIndex: (index: number, options?: VirtualListScrollOptions) => void;\n /** Row index the active row starts on, normally the selected one. */\n initialIndex: number;\n multiSelect: boolean;\n /** Undefined when the consumer gave the rows no activation of their own. */\n onActivate?: (index: number) => void;\n onToggleSelect: (index: number) => void;\n}\n\ninterface UseRowNavigationResult {\n /** Index of the row the arrow keys are on, or -1 when navigation is off. */\n activeIndex: number;\n /** Keeps the arrow keys where the pointer last was. */\n setActiveIndex: (index: number) => void;\n /**\n * The row currently being used, or -1. Its controls are in the tab order and\n * it stays mounted even when it scrolls out of the window.\n */\n enteredIndex: number;\n containerProps: {\n tabIndex?: number;\n onKeyDown?: (event: React.KeyboardEvent) => void;\n onFocus?: (event: React.FocusEvent) => void;\n onBlur?: (event: React.FocusEvent) => void;\n };\n}\n\n/**\n * Arrow-key navigation over the rows, with the tab stop on the list itself.\n *\n * The stop cannot sit on the active row, the way a roving tabindex normally\n * would: a virtualized row unmounts when it scrolls out of the window, and the\n * list would be left with no tab stop at all (the same reason `Tree` puts its\n * stop on its container). Instead the list keeps the focus and points at the\n * active row with `aria-activedescendant`.\n *\n * Keys are only handled when the list element itself has the focus. A cell may\n * hold an input, a dropdown or a button, and every one of them wants the arrows,\n * Enter and Space for itself. `F2` steps into such a row and `Escape` steps back\n * out, which is the one pair of keys that does work from inside a cell.\n */\nexport function useRowNavigation({\n enabled,\n rowCount,\n offsets,\n rootRef,\n headerRef,\n scrollToIndex,\n initialIndex,\n multiSelect,\n onActivate,\n onToggleSelect,\n}: UseRowNavigationArgs): UseRowNavigationResult {\n const [index, setIndex] = useState(initialIndex);\n const [enteredIndex, setEnteredIndex] = useState(-1);\n const offsetsRef = useRef(offsets);\n offsetsRef.current = offsets;\n\n // Rows come and go (a filter, a page, a reset), so the active index is clamped\n // during render rather than corrected in an effect.\n const activeIndex = enabled && rowCount > 0 ? Math.min(Math.max(index, 0), rowCount - 1) : -1;\n if (activeIndex >= 0 && activeIndex !== index) setIndex(activeIndex);\n\n /** Scrolls only when the row isn't already fully in view, so paging doesn't jump. */\n function revealRow(target: number) {\n const el = rootRef.current;\n if (!el) return;\n const headerHeight = headerRef.current?.offsetHeight ?? 0;\n const rowTop = offsetsRef.current[target] ?? 0;\n const rowBottom = offsetsRef.current[target + 1] ?? rowTop;\n if (rowTop < el.scrollTop + headerHeight) scrollToIndex(target, { align: 'start' });\n else if (rowBottom > el.scrollTop + el.clientHeight) scrollToIndex(target, { align: 'end' });\n }\n\n function moveTo(target: number) {\n const next = Math.max(0, Math.min(target, rowCount - 1));\n setIndex(next);\n revealRow(next);\n }\n\n /** How many rows fit in the viewport below the sticky header, at least one. */\n function pageSize(from: number): number {\n const el = rootRef.current;\n if (!el) return 1;\n const usable = Math.max(0, el.clientHeight - (headerRef.current?.offsetHeight ?? 0));\n const edge = findStartIndex(offsetsRef.current, (offsetsRef.current[from] ?? 0) + usable, rowCount);\n return Math.max(1, edge - from);\n }\n\n /** Moves the focus into the row's first control. False when it has none. */\n function enterRow(target: number): boolean {\n const row = rootRef.current?.querySelector(`[data-index=\"${target}\"]`);\n const focusable = row?.querySelector<HTMLElement>(ROW_ENTER_SELECTOR);\n if (!focusable) return false;\n // The row's controls are still out of the tab order at this point, which is\n // no obstacle to focusing one directly; the render that follows puts them\n // back in, so Tab then moves along that row.\n setEnteredIndex(target);\n focusable.focus();\n return true;\n }\n\n function rowIndexOf(node: EventTarget | null): number {\n const row = (node as HTMLElement | null)?.closest?.<HTMLElement>('[data-index]');\n const parsed = Number(row?.dataset.index);\n return Number.isFinite(parsed) ? parsed : -1;\n }\n\n /** A click or a Tab landing in a row counts as entering it, same as F2. */\n function handleFocus(event: React.FocusEvent) {\n if (event.target === event.currentTarget) return;\n const rowIndex = rowIndexOf(event.target);\n if (rowIndex < 0) return;\n setEnteredIndex(rowIndex);\n setIndex(rowIndex);\n }\n\n /** Leaving the entered row puts its controls back out of the tab order. */\n function handleBlur(event: React.FocusEvent) {\n if (rowIndexOf(event.relatedTarget) === enteredIndex) return;\n setEnteredIndex(-1);\n }\n\n /** Escape from inside a cell: hands the focus back to the list. */\n function handleExitKey(event: React.KeyboardEvent): void {\n // Something inside the row may own Escape, a dropdown closing for instance.\n if (event.key !== 'Escape' || event.defaultPrevented) return;\n const rowIndex = rowIndexOf(event.target);\n if (rowIndex >= 0) setIndex(rowIndex);\n setEnteredIndex(-1);\n rootRef.current?.focus();\n event.preventDefault();\n }\n\n function handleKeyDown(event: React.KeyboardEvent) {\n // Focus inside a cell means the row's own content is being used.\n if (event.target !== event.currentTarget) {\n handleExitKey(event);\n return;\n }\n switch (event.key) {\n case 'ArrowDown':\n moveTo(activeIndex + 1);\n break;\n case 'ArrowUp':\n moveTo(activeIndex - 1);\n break;\n case 'Home':\n moveTo(0);\n break;\n case 'End':\n moveTo(rowCount - 1);\n break;\n case 'PageDown':\n moveTo(activeIndex + pageSize(activeIndex));\n break;\n case 'PageUp':\n moveTo(activeIndex - pageSize(Math.max(0, activeIndex - 1)));\n break;\n case 'Enter':\n // With no activation of its own, a row's content is the only thing\n // Enter could mean, so it steps in rather than doing nothing.\n if (onActivate) onActivate(activeIndex);\n else if (!enterRow(activeIndex)) return;\n break;\n case 'F2':\n if (!enterRow(activeIndex)) return;\n break;\n case ' ':\n if (!multiSelect) return;\n onToggleSelect(activeIndex);\n break;\n default:\n return;\n }\n // Reached only by a handled key, so the page never scrolls under the list.\n event.preventDefault();\n }\n\n if (activeIndex < 0) return { activeIndex, enteredIndex: -1, setActiveIndex: setIndex, containerProps: {} };\n return {\n activeIndex,\n enteredIndex,\n setActiveIndex: setIndex,\n containerProps: { tabIndex: 0, onKeyDown: handleKeyDown, onFocus: handleFocus, onBlur: handleBlur },\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useRowNavigation.mjs","names":[],"sources":["../../../../package/common/virtualList/useRowNavigation.ts"],"sourcesContent":["import React, { useLayoutEffect, useRef, useState } from 'react';\n\nimport { findStartIndex, ROW_ENTER_SELECTOR, ROW_FOCUSABLE_SELECTOR } from './virtualList.utils';\nimport type { VirtualListScrollOptions } from './virtualList.types';\nimport type { VisibleSlice } from './useVirtualWindow';\n\ninterface UseRowNavigationArgs {\n enabled: boolean;\n tabAcrossRows: boolean;\n rowCount: number;\n /** Row tops, from the windowing engine, used for paging and visibility. */\n offsets: number[];\n rootRef: React.RefObject<HTMLDivElement | null>;\n headerRef: React.RefObject<HTMLDivElement | null>;\n /** Read live: a held arrow key moves the slice faster than the state commits. */\n getVisibleSlice: () => VisibleSlice;\n /** The whole scrollport, which is what one PageDown covers. */\n viewportHeight: number;\n scrollToIndex: (index: number, options?: VirtualListScrollOptions) => void;\n /** Row index the active row starts on, normally the selected one. */\n initialIndex: number;\n multiSelect: boolean;\n /** Undefined when the consumer gave the rows no activation of their own. */\n onActivate?: (index: number) => void;\n onToggleSelect: (index: number) => void;\n}\n\ninterface UseRowNavigationResult {\n /** Index of the row the arrow keys are on, or -1 when navigation is off. */\n activeIndex: number;\n /** Keeps the arrow keys where the pointer last was. */\n setActiveIndex: (index: number) => void;\n /**\n * The row currently being used, or -1. Its controls are in the tab order and\n * it stays mounted even when it scrolls out of the window.\n */\n enteredIndex: number;\n containerProps: {\n tabIndex?: number;\n onKeyDown?: (event: React.KeyboardEvent) => void;\n onFocus?: (event: React.FocusEvent) => void;\n onBlur?: (event: React.FocusEvent) => void;\n };\n}\n\n/**\n * Arrow-key navigation over the rows, with the tab stop on the list itself.\n *\n * The stop cannot sit on the active row, the way a roving tabindex normally\n * would: a virtualized row unmounts when it scrolls out of the window, and the\n * list would be left with no tab stop at all (the same reason `Tree` puts its\n * stop on its container). Instead the list keeps the focus and points at the\n * active row with `aria-activedescendant`.\n *\n * Keys are only handled when the list element itself has the focus. A cell may\n * hold an input, a dropdown or a button, and every one of them wants the arrows,\n * Enter and Space for itself. `F2` steps into such a row and `Escape` steps back\n * out, which is the one pair of keys that does work from inside a cell.\n *\n * `tabAcrossRows` adds the second pair, Tab and Shift+Tab, which the browser\n * cannot carry across a row boundary itself: the next row may not be in the DOM\n * when it looks for the next stop.\n */\nexport function useRowNavigation({\n enabled,\n tabAcrossRows,\n rowCount,\n offsets,\n rootRef,\n headerRef,\n getVisibleSlice,\n viewportHeight,\n scrollToIndex,\n initialIndex,\n multiSelect,\n onActivate,\n onToggleSelect,\n}: UseRowNavigationArgs): UseRowNavigationResult {\n const [index, setIndex] = useState(initialIndex);\n const [enteredIndex, setEnteredIndex] = useState(-1);\n const offsetsRef = useRef(offsets);\n offsetsRef.current = offsets;\n\n // Rows come and go (a filter, a page, a reset), so the active index is clamped\n // during render rather than corrected in an effect.\n const activeIndex = enabled && rowCount > 0 ? Math.min(Math.max(index, 0), rowCount - 1) : -1;\n if (activeIndex >= 0 && activeIndex !== index) setIndex(activeIndex);\n const navigable = activeIndex >= 0;\n\n /** Scrolls only when the row isn't already fully in view, so paging doesn't jump. */\n function revealRow(target: number) {\n const headerHeight = headerRef.current?.offsetHeight ?? 0;\n const rowTop = offsetsRef.current[target] ?? 0;\n const rowBottom = offsetsRef.current[target + 1] ?? rowTop;\n const { top, height } = getVisibleSlice();\n if (rowTop < top + headerHeight) scrollToIndex(target, { align: 'start' });\n else if (rowBottom > top + height) scrollToIndex(target, { align: 'end' });\n }\n\n function moveTo(target: number) {\n const next = Math.max(0, Math.min(target, rowCount - 1));\n setIndex(next);\n revealRow(next);\n }\n\n /** How many rows fit in the viewport below the sticky header, at least one. */\n function pageSize(from: number): number {\n const usable = Math.max(0, viewportHeight - (headerRef.current?.offsetHeight ?? 0));\n const edge = findStartIndex(offsetsRef.current, (offsetsRef.current[from] ?? 0) + usable, rowCount);\n return Math.max(1, edge - from);\n }\n\n function rowElement(target: number): HTMLElement | null {\n return rootRef.current?.querySelector<HTMLElement>(`[data-index=\"${target}\"]`) ?? null;\n }\n\n /** Moves the focus into the row's first control. False when it has none. */\n function enterRow(target: number): boolean {\n const focusable = rowElement(target)?.querySelector<HTMLElement>(ROW_ENTER_SELECTOR);\n if (!focusable) return false;\n // The row's controls are still out of the tab order at this point, which is\n // no obstacle to focusing one directly; the render that follows puts them\n // back in, so Tab then moves along that row.\n setEnteredIndex(target);\n focusable.focus();\n return true;\n }\n\n // A row reached by Tab may not be in the DOM yet: entering it is what pins it\n // there, and that only takes effect on the next commit.\n const pendingFocusRef = useRef<{ index: number; edge: 'first' | 'last' } | null>(null);\n // Set when Tab ran out of rows to land in, so the next one is left to the browser.\n const releaseTabRef = useRef(false);\n // The chain of updates below is bounded: each pass clears the ref and moves one\n // row closer to an end of the list.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(() => {\n const pending = pendingFocusRef.current;\n if (!pending) return;\n // Cleared whether or not the row was found: the commit that set this renders\n // it, so a second attempt at the same row would be a later commit stealing\n // the focus.\n pendingFocusRef.current = null;\n const focusables = rowElement(pending.index)?.querySelectorAll<HTMLElement>(ROW_ENTER_SELECTOR);\n if (focusables?.length) {\n (pending.edge === 'first' ? focusables[0] : focusables[focusables.length - 1]).focus();\n return;\n }\n // The Tab that got here was already prevented, so a row with nothing to focus\n // must not swallow it.\n const next = pending.index + (pending.edge === 'first' ? 1 : -1);\n if (next >= 0 && next < rowCount) {\n pendingFocusRef.current = { index: next, edge: pending.edge };\n setEnteredIndex(next);\n revealRow(next);\n return;\n }\n setEnteredIndex(-1);\n // Backwards needs no release: Shift+Tab off the list's own stop already leaves.\n releaseTabRef.current = navigable && pending.edge === 'first';\n if (navigable) rootRef.current?.focus();\n });\n\n function rowIndexOf(node: EventTarget | null): number {\n const row = (node as HTMLElement | null)?.closest?.<HTMLElement>('[data-index]');\n const parsed = Number(row?.dataset.index);\n return Number.isFinite(parsed) ? parsed : -1;\n }\n\n /** A click or a Tab landing in a row counts as entering it, same as F2. */\n function handleFocus(event: React.FocusEvent) {\n if (event.target === event.currentTarget) return;\n const rowIndex = rowIndexOf(event.target);\n if (rowIndex < 0) return;\n setEnteredIndex(rowIndex);\n setIndex(rowIndex);\n }\n\n /** Leaving the entered row puts its controls back out of the tab order. */\n function handleBlur(event: React.FocusEvent) {\n // A release only holds while the list keeps the focus. `contains` counts the\n // list itself, which is where the release puts the focus in the first place.\n if (!event.currentTarget.contains(event.relatedTarget)) releaseTabRef.current = false;\n if (rowIndexOf(event.relatedTarget) === enteredIndex) return;\n setEnteredIndex(-1);\n }\n\n /** Escape from inside a cell: hands the focus back to the list. */\n function handleExitKey(event: React.KeyboardEvent): void {\n // Something inside the row may own Escape, a dropdown closing for instance.\n if (event.key !== 'Escape' || event.defaultPrevented || !navigable) return;\n const rowIndex = rowIndexOf(event.target);\n if (rowIndex >= 0) setIndex(rowIndex);\n setEnteredIndex(-1);\n rootRef.current?.focus();\n event.preventDefault();\n }\n\n /**\n * Only the row's last control (first, going back) is taken over: everywhere\n * else the browser's order is already right, and at the ends of the list Tab\n * is left alone so it can leave.\n */\n function handleTabKey(event: React.KeyboardEvent): void {\n if (!tabAcrossRows || event.key !== 'Tab' || event.defaultPrevented) return;\n if (event.altKey || event.ctrlKey || event.metaKey) return;\n const rowIndex = rowIndexOf(event.target);\n if (rowIndex < 0) return;\n const focusables = rowElement(rowIndex)?.querySelectorAll<HTMLElement>(ROW_FOCUSABLE_SELECTOR);\n if (!focusables?.length) return;\n const at = Array.prototype.indexOf.call(focusables, event.target);\n if (at < 0) return;\n const back = event.shiftKey;\n if (back ? at > 0 : at < focusables.length - 1) return;\n const target = back ? rowIndex - 1 : rowIndex + 1;\n if (target < 0 || target >= rowCount) return;\n event.preventDefault();\n // The active row follows the focus, through `handleFocus`, so that it stays\n // where the user is when the focus never lands.\n setEnteredIndex(target);\n revealRow(target);\n pendingFocusRef.current = { index: target, edge: back ? 'last' : 'first' };\n }\n\n function handleKeyDown(event: React.KeyboardEvent) {\n // Focus inside a cell means the row's own content is being used.\n if (event.target !== event.currentTarget) {\n handleTabKey(event);\n handleExitKey(event);\n return;\n }\n // The consumer's own handler ran first and may have taken the key, which is\n // what `handleTabKey` and `handleExitKey` already check for.\n if (!navigable || event.defaultPrevented) return;\n const releasing = releaseTabRef.current;\n releaseTabRef.current = false;\n switch (event.key) {\n case 'ArrowDown':\n moveTo(activeIndex + 1);\n break;\n case 'ArrowUp':\n moveTo(activeIndex - 1);\n break;\n case 'Home':\n moveTo(0);\n break;\n case 'End':\n moveTo(rowCount - 1);\n break;\n case 'PageDown':\n moveTo(activeIndex + pageSize(activeIndex));\n break;\n case 'PageUp':\n moveTo(activeIndex - pageSize(Math.max(0, activeIndex - 1)));\n break;\n case 'Enter':\n // With no activation of its own, a row's content is the only thing\n // Enter could mean, so it steps in rather than doing nothing.\n if (onActivate) onActivate(activeIndex);\n else if (!enterRow(activeIndex)) return;\n break;\n case 'F2':\n if (!enterRow(activeIndex)) return;\n break;\n case 'Tab':\n // The list's own tab stop is in front of rows whose controls are all\n // parked, so without this Tab would step over every one of them. Which is\n // the point once `releasing` says Tab has run out of rows to land in.\n if (!tabAcrossRows || event.shiftKey || releasing || !enterRow(activeIndex)) return;\n break;\n case ' ':\n if (!multiSelect) return;\n onToggleSelect(activeIndex);\n break;\n default:\n return;\n }\n // Reached only by a handled key, so the page never scrolls under the list.\n event.preventDefault();\n }\n\n if (!navigable && !tabAcrossRows)\n return { activeIndex, enteredIndex: -1, setActiveIndex: setIndex, containerProps: {} };\n const containerProps: UseRowNavigationResult['containerProps'] = {\n onKeyDown: handleKeyDown,\n onFocus: handleFocus,\n onBlur: handleBlur,\n };\n // Without arrow navigation the list is not a composite widget and must not take\n // a stop of its own. The key is left out rather than set to `undefined`, which\n // would erase a `tabIndex` the consumer passed.\n if (navigable) containerProps.tabIndex = 0;\n return { activeIndex, enteredIndex, setActiveIndex: setIndex, containerProps };\n}\n"],"mappings":"4LA+DA,SAAgB,EAAiB,CAC/B,UACA,gBACA,WACA,UACA,UACA,YACA,kBACA,iBACA,gBACA,eACA,cACA,aACA,kBAC+C,CAC/C,GAAM,CAAC,EAAO,GAAY,EAAS,CAAY,EACzC,CAAC,EAAc,GAAmB,EAAS,EAAE,EAC7C,EAAa,EAAO,CAAO,EACjC,EAAW,QAAU,EAIrB,IAAM,EAAc,GAAW,EAAW,EAAI,KAAK,IAAI,KAAK,IAAI,EAAO,CAAC,EAAG,EAAW,CAAC,EAAI,GACvF,GAAe,GAAK,IAAgB,GAAO,EAAS,CAAW,EACnE,IAAM,EAAY,GAAe,EAGjC,SAAS,EAAU,EAAgB,CACjC,IAAM,EAAe,EAAU,SAAS,cAAgB,EAClD,EAAS,EAAW,QAAQ,IAAW,EACvC,EAAY,EAAW,QAAQ,EAAS,IAAM,EAC9C,CAAE,MAAK,UAAW,EAAgB,EACpC,EAAS,EAAM,EAAc,EAAc,EAAQ,CAAE,MAAO,OAAQ,CAAC,EAChE,EAAY,EAAM,GAAQ,EAAc,EAAQ,CAAE,MAAO,KAAM,CAAC,CAC3E,CAEA,SAAS,EAAO,EAAgB,CAC9B,IAAM,EAAO,KAAK,IAAI,EAAG,KAAK,IAAI,EAAQ,EAAW,CAAC,CAAC,EACvD,EAAS,CAAI,EACb,EAAU,CAAI,CAChB,CAGA,SAAS,EAAS,EAAsB,CACtC,IAAM,EAAS,KAAK,IAAI,EAAG,GAAkB,EAAU,SAAS,cAAgB,EAAE,EAC5E,EAAO,EAAe,EAAW,SAAU,EAAW,QAAQ,IAAS,GAAK,EAAQ,CAAQ,EAClG,OAAO,KAAK,IAAI,EAAG,EAAO,CAAI,CAChC,CAEA,SAAS,EAAW,EAAoC,CACtD,OAAO,EAAQ,SAAS,cAA2B,gBAAgB,EAAO,GAAG,GAAK,IACpF,CAGA,SAAS,EAAS,EAAyB,CACzC,IAAM,EAAY,EAAW,CAAM,CAAC,EAAE,cAA2B,CAAkB,EAOnF,OANK,GAIL,EAAgB,CAAM,EACtB,EAAU,MAAM,EACT,IANgB,EAOzB,CAIA,IAAM,EAAkB,EAAyD,IAAI,EAE/E,EAAgB,EAAO,EAAK,EAIlC,MAAsB,CACpB,IAAM,EAAU,EAAgB,QAChC,GAAI,CAAC,EAAS,OAId,EAAgB,QAAU,KAC1B,IAAM,EAAa,EAAW,EAAQ,KAAK,CAAC,EAAE,iBAA8B,CAAkB,EAC9F,GAAI,GAAY,OAAQ,EACrB,EAAQ,OAAS,QAAU,EAAW,GAAK,EAAW,EAAW,OAAS,GAAA,CAAI,MAAM,EACrF,MACF,CAGA,IAAM,EAAO,EAAQ,OAAS,EAAQ,OAAS,QAAU,EAAI,IAC7D,GAAI,GAAQ,GAAK,EAAO,EAAU,CAChC,EAAgB,QAAU,CAAE,MAAO,EAAM,KAAM,EAAQ,IAAK,EAC5D,EAAgB,CAAI,EACpB,EAAU,CAAI,EACd,MACF,CACA,EAAgB,EAAE,EAElB,EAAc,QAAU,GAAa,EAAQ,OAAS,QAClD,GAAW,EAAQ,SAAS,MAAM,CACxC,CAAC,EAED,SAAS,EAAW,EAAkC,CACpD,IAAM,EAAO,GAA6B,UAAuB,cAAc,EACzE,EAAS,OAAO,GAAK,QAAQ,KAAK,EACxC,OAAO,OAAO,SAAS,CAAM,EAAI,EAAS,EAC5C,CAGA,SAAS,EAAY,EAAyB,CAC5C,GAAI,EAAM,SAAW,EAAM,cAAe,OAC1C,IAAM,EAAW,EAAW,EAAM,MAAM,EACpC,EAAW,IACf,EAAgB,CAAQ,EACxB,EAAS,CAAQ,EACnB,CAGA,SAAS,EAAW,EAAyB,CAGtC,EAAM,cAAc,SAAS,EAAM,aAAa,IAAG,EAAc,QAAU,IAC5E,EAAW,EAAM,aAAa,IAAM,GACxC,EAAgB,EAAE,CACpB,CAGA,SAAS,EAAc,EAAkC,CAEvD,GAAI,EAAM,MAAQ,UAAY,EAAM,kBAAoB,CAAC,EAAW,OACpE,IAAM,EAAW,EAAW,EAAM,MAAM,EACpC,GAAY,GAAG,EAAS,CAAQ,EACpC,EAAgB,EAAE,EAClB,EAAQ,SAAS,MAAM,EACvB,EAAM,eAAe,CACvB,CAOA,SAAS,EAAa,EAAkC,CAEtD,GADI,CAAC,GAAiB,EAAM,MAAQ,OAAS,EAAM,kBAC/C,EAAM,QAAU,EAAM,SAAW,EAAM,QAAS,OACpD,IAAM,EAAW,EAAW,EAAM,MAAM,EACxC,GAAI,EAAW,EAAG,OAClB,IAAM,EAAa,EAAW,CAAQ,CAAC,EAAE,iBAA8B,CAAsB,EAC7F,GAAI,CAAC,GAAY,OAAQ,OACzB,IAAM,EAAK,MAAM,UAAU,QAAQ,KAAK,EAAY,EAAM,MAAM,EAChE,GAAI,EAAK,EAAG,OACZ,IAAM,EAAO,EAAM,SACnB,GAAI,EAAO,EAAK,EAAI,EAAK,EAAW,OAAS,EAAG,OAChD,IAAM,EAAS,EAAO,EAAW,EAAI,EAAW,EAC5C,EAAS,GAAK,GAAU,IAC5B,EAAM,eAAe,EAGrB,EAAgB,CAAM,EACtB,EAAU,CAAM,EAChB,EAAgB,QAAU,CAAE,MAAO,EAAQ,KAAM,EAAO,OAAS,OAAQ,EAC3E,CAEA,SAAS,EAAc,EAA4B,CAEjD,GAAI,EAAM,SAAW,EAAM,cAAe,CACxC,EAAa,CAAK,EAClB,EAAc,CAAK,EACnB,MACF,CAGA,GAAI,CAAC,GAAa,EAAM,iBAAkB,OAC1C,IAAM,EAAY,EAAc,QAEhC,OADA,EAAc,QAAU,GAChB,EAAM,IAAd,CACE,IAAK,YACH,EAAO,EAAc,CAAC,EACtB,MACF,IAAK,UACH,EAAO,EAAc,CAAC,EACtB,MACF,IAAK,OACH,EAAO,CAAC,EACR,MACF,IAAK,MACH,EAAO,EAAW,CAAC,EACnB,MACF,IAAK,WACH,EAAO,EAAc,EAAS,CAAW,CAAC,EAC1C,MACF,IAAK,SACH,EAAO,EAAc,EAAS,KAAK,IAAI,EAAG,EAAc,CAAC,CAAC,CAAC,EAC3D,MACF,IAAK,QAGH,GAAI,EAAY,EAAW,CAAW,OACjC,GAAI,CAAC,EAAS,CAAW,EAAG,OACjC,MACF,IAAK,KACH,GAAI,CAAC,EAAS,CAAW,EAAG,OAC5B,MACF,IAAK,MAIH,GAAI,CAAC,GAAiB,EAAM,UAAY,GAAa,CAAC,EAAS,CAAW,EAAG,OAC7E,MACF,IAAK,IACH,GAAI,CAAC,EAAa,OAClB,EAAe,CAAW,EAC1B,MACF,QACE,MACJ,CAEA,EAAM,eAAe,CACvB,CAEA,GAAI,CAAC,GAAa,CAAC,EACjB,MAAO,CAAE,cAAa,aAAc,GAAI,eAAgB,EAAU,eAAgB,CAAC,CAAE,EACvF,IAAM,EAA2D,CAC/D,UAAW,EACX,QAAS,EACT,OAAQ,CACV,EAKA,OADI,IAAW,EAAe,SAAW,GAClC,CAAE,cAAa,eAAc,eAAgB,EAAU,gBAAe,CAC/E"}
@@ -1,2 +1,2 @@
1
- "use client";import{buildOffsets as e,findStartIndex as t,getVisibleRange as n}from"./virtualList.utils.mjs";import{useCallback as r,useEffect as i,useLayoutEffect as a,useMemo as o,useRef as s,useState as c}from"react";function l(e,t){if(typeof e==`number`)return e;let n=e.trim();if(n===``)return 0;let r=Number(n);if(!Number.isNaN(r))return r;let i=/^(-?\d*\.?\d+)px$/.exec(n);if(i)return parseFloat(i[1]);if(typeof document>`u`||!t)return 0;let a=document.createElement(`div`);a.style.cssText=`position:absolute;visibility:hidden;height:${n};`,t.appendChild(a);let o=a.getBoundingClientRect().height;return t.removeChild(a),Number.isFinite(o)?o:0}var u=20;function d({items:d,keys:f,getItemKey:p,estimatedRowHeight:m,rowGap:h,overscan:g,onLoadMore:_,loadMoreThreshold:v,isLoading:y}){let[b,x]=c(0),[S,C]=c(0),[w,T]=c(0),[E,D]=c(()=>l(h,null)),[O,k]=c(new Map),[A,j]=c(!1),M=s(!1),N=s(null),P=s(null),F=s([]),I=s(null),L=s(0),R=s([]),[z]=c(()=>typeof ResizeObserver>`u`?null:new ResizeObserver(e=>{k(t=>{let n=null;for(let r of e){let e=r.target,i=Number(e.dataset.index);if(Number.isNaN(i))continue;let a=R.current[i];if(a===void 0)continue;let o=r.borderBoxSize?.[0],s=o?Math.round(o.blockSize):e.offsetHeight;t.get(a)!==s&&(n||=new Map(t),n.set(a,s))}return n??t})}));i(()=>()=>z?.disconnect(),[z]),a(()=>{let e=N.current;if(!e)return;C(e.clientHeight),T(e.clientWidth);let t=new ResizeObserver(()=>{C(e.clientHeight),T(e.clientWidth)});return t.observe(e),()=>t.disconnect()},[]);let B=s(_);a(()=>{B.current=_});let V=r(()=>{let e=B.current;e&&!M.current&&(M.current=!0,j(!0),e().finally(()=>{M.current=!1,j(!1)}))},[]),H=r(e=>{let t=e.currentTarget;L.current=t.scrollTop,I.current??=requestAnimationFrame(()=>{I.current=null,x(L.current)}),t.scrollTop+t.clientHeight>=t.scrollHeight-v&&V()},[v,V]);i(()=>()=>{I.current!=null&&cancelAnimationFrame(I.current)},[]),a(()=>{R.current=f}),a(()=>{let e=l(h,N.current);D(t=>t===e?t:e)},[h,w,S]);let U=o(()=>{if(O.size===0)return m;let e=0;for(let t of O.values())e+=t;return e/O.size},[O,m]),{offsets:W,totalHeight:G}=o(()=>e(f,O,U,E),[f,O,U,E]),K=s(``);i(()=>{if(!_||y||A||S===0)return;let e=P.current?.offsetHeight??0;if(G>=S-e)return;let t=`${d.length}:${S}`;K.current!==t&&(K.current=t,V())},[_,y,A,d.length,G,S,V]),a(()=>{let e=N.current,n=F.current;if(F.current=W,!e||n.length===0||Y.current)return;let r=e.scrollTop;if(r<=0)return;let i=n.length-1,a=t(n,r,i);if(a>=d.length)return;let o=r-n[a],s=W[a]+o;Math.abs(s-r)>.5&&(e.scrollTop=s,L.current=s,x(s))},[W,d.length]);let q=r((e,t)=>{let n=N.current;if(!n)return;let r=Math.max(0,e);typeof n.scrollTo==`function`?n.scrollTo({top:r,behavior:t??`auto`}):n.scrollTop=r},[]),J=r((e,t)=>{let n=N.current;if(!n||d.length===0)return null;let r=Math.max(0,Math.min(e,d.length-1)),i=W[r]??0,a=(W[r+1]??i)-i,o=P.current?.offsetHeight??0,s=Math.max(0,n.clientHeight-o),c=t?.align??`start`,l=i;return c===`center`?l-=(s-a)/2:c===`end`&&(l-=s-a),l},[d.length,W]),Y=s(null);a(()=>{let e=Y.current,t=N.current;if(!e)return;let n=e.items===d&&t?J(e.index,e.options):null;if(n==null){Y.current=null;return}let r=Math.abs(n-e.lastTarget)<=1;if(r&&Math.abs(t.scrollTop-n)<=1||e.attempts>=u){Y.current=null;return}r||(e.lastTarget=n,e.attempts+=1,q(n,e.options?.behavior))}),i(()=>{let e=N.current;if(!e)return;let t=()=>{Y.current=null};return e.addEventListener(`wheel`,t,{passive:!0}),e.addEventListener(`touchstart`,t,{passive:!0}),e.addEventListener(`keydown`,t),()=>{e.removeEventListener(`wheel`,t),e.removeEventListener(`touchstart`,t),e.removeEventListener(`keydown`,t)}},[]);let X=o(()=>{function e(e,t){let n=J(e,t);n!=null&&(Y.current={index:e,options:t,lastTarget:n,attempts:0,items:d},q(n,t?.behavior))}return{scrollToIndex:e,scrollToKey(t,n){let r=d.findIndex((e,n)=>(p?p(e,n):n)===t);r>=0&&e(r,n)},scrollToTop(e){Y.current=null,q(0,e?.behavior)},getScrollElement:()=>N.current}},[d,p,J,q]),{startIndex:Z,endIndex:Q}=o(()=>n(W,b,S,g,d.length),[W,b,S,g,d.length]);return{rootRef:N,headerRef:P,rowObserver:z,keysRef:R,offsets:W,totalHeight:G,containerHeight:S,containerWidth:w,startIndex:Z,endIndex:Q,handleScroll:H,showLoadMoreSpinner:A,scrollApi:X}}export{d as useVirtualWindow};
1
+ "use client";import{getScrollParent as e}from"../utils/scrollAncestors.mjs";import{buildOffsets as t,findStartIndex as n,getVisibleRange as r}from"./virtualList.utils.mjs";import{isWindowScroller as i,measureViewport as a,resolveScroller as o,scrollportBounds as s}from"./scroller.mjs";import{useCallback as c,useEffect as l,useLayoutEffect as u,useMemo as d,useRef as f,useState as p}from"react";function ee(e,t){if(typeof e==`number`)return e;let n=e.trim();if(n===``)return 0;let r=Number(n);if(!Number.isNaN(r))return r;let i=/^(-?\d*\.?\d+)px$/.exec(n);if(i)return parseFloat(i[1]);if(typeof document>`u`||!t)return 0;let a=document.createElement(`div`);a.style.cssText=`position:absolute;visibility:hidden;height:${n};`,t.appendChild(a);let o=a.getBoundingClientRect().height;return t.removeChild(a),Number.isFinite(o)?o:0}var te=100,ne=20;function m({items:m,keys:h,getItemKey:g,estimatedRowHeight:re,rowGap:_,overscan:ie,onLoadMore:v,loadMoreThreshold:y,isLoading:b,scrollParent:x}){let[S,ae]=p(0),[C,oe]=p(0),[se,ce]=p(0),[w,le]=p(0),[T,ue]=p(()=>ee(_,null)),[E,de]=p(new Map),[D,O]=p(!1),k=f(!1),A=f(null),j=f(null),M=f([]),N=f(null),P=f([]),F=x!=null,I=f(null),L=f(null),[R,fe]=p(null);u(()=>{let t;if(x===`nearest`){let n=L.current;(!n||!i(n)&&!n.isConnected)&&(L.current=A.current?e(A.current)??window:null),t=L.current}else t=o(x);I.current=t,fe(e=>e===t?e:t)});let[z]=p(()=>typeof ResizeObserver>`u`?null:new ResizeObserver(e=>{de(t=>{let n=null;for(let r of e){let e=r.target,i=Number(e.dataset.index);if(Number.isNaN(i))continue;let a=P.current[i];if(a===void 0)continue;let o=r.borderBoxSize?.[0],s=o?Math.round(o.blockSize):e.offsetHeight;t.get(a)!==s&&(n||=new Map(t),n.set(a,s))}return n??t})}));l(()=>()=>z?.disconnect(),[z]);let B=c(e=>{let t=Math.max(0,e.rel);ae(e=>e===t?e:t),oe(t=>t===e.visibleHeight?t:e.visibleHeight),ce(t=>t===e.viewportHeight?t:e.viewportHeight),le(t=>t===e.width?t:e.width)},[]),V=c(()=>{let e=A.current;e&&B(a(e,I.current))},[B]),H=c(()=>{let e=A.current;if(!e)return{top:0,height:0};let t=a(e,I.current);return{top:Math.max(0,t.rel),height:t.visibleHeight}},[]),pe=c(()=>{let e=A.current;if(!e)return null;let t=e.getBoundingClientRect(),n=I.current;if(!n)return{top:t.top,bottom:t.bottom,left:t.left,right:t.right};let{top:r,height:i}=s(n);return{top:Math.max(t.top,r),bottom:Math.min(t.bottom,r+i),left:t.left,right:t.right}},[]),me=c(e=>{let t=A.current;if(!t)return;let n=I.current;n?i(n)?n.scrollBy(0,e):n.scrollTop+=e:t.scrollTop+=e},[]);u(()=>{let e=A.current;if(!e)return;V();let t=typeof ResizeObserver>`u`?null:new ResizeObserver(()=>V());return t?.observe(e),R?(t?.observe(i(R)?document.documentElement:R),window.addEventListener(`resize`,V),()=>{t?.disconnect(),window.removeEventListener(`resize`,V)}):()=>t?.disconnect()},[R,V]);let he=f(v);u(()=>{he.current=v});let U=c(()=>{let e=he.current;e&&!k.current&&(k.current=!0,O(!0),e().finally(()=>{k.current=!1,O(!1)}))},[]),ge=f(-1),W=c(()=>{let e=A.current;if(!e)return;let t=a(e,I.current);N.current??=requestAnimationFrame(()=>{N.current=null,V()}),t.distanceToEnd<=y&&ge.current!==P.current.length&&(ge.current=P.current.length,U())},[V,y,U]);l(()=>{let e=R??A.current;if(e)return e.addEventListener(`scroll`,W,{passive:!0}),()=>e.removeEventListener(`scroll`,W)},[R,W]),l(()=>()=>{N.current!=null&&cancelAnimationFrame(N.current)},[]),u(()=>{P.current=h}),u(()=>{let e=ee(_,A.current);ue(t=>t===e?t:e)},[_,w,C]);let _e=d(()=>{if(E.size===0)return re;let e=0;for(let t of E.values())e+=t;return e/E.size},[E,re]),{offsets:G,totalHeight:K}=d(()=>t(h,E,_e,T),[h,E,_e,T]),ve=f(``),q=F?se:C;l(()=>{if(!v||b||D||q===0)return;let e=j.current?.offsetHeight??0;if(K>=q-e)return;let t=`${m.length}:${q}`;ve.current!==t&&(ve.current=t,U())},[v,b,D,m.length,K,q,U]);let J=c((e,t)=>{let n=A.current;if(!n)return;let r=I.current;if(!r){let r=Math.max(0,e);t&&typeof n.scrollTo==`function`?n.scrollTo({top:r,behavior:t}):n.scrollTop=r;return}let{rel:o}=a(n,r);if(i(r)){r.scrollTo({top:Math.max(0,r.scrollY+e-o),behavior:t??`auto`});return}let s=Math.max(0,r.scrollTop+e-o);t&&typeof r.scrollTo==`function`?r.scrollTo({top:s,behavior:t}):r.scrollTop=s},[]);u(()=>{let e=A.current,t=M.current;if(M.current=G,!e||t.length===0||X.current)return;let r=H().top;if(r<=0)return;let i=t.length-1,a=n(t,r,i);if(a>=m.length)return;let o=r-t[a],s=G[a]+o;Math.abs(s-r)>.5&&(J(s),ae(s))},[G,m.length,H,J]);let Y=c((e,t)=>{let n=A.current;if(!n||m.length===0)return null;let r=Math.max(0,Math.min(e,m.length-1)),i=G[r]??0,o=(G[r+1]??i)-i,s=j.current?.offsetHeight??0,c=Math.max(0,a(n,I.current).viewportHeight-s),l=t?.align??`start`,u=i;return l===`center`?u-=(c-o)/2:l===`end`&&(u-=c-o),u},[m.length,G]),X=f(null);u(()=>{let e=X.current,t=A.current;if(!e)return;let n=e.items===m&&t?Y(e.index,e.options):null;if(n==null){X.current=null;return}let r=Math.abs(n-e.lastTarget)<=1;if(r&&Math.abs(H().top-n)<=1||e.attempts>=ne){X.current=null;return}r||(e.lastTarget=n,e.attempts+=1,J(n,e.options?.behavior))}),l(()=>{let e=A.current;if(!e)return;let t=()=>{X.current=null},n=R??e;return n.addEventListener(`wheel`,t,{passive:!0}),n.addEventListener(`touchstart`,t,{passive:!0}),e.addEventListener(`keydown`,t),()=>{n.removeEventListener(`wheel`,t),n.removeEventListener(`touchstart`,t),e.removeEventListener(`keydown`,t)}},[R]);let ye=d(()=>{function e(e,t){let n=Y(e,t);n!=null&&(X.current={index:e,options:t,lastTarget:n,attempts:0,items:m},J(n,t?.behavior))}return{scrollToIndex:e,scrollToKey(t,n){let r=m.findIndex((e,n)=>(g?g(e,n):n)===t);r>=0&&e(r,n)},scrollToTop(e){X.current=null,J(0,e?.behavior)},getScrollElement:()=>{let e=I.current;return e?i(e)?null:e:A.current},remeasure:V}},[m,g,Y,J,V]),{startIndex:Z,endIndex:Q}=d(()=>r(G,S,C,ie,m.length),[G,S,C,ie,m.length]),$=f(!1);return l(()=>{$.current||C===0||m.length<te||Q-Z+1<m.length||($.current=!0,console.warn(`VirtualList: rendering all ${m.length} rows, so nothing is being virtualized. Its viewport is as tall as its content. Give the list a definite height, or pass scrollParent to have it ride a scrollport it does not own.`))},[C,m.length,Z,Q]),{rootRef:A,headerRef:j,rowObserver:z,keysRef:P,offsets:G,totalHeight:K,containerHeight:C,viewportHeight:se,containerWidth:w,startIndex:Z,endIndex:Q,showLoadMoreSpinner:D,isWindowed:F,getVisibleSlice:H,getVisibleRect:pe,scrollBy:me,scrollApi:ye}}export{m as useVirtualWindow};
2
2
  //# sourceMappingURL=useVirtualWindow.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"useVirtualWindow.mjs","names":[],"sources":["../../../../package/common/virtualList/useVirtualWindow.ts"],"sourcesContent":["import React, { useState, useRef, useEffect, useLayoutEffect, useMemo, useCallback } from 'react';\n\nimport { buildOffsets, findStartIndex, getVisibleRange } from './virtualList.utils';\nimport type { VirtualListHandle, VirtualListScrollOptions } from './virtualList.types';\n\n/**\n * Resolve a `rowGap` value to pixels for the offset math. Numbers and plain\n * `px`/numeric strings parse directly (no DOM, SSR/test-safe); any other CSS\n * length (`em`, `rem`, `%`, `var(...)`) is resolved via a hidden probe appended\n * to `contextEl` so it inherits the list's font-size and custom properties.\n */\nfunction resolveCssLength(value: number | string, contextEl: HTMLElement | null): number {\n if (typeof value === 'number') return value;\n const trimmed = value.trim();\n if (trimmed === '') return 0;\n const asNumber = Number(trimmed);\n if (!Number.isNaN(asNumber)) return asNumber; // \"8\" → 8\n const pxMatch = /^(-?\\d*\\.?\\d+)px$/.exec(trimmed);\n if (pxMatch) return parseFloat(pxMatch[1]); // \"8px\" → 8\n if (typeof document === 'undefined' || !contextEl) return 0;\n const probe = document.createElement('div');\n probe.style.cssText = `position:absolute;visibility:hidden;height:${trimmed};`;\n contextEl.appendChild(probe);\n const px = probe.getBoundingClientRect().height;\n contextEl.removeChild(probe);\n return Number.isFinite(px) ? px : 0;\n}\n\ninterface UseVirtualWindowArgs<T> {\n items: T[];\n /** Stable key per row (already derived by the caller). */\n keys: (string | number)[];\n getItemKey?: (item: T, index: number) => string | number;\n estimatedRowHeight: number;\n rowGap: number | string;\n overscan: number;\n onLoadMore?: () => Promise<void>;\n loadMoreThreshold: number;\n /** Initial/filter load in progress — the body is replaced by a spinner. */\n isLoading: boolean;\n}\n\ninterface UseVirtualWindowResult {\n rootRef: React.RefObject<HTMLDivElement | null>;\n headerRef: React.RefObject<HTMLDivElement | null>;\n /** Shared by every row to measure its height (read back via `data-index`). */\n rowObserver: ResizeObserver | null;\n /** Latest index → key mapping, for async consumers (e.g. the drag hook). */\n keysRef: React.RefObject<(string | number)[]>;\n offsets: number[];\n totalHeight: number;\n containerHeight: number;\n containerWidth: number;\n startIndex: number;\n endIndex: number;\n handleScroll: (e: React.UIEvent<HTMLDivElement>) => void;\n showLoadMoreSpinner: boolean;\n /** The scroll-related imperative methods, for the component to compose into\n * the forwarded handle alongside column controls. */\n scrollApi: Omit<VirtualListHandle, 'recalculateColumns' | 'resetColumnWidths'>;\n}\n\n/**\n * The generic virtualization engine: viewport measurement, per-row height\n * caching, offset/visible-range computation, scroll coalescing, scroll\n * anchoring on height changes, infinite-load triggering, and the imperative\n * scroll handle. Mode-agnostic — knows nothing about columns, selection or\n * dragging.\n */\n/**\n * Loop guard for a jump re-aiming at its target. It normally ends because the\n * target stops moving, long before this; the cap is only there so a target that\n * can never be reached, the last row asked for with `align: 'start'`, can't\n * re-aim forever.\n */\nconst MAX_JUMP_CORRECTIONS = 20;\n\nexport function useVirtualWindow<T>({\n items,\n keys,\n getItemKey,\n estimatedRowHeight,\n rowGap,\n overscan,\n onLoadMore,\n loadMoreThreshold,\n isLoading,\n}: UseVirtualWindowArgs<T>): UseVirtualWindowResult {\n const [scrollTop, setScrollTop] = useState(0);\n const [containerHeight, setContainerHeight] = useState(0);\n const [containerWidth, setContainerWidth] = useState(0);\n // rowGap may be any CSS length; resolve it to px for the offset math. Seed with\n // the synchronous fast path (number / px), then refine before paint for units\n // that need the DOM (em/rem/%/var).\n const [resolvedRowGap, setResolvedRowGap] = useState<number>(() => resolveCssLength(rowGap, null));\n const [heightsMap, setHeightsMap] = useState<Map<string | number, number>>(new Map());\n const [showLoadMoreSpinner, setShowLoadMoreSpinner] = useState(false);\n const isLoadingMoreRef = useRef(false);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const headerRef = useRef<HTMLDivElement>(null);\n const prevOffsetsRef = useRef<number[]>([]);\n const scrollRafRef = useRef<number | null>(null);\n const pendingScrollTopRef = useRef(0);\n const keysRef = useRef<(string | number)[]>([]);\n\n // One ResizeObserver shared by every row, created once via a lazy state\n // initializer so rows mounting in the same commit can register in their\n // layout effect. Batching all entries into a single setState avoids a\n // re-render per row when many rows enter the viewport at once (the main\n // source of scroll jank). The row index is read back from `data-index`.\n const [rowObserver] = useState<ResizeObserver | null>(() => {\n if (typeof ResizeObserver === 'undefined') return null;\n return new ResizeObserver((entries) => {\n setHeightsMap((prev) => {\n let next: Map<string | number, number> | null = null;\n for (const entry of entries) {\n const el = entry.target as HTMLElement;\n const idx = Number(el.dataset.index);\n if (Number.isNaN(idx)) continue;\n // Resolve the row's stable key so the cache survives reorders/filters.\n const key = keysRef.current[idx];\n if (key === undefined) continue;\n // Prefer the size the observer already computed — reading offsetHeight\n // here would force a synchronous reflow on every measurement batch.\n const box = entry.borderBoxSize?.[0];\n const h = box ? Math.round(box.blockSize) : el.offsetHeight;\n if (prev.get(key) === h) continue;\n if (!next) next = new Map(prev);\n next.set(key, h);\n }\n return next ?? prev;\n });\n });\n });\n useEffect(() => () => rowObserver?.disconnect(), [rowObserver]);\n\n useLayoutEffect(() => {\n const el = rootRef.current;\n if (!el) return;\n // Measure synchronously before the first paint so the initial render fills\n // the viewport, instead of painting a few overscan rows and re-rendering.\n setContainerHeight(el.clientHeight);\n setContainerWidth(el.clientWidth);\n // Track width too: `fit` columns are measured against this box and capped at\n // its width, so when the container settles to its real width after a route\n // transition / async layout, that change has to re-trigger the measure —\n // otherwise an early measure against a 0/tiny width gets pinned and sticks.\n const observer = new ResizeObserver(() => {\n setContainerHeight(el.clientHeight);\n setContainerWidth(el.clientWidth);\n });\n observer.observe(el);\n return () => observer.disconnect();\n }, []);\n\n // Read through a ref so the trigger below stays stable even when the consumer\n // passes a fresh `onLoadMore` closure on every render.\n const onLoadMoreRef = useRef(onLoadMore);\n useLayoutEffect(() => {\n onLoadMoreRef.current = onLoadMore;\n });\n\n /** Start a load unless one is already in flight (and drive the bottom spinner). */\n const triggerLoadMore = useCallback(() => {\n const load = onLoadMoreRef.current;\n if (!load || isLoadingMoreRef.current) return;\n isLoadingMoreRef.current = true;\n setShowLoadMoreSpinner(true);\n load().finally(() => {\n isLoadingMoreRef.current = false;\n setShowLoadMoreSpinner(false);\n });\n }, []);\n\n const handleScroll = useCallback(\n (e: React.UIEvent<HTMLDivElement>) => {\n const el = e.currentTarget;\n // Coalesce scroll-driven re-renders to at most one per animation frame.\n // Scroll events can fire several times per frame (especially while dragging\n // the scrollbar thumb); rendering the visible window once per frame keeps\n // the content in step with the cursor instead of lagging behind.\n pendingScrollTopRef.current = el.scrollTop;\n if (scrollRafRef.current == null) {\n scrollRafRef.current = requestAnimationFrame(() => {\n scrollRafRef.current = null;\n setScrollTop(pendingScrollTopRef.current);\n });\n }\n\n if (el.scrollTop + el.clientHeight >= el.scrollHeight - loadMoreThreshold) triggerLoadMore();\n },\n [loadMoreThreshold, triggerLoadMore],\n );\n\n useEffect(\n () => () => {\n if (scrollRafRef.current != null) cancelAnimationFrame(scrollRafRef.current);\n },\n [],\n );\n\n useLayoutEffect(() => {\n keysRef.current = keys;\n });\n\n // Resolve the gap to px before paint, re-running when the value or the\n // container's layout changes (a theme/font change that alters em/var is caught\n // by the container ResizeObserver feeding containerWidth/Height).\n useLayoutEffect(() => {\n const px = resolveCssLength(rowGap, rootRef.current);\n setResolvedRowGap((prev) => (prev === px ? prev : px));\n }, [rowGap, containerWidth, containerHeight]);\n\n // What an unmeasured row is assumed to be. `estimatedRowHeight` is only the\n // seed: once rows have been measured, their average is a far better guess, and\n // it is the guess every row between here and a distant jump target is made of.\n // With a fixed estimate the error compounds over that distance (40 assumed\n // against 35 real puts row 75 at row 86), and no amount of re-aiming fixes it,\n // because the rows that are wrong are precisely the ones never rendered.\n const assumedRowHeight = useMemo(() => {\n if (heightsMap.size === 0) return estimatedRowHeight;\n let total = 0;\n for (const height of heightsMap.values()) total += height;\n return total / heightsMap.size;\n }, [heightsMap, estimatedRowHeight]);\n\n const { offsets, totalHeight } = useMemo(\n () => buildOffsets(keys, heightsMap, assumedRowHeight, resolvedRowGap),\n [keys, heightsMap, assumedRowHeight, resolvedRowGap],\n );\n\n // The scroll trigger above can only fire on a list that actually scrolls. When\n // a batch is smaller than the viewport there is no overflow, so no scroll event\n // ever happens and lazy loading dead-ends after the first batch. Keep asking\n // for more until the content overflows; the consumer ends the loop by appending\n // nothing (or by dropping `onLoadMore` once its source is exhausted).\n //\n // Each (row count, viewport height) pair is probed at most once, so rows\n // settling to their measured heights can't re-ask a source that already ran\n // dry, and progress depends entirely on the consumer appending rows.\n const fillProbeRef = useRef('');\n useEffect(() => {\n if (!onLoadMore || isLoading || showLoadMoreSpinner) return;\n // 0 while the list is unmeasured or `display: none` — nothing to fill yet.\n if (containerHeight === 0) return;\n // The sticky header eats into the scroll area, so it isn't usable space.\n const headerH = headerRef.current?.offsetHeight ?? 0;\n if (totalHeight >= containerHeight - headerH) return; // already scrollable\n const probe = `${items.length}:${containerHeight}`;\n if (fillProbeRef.current === probe) return;\n fillProbeRef.current = probe;\n triggerLoadMore();\n }, [onLoadMore, isLoading, showLoadMoreSpinner, items.length, totalHeight, containerHeight, triggerLoadMore]);\n\n useLayoutEffect(() => {\n const el = rootRef.current;\n const prevOffsets = prevOffsetsRef.current;\n prevOffsetsRef.current = offsets;\n\n // Nothing to anchor against on the first pass or when pinned at the top.\n if (!el || prevOffsets.length === 0) return;\n // A jump in flight is aiming at a row; anchoring would hold the row it\n // happens to be passing instead, and the two would fight to a standstill.\n if (pendingJumpRef.current) return;\n const curScrollTop = el.scrollTop;\n if (curScrollTop <= 0) return;\n\n // Scroll anchoring: pin the row at the top of the viewport. Find it under\n // the *previous* offsets, then re-derive scrollTop from its *new* offset so\n // it stays visually fixed when rows above it are measured or resized. This\n // is more robust than summing per-row height deltas — it can't drift, and\n // one correction covers any number of simultaneous changes above the fold.\n const prevCount = prevOffsets.length - 1;\n const anchor = findStartIndex(prevOffsets, curScrollTop, prevCount);\n if (anchor >= items.length) return;\n const within = curScrollTop - prevOffsets[anchor];\n const target = offsets[anchor] + within;\n if (Math.abs(target - curScrollTop) > 0.5) {\n el.scrollTop = target;\n pendingScrollTopRef.current = target;\n setScrollTop(target);\n }\n }, [offsets, items.length]);\n\n const scrollToOffset = useCallback((top: number, behavior?: ScrollBehavior) => {\n const el = rootRef.current;\n if (!el) return;\n const clamped = Math.max(0, top);\n if (typeof el.scrollTo === 'function') el.scrollTo({ top: clamped, behavior: behavior ?? 'auto' });\n else el.scrollTop = clamped;\n }, []);\n\n /** Scroll position that puts row `index` where `align` asks for, or null if there is none. */\n const computeScrollTop = useCallback(\n (index: number, options?: VirtualListScrollOptions): number | null => {\n const el = rootRef.current;\n if (!el || items.length === 0) return null;\n const i = Math.max(0, Math.min(index, items.length - 1));\n const rowTop = offsets[i] ?? 0;\n const rowHeight = (offsets[i + 1] ?? rowTop) - rowTop;\n // Row offsets are measured from the top of the body, which itself starts\n // below the sticky header: the header is in flow, so scrolling by exactly\n // `rowTop` already puts the row flush under it. Subtracting the header\n // height on top of that, as this once did, left the row one header lower,\n // which in a table whose rows are about as tall as its header reads as\n // landing a row short.\n const headerHeight = headerRef.current?.offsetHeight ?? 0;\n // What is left of the viewport once the header has taken its share.\n const usable = Math.max(0, el.clientHeight - headerHeight);\n const align = options?.align ?? 'start';\n let top = rowTop;\n if (align === 'center') top -= (usable - rowHeight) / 2;\n else if (align === 'end') top -= usable - rowHeight;\n return top;\n },\n [items.length, offsets],\n );\n\n // A jump lands on the wrong row while the rows between here and there have\n // never been rendered: their offsets are `estimatedRowHeight` guesses, and the\n // error compounds over the distance (40px guessed against 35px real puts row\n // 75 at row 86). Scrolling there renders those rows, measuring corrects the\n // offsets, and the effect below re-aims at the target until it stops moving.\n // This is why the same jump is accurate once the list has been scrolled\n // through: by then every row in between carries a measured height.\n const pendingJumpRef = useRef<{\n index: number;\n options?: VirtualListScrollOptions;\n lastTarget: number;\n attempts: number;\n items: T[];\n } | null>(null);\n\n // Deliberately without a dependency list: every commit is a chance that new\n // measurements have moved the target, including the ones that come from the\n // scroll position itself changing mid-animation.\n useLayoutEffect(() => {\n const pending = pendingJumpRef.current;\n const el = rootRef.current;\n if (!pending) return;\n const target = pending.items === items && el ? computeScrollTop(pending.index, pending.options) : null;\n if (target == null) {\n pendingJumpRef.current = null; // new data supersedes an in-flight jump\n return;\n }\n const isStable = Math.abs(target - pending.lastTarget) <= 1;\n // Only once it has both stopped moving and been reached: with a smooth\n // behaviour the target can sit still for several frames while the animation\n // is still on its way there, and giving up then would leave it short.\n if ((isStable && Math.abs(el!.scrollTop - target) <= 1) || pending.attempts >= MAX_JUMP_CORRECTIONS) {\n pendingJumpRef.current = null;\n return;\n }\n if (isStable) return;\n pending.lastTarget = target;\n pending.attempts += 1;\n scrollToOffset(target, pending.options?.behavior);\n });\n\n // Any input of the user's own outranks a jump still in flight: being pulled\n // back to a row you just scrolled away from is worse than not arriving.\n useEffect(() => {\n const el = rootRef.current;\n if (!el) return;\n const cancel = () => {\n pendingJumpRef.current = null;\n };\n el.addEventListener('wheel', cancel, { passive: true });\n el.addEventListener('touchstart', cancel, { passive: true });\n el.addEventListener('keydown', cancel);\n return () => {\n el.removeEventListener('wheel', cancel);\n el.removeEventListener('touchstart', cancel);\n el.removeEventListener('keydown', cancel);\n };\n }, []);\n\n const scrollApi = useMemo<Omit<VirtualListHandle, 'recalculateColumns' | 'resetColumnWidths'>>(() => {\n function scrollToIndex(index: number, options?: VirtualListScrollOptions) {\n const target = computeScrollTop(index, options);\n if (target == null) return;\n pendingJumpRef.current = { index, options, lastTarget: target, attempts: 0, items };\n scrollToOffset(target, options?.behavior);\n }\n return {\n scrollToIndex,\n scrollToKey(key, options) {\n const i = items.findIndex((item, idx) => (getItemKey ? getItemKey(item, idx) : idx) === key);\n if (i >= 0) scrollToIndex(i, options);\n },\n scrollToTop(options) {\n pendingJumpRef.current = null;\n scrollToOffset(0, options?.behavior);\n },\n getScrollElement: () => rootRef.current,\n };\n }, [items, getItemKey, computeScrollTop, scrollToOffset]);\n\n const { startIndex, endIndex } = useMemo(\n () => getVisibleRange(offsets, scrollTop, containerHeight, overscan, items.length),\n [offsets, scrollTop, containerHeight, overscan, items.length],\n );\n\n return {\n rootRef,\n headerRef,\n rowObserver,\n keysRef,\n offsets,\n totalHeight,\n containerHeight,\n containerWidth,\n startIndex,\n endIndex,\n handleScroll,\n showLoadMoreSpinner,\n scrollApi,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"useVirtualWindow.mjs","names":[],"sources":["../../../../package/common/virtualList/useVirtualWindow.ts"],"sourcesContent":["import React, { useState, useRef, useEffect, useLayoutEffect, useMemo, useCallback } from 'react';\n\nimport { buildOffsets, findStartIndex, getVisibleRange } from './virtualList.utils';\nimport { getScrollParent } from '../utils/scrollAncestors';\nimport { isWindowScroller, measureViewport, resolveScroller, scrollportBounds } from './scroller';\nimport type { Scroller, Viewport } from './scroller';\nimport type { VirtualListHandle, VirtualListScrollOptions, VirtualListScrollParent } from './virtualList.types';\n\n/**\n * Resolve a `rowGap` value to pixels for the offset math. Numbers and plain\n * `px`/numeric strings parse directly (no DOM, SSR/test-safe); any other CSS\n * length (`em`, `rem`, `%`, `var(...)`) is resolved via a hidden probe appended\n * to `contextEl` so it inherits the list's font-size and custom properties.\n */\nfunction resolveCssLength(value: number | string, contextEl: HTMLElement | null): number {\n if (typeof value === 'number') return value;\n const trimmed = value.trim();\n if (trimmed === '') return 0;\n const asNumber = Number(trimmed);\n if (!Number.isNaN(asNumber)) return asNumber; // \"8\" → 8\n const pxMatch = /^(-?\\d*\\.?\\d+)px$/.exec(trimmed);\n if (pxMatch) return parseFloat(pxMatch[1]); // \"8px\" → 8\n if (typeof document === 'undefined' || !contextEl) return 0;\n const probe = document.createElement('div');\n probe.style.cssText = `position:absolute;visibility:hidden;height:${trimmed};`;\n contextEl.appendChild(probe);\n const px = probe.getBoundingClientRect().height;\n contextEl.removeChild(probe);\n return Number.isFinite(px) ? px : 0;\n}\n\n/** No viewport holds this many rows at once, so rendering them all is a bug. */\nconst NOT_VIRTUALIZING_ROWS = 100;\n\ninterface UseVirtualWindowArgs<T> {\n items: T[];\n /** Stable key per row (already derived by the caller). */\n keys: (string | number)[];\n getItemKey?: (item: T, index: number) => string | number;\n estimatedRowHeight: number;\n rowGap: number | string;\n overscan: number;\n onLoadMore?: () => Promise<void>;\n loadMoreThreshold: number;\n /** Initial/filter load in progress — the body is replaced by a spinner. */\n isLoading: boolean;\n scrollParent?: VirtualListScrollParent;\n}\n\n/** Where the list is on screen, which is not its own box once it rides a scrollport. */\nexport interface ListViewportRect {\n top: number;\n bottom: number;\n left: number;\n right: number;\n}\n\n/** The list-space slice currently on screen, read live for keyboard navigation. */\nexport interface VisibleSlice {\n top: number;\n height: number;\n}\n\ninterface UseVirtualWindowResult {\n rootRef: React.RefObject<HTMLDivElement | null>;\n headerRef: React.RefObject<HTMLDivElement | null>;\n /** Shared by every row to measure its height (read back via `data-index`). */\n rowObserver: ResizeObserver | null;\n /** Latest index → key mapping, for async consumers (e.g. the drag hook). */\n keysRef: React.RefObject<(string | number)[]>;\n offsets: number[];\n totalHeight: number;\n /** The list's visible slice, which is what decides which rows are rendered. */\n containerHeight: number;\n /** The whole scrollport, which is what a scroll target is aimed within. */\n viewportHeight: number;\n containerWidth: number;\n startIndex: number;\n endIndex: number;\n showLoadMoreSpinner: boolean;\n isWindowed: boolean;\n getVisibleSlice: () => VisibleSlice;\n getVisibleRect: () => ListViewportRect | null;\n scrollBy: (dy: number) => void;\n /** The scroll-related imperative methods, for the component to compose into\n * the forwarded handle alongside column controls. */\n scrollApi: Omit<VirtualListHandle, 'recalculateColumns' | 'resetColumnWidths'>;\n}\n\n/**\n * The generic virtualization engine: viewport measurement, per-row height\n * caching, offset/visible-range computation, scroll coalescing, scroll\n * anchoring on height changes, infinite-load triggering, and the imperative\n * scroll handle. Mode-agnostic — knows nothing about columns, selection or\n * dragging.\n */\n/**\n * Loop guard for a jump re-aiming at its target. It normally ends because the\n * target stops moving, long before this; the cap is only there so a target that\n * can never be reached, the last row asked for with `align: 'start'`, can't\n * re-aim forever.\n */\nconst MAX_JUMP_CORRECTIONS = 20;\n\nexport function useVirtualWindow<T>({\n items,\n keys,\n getItemKey,\n estimatedRowHeight,\n rowGap,\n overscan,\n onLoadMore,\n loadMoreThreshold,\n isLoading,\n scrollParent,\n}: UseVirtualWindowArgs<T>): UseVirtualWindowResult {\n const [scrollTop, setScrollTop] = useState(0);\n const [containerHeight, setContainerHeight] = useState(0);\n const [viewportHeight, setViewportHeight] = useState(0);\n const [containerWidth, setContainerWidth] = useState(0);\n // rowGap may be any CSS length; resolve it to px for the offset math. Seed with\n // the synchronous fast path (number / px), then refine before paint for units\n // that need the DOM (em/rem/%/var).\n const [resolvedRowGap, setResolvedRowGap] = useState<number>(() => resolveCssLength(rowGap, null));\n const [heightsMap, setHeightsMap] = useState<Map<string | number, number>>(new Map());\n const [showLoadMoreSpinner, setShowLoadMoreSpinner] = useState(false);\n const isLoadingMoreRef = useRef(false);\n\n const rootRef = useRef<HTMLDivElement>(null);\n const headerRef = useRef<HTMLDivElement>(null);\n const prevOffsetsRef = useRef<number[]>([]);\n const scrollRafRef = useRef<number | null>(null);\n const keysRef = useRef<(string | number)[]>([]);\n\n const isWindowed = scrollParent != null;\n // The ref is what every callback reads, so none of them has to be rebuilt when\n // the scroller resolves; the state exists only to re-run the listener effects.\n const scrollerRef = useRef<Scroller | null>(null);\n const nearestRef = useRef<Scroller | null>(null);\n const [scroller, setScroller] = useState<Scroller | null>(null);\n // No dependency list, and declared before every effect that measures: a\n // getter's identity changes each render and the element it returns can appear\n // at any commit. The setState cannot chain, because what it stores is stable\n // across commits and it bails out on an equal one.\n // oxlint-disable-next-line react-hooks/exhaustive-deps\n useLayoutEffect(() => {\n let next: Scroller | null;\n if (scrollParent === 'nearest') {\n // Walking the ancestors reads computed style and so forces a style recalc:\n // resolve once, and again only if what it found has left the DOM.\n const cached = nearestRef.current;\n if (!cached || (!isWindowScroller(cached) && !cached.isConnected))\n nearestRef.current = rootRef.current ? (getScrollParent(rootRef.current) ?? window) : null;\n next = nearestRef.current;\n } else {\n next = resolveScroller(scrollParent);\n }\n scrollerRef.current = next;\n setScroller((prev) => (prev === next ? prev : next));\n });\n\n // One ResizeObserver shared by every row, created once via a lazy state\n // initializer so rows mounting in the same commit can register in their\n // layout effect. Batching all entries into a single setState avoids a\n // re-render per row when many rows enter the viewport at once (the main\n // source of scroll jank). The row index is read back from `data-index`.\n const [rowObserver] = useState<ResizeObserver | null>(() => {\n if (typeof ResizeObserver === 'undefined') return null;\n return new ResizeObserver((entries) => {\n setHeightsMap((prev) => {\n let next: Map<string | number, number> | null = null;\n for (const entry of entries) {\n const el = entry.target as HTMLElement;\n const idx = Number(el.dataset.index);\n if (Number.isNaN(idx)) continue;\n // Resolve the row's stable key so the cache survives reorders/filters.\n const key = keysRef.current[idx];\n if (key === undefined) continue;\n // Prefer the size the observer already computed — reading offsetHeight\n // here would force a synchronous reflow on every measurement batch.\n const box = entry.borderBoxSize?.[0];\n const h = box ? Math.round(box.blockSize) : el.offsetHeight;\n if (prev.get(key) === h) continue;\n if (!next) next = new Map(prev);\n next.set(key, h);\n }\n return next ?? prev;\n });\n });\n });\n useEffect(() => () => rowObserver?.disconnect(), [rowObserver]);\n\n const applyViewport = useCallback((v: Viewport) => {\n const top = Math.max(0, v.rel);\n setScrollTop((prev) => (prev === top ? prev : top));\n setContainerHeight((prev) => (prev === v.visibleHeight ? prev : v.visibleHeight));\n setViewportHeight((prev) => (prev === v.viewportHeight ? prev : v.viewportHeight));\n setContainerWidth((prev) => (prev === v.width ? prev : v.width));\n }, []);\n\n const syncViewport = useCallback(() => {\n const el = rootRef.current;\n if (!el) return;\n applyViewport(measureViewport(el, scrollerRef.current));\n }, [applyViewport]);\n\n const getVisibleSlice = useCallback((): VisibleSlice => {\n const el = rootRef.current;\n if (!el) return { top: 0, height: 0 };\n const v = measureViewport(el, scrollerRef.current);\n return { top: Math.max(0, v.rel), height: v.visibleHeight };\n }, []);\n\n const getVisibleRect = useCallback((): ListViewportRect | null => {\n const el = rootRef.current;\n if (!el) return null;\n const rect = el.getBoundingClientRect();\n const scrollerNow = scrollerRef.current;\n if (!scrollerNow) return { top: rect.top, bottom: rect.bottom, left: rect.left, right: rect.right };\n const { top, height } = scrollportBounds(scrollerNow);\n return {\n top: Math.max(rect.top, top),\n bottom: Math.min(rect.bottom, top + height),\n left: rect.left,\n right: rect.right,\n };\n }, []);\n\n const scrollBy = useCallback((dy: number) => {\n const el = rootRef.current;\n if (!el) return;\n const scrollerNow = scrollerRef.current;\n if (!scrollerNow) el.scrollTop += dy;\n else if (isWindowScroller(scrollerNow)) scrollerNow.scrollBy(0, dy);\n else scrollerNow.scrollTop += dy;\n }, []);\n\n useLayoutEffect(() => {\n const el = rootRef.current;\n if (!el) return;\n // Measure synchronously before the first paint so the initial render fills\n // the viewport, instead of painting a few overscan rows and re-rendering.\n syncViewport();\n const observer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver(() => syncViewport());\n // Track width too: `fit` columns are measured against this box and capped at\n // its width, so when the container settles to its real width after a route\n // transition / async layout, that change has to re-trigger the measure —\n // otherwise an early measure against a 0/tiny width gets pinned and sticks.\n observer?.observe(el);\n if (!scroller) return () => observer?.disconnect();\n // Where the list sits inside its scrollport is half of every measurement,\n // and an element that merely *moves* fires nothing. The scrollport's own\n // content box catches the common cause, something above the list growing;\n // `remeasure()` is there for the rest.\n observer?.observe(isWindowScroller(scroller) ? document.documentElement : scroller);\n window.addEventListener('resize', syncViewport);\n return () => {\n observer?.disconnect();\n window.removeEventListener('resize', syncViewport);\n };\n }, [scroller, syncViewport]);\n\n // Read through a ref so the trigger below stays stable even when the consumer\n // passes a fresh `onLoadMore` closure on every render.\n const onLoadMoreRef = useRef(onLoadMore);\n useLayoutEffect(() => {\n onLoadMoreRef.current = onLoadMore;\n });\n\n /** Start a load unless one is already in flight (and drive the bottom spinner). */\n const triggerLoadMore = useCallback(() => {\n const load = onLoadMoreRef.current;\n if (!load || isLoadingMoreRef.current) return;\n isLoadingMoreRef.current = true;\n setShowLoadMoreSpinner(true);\n load().finally(() => {\n isLoadingMoreRef.current = false;\n setShowLoadMoreSpinner(false);\n });\n }, []);\n\n // One ask per batch, the same probe the fill check below uses. The end of the\n // list staying in view keeps qualifying, and in `scrollParent` mode it does so\n // for the whole rest of the page.\n const loadMoreProbeRef = useRef(-1);\n const handleScroll = useCallback(() => {\n const el = rootRef.current;\n if (!el) return;\n // Only the load-more check needs its answer now; the render measures again in\n // the frame, so a scroll-anchoring correction landing in between is not undone\n // by a reading taken before it.\n const v = measureViewport(el, scrollerRef.current);\n // Coalesce scroll-driven re-renders to at most one per animation frame.\n // Scroll events can fire several times per frame (especially while dragging\n // the scrollbar thumb); rendering the visible window once per frame keeps\n // the content in step with the cursor instead of lagging behind.\n if (scrollRafRef.current == null) {\n scrollRafRef.current = requestAnimationFrame(() => {\n scrollRafRef.current = null;\n syncViewport();\n });\n }\n\n if (v.distanceToEnd <= loadMoreThreshold && loadMoreProbeRef.current !== keysRef.current.length) {\n loadMoreProbeRef.current = keysRef.current.length;\n triggerLoadMore();\n }\n }, [syncViewport, loadMoreThreshold, triggerLoadMore]);\n\n // Imperative rather than an `onScroll` prop, because with `scrollParent` the\n // events happen on an element the list does not render. Doing it the same way\n // in both modes also leaves the root's `onScroll` free for the consumer.\n useEffect(() => {\n const target: EventTarget | null = scroller ?? rootRef.current;\n if (!target) return;\n target.addEventListener('scroll', handleScroll, { passive: true });\n return () => target.removeEventListener('scroll', handleScroll);\n }, [scroller, handleScroll]);\n\n useEffect(\n () => () => {\n if (scrollRafRef.current != null) cancelAnimationFrame(scrollRafRef.current);\n },\n [],\n );\n\n useLayoutEffect(() => {\n keysRef.current = keys;\n });\n\n // Resolve the gap to px before paint, re-running when the value or the\n // container's layout changes (a theme/font change that alters em/var is caught\n // by the container ResizeObserver feeding containerWidth/Height).\n useLayoutEffect(() => {\n const px = resolveCssLength(rowGap, rootRef.current);\n setResolvedRowGap((prev) => (prev === px ? prev : px));\n }, [rowGap, containerWidth, containerHeight]);\n\n // What an unmeasured row is assumed to be. `estimatedRowHeight` is only the\n // seed: once rows have been measured, their average is a far better guess, and\n // it is the guess every row between here and a distant jump target is made of.\n // With a fixed estimate the error compounds over that distance (40 assumed\n // against 35 real puts row 75 at row 86), and no amount of re-aiming fixes it,\n // because the rows that are wrong are precisely the ones never rendered.\n const assumedRowHeight = useMemo(() => {\n if (heightsMap.size === 0) return estimatedRowHeight;\n let total = 0;\n for (const height of heightsMap.values()) total += height;\n return total / heightsMap.size;\n }, [heightsMap, estimatedRowHeight]);\n\n const { offsets, totalHeight } = useMemo(\n () => buildOffsets(keys, heightsMap, assumedRowHeight, resolvedRowGap),\n [keys, heightsMap, assumedRowHeight, resolvedRowGap],\n );\n\n // The scroll trigger above can only fire on a list that actually scrolls. When\n // a batch is smaller than the viewport there is no overflow, so no scroll event\n // ever happens and lazy loading dead-ends after the first batch. Keep asking\n // for more until the content overflows; the consumer ends the loop by appending\n // nothing (or by dropping `onLoadMore` once its source is exhausted).\n //\n // Each (row count, viewport height) pair is probed at most once, so rows\n // settling to their measured heights can't re-ask a source that already ran\n // dry, and progress depends entirely on the consumer appending rows.\n //\n // The bar to clear is the whole scrollport, not the slice the list occupies\n // now: a windowed list scrolling in from the bottom of a page would otherwise\n // stop filling at whatever happened to show.\n const fillProbeRef = useRef('');\n const fillHeight = isWindowed ? viewportHeight : containerHeight;\n useEffect(() => {\n if (!onLoadMore || isLoading || showLoadMoreSpinner) return;\n // 0 while the list is unmeasured or `display: none` — nothing to fill yet.\n if (fillHeight === 0) return;\n // The sticky header eats into the scroll area, so it isn't usable space.\n const headerH = headerRef.current?.offsetHeight ?? 0;\n if (totalHeight >= fillHeight - headerH) return; // already scrollable\n const probe = `${items.length}:${fillHeight}`;\n if (fillProbeRef.current === probe) return;\n fillProbeRef.current = probe;\n triggerLoadMore();\n }, [onLoadMore, isLoading, showLoadMoreSpinner, items.length, totalHeight, fillHeight, triggerLoadMore]);\n\n /** Put list-space offset `top` at the top of the scrollport, whoever owns it. */\n const scrollToOffset = useCallback((top: number, behavior?: ScrollBehavior) => {\n const el = rootRef.current;\n if (!el) return;\n const scrollerNow = scrollerRef.current;\n if (!scrollerNow) {\n const clamped = Math.max(0, top);\n // Assignment rather than `scrollTo` when no behaviour was asked for: it is\n // what the anchoring correction needs, and the two are the same for `auto`.\n if (behavior && typeof el.scrollTo === 'function') el.scrollTo({ top: clamped, behavior });\n else el.scrollTop = clamped;\n return;\n }\n // List space is relative, so the move is a delta on where the scrollport is\n // now, and `rel` is unclamped here: a list still below it has to be reachable\n // from above as well as from within.\n const { rel } = measureViewport(el, scrollerNow);\n if (isWindowScroller(scrollerNow)) {\n scrollerNow.scrollTo({ top: Math.max(0, scrollerNow.scrollY + top - rel), behavior: behavior ?? 'auto' });\n return;\n }\n const target = Math.max(0, scrollerNow.scrollTop + top - rel);\n if (behavior && typeof scrollerNow.scrollTo === 'function') scrollerNow.scrollTo({ top: target, behavior });\n else scrollerNow.scrollTop = target;\n }, []);\n\n useLayoutEffect(() => {\n const el = rootRef.current;\n const prevOffsets = prevOffsetsRef.current;\n prevOffsetsRef.current = offsets;\n\n // Nothing to anchor against on the first pass or when pinned at the top.\n if (!el || prevOffsets.length === 0) return;\n // A jump in flight is aiming at a row; anchoring would hold the row it\n // happens to be passing instead, and the two would fight to a standstill.\n if (pendingJumpRef.current) return;\n const curScrollTop = getVisibleSlice().top;\n if (curScrollTop <= 0) return;\n\n // Scroll anchoring: pin the row at the top of the viewport. Find it under\n // the *previous* offsets, then re-derive scrollTop from its *new* offset so\n // it stays visually fixed when rows above it are measured or resized. This\n // is more robust than summing per-row height deltas — it can't drift, and\n // one correction covers any number of simultaneous changes above the fold.\n const prevCount = prevOffsets.length - 1;\n const anchor = findStartIndex(prevOffsets, curScrollTop, prevCount);\n if (anchor >= items.length) return;\n const within = curScrollTop - prevOffsets[anchor];\n const target = offsets[anchor] + within;\n if (Math.abs(target - curScrollTop) > 0.5) {\n scrollToOffset(target);\n setScrollTop(target);\n }\n }, [offsets, items.length, getVisibleSlice, scrollToOffset]);\n\n /** Scroll position that puts row `index` where `align` asks for, or null if there is none. */\n const computeScrollTop = useCallback(\n (index: number, options?: VirtualListScrollOptions): number | null => {\n const el = rootRef.current;\n if (!el || items.length === 0) return null;\n const i = Math.max(0, Math.min(index, items.length - 1));\n const rowTop = offsets[i] ?? 0;\n const rowHeight = (offsets[i + 1] ?? rowTop) - rowTop;\n // Row offsets are measured from the top of the body, which itself starts\n // below the sticky header: the header is in flow, so scrolling by exactly\n // `rowTop` already puts the row flush under it. Subtracting the header\n // height on top of that, as this once did, left the row one header lower,\n // which in a table whose rows are about as tall as its header reads as\n // landing a row short.\n const headerHeight = headerRef.current?.offsetHeight ?? 0;\n // The whole scrollport, not the slice shown now: the jump is aimed at where\n // the row lands after the scroll, by which point the list covers it.\n const usable = Math.max(0, measureViewport(el, scrollerRef.current).viewportHeight - headerHeight);\n const align = options?.align ?? 'start';\n let top = rowTop;\n if (align === 'center') top -= (usable - rowHeight) / 2;\n else if (align === 'end') top -= usable - rowHeight;\n return top;\n },\n [items.length, offsets],\n );\n\n // A jump lands on the wrong row while the rows between here and there have\n // never been rendered: their offsets are `estimatedRowHeight` guesses, and the\n // error compounds over the distance (40px guessed against 35px real puts row\n // 75 at row 86). Scrolling there renders those rows, measuring corrects the\n // offsets, and the effect below re-aims at the target until it stops moving.\n // This is why the same jump is accurate once the list has been scrolled\n // through: by then every row in between carries a measured height.\n const pendingJumpRef = useRef<{\n index: number;\n options?: VirtualListScrollOptions;\n lastTarget: number;\n attempts: number;\n items: T[];\n } | null>(null);\n\n // Deliberately without a dependency list: every commit is a chance that new\n // measurements have moved the target, including the ones that come from the\n // scroll position itself changing mid-animation.\n useLayoutEffect(() => {\n const pending = pendingJumpRef.current;\n const el = rootRef.current;\n if (!pending) return;\n const target = pending.items === items && el ? computeScrollTop(pending.index, pending.options) : null;\n if (target == null) {\n pendingJumpRef.current = null; // new data supersedes an in-flight jump\n return;\n }\n const isStable = Math.abs(target - pending.lastTarget) <= 1;\n // Only once it has both stopped moving and been reached: with a smooth\n // behaviour the target can sit still for several frames while the animation\n // is still on its way there, and giving up then would leave it short.\n if ((isStable && Math.abs(getVisibleSlice().top - target) <= 1) || pending.attempts >= MAX_JUMP_CORRECTIONS) {\n pendingJumpRef.current = null;\n return;\n }\n if (isStable) return;\n pending.lastTarget = target;\n pending.attempts += 1;\n scrollToOffset(target, pending.options?.behavior);\n });\n\n // Any input of the user's own outranks a jump still in flight: being pulled\n // back to a row you just scrolled away from is worse than not arriving.\n useEffect(() => {\n const el = rootRef.current;\n if (!el) return;\n const cancel = () => {\n pendingJumpRef.current = null;\n };\n // The wheel and the finger belong to whatever scrolls; the keys belong to\n // the list, and hijacking them page-wide would cancel on any typing.\n const pointerTarget: EventTarget = scroller ?? el;\n pointerTarget.addEventListener('wheel', cancel, { passive: true });\n pointerTarget.addEventListener('touchstart', cancel, { passive: true });\n el.addEventListener('keydown', cancel);\n return () => {\n pointerTarget.removeEventListener('wheel', cancel);\n pointerTarget.removeEventListener('touchstart', cancel);\n el.removeEventListener('keydown', cancel);\n };\n }, [scroller]);\n\n const scrollApi = useMemo<Omit<VirtualListHandle, 'recalculateColumns' | 'resetColumnWidths'>>(() => {\n function scrollToIndex(index: number, options?: VirtualListScrollOptions) {\n const target = computeScrollTop(index, options);\n if (target == null) return;\n pendingJumpRef.current = { index, options, lastTarget: target, attempts: 0, items };\n scrollToOffset(target, options?.behavior);\n }\n return {\n scrollToIndex,\n scrollToKey(key, options) {\n const i = items.findIndex((item, idx) => (getItemKey ? getItemKey(item, idx) : idx) === key);\n if (i >= 0) scrollToIndex(i, options);\n },\n scrollToTop(options) {\n pendingJumpRef.current = null;\n scrollToOffset(0, options?.behavior);\n },\n getScrollElement: () => {\n const scrollerNow = scrollerRef.current;\n if (!scrollerNow) return rootRef.current;\n return isWindowScroller(scrollerNow) ? null : scrollerNow;\n },\n remeasure: syncViewport,\n };\n }, [items, getItemKey, computeScrollTop, scrollToOffset, syncViewport]);\n\n const { startIndex, endIndex } = useMemo(\n () => getVisibleRange(offsets, scrollTop, containerHeight, overscan, items.length),\n [offsets, scrollTop, containerHeight, overscan, items.length],\n );\n\n // Virtualizing nothing fails silently: the page is simply slow, which points\n // nowhere near the cause. That cause is almost always a viewport as tall as its\n // content, which is what a percentage height amounts to inside a parent with no\n // definite height of its own.\n const warnedRef = useRef(false);\n useEffect(() => {\n if (warnedRef.current || containerHeight === 0) return;\n if (items.length < NOT_VIRTUALIZING_ROWS || endIndex - startIndex + 1 < items.length) return;\n warnedRef.current = true;\n console.warn(\n `VirtualList: rendering all ${items.length} rows, so nothing is being virtualized. Its viewport is as tall as its content. Give the list a definite height, or pass scrollParent to have it ride a scrollport it does not own.`,\n );\n }, [containerHeight, items.length, startIndex, endIndex]);\n\n return {\n rootRef,\n headerRef,\n rowObserver,\n keysRef,\n offsets,\n totalHeight,\n containerHeight,\n viewportHeight,\n containerWidth,\n startIndex,\n endIndex,\n showLoadMoreSpinner,\n isWindowed,\n getVisibleSlice,\n getVisibleRect,\n scrollBy,\n scrollApi,\n };\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";import{useLabels as e}from"../configProvider/useLabels.mjs";import t from"../loading/spinnerLoading/index.mjs";import n from"../checkbox/checkbox.mjs";import r from"./virtualList.module.mjs";import"./virtualList.utils.mjs";import ee from"./virtualRow.mjs";import i from"./virtualListHeader.mjs";import{useVirtualWindow as a}from"./useVirtualWindow.mjs";import{useSelection as o}from"./useSelection.mjs";import{useColumns as te}from"./useColumns.mjs";import{useRowDrag as ne}from"./useRowDrag.mjs";import{useSorting as re}from"./useSorting.mjs";import{useColumnResize as ie}from"./useColumnResize.mjs";import{useRowNavigation as ae}from"./useRowNavigation.mjs";import{useCallback as s,useEffect as oe,useId as se,useImperativeHandle as ce,useMemo as c,useRef as le}from"react";import l from"classnames";import{Fragment as ue,jsx as u,jsxs as d}from"react/jsx-runtime";var de={selectRow:`Select row`,selectAllRows:`Select all rows`,toggleColumns:`Toggle column visibility`,columnsHidden:e=>`${e} columns hidden`,resetColumnWidths:`Reset column widths`};function f({ref:f,items:fe,renderRow:pe,ariaRoles:p,columns:m,height:me=`100%`,estimatedRowHeight:he=40,overscan:ge=3,rowGap:_e=0,rowPadding:h,selectedKey:g,getItemKey:_,onRowClick:v,keyboardNavigation:y=!0,visibleColumnKeys:ve,onVisibleColumnsChange:ye,persistColumnsKey:b,resizableColumns:be=!1,columnWidths:xe,onColumnWidthsChange:Se,labels:Ce,sort:we,defaultSort:Te,onSortChange:Ee,showColumnToggle:De,onLoadMore:Oe,loadMoreThreshold:ke=100,isLoading:x=!1,multiSelect:S=!1,selectedKeys:Ae,onSelectionChange:je,reorderable:C=!1,onReorder:Me,longPressDelay:Ne=400,treeReorder:w=!1,getItemDepth:T,isGroup:E,onTreeDrop:Pe,treeIndentPx:Fe=24,dragHandle:Ie=!1,showDivider:Le=!0,rowHover:Re=!0,className:ze,style:Be,classNames:D,styles:O,...Ve}){let k=e(`VirtualList`,Ce,de),{rows:A,effectiveSort:He,isSorted:j,handleHeaderSort:Ue}=re({items:fe,columns:m,sort:we,defaultSort:Te,onSortChange:Ee}),M=s((e,t)=>_?_(e,t):t,[_]),N=c(()=>A.map((e,t)=>M(e,t)),[A,M]),We=c(()=>T?A.map((e,t)=>T(e,t)):[],[A,T]),Ge=s(e=>E?E(A[e],e):!1,[E,A]),P=a({items:A,keys:N,getItemKey:_,estimatedRowHeight:he,rowGap:_e,overscan:ge,onLoadMore:Oe,loadMoreThreshold:ke,isLoading:x}),{rootRef:F,headerRef:I,rowObserver:Ke,keysRef:qe,offsets:L,totalHeight:Je,startIndex:R,endIndex:z}=P,B=le([]),V=ie({columns:m,visibleColumnsRef:B,resizableColumns:be,columnWidths:xe,onColumnWidthsChange:Se,persistColumnsKey:b,rootRef:F}),H=o({items:A,getKey:M,multiSelect:S,selectedKeys:Ae,onSelectionChange:je}),{effectiveSelectedKeys:U}=H,W=te({columns:m,items:A,multiSelect:S,visibleColumnKeys:ve,onVisibleColumnsChange:ye,persistColumnsKey:b,showColumnToggle:De,userWidths:V.widths,isFrozen:V.isFrozen,rootRef:F,startIndex:R,endIndex:z,containerHeight:P.containerHeight,containerWidth:P.containerWidth}),{hasColumns:G,visibleColumns:K,columnTemplate:Ye,widthVars:Xe,shouldShowToggle:Ze,recalculateColumns:q}=W;B.current=K,ce(f,()=>({...P.scrollApi,recalculateColumns:q,resetColumnWidths:V.reset}),[P.scrollApi,q,V.reset]);let Qe=j&&(C||w);oe(()=>{Qe&&console.warn(`VirtualList: row reordering is disabled while the list is sorted. Clear the sort to drag rows.`)},[Qe]);let J=ne({reorderable:C&&!j,treeReorder:w&&!j,onReorder:Me,onTreeDrop:Pe,longPressDelay:Ne,dragHandle:Ie,treeIndentPx:Fe,keys:N,depths:We,isGroupAt:Ge,rootRef:F,keysRef:qe}),$e=se(),Y=e=>`${$e}-row-${e}`,X=ae({enabled:y,rowCount:A.length,offsets:L,rootRef:F,headerRef:I,scrollToIndex:P.scrollApi.scrollToIndex,initialIndex:g==null?0:Math.max(0,N.indexOf(g)),multiSelect:S,onActivate:v&&(e=>v(A[e],e)),onToggleSelect:e=>H.handleRowSelect(N[e])}),et=typeof h==`number`?`${h}px`:h,Z=e=>{let t=A[e],i=M(t,e),a=g!=null&&i===g||U.has(i),o=J.getRowDragProps(e,i);return u(ee,{id:Y(e),index:e,top:L[e],padding:et,columnTemplate:Ye,isSelected:a,isActive:e===X.activeIndex,manageTabStops:y,isEntered:e===X.enteredIndex,activeClassName:D?.activeRow,activeStyle:O?.activeRow,showDivider:Le,hover:Re,onClick:()=>{J.consumeSuppressedClick()||(X.setActiveIndex(e),v?.(t,e))},observer:Ke,ariaRowIndex:G?e+2:void 0,ariaPosInSet:G?void 0:e+1,ariaSetSize:G?void 0:A.length,rowRole:p?.row??(G?`row`:`listitem`),reserveToggleGutter:Ze,...o,dropIndicatorClassName:D?.dropIndicator,dropIndicatorStyle:O?.dropIndicator,rowClassName:D?.row,rowStyle:O?.row,children:G?d(ue,{children:[S&&u(`div`,{className:l(r.virtualListSelectCell,D?.selectCell),style:O?.selectCell,role:`gridcell`,onClick:e=>e.stopPropagation(),children:u(n,{selected:U.has(i),onToggle:()=>H.handleRowSelect(i),"aria-label":k.selectRow,tabIndex:-1})}),K.map(n=>u(`div`,{className:l(r.virtualListCell,D?.cell),style:O?.cell,role:`gridcell`,"data-col-key":n.key,"data-fit-key":n.width.type===`fit`?n.key:void 0,children:n.renderCell(t,e)},n.key))]}):pe?.(t,e)},i)},Q=[];for(let e=R;e<=z;e++)Q.push(Z(e));let $=J.draggingKey==null?-1:N.indexOf(J.draggingKey);return $>=0&&($<R||$>z)&&Q.push(Z($)),X.enteredIndex>=0&&(X.enteredIndex<R||X.enteredIndex>z)&&Q.push(Z(X.enteredIndex)),d(`div`,{...Ve,...X.containerProps,ref:F,className:l(r.virtualList,J.draggingKey!=null&&r.virtualListReordering,V.isResizing&&r.virtualListResizing,ze,D?.root),style:{height:me,...Xe,...Be,...O?.root},onScroll:P.handleScroll,role:p?.container??(G?`grid`:`list`),"aria-rowcount":G?A.length+1:void 0,"aria-activedescendant":X.activeIndex>=0&&(p?.container??(G?`grid`:`list`))!==`list`?Y(X.activeIndex):void 0,children:[G&&u(i,{headerRef:I,columns:m,cols:W,resize:V,sort:He,onSort:Ue,multiSelect:S,allSelected:H.allSelected,someSelected:H.someSelected,onSelectAll:H.handleSelectAll,labels:k,classNames:D,styles:O}),x?u(`div`,{className:r.virtualListLoadingFull,children:u(t,{className:r.virtualListSpinner})}):u(`div`,{className:l(r.virtualListBody,D?.body),style:{height:Je,...O?.body},children:Q}),P.showLoadMoreSpinner&&u(`div`,{className:l(r.virtualListLoadingIndicator,D?.loadingIndicator),style:O?.loadingIndicator,children:u(t,{className:r.virtualListSpinner})})]})}export{f as default};
1
+ "use client";import{useLabels as e}from"../configProvider/useLabels.mjs";import t from"../loading/spinnerLoading/index.mjs";import n from"../checkbox/checkbox.mjs";import r from"./virtualList.module.mjs";import"./virtualList.utils.mjs";import ee from"./virtualRow.mjs";import i from"./virtualListHeader.mjs";import{useVirtualWindow as a}from"./useVirtualWindow.mjs";import{useSelection as o}from"./useSelection.mjs";import{useColumns as te}from"./useColumns.mjs";import{useRowDrag as ne}from"./useRowDrag.mjs";import{useSorting as re}from"./useSorting.mjs";import{useColumnResize as ie}from"./useColumnResize.mjs";import{useRowNavigation as ae}from"./useRowNavigation.mjs";import{useCallback as oe,useEffect as s,useId as se,useImperativeHandle as ce,useMemo as c,useRef as le}from"react";import l from"classnames";import{Fragment as ue,jsx as u,jsxs as d}from"react/jsx-runtime";var de={selectRow:`Select row`,selectAllRows:`Select all rows`,toggleColumns:`Toggle column visibility`,columnsHidden:e=>`${e} columns hidden`,resetColumnWidths:`Reset column widths`};function f(e,t){return!e||!t?e??t:n=>{e(n),t(n)}}function p(p){let{ref:fe,items:pe,renderRow:me,ariaRoles:m,columns:h,height:he=`100%`,estimatedRowHeight:ge=40,overscan:_e=3,rowGap:ve=0,rowPadding:g,selectedKey:_,getItemKey:v,onRowClick:y,keyboardNavigation:b=!0,tabAcrossRows:ye=!1,scrollParent:be,visibleColumnKeys:xe,onVisibleColumnsChange:Se,persistColumnsKey:x,resizableColumns:Ce=!1,columnWidths:we,onColumnWidthsChange:Te,labels:Ee,sort:De,defaultSort:Oe,onSortChange:ke,showColumnToggle:Ae,onLoadMore:je,loadMoreThreshold:Me=100,isLoading:S=!1,multiSelect:C=!1,selectedKeys:Ne,onSelectionChange:Pe,reorderable:w=!1,onReorder:Fe,longPressDelay:Ie=400,treeReorder:T=!1,getItemDepth:E,isGroup:D,onTreeDrop:Le,treeIndentPx:Re=24,dragHandle:ze=!1,showDivider:Be=!0,rowHover:Ve=!0,className:He,style:Ue,classNames:O,styles:k,...A}=p,j=p.scrollParent!=null&&p.height!==void 0;s(()=>{j&&console.warn("VirtualList: `height` is ignored alongside `scrollParent`, which leaves the list no viewport to size.")},[j]);let M=e(`VirtualList`,Ee,de),{rows:N,effectiveSort:We,isSorted:P,handleHeaderSort:Ge}=re({items:pe,columns:h,sort:De,defaultSort:Oe,onSortChange:ke}),F=oe((e,t)=>v?v(e,t):t,[v]),I=c(()=>N.map((e,t)=>F(e,t)),[N,F]),Ke=c(()=>E?N.map((e,t)=>E(e,t)):[],[N,E]),qe=oe(e=>D?D(N[e],e):!1,[D,N]),L=a({items:N,keys:I,getItemKey:v,estimatedRowHeight:ge,rowGap:ve,overscan:_e,onLoadMore:je,loadMoreThreshold:Me,isLoading:S,scrollParent:be}),{rootRef:R,headerRef:z,rowObserver:Je,keysRef:Ye,offsets:B,totalHeight:Xe,startIndex:V,endIndex:H}=L,U=le([]),W=ie({columns:h,visibleColumnsRef:U,resizableColumns:Ce,columnWidths:we,onColumnWidthsChange:Te,persistColumnsKey:x,rootRef:R}),G=o({items:N,getKey:F,multiSelect:C,selectedKeys:Ne,onSelectionChange:Pe}),{effectiveSelectedKeys:K}=G,q=te({columns:h,items:N,multiSelect:C,visibleColumnKeys:xe,onVisibleColumnsChange:Se,persistColumnsKey:x,showColumnToggle:Ae,userWidths:W.widths,isFrozen:W.isFrozen,rootRef:R,startIndex:V,endIndex:H,containerHeight:L.containerHeight,containerWidth:L.containerWidth}),{hasColumns:J,visibleColumns:Ze,columnTemplate:Qe,widthVars:$e,shouldShowToggle:et,recalculateColumns:tt}=q;U.current=Ze,ce(fe,()=>({...L.scrollApi,recalculateColumns:tt,resetColumnWidths:W.reset}),[L.scrollApi,tt,W.reset]);let nt=P&&(w||T);s(()=>{nt&&console.warn(`VirtualList: row reordering is disabled while the list is sorted. Clear the sort to drag rows.`)},[nt]);let Y=ne({reorderable:w&&!P,treeReorder:T&&!P,onReorder:Fe,onTreeDrop:Le,longPressDelay:Ie,dragHandle:ze,treeIndentPx:Re,keys:I,depths:Ke,isGroupAt:qe,rootRef:R,keysRef:Ye,getVisibleRect:L.getVisibleRect,scrollBy:L.scrollBy}),rt=se(),it=e=>`${rt}-row-${e}`,X=ae({enabled:b,tabAcrossRows:ye,rowCount:N.length,offsets:B,rootRef:R,headerRef:z,getVisibleSlice:L.getVisibleSlice,viewportHeight:L.viewportHeight,scrollToIndex:L.scrollApi.scrollToIndex,initialIndex:_==null?0:Math.max(0,I.indexOf(_)),multiSelect:C,onActivate:y&&(e=>y(N[e],e)),onToggleSelect:e=>G.handleRowSelect(I[e])}),at=typeof g==`number`?`${g}px`:g,Z=e=>{let t=N[e],i=F(t,e),a=_!=null&&i===_||K.has(i),o=Y.getRowDragProps(e,i);return u(ee,{id:it(e),index:e,top:B[e],padding:at,columnTemplate:Qe,isSelected:a,isActive:e===X.activeIndex,manageTabStops:b,isEntered:e===X.enteredIndex,activeClassName:O?.activeRow,activeStyle:k?.activeRow,showDivider:Be,hover:Ve,onClick:()=>{Y.consumeSuppressedClick()||(X.setActiveIndex(e),y?.(t,e))},observer:Je,ariaRowIndex:J?e+2:void 0,ariaPosInSet:J?void 0:e+1,ariaSetSize:J?void 0:N.length,rowRole:m?.row??(J?`row`:`listitem`),reserveToggleGutter:et,...o,dropIndicatorClassName:O?.dropIndicator,dropIndicatorStyle:k?.dropIndicator,rowClassName:O?.row,rowStyle:k?.row,children:J?d(ue,{children:[C&&u(`div`,{className:l(r.virtualListSelectCell,O?.selectCell),style:k?.selectCell,role:`gridcell`,onClick:e=>e.stopPropagation(),children:u(n,{selected:K.has(i),onToggle:()=>G.handleRowSelect(i),"aria-label":M.selectRow,tabIndex:-1})}),Ze.map(n=>u(`div`,{className:l(r.virtualListCell,O?.cell),style:k?.cell,role:`gridcell`,"data-col-key":n.key,"data-fit-key":n.width.type===`fit`?n.key:void 0,children:n.renderCell(t,e)},n.key))]}):me?.(t,e)},i)},Q=[];for(let e=V;e<=H;e++)Q.push(Z(e));let $=Y.draggingKey==null?-1:I.indexOf(Y.draggingKey);$>=0&&($<V||$>H)&&Q.push(Z($)),X.enteredIndex>=0&&(X.enteredIndex<V||X.enteredIndex>H)&&Q.push(Z(X.enteredIndex));let ot={...X.containerProps,onKeyDown:f(A.onKeyDown,X.containerProps.onKeyDown),onFocus:f(A.onFocus,X.containerProps.onFocus),onBlur:f(A.onBlur,X.containerProps.onBlur)};return d(`div`,{...A,...ot,ref:R,className:l(r.virtualList,L.isWindowed&&r.virtualListWindowed,Y.draggingKey!=null&&r.virtualListReordering,W.isResizing&&r.virtualListResizing,He,O?.root),style:{...L.isWindowed?null:{height:he},...$e,...Ue,...k?.root},role:m?.container??(J?`grid`:`list`),"aria-rowcount":J?N.length+1:void 0,"aria-activedescendant":X.activeIndex>=0&&(m?.container??(J?`grid`:`list`))!==`list`?it(X.activeIndex):void 0,children:[J&&u(i,{headerRef:z,columns:h,cols:q,resize:W,sort:We,onSort:Ge,multiSelect:C,allSelected:G.allSelected,someSelected:G.someSelected,onSelectAll:G.handleSelectAll,labels:M,classNames:O,styles:k}),S?u(`div`,{className:r.virtualListLoadingFull,children:u(t,{className:r.virtualListSpinner})}):u(`div`,{className:l(r.virtualListBody,O?.body),style:{height:Xe,...k?.body},children:Q}),L.showLoadMoreSpinner&&u(`div`,{className:l(r.virtualListLoadingIndicator,O?.loadingIndicator),style:k?.loadingIndicator,children:u(t,{className:r.virtualListSpinner})})]})}export{p as default};
2
2
  //# sourceMappingURL=virtualList.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"virtualList.mjs","names":[],"sources":["../../../../package/common/virtualList/virtualList.tsx"],"sourcesContent":["import React, { useMemo, useCallback, useEffect, useId, useImperativeHandle, useRef } from 'react';\nimport cx from 'classnames';\nimport styles from './virtualList.module.pcss';\nimport SpinnerLoading from '../loading/spinnerLoading';\nimport Checkbox from '../checkbox';\nimport VirtualRow from './virtualRow';\nimport VirtualListHeader from './virtualListHeader';\nimport { useVirtualWindow } from './useVirtualWindow';\nimport { useSelection } from './useSelection';\nimport { useColumns } from './useColumns';\nimport { useRowDrag } from './useRowDrag';\nimport { useSorting } from './useSorting';\nimport { useColumnResize } from './useColumnResize';\nimport { useRowNavigation } from './useRowNavigation';\nimport { useLabels } from '../configProvider';\nimport type { VirtualListColumn, VirtualListLabels, VirtualListProps } from './virtualList.types';\n\nconst DEFAULT_LABELS: Required<VirtualListLabels> = {\n selectRow: 'Select row',\n selectAllRows: 'Select all rows',\n toggleColumns: 'Toggle column visibility',\n columnsHidden: (count) => `${count} columns hidden`,\n resetColumnWidths: 'Reset column widths',\n};\n\n// Pure helpers live in ./virtualList.utils; re-exported here so existing\n// imports (and unit tests) keep working against the component entry point.\nexport {\n buildOffsets,\n findStartIndex,\n getVisibleRange,\n resolveReorderTarget,\n resolveTreeDropTarget,\n lastDescendantIndex,\n buildColumnTemplate,\n compareSortValues,\n isColumnSortable,\n isEmptySortValue,\n nextSort,\n sortItems,\n} from './virtualList.utils';\n\n// ─── Orchestrator ─────────────────────────────────────────────────────────────\n// VirtualListInner wires the hooks together and renders the two pieces of\n// markup. Each concern lives in its own module:\n// useSorting — table mode: header sort state, and the sorted row order\n// useVirtualWindow — generic scroll / measurement / windowing (all modes)\n// useColumnResize — table mode: user column widths, and the drag that sets them\n// useSelection — multi-row selection\n// useColumns — table mode: columns, visibility, persistence, fit widths\n// useRowDrag — reorder + tree drag-and-drop (mouse + touch)\n// useRowNavigation — arrow-key navigation over the rows\n// virtualListHeader — the header row: sorting, resizing, the column menu\n// virtualRow — one positioned row, with its drag wiring\n//\n// The hook order is not free: useColumnResize decides whether the layout is\n// frozen, which useColumns needs to build the grid template.\n\nfunction VirtualList<T>({\n ref,\n items,\n renderRow,\n ariaRoles,\n columns,\n height = '100%',\n estimatedRowHeight = 40,\n overscan = 3,\n rowGap = 0,\n rowPadding,\n selectedKey,\n getItemKey,\n onRowClick,\n keyboardNavigation = true,\n visibleColumnKeys,\n onVisibleColumnsChange,\n persistColumnsKey,\n resizableColumns = false,\n columnWidths,\n onColumnWidthsChange,\n labels,\n sort,\n defaultSort,\n onSortChange,\n showColumnToggle,\n onLoadMore,\n loadMoreThreshold = 100,\n isLoading = false,\n multiSelect = false,\n selectedKeys,\n onSelectionChange,\n reorderable = false,\n onReorder,\n longPressDelay = 400,\n treeReorder = false,\n getItemDepth,\n isGroup,\n onTreeDrop,\n treeIndentPx = 24,\n dragHandle = false,\n showDivider = true,\n rowHover = true,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n}: VirtualListProps<T>) {\n const text = useLabels('VirtualList', labels, DEFAULT_LABELS);\n const { rows, effectiveSort, isSorted, handleHeaderSort } = useSorting<T>({\n items,\n columns,\n sort,\n defaultSort,\n onSortChange,\n });\n\n const getKey = useCallback(\n (item: T, index: number): string | number => (getItemKey ? getItemKey(item, index) : index),\n [getItemKey],\n );\n const keys = useMemo(() => rows.map((item, i) => getKey(item, i)), [rows, getKey]);\n const depths = useMemo(\n () => (getItemDepth ? rows.map((item, i) => getItemDepth(item, i)) : []),\n [rows, getItemDepth],\n );\n const isGroupAt = useCallback((i: number) => (isGroup ? isGroup(rows[i], i) : false), [isGroup, rows]);\n\n const win = useVirtualWindow<T>({\n items: rows,\n keys,\n getItemKey,\n estimatedRowHeight,\n rowGap,\n overscan,\n onLoadMore,\n loadMoreThreshold,\n isLoading,\n });\n const { rootRef, headerRef, rowObserver, keysRef, offsets, totalHeight, startIndex, endIndex } = win;\n\n // Filled from `cols` below: the resize hook only reads the visible set when a\n // handle is actually used, which is what lets it run before the hook that\n // derives it.\n const visibleColumnsRef = useRef<VirtualListColumn<T>[]>([]);\n const columnResize = useColumnResize<T>({\n columns,\n visibleColumnsRef,\n resizableColumns,\n columnWidths,\n onColumnWidthsChange,\n persistColumnsKey,\n rootRef,\n });\n\n const selection = useSelection<T>({ items: rows, getKey, multiSelect, selectedKeys, onSelectionChange });\n const { effectiveSelectedKeys } = selection;\n\n const cols = useColumns<T>({\n columns,\n items: rows,\n multiSelect,\n visibleColumnKeys,\n onVisibleColumnsChange,\n persistColumnsKey,\n showColumnToggle,\n userWidths: columnResize.widths,\n isFrozen: columnResize.isFrozen,\n rootRef,\n startIndex,\n endIndex,\n containerHeight: win.containerHeight,\n containerWidth: win.containerWidth,\n });\n // The rest of `cols` is the header's business and goes there whole.\n const { hasColumns, visibleColumns, columnTemplate, widthVars, shouldShowToggle, recalculateColumns } = cols;\n visibleColumnsRef.current = visibleColumns;\n\n // Compose the forwarded handle from the hooks that own each piece: scroll\n // methods from the windowing engine, column controls from the column hooks.\n useImperativeHandle(ref, () => ({ ...win.scrollApi, recalculateColumns, resetColumnWidths: columnResize.reset }), [\n win.scrollApi,\n recalculateColumns,\n columnResize.reset,\n ]);\n\n // Dragging a row to position N is meaningless while the list decides the\n // order: the drop would be undone by the next sort pass.\n const dragBlockedBySort = isSorted && (reorderable || treeReorder);\n useEffect(() => {\n if (dragBlockedBySort)\n console.warn('VirtualList: row reordering is disabled while the list is sorted. Clear the sort to drag rows.');\n }, [dragBlockedBySort]);\n\n const drag = useRowDrag({\n reorderable: reorderable && !isSorted,\n treeReorder: treeReorder && !isSorted,\n onReorder,\n onTreeDrop,\n longPressDelay,\n dragHandle,\n treeIndentPx,\n keys,\n depths,\n isGroupAt,\n rootRef,\n keysRef,\n });\n\n const listId = useId();\n const rowDomId = (index: number) => `${listId}-row-${index}`;\n const nav = useRowNavigation({\n enabled: keyboardNavigation,\n rowCount: rows.length,\n offsets,\n rootRef,\n headerRef,\n scrollToIndex: win.scrollApi.scrollToIndex,\n initialIndex: selectedKey != null ? Math.max(0, keys.indexOf(selectedKey)) : 0,\n multiSelect,\n onActivate: onRowClick && ((index: number) => onRowClick(rows[index], index)),\n onToggleSelect: (index) => selection.handleRowSelect(keys[index]),\n });\n\n const rowPaddingValue = typeof rowPadding === 'number' ? `${rowPadding}px` : rowPadding;\n\n const renderRowAt = (i: number) => {\n const item = rows[i];\n const key = getKey(item, i);\n const isSelected = (selectedKey != null && key === selectedKey) || effectiveSelectedKeys.has(key);\n const dragProps = drag.getRowDragProps(i, key);\n\n return (\n <VirtualRow\n key={key}\n id={rowDomId(i)}\n index={i}\n top={offsets[i]}\n padding={rowPaddingValue}\n columnTemplate={columnTemplate}\n isSelected={isSelected}\n isActive={i === nav.activeIndex}\n manageTabStops={keyboardNavigation}\n isEntered={i === nav.enteredIndex}\n activeClassName={classNames?.activeRow}\n activeStyle={slotStyles?.activeRow}\n showDivider={showDivider}\n hover={rowHover}\n onClick={() => {\n if (drag.consumeSuppressedClick()) return;\n // Keep the arrows where the pointer last was.\n nav.setActiveIndex(i);\n onRowClick?.(item, i);\n }}\n observer={rowObserver}\n ariaRowIndex={hasColumns ? i + 2 : undefined}\n ariaPosInSet={hasColumns ? undefined : i + 1}\n ariaSetSize={hasColumns ? undefined : rows.length}\n rowRole={ariaRoles?.row ?? (hasColumns ? 'row' : 'listitem')}\n reserveToggleGutter={shouldShowToggle}\n {...dragProps}\n dropIndicatorClassName={classNames?.dropIndicator}\n dropIndicatorStyle={slotStyles?.dropIndicator}\n rowClassName={classNames?.row}\n rowStyle={slotStyles?.row}\n >\n {hasColumns ? (\n <>\n {multiSelect && (\n <div\n className={cx(styles.virtualListSelectCell, classNames?.selectCell)}\n style={slotStyles?.selectCell}\n role='gridcell'\n onClick={(e) => e.stopPropagation()}\n >\n <Checkbox\n selected={effectiveSelectedKeys.has(key)}\n onToggle={() => selection.handleRowSelect(key)}\n aria-label={text.selectRow}\n // One tab stop per rendered row, changing as the list scrolls,\n // would strand the focus the moment a row unmounts. Space on\n // the active row is the keyboard path instead.\n tabIndex={-1}\n />\n </div>\n )}\n {visibleColumns.map((col) => (\n <div\n key={col.key}\n className={cx(styles.virtualListCell, classNames?.cell)}\n style={slotStyles?.cell}\n role='gridcell'\n data-col-key={col.key}\n data-fit-key={col.width.type === 'fit' ? col.key : undefined}\n >\n {col.renderCell(item, i)}\n </div>\n ))}\n </>\n ) : (\n renderRow?.(item, i)\n )}\n </VirtualRow>\n );\n };\n\n const visibleRows: React.ReactNode[] = [];\n for (let i = startIndex; i <= endIndex; i++) visibleRows.push(renderRowAt(i));\n\n // Keep the row being touch-dragged mounted even if it scrolls out of the\n // window. Touch events are dispatched to the element under the finger at\n // touchstart; if that row unmounts, the browser stops delivering touchmove\n // and the drag freezes. Pinning its DOM node keeps the gesture alive.\n const draggedIndex = drag.draggingKey != null ? keys.indexOf(drag.draggingKey) : -1;\n if (draggedIndex >= 0 && (draggedIndex < startIndex || draggedIndex > endIndex)) {\n visibleRows.push(renderRowAt(draggedIndex));\n }\n\n // Same reason, one step further: the row being used holds the focus, and\n // unmounting it would drop that focus to the document body mid-scroll.\n if (nav.enteredIndex >= 0 && (nav.enteredIndex < startIndex || nav.enteredIndex > endIndex)) {\n visibleRows.push(renderRowAt(nav.enteredIndex));\n }\n\n return (\n <div\n {...rest}\n {...nav.containerProps}\n ref={rootRef}\n className={cx(\n styles.virtualList,\n drag.draggingKey != null && styles.virtualListReordering,\n columnResize.isResizing && styles.virtualListResizing,\n className,\n classNames?.root,\n )}\n style={{ height, ...widthVars, ...style, ...slotStyles?.root }}\n onScroll={win.handleScroll}\n role={ariaRoles?.container ?? (hasColumns ? 'grid' : 'list')}\n aria-rowcount={hasColumns ? rows.length + 1 : undefined}\n // Only a composite role may point at an active descendant, and the default\n // `list` is not one. In list mode, pass `ariaRoles` (e.g. listbox/option)\n // to have the active row announced.\n aria-activedescendant={\n nav.activeIndex >= 0 && (ariaRoles?.container ?? (hasColumns ? 'grid' : 'list')) !== 'list'\n ? rowDomId(nav.activeIndex)\n : undefined\n }\n >\n {hasColumns && (\n <VirtualListHeader<T>\n headerRef={headerRef}\n columns={columns!}\n cols={cols}\n resize={columnResize}\n sort={effectiveSort}\n onSort={handleHeaderSort}\n multiSelect={multiSelect}\n allSelected={selection.allSelected}\n someSelected={selection.someSelected}\n onSelectAll={selection.handleSelectAll}\n labels={text}\n classNames={classNames}\n styles={slotStyles}\n />\n )}\n {isLoading ? (\n <div className={styles.virtualListLoadingFull}>\n <SpinnerLoading className={styles.virtualListSpinner} />\n </div>\n ) : (\n <div\n className={cx(styles.virtualListBody, classNames?.body)}\n style={{ height: totalHeight, ...slotStyles?.body }}\n >\n {visibleRows}\n </div>\n )}\n {win.showLoadMoreSpinner && (\n <div\n className={cx(styles.virtualListLoadingIndicator, classNames?.loadingIndicator)}\n style={slotStyles?.loadingIndicator}\n >\n <SpinnerLoading className={styles.virtualListSpinner} />\n </div>\n )}\n </div>\n );\n}\n\nexport default VirtualList;\n"],"mappings":"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"}
1
+ {"version":3,"file":"virtualList.mjs","names":[],"sources":["../../../../package/common/virtualList/virtualList.tsx"],"sourcesContent":["import React, { useMemo, useCallback, useEffect, useId, useImperativeHandle, useRef } from 'react';\nimport cx from 'classnames';\nimport styles from './virtualList.module.pcss';\nimport SpinnerLoading from '../loading/spinnerLoading';\nimport Checkbox from '../checkbox';\nimport VirtualRow from './virtualRow';\nimport VirtualListHeader from './virtualListHeader';\nimport { useVirtualWindow } from './useVirtualWindow';\nimport { useSelection } from './useSelection';\nimport { useColumns } from './useColumns';\nimport { useRowDrag } from './useRowDrag';\nimport { useSorting } from './useSorting';\nimport { useColumnResize } from './useColumnResize';\nimport { useRowNavigation } from './useRowNavigation';\nimport { useLabels } from '../configProvider';\nimport type { VirtualListColumn, VirtualListLabels, VirtualListProps } from './virtualList.types';\n\nconst DEFAULT_LABELS: Required<VirtualListLabels> = {\n selectRow: 'Select row',\n selectAllRows: 'Select all rows',\n toggleColumns: 'Toggle column visibility',\n columnsHidden: (count) => `${count} columns hidden`,\n resetColumnWidths: 'Reset column widths',\n};\n\n// Pure helpers live in ./virtualList.utils; re-exported here so existing\n// imports (and unit tests) keep working against the component entry point.\nexport {\n buildOffsets,\n findStartIndex,\n getVisibleRange,\n resolveReorderTarget,\n resolveTreeDropTarget,\n lastDescendantIndex,\n buildColumnTemplate,\n compareSortValues,\n isColumnSortable,\n isEmptySortValue,\n nextSort,\n sortItems,\n} from './virtualList.utils';\n\n// ─── Orchestrator ─────────────────────────────────────────────────────────────\n// VirtualListInner wires the hooks together and renders the two pieces of\n// markup. Each concern lives in its own module:\n// useSorting — table mode: header sort state, and the sorted row order\n// useVirtualWindow — generic scroll / measurement / windowing (all modes)\n// useColumnResize — table mode: user column widths, and the drag that sets them\n// useSelection — multi-row selection\n// useColumns — table mode: columns, visibility, persistence, fit widths\n// useRowDrag — reorder + tree drag-and-drop (mouse + touch)\n// useRowNavigation — arrow-key navigation over the rows\n// virtualListHeader — the header row: sorting, resizing, the column menu\n// virtualRow — one positioned row, with its drag wiring\n//\n// The hook order is not free: useColumnResize decides whether the layout is\n// frozen, which useColumns needs to build the grid template.\n\n/** One handler after the other, or whichever of the two exists. */\nfunction chain<E>(theirs: ((event: E) => void) | undefined, ours: ((event: E) => void) | undefined) {\n if (!theirs || !ours) return theirs ?? ours;\n return (event: E) => {\n theirs(event);\n ours(event);\n };\n}\n\nfunction VirtualList<T>(props: VirtualListProps<T>) {\n const {\n ref,\n items,\n renderRow,\n ariaRoles,\n columns,\n height = '100%',\n estimatedRowHeight = 40,\n overscan = 3,\n rowGap = 0,\n rowPadding,\n selectedKey,\n getItemKey,\n onRowClick,\n keyboardNavigation = true,\n tabAcrossRows = false,\n scrollParent,\n visibleColumnKeys,\n onVisibleColumnsChange,\n persistColumnsKey,\n resizableColumns = false,\n columnWidths,\n onColumnWidthsChange,\n labels,\n sort,\n defaultSort,\n onSortChange,\n showColumnToggle,\n onLoadMore,\n loadMoreThreshold = 100,\n isLoading = false,\n multiSelect = false,\n selectedKeys,\n onSelectionChange,\n reorderable = false,\n onReorder,\n longPressDelay = 400,\n treeReorder = false,\n getItemDepth,\n isGroup,\n onTreeDrop,\n treeIndentPx = 24,\n dragHandle = false,\n showDivider = true,\n rowHover = true,\n className,\n style,\n classNames,\n styles: slotStyles,\n ...rest\n } = props;\n // Read off the raw props: the destructured value carries a default and so\n // cannot tell a passed '100%' from an omitted one.\n const heightIgnored = props.scrollParent != null && props.height !== undefined;\n useEffect(() => {\n if (heightIgnored)\n console.warn(\n 'VirtualList: `height` is ignored alongside `scrollParent`, which leaves the list no viewport to size.',\n );\n }, [heightIgnored]);\n\n const text = useLabels('VirtualList', labels, DEFAULT_LABELS);\n const { rows, effectiveSort, isSorted, handleHeaderSort } = useSorting<T>({\n items,\n columns,\n sort,\n defaultSort,\n onSortChange,\n });\n\n const getKey = useCallback(\n (item: T, index: number): string | number => (getItemKey ? getItemKey(item, index) : index),\n [getItemKey],\n );\n const keys = useMemo(() => rows.map((item, i) => getKey(item, i)), [rows, getKey]);\n const depths = useMemo(\n () => (getItemDepth ? rows.map((item, i) => getItemDepth(item, i)) : []),\n [rows, getItemDepth],\n );\n const isGroupAt = useCallback((i: number) => (isGroup ? isGroup(rows[i], i) : false), [isGroup, rows]);\n\n const win = useVirtualWindow<T>({\n items: rows,\n keys,\n getItemKey,\n estimatedRowHeight,\n rowGap,\n overscan,\n onLoadMore,\n loadMoreThreshold,\n isLoading,\n scrollParent,\n });\n const { rootRef, headerRef, rowObserver, keysRef, offsets, totalHeight, startIndex, endIndex } = win;\n\n // Filled from `cols` below: the resize hook only reads the visible set when a\n // handle is actually used, which is what lets it run before the hook that\n // derives it.\n const visibleColumnsRef = useRef<VirtualListColumn<T>[]>([]);\n const columnResize = useColumnResize<T>({\n columns,\n visibleColumnsRef,\n resizableColumns,\n columnWidths,\n onColumnWidthsChange,\n persistColumnsKey,\n rootRef,\n });\n\n const selection = useSelection<T>({ items: rows, getKey, multiSelect, selectedKeys, onSelectionChange });\n const { effectiveSelectedKeys } = selection;\n\n const cols = useColumns<T>({\n columns,\n items: rows,\n multiSelect,\n visibleColumnKeys,\n onVisibleColumnsChange,\n persistColumnsKey,\n showColumnToggle,\n userWidths: columnResize.widths,\n isFrozen: columnResize.isFrozen,\n rootRef,\n startIndex,\n endIndex,\n containerHeight: win.containerHeight,\n containerWidth: win.containerWidth,\n });\n // The rest of `cols` is the header's business and goes there whole.\n const { hasColumns, visibleColumns, columnTemplate, widthVars, shouldShowToggle, recalculateColumns } = cols;\n visibleColumnsRef.current = visibleColumns;\n\n // Compose the forwarded handle from the hooks that own each piece: scroll\n // methods from the windowing engine, column controls from the column hooks.\n useImperativeHandle(ref, () => ({ ...win.scrollApi, recalculateColumns, resetColumnWidths: columnResize.reset }), [\n win.scrollApi,\n recalculateColumns,\n columnResize.reset,\n ]);\n\n // Dragging a row to position N is meaningless while the list decides the\n // order: the drop would be undone by the next sort pass.\n const dragBlockedBySort = isSorted && (reorderable || treeReorder);\n useEffect(() => {\n if (dragBlockedBySort)\n console.warn('VirtualList: row reordering is disabled while the list is sorted. Clear the sort to drag rows.');\n }, [dragBlockedBySort]);\n\n const drag = useRowDrag({\n reorderable: reorderable && !isSorted,\n treeReorder: treeReorder && !isSorted,\n onReorder,\n onTreeDrop,\n longPressDelay,\n dragHandle,\n treeIndentPx,\n keys,\n depths,\n isGroupAt,\n rootRef,\n keysRef,\n getVisibleRect: win.getVisibleRect,\n scrollBy: win.scrollBy,\n });\n\n const listId = useId();\n const rowDomId = (index: number) => `${listId}-row-${index}`;\n const nav = useRowNavigation({\n enabled: keyboardNavigation,\n tabAcrossRows,\n rowCount: rows.length,\n offsets,\n rootRef,\n headerRef,\n getVisibleSlice: win.getVisibleSlice,\n viewportHeight: win.viewportHeight,\n scrollToIndex: win.scrollApi.scrollToIndex,\n initialIndex: selectedKey != null ? Math.max(0, keys.indexOf(selectedKey)) : 0,\n multiSelect,\n onActivate: onRowClick && ((index: number) => onRowClick(rows[index], index)),\n onToggleSelect: (index) => selection.handleRowSelect(keys[index]),\n });\n\n const rowPaddingValue = typeof rowPadding === 'number' ? `${rowPadding}px` : rowPadding;\n\n const renderRowAt = (i: number) => {\n const item = rows[i];\n const key = getKey(item, i);\n const isSelected = (selectedKey != null && key === selectedKey) || effectiveSelectedKeys.has(key);\n const dragProps = drag.getRowDragProps(i, key);\n\n return (\n <VirtualRow\n key={key}\n id={rowDomId(i)}\n index={i}\n top={offsets[i]}\n padding={rowPaddingValue}\n columnTemplate={columnTemplate}\n isSelected={isSelected}\n isActive={i === nav.activeIndex}\n manageTabStops={keyboardNavigation}\n isEntered={i === nav.enteredIndex}\n activeClassName={classNames?.activeRow}\n activeStyle={slotStyles?.activeRow}\n showDivider={showDivider}\n hover={rowHover}\n onClick={() => {\n if (drag.consumeSuppressedClick()) return;\n // Keep the arrows where the pointer last was.\n nav.setActiveIndex(i);\n onRowClick?.(item, i);\n }}\n observer={rowObserver}\n ariaRowIndex={hasColumns ? i + 2 : undefined}\n ariaPosInSet={hasColumns ? undefined : i + 1}\n ariaSetSize={hasColumns ? undefined : rows.length}\n rowRole={ariaRoles?.row ?? (hasColumns ? 'row' : 'listitem')}\n reserveToggleGutter={shouldShowToggle}\n {...dragProps}\n dropIndicatorClassName={classNames?.dropIndicator}\n dropIndicatorStyle={slotStyles?.dropIndicator}\n rowClassName={classNames?.row}\n rowStyle={slotStyles?.row}\n >\n {hasColumns ? (\n <>\n {multiSelect && (\n <div\n className={cx(styles.virtualListSelectCell, classNames?.selectCell)}\n style={slotStyles?.selectCell}\n role='gridcell'\n onClick={(e) => e.stopPropagation()}\n >\n <Checkbox\n selected={effectiveSelectedKeys.has(key)}\n onToggle={() => selection.handleRowSelect(key)}\n aria-label={text.selectRow}\n // One tab stop per rendered row, changing as the list scrolls,\n // would strand the focus the moment a row unmounts. Space on\n // the active row is the keyboard path instead.\n tabIndex={-1}\n />\n </div>\n )}\n {visibleColumns.map((col) => (\n <div\n key={col.key}\n className={cx(styles.virtualListCell, classNames?.cell)}\n style={slotStyles?.cell}\n role='gridcell'\n data-col-key={col.key}\n data-fit-key={col.width.type === 'fit' ? col.key : undefined}\n >\n {col.renderCell(item, i)}\n </div>\n ))}\n </>\n ) : (\n renderRow?.(item, i)\n )}\n </VirtualRow>\n );\n };\n\n const visibleRows: React.ReactNode[] = [];\n for (let i = startIndex; i <= endIndex; i++) visibleRows.push(renderRowAt(i));\n\n // Keep the row being touch-dragged mounted even if it scrolls out of the\n // window. Touch events are dispatched to the element under the finger at\n // touchstart; if that row unmounts, the browser stops delivering touchmove\n // and the drag freezes. Pinning its DOM node keeps the gesture alive.\n const draggedIndex = drag.draggingKey != null ? keys.indexOf(drag.draggingKey) : -1;\n if (draggedIndex >= 0 && (draggedIndex < startIndex || draggedIndex > endIndex)) {\n visibleRows.push(renderRowAt(draggedIndex));\n }\n\n // Same reason, one step further: the row being used holds the focus, and\n // unmounting it would drop that focus to the document body mid-scroll.\n if (nav.enteredIndex >= 0 && (nav.enteredIndex < startIndex || nav.enteredIndex > endIndex)) {\n visibleRows.push(renderRowAt(nav.enteredIndex));\n }\n\n // Navigation must not replace handlers the consumer passed. Theirs runs first,\n // and a `preventDefault` in it keeps the event: the navigation handlers all\n // check `defaultPrevented`.\n const containerProps = {\n ...nav.containerProps,\n onKeyDown: chain<React.KeyboardEvent<HTMLDivElement>>(rest.onKeyDown, nav.containerProps.onKeyDown),\n onFocus: chain<React.FocusEvent<HTMLDivElement>>(rest.onFocus, nav.containerProps.onFocus),\n onBlur: chain<React.FocusEvent<HTMLDivElement>>(rest.onBlur, nav.containerProps.onBlur),\n };\n\n return (\n <div\n {...rest}\n {...containerProps}\n ref={rootRef}\n className={cx(\n styles.virtualList,\n win.isWindowed && styles.virtualListWindowed,\n drag.draggingKey != null && styles.virtualListReordering,\n columnResize.isResizing && styles.virtualListResizing,\n className,\n classNames?.root,\n )}\n style={{ ...(win.isWindowed ? null : { height }), ...widthVars, ...style, ...slotStyles?.root }}\n role={ariaRoles?.container ?? (hasColumns ? 'grid' : 'list')}\n aria-rowcount={hasColumns ? rows.length + 1 : undefined}\n // Only a composite role may point at an active descendant, and the default\n // `list` is not one. In list mode, pass `ariaRoles` (e.g. listbox/option)\n // to have the active row announced.\n aria-activedescendant={\n nav.activeIndex >= 0 && (ariaRoles?.container ?? (hasColumns ? 'grid' : 'list')) !== 'list'\n ? rowDomId(nav.activeIndex)\n : undefined\n }\n >\n {hasColumns && (\n <VirtualListHeader<T>\n headerRef={headerRef}\n columns={columns!}\n cols={cols}\n resize={columnResize}\n sort={effectiveSort}\n onSort={handleHeaderSort}\n multiSelect={multiSelect}\n allSelected={selection.allSelected}\n someSelected={selection.someSelected}\n onSelectAll={selection.handleSelectAll}\n labels={text}\n classNames={classNames}\n styles={slotStyles}\n />\n )}\n {isLoading ? (\n <div className={styles.virtualListLoadingFull}>\n <SpinnerLoading className={styles.virtualListSpinner} />\n </div>\n ) : (\n <div\n className={cx(styles.virtualListBody, classNames?.body)}\n style={{ height: totalHeight, ...slotStyles?.body }}\n >\n {visibleRows}\n </div>\n )}\n {win.showLoadMoreSpinner && (\n <div\n className={cx(styles.virtualListLoadingIndicator, classNames?.loadingIndicator)}\n style={slotStyles?.loadingIndicator}\n >\n <SpinnerLoading className={styles.virtualListSpinner} />\n </div>\n )}\n </div>\n );\n}\n\nexport default VirtualList;\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- "use client";var e=`virtualList_qBPNc`,t=`virtualList-drag-clone_artnC`,n=`virtualList-header_0Ellb`,r=`virtualList-header-cell_c8NGV`,i=`virtualList-header-resize_cbYFc`,a=`virtualList-header-sort_Cdpe3`,o=`virtualList-header-sort-label_6MaZ3`,s=`virtualList-header-sort-icon_Z-Nrd`,c=`virtualList-header-sort-icon-active_g7qrb`,l=`virtualList-header-toggle_pYFxD`,u=`virtualList-toggle-panel_Uksmk`,d=`virtualList-toggle-wrapper_JP32M`,f=`virtualList-toggle-popover_gbpd7`,p=`virtualList-toggle-item_5gu5d`,m=`virtualList-toggle-reset_3UANJ`,h=`virtualList-column-chip_nOqr6`,g=`virtualList-select-cell_s9kAy`,_=`virtualList-body_k-fRr`,v=`virtualList-row-active_H5r5-`,y=`virtualList-resizing_28Mn4`,b=`virtualList-row_TTY50`,x=`virtualList-row-no-divider_Ct0gV`,S=`virtualList-row-no-hover_2d3mH`,C=`virtualList-row-selected_hwEN-`,w=`virtualList-row-draggable_I86ha`,T=`virtualList-row-dragging_6T5tQ`,E=`virtualList-row-drop-inside_Uhi9n`,D=`virtualList-drop-line_EM-g4`,O=`virtualList-drop-line-before_n-0iZ`,k=`virtualList-drop-line-after_2dld-`,A=`virtualList-reordering_Rt9E3`,j=`virtualList-cell_H0zBv`,M=`virtualList-loading-full_wzZNM`,N=`virtualList-loading-indicator_EJced`,P=`virtualList-spinner_apClW`,F={virtualList:e,"virtualList-drag-clone":`virtualList-drag-clone_artnC`,virtualListDragClone:t,"virtualList-header":`virtualList-header_0Ellb`,virtualListHeader:n,"virtualList-header-cell":`virtualList-header-cell_c8NGV`,virtualListHeaderCell:r,"virtualList-header-resize":`virtualList-header-resize_cbYFc`,virtualListHeaderResize:i,"virtualList-header-sort":`virtualList-header-sort_Cdpe3`,virtualListHeaderSort:a,"virtualList-header-sort-label":`virtualList-header-sort-label_6MaZ3`,virtualListHeaderSortLabel:o,"virtualList-header-sort-icon":`virtualList-header-sort-icon_Z-Nrd`,virtualListHeaderSortIcon:s,"virtualList-header-sort-icon-active":`virtualList-header-sort-icon-active_g7qrb`,virtualListHeaderSortIconActive:c,"virtualList-header-toggle":`virtualList-header-toggle_pYFxD`,virtualListHeaderToggle:l,"virtualList-toggle-panel":`virtualList-toggle-panel_Uksmk`,virtualListTogglePanel:u,"virtualList-toggle-wrapper":`virtualList-toggle-wrapper_JP32M`,virtualListToggleWrapper:d,"virtualList-toggle-popover":`virtualList-toggle-popover_gbpd7`,virtualListTogglePopover:f,"virtualList-toggle-item":`virtualList-toggle-item_5gu5d`,virtualListToggleItem:p,"virtualList-toggle-reset":`virtualList-toggle-reset_3UANJ`,virtualListToggleReset:m,"virtualList-column-chip":`virtualList-column-chip_nOqr6`,virtualListColumnChip:h,"virtualList-select-cell":`virtualList-select-cell_s9kAy`,virtualListSelectCell:g,"virtualList-body":`virtualList-body_k-fRr`,virtualListBody:_,"virtualList-row-active":`virtualList-row-active_H5r5-`,virtualListRowActive:v,"virtualList-resizing":`virtualList-resizing_28Mn4`,virtualListResizing:y,"virtualList-row":`virtualList-row_TTY50`,virtualListRow:b,"virtualList-row-no-divider":`virtualList-row-no-divider_Ct0gV`,virtualListRowNoDivider:x,"virtualList-row-no-hover":`virtualList-row-no-hover_2d3mH`,virtualListRowNoHover:S,"virtualList-row-selected":`virtualList-row-selected_hwEN-`,virtualListRowSelected:C,"virtualList-row-draggable":`virtualList-row-draggable_I86ha`,virtualListRowDraggable:w,"virtualList-row-dragging":`virtualList-row-dragging_6T5tQ`,virtualListRowDragging:T,"virtualList-row-drop-inside":`virtualList-row-drop-inside_Uhi9n`,virtualListRowDropInside:E,"virtualList-drop-line":`virtualList-drop-line_EM-g4`,virtualListDropLine:D,"virtualList-drop-line-before":`virtualList-drop-line-before_n-0iZ`,virtualListDropLineBefore:O,"virtualList-drop-line-after":`virtualList-drop-line-after_2dld-`,virtualListDropLineAfter:k,"virtualList-reordering":`virtualList-reordering_Rt9E3`,virtualListReordering:A,"virtualList-cell":`virtualList-cell_H0zBv`,virtualListCell:j,"virtualList-loading-full":`virtualList-loading-full_wzZNM`,virtualListLoadingFull:M,"virtualList-loading-indicator":`virtualList-loading-indicator_EJced`,virtualListLoadingIndicator:N,"virtualList-spinner":`virtualList-spinner_apClW`,virtualListSpinner:P};export{F as default,e as virtualList,_ as virtualListBody,j as virtualListCell,h as virtualListColumnChip,t as virtualListDragClone,D as virtualListDropLine,k as virtualListDropLineAfter,O as virtualListDropLineBefore,n as virtualListHeader,r as virtualListHeaderCell,i as virtualListHeaderResize,a as virtualListHeaderSort,s as virtualListHeaderSortIcon,c as virtualListHeaderSortIconActive,o as virtualListHeaderSortLabel,l as virtualListHeaderToggle,M as virtualListLoadingFull,N as virtualListLoadingIndicator,A as virtualListReordering,y as virtualListResizing,b as virtualListRow,v as virtualListRowActive,w as virtualListRowDraggable,T as virtualListRowDragging,E as virtualListRowDropInside,x as virtualListRowNoDivider,S as virtualListRowNoHover,C as virtualListRowSelected,g as virtualListSelectCell,P as virtualListSpinner,p as virtualListToggleItem,u as virtualListTogglePanel,f as virtualListTogglePopover,m as virtualListToggleReset,d as virtualListToggleWrapper};
1
+ "use client";var e=`virtualList_qBPNc`,t=`virtualList-drag-clone_artnC`,n=`virtualList-header_0Ellb`,r=`virtualList-header-cell_c8NGV`,i=`virtualList-header-resize_cbYFc`,a=`virtualList-header-sort_Cdpe3`,o=`virtualList-header-sort-label_6MaZ3`,s=`virtualList-header-sort-icon_Z-Nrd`,c=`virtualList-header-sort-icon-active_g7qrb`,l=`virtualList-header-toggle_pYFxD`,u=`virtualList-toggle-panel_Uksmk`,d=`virtualList-toggle-wrapper_JP32M`,f=`virtualList-toggle-popover_gbpd7`,p=`virtualList-toggle-item_5gu5d`,m=`virtualList-toggle-reset_3UANJ`,h=`virtualList-column-chip_nOqr6`,g=`virtualList-select-cell_s9kAy`,_=`virtualList-body_k-fRr`,v=`virtualList-row-active_H5r5-`,y=`virtualList-resizing_28Mn4`,b=`virtualList-row_TTY50`,x=`virtualList-row-no-divider_Ct0gV`,S=`virtualList-row-no-hover_2d3mH`,C=`virtualList-row-selected_hwEN-`,w=`virtualList-row-draggable_I86ha`,T=`virtualList-row-dragging_6T5tQ`,E=`virtualList-row-drop-inside_Uhi9n`,D=`virtualList-drop-line_EM-g4`,O=`virtualList-drop-line-before_n-0iZ`,k=`virtualList-drop-line-after_2dld-`,A=`virtualList-reordering_Rt9E3`,j=`virtualList-cell_H0zBv`,M=`virtualList-loading-full_wzZNM`,N=`virtualList-loading-indicator_EJced`,P=`virtualList-spinner_apClW`,F=`virtualList-windowed_riUyT`,I={virtualList:e,"virtualList-drag-clone":`virtualList-drag-clone_artnC`,virtualListDragClone:t,"virtualList-header":`virtualList-header_0Ellb`,virtualListHeader:n,"virtualList-header-cell":`virtualList-header-cell_c8NGV`,virtualListHeaderCell:r,"virtualList-header-resize":`virtualList-header-resize_cbYFc`,virtualListHeaderResize:i,"virtualList-header-sort":`virtualList-header-sort_Cdpe3`,virtualListHeaderSort:a,"virtualList-header-sort-label":`virtualList-header-sort-label_6MaZ3`,virtualListHeaderSortLabel:o,"virtualList-header-sort-icon":`virtualList-header-sort-icon_Z-Nrd`,virtualListHeaderSortIcon:s,"virtualList-header-sort-icon-active":`virtualList-header-sort-icon-active_g7qrb`,virtualListHeaderSortIconActive:c,"virtualList-header-toggle":`virtualList-header-toggle_pYFxD`,virtualListHeaderToggle:l,"virtualList-toggle-panel":`virtualList-toggle-panel_Uksmk`,virtualListTogglePanel:u,"virtualList-toggle-wrapper":`virtualList-toggle-wrapper_JP32M`,virtualListToggleWrapper:d,"virtualList-toggle-popover":`virtualList-toggle-popover_gbpd7`,virtualListTogglePopover:f,"virtualList-toggle-item":`virtualList-toggle-item_5gu5d`,virtualListToggleItem:p,"virtualList-toggle-reset":`virtualList-toggle-reset_3UANJ`,virtualListToggleReset:m,"virtualList-column-chip":`virtualList-column-chip_nOqr6`,virtualListColumnChip:h,"virtualList-select-cell":`virtualList-select-cell_s9kAy`,virtualListSelectCell:g,"virtualList-body":`virtualList-body_k-fRr`,virtualListBody:_,"virtualList-row-active":`virtualList-row-active_H5r5-`,virtualListRowActive:v,"virtualList-resizing":`virtualList-resizing_28Mn4`,virtualListResizing:y,"virtualList-row":`virtualList-row_TTY50`,virtualListRow:b,"virtualList-row-no-divider":`virtualList-row-no-divider_Ct0gV`,virtualListRowNoDivider:x,"virtualList-row-no-hover":`virtualList-row-no-hover_2d3mH`,virtualListRowNoHover:S,"virtualList-row-selected":`virtualList-row-selected_hwEN-`,virtualListRowSelected:C,"virtualList-row-draggable":`virtualList-row-draggable_I86ha`,virtualListRowDraggable:w,"virtualList-row-dragging":`virtualList-row-dragging_6T5tQ`,virtualListRowDragging:T,"virtualList-row-drop-inside":`virtualList-row-drop-inside_Uhi9n`,virtualListRowDropInside:E,"virtualList-drop-line":`virtualList-drop-line_EM-g4`,virtualListDropLine:D,"virtualList-drop-line-before":`virtualList-drop-line-before_n-0iZ`,virtualListDropLineBefore:O,"virtualList-drop-line-after":`virtualList-drop-line-after_2dld-`,virtualListDropLineAfter:k,"virtualList-reordering":`virtualList-reordering_Rt9E3`,virtualListReordering:A,"virtualList-cell":`virtualList-cell_H0zBv`,virtualListCell:j,"virtualList-loading-full":`virtualList-loading-full_wzZNM`,virtualListLoadingFull:M,"virtualList-loading-indicator":`virtualList-loading-indicator_EJced`,virtualListLoadingIndicator:N,"virtualList-spinner":`virtualList-spinner_apClW`,virtualListSpinner:P,"virtualList-windowed":`virtualList-windowed_riUyT`,virtualListWindowed:F};export{I as default,e as virtualList,_ as virtualListBody,j as virtualListCell,h as virtualListColumnChip,t as virtualListDragClone,D as virtualListDropLine,k as virtualListDropLineAfter,O as virtualListDropLineBefore,n as virtualListHeader,r as virtualListHeaderCell,i as virtualListHeaderResize,a as virtualListHeaderSort,s as virtualListHeaderSortIcon,c as virtualListHeaderSortIconActive,o as virtualListHeaderSortLabel,l as virtualListHeaderToggle,M as virtualListLoadingFull,N as virtualListLoadingIndicator,A as virtualListReordering,y as virtualListResizing,b as virtualListRow,v as virtualListRowActive,w as virtualListRowDraggable,T as virtualListRowDragging,E as virtualListRowDropInside,x as virtualListRowNoDivider,S as virtualListRowNoHover,C as virtualListRowSelected,g as virtualListSelectCell,P as virtualListSpinner,p as virtualListToggleItem,u as virtualListTogglePanel,f as virtualListTogglePopover,m as virtualListToggleReset,d as virtualListToggleWrapper,F as virtualListWindowed};
2
2
  //# sourceMappingURL=virtualList.module.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"virtualList.module.mjs","names":[],"sources":["../../../../package/common/virtualList/virtualList.module.pcss"],"sourcesContent":[".virtualList {\n overflow-y: auto;\n box-sizing: border-box;\n color: var(--text-color);\n font-family: var(--default-font);\n\n /* Disable the browser's own scroll anchoring — the component does its own\n scroll-position correction when row heights change, and the two fighting\n each other causes the thumb to desync while dragging. */\n overflow-anchor: none;\n\n /* Reserve the scrollbar gutter so rows don't re-wrap (and re-measure) when the\n vertical scrollbar appears/disappears as content grows. */\n scrollbar-gutter: stable;\n\n /* Establish a stacking context so every z-index inside the list (sticky\n header, toggle popover, drag clone) stays scoped to this component and\n can't paint over outside content like a modal. */\n isolation: isolate;\n\n &-drag-clone {\n box-sizing: border-box;\n contain: layout paint style;\n cursor: grabbing;\n\n /* It tracks the finger via transform — never animate that. */\n transition: none;\n }\n\n &-header {\n display: grid;\n position: sticky;\n top: 0;\n\n /* Frozen layouts are exact pixels and may total more than the viewport, so\n the header and the row canvas below both take that total and the list\n scrolls sideways. `--vl-total` is only set once a column has been\n resized; until then this resolves to 100% and nothing changes. */\n width: max(100%, var(--vl-total, 0px));\n\n /* Above focused rows (z-index 1) so a focused row scrolled under the header\n never paints over it; the drag clone (z-index 3) still floats above both. */\n z-index: 2;\n background: var(--background-accent);\n border-bottom: 1px solid var(--border-color);\n box-sizing: border-box;\n\n &-cell {\n padding: 8px 12px;\n font-weight: 600;\n font-size: 13px;\n color: var(--text-dark);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n box-sizing: border-box;\n\n /* Anchors the resize handle to the cell's trailing edge. */\n position: relative;\n\n /* The cell, not the button inside it, carries the header's tab stop, so\n this is the ring a keyboard user follows. Inset because the cell clips\n its overflow and an outward outline would be cut off. */\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -1px;\n }\n }\n\n /* Resize handle: a grab strip on the column's trailing edge, wider than the\n line it sits on because a 1px target is unhittable. It stays inside the\n cell rather than straddling the edge, since the cell clips its overflow\n to keep a long header out of its neighbour. */\n &-resize {\n position: absolute;\n top: 0;\n bottom: 0;\n right: 0;\n width: 10px;\n cursor: col-resize;\n -webkit-tap-highlight-color: transparent;\n touch-action: none;\n\n /* The line itself, drawn on the trailing edge and revealed on hover. */\n &::after {\n content: '';\n position: absolute;\n top: 4px;\n bottom: 4px;\n right: 0;\n width: 2px;\n border-radius: 1px;\n background: var(--primary-color);\n opacity: 0;\n transition: opacity 0.12s;\n }\n\n &:hover::after,\n &:active::after {\n opacity: 1;\n }\n }\n\n /* The clickable part of a sortable header cell. Strips the button chrome and\n inherits the cell's typography, so a sortable header looks identical to a\n plain one until it's hovered. */\n &-sort {\n font: inherit;\n color: inherit;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n max-width: 100%;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: 2px;\n }\n\n /* The cell's own ellipsis can't apply once its content is a flex button,\n so the truncation moves onto the label. */\n &-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &-icon {\n flex: none;\n font-size: 11px;\n opacity: 0.35;\n transition:\n opacity 0.15s,\n color 0.15s;\n\n &-active {\n opacity: 1;\n color: var(--primary-color);\n }\n }\n\n &:hover &-icon {\n opacity: 0.75;\n }\n\n &:hover &-icon-active {\n opacity: 1;\n }\n }\n\n &-toggle {\n background: none;\n border: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n padding: 4px 8px;\n color: var(--text-dark);\n display: flex;\n align-items: center;\n border-radius: var(--default-border-radius);\n transition: background 0.15s;\n\n &:hover {\n background: var(--background-accent-light);\n color: var(--text-color);\n }\n }\n }\n\n /* The entries pad themselves so a hover highlight reaches the panel edge. */\n &-toggle-panel {\n padding: 4px 0;\n }\n\n &-toggle {\n &-wrapper {\n position: absolute;\n right: 0;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n padding: 0 4px;\n }\n\n /* Chrome (background, shadow, radius, positioning) is provided by the\n Popover panel this renders inside — keep only sizing here. */\n &-popover {\n display: flex;\n flex-direction: column;\n min-width: 160px;\n }\n\n &-item {\n padding: 4px 12px;\n white-space: nowrap;\n transition: background 0.1s;\n font-size: 14px;\n\n /* `:focus` as well, because the panel takes the focus when the keyboard\n opens it, and the checkbox itself is the focusable element. Not\n `--background-accent-light`: it is the same colour as that panel in the\n default theme. */\n &:hover,\n &:focus {\n background: var(--background-accent);\n }\n }\n\n /* Only rendered once a column has actually been resized, so it reads as an\n undo for something the user just did rather than a permanent option. */\n &-reset {\n margin-top: 4px;\n padding: 6px 12px;\n border: none;\n border-top: 1px solid var(--border-color);\n background: none;\n font: inherit;\n font-size: 13px;\n color: var(--text-color);\n text-align: left;\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition: background 0.1s;\n\n &:hover,\n &:focus {\n background: var(--background-accent);\n }\n }\n }\n\n /* Count badge on the column-toggle cog: how many columns the user has hidden.\n Positioned at the cog's upper-right corner; non-interactive. */\n &-column-chip {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--primary-color);\n color: var(--text-on-primary);\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n border-radius: 8px;\n pointer-events: none;\n }\n\n &-select-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 4px;\n box-sizing: border-box;\n\n /* Only the header's select-all cell is focusable; the ones in rows are not. */\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -1px;\n }\n }\n\n &-body {\n position: relative;\n box-sizing: border-box;\n\n /* Rows are absolute with left/right 0, so they inherit this width and stay\n aligned with the header when the table is wider than the viewport. */\n width: max(100%, var(--vl-total, 0px));\n }\n\n /* The list itself holds the tab stop, so the active row cannot use a\n `:focus-visible` of its own. Ringing the list instead would mark the whole\n table rather than the row the arrow keys are on, so the list drops its own\n outline and lends it to that row. Scoped to `:focus-visible`, a mouse user\n never sees it. */\n &:focus-visible {\n outline: none;\n\n & .virtualList-row-active {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -2px;\n }\n }\n\n /* A resize drag travels over the rows, and their hover highlight following the\n pointer is noise. Inert rather than a `:hover` override, which would have to\n out-specify the selected-row rule to work. The drag itself is unaffected:\n pointer capture bypasses hit testing. */\n &-resizing &-body {\n pointer-events: none;\n }\n\n &-row {\n border-bottom: 1px solid var(--border-color);\n box-sizing: border-box;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition: background 0.1s;\n\n /* Isolate each row's layout/style so measuring or restyling one row can't force\n the whole list to re-layout — keeps fast scrolling smooth. */\n contain: layout style;\n\n &-no-divider {\n border-bottom: none;\n }\n\n &:hover {\n background: var(--background-accent-light);\n }\n\n /* Opt out of the hover highlight (e.g. when each row is a Card that handles\n its own hover). Declared before the `-selected` rule below, so a selected\n row's highlight still wins on hover. */\n &-no-hover:hover {\n background: transparent;\n }\n\n &-selected {\n background: var(--primary-lighter);\n\n &:hover {\n background: var(--primary-lighter);\n }\n }\n\n &-draggable {\n cursor: grab;\n\n /* Stop the mobile long-press from selecting text or showing the callout. */\n user-select: none;\n -webkit-touch-callout: none;\n\n &:active {\n cursor: grabbing;\n }\n }\n\n &-dragging {\n opacity: 0.4;\n }\n\n /* Tree-reorder `inside` drop: the dragged node becomes a child of this row,\n so highlight the whole row instead of drawing a between-rows line. */\n &-drop-inside {\n background: var(--primary-lighter);\n box-shadow: inset 0 0 0 2px var(--primary-color);\n }\n }\n\n /* Tree-reorder `before`/`after` drop line. Positioned inside the (relatively\n positioned absolute) row; `left` is set inline to the target's indent so it\n starts where the dropped content will. */\n &-drop-line {\n position: absolute;\n right: 0;\n height: 2px;\n background: var(--primary-color);\n pointer-events: none;\n z-index: 1;\n\n /* A small knob at the line's start, the usual tree drop-indicator look. */\n &::before {\n content: '';\n position: absolute;\n left: 0;\n top: 50%;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--primary-color);\n transform: translate(-50%, -50%);\n }\n\n &-before {\n top: -1px;\n }\n\n &-after {\n bottom: -1px;\n }\n }\n\n /* While a row is being dragged, suppress hover backgrounds so the pointer\n moving across rows doesn't light up the wrong one, and stop text selection.\n `touch-action: none` stops the touch drag from also scrolling the list\n (only active once the long-press has armed the drag). */\n &-reordering {\n touch-action: none;\n\n .virtualList-row {\n user-select: none;\n\n &:hover {\n background: transparent;\n }\n\n &.virtualList-row-selected:hover {\n background: var(--primary-lighter);\n }\n\n &.virtualList-row-dragging {\n background: transparent;\n }\n }\n }\n\n &-cell {\n padding: 8px 12px;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 14px;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n min-width: 0;\n\n /* `fit` columns are content-sized — keep their cells on a single line so the\n content can't wrap, and so the width measurement reads the true intrinsic\n width. (Other cells are free to wrap for variable-height rows.) */\n &[data-fit-key] {\n white-space: nowrap;\n }\n }\n\n &-loading {\n &-full {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n }\n\n &-indicator {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 8px 0;\n }\n }\n\n &-spinner {\n width: 32px;\n height: 32px;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"virtualList.module.mjs","names":[],"sources":["../../../../package/common/virtualList/virtualList.module.pcss"],"sourcesContent":[".virtualList {\n overflow-y: auto;\n box-sizing: border-box;\n color: var(--text-color);\n font-family: var(--default-font);\n\n /* Disable the browser's own scroll anchoring — the component does its own\n scroll-position correction when row heights change, and the two fighting\n each other causes the thumb to desync while dragging. */\n overflow-anchor: none;\n\n /* Reserve the scrollbar gutter so rows don't re-wrap (and re-measure) when the\n vertical scrollbar appears/disappears as content grows. */\n scrollbar-gutter: stable;\n\n /* Establish a stacking context so every z-index inside the list (sticky\n header, toggle popover, drag clone) stays scoped to this component and\n can't paint over outside content like a modal. */\n isolation: isolate;\n\n &-drag-clone {\n box-sizing: border-box;\n contain: layout paint style;\n cursor: grabbing;\n\n /* It tracks the finger via transform — never animate that. */\n transition: none;\n }\n\n &-header {\n display: grid;\n position: sticky;\n\n /* With `scrollParent` the header sticks to the page's scrollport, where a\n fixed app bar may already sit; the list is given the offset from outside. */\n top: var(--vl-header-top, 0);\n\n /* Frozen layouts are exact pixels and may total more than the viewport, so\n the header and the row canvas below both take that total and the list\n scrolls sideways. `--vl-total` is only set once a column has been\n resized; until then this resolves to 100% and nothing changes. */\n width: max(100%, var(--vl-total, 0px));\n\n /* Above focused rows (z-index 1) so a focused row scrolled under the header\n never paints over it; the drag clone (z-index 3) still floats above both. */\n z-index: 2;\n background: var(--background-accent);\n border-bottom: 1px solid var(--border-color);\n box-sizing: border-box;\n\n &-cell {\n padding: 8px 12px;\n font-weight: 600;\n font-size: 13px;\n color: var(--text-dark);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n box-sizing: border-box;\n\n /* Anchors the resize handle to the cell's trailing edge. */\n position: relative;\n\n /* The cell, not the button inside it, carries the header's tab stop, so\n this is the ring a keyboard user follows. Inset because the cell clips\n its overflow and an outward outline would be cut off. */\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -1px;\n }\n }\n\n /* Resize handle: a grab strip on the column's trailing edge, wider than the\n line it sits on because a 1px target is unhittable. It stays inside the\n cell rather than straddling the edge, since the cell clips its overflow\n to keep a long header out of its neighbour. */\n &-resize {\n position: absolute;\n top: 0;\n bottom: 0;\n right: 0;\n width: 10px;\n cursor: col-resize;\n -webkit-tap-highlight-color: transparent;\n touch-action: none;\n\n /* The line itself, drawn on the trailing edge and revealed on hover. */\n &::after {\n content: '';\n position: absolute;\n top: 4px;\n bottom: 4px;\n right: 0;\n width: 2px;\n border-radius: 1px;\n background: var(--primary-color);\n opacity: 0;\n transition: opacity 0.12s;\n }\n\n &:hover::after,\n &:active::after {\n opacity: 1;\n }\n }\n\n /* The clickable part of a sortable header cell. Strips the button chrome and\n inherits the cell's typography, so a sortable header looks identical to a\n plain one until it's hovered. */\n &-sort {\n font: inherit;\n color: inherit;\n background: none;\n border: none;\n margin: 0;\n padding: 0;\n display: flex;\n align-items: center;\n gap: 6px;\n min-width: 0;\n max-width: 100%;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: 2px;\n }\n\n /* The cell's own ellipsis can't apply once its content is a flex button,\n so the truncation moves onto the label. */\n &-label {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n }\n\n &-icon {\n flex: none;\n font-size: 11px;\n opacity: 0.35;\n transition:\n opacity 0.15s,\n color 0.15s;\n\n &-active {\n opacity: 1;\n color: var(--primary-color);\n }\n }\n\n &:hover &-icon {\n opacity: 0.75;\n }\n\n &:hover &-icon-active {\n opacity: 1;\n }\n }\n\n &-toggle {\n background: none;\n border: none;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n padding: 4px 8px;\n color: var(--text-dark);\n display: flex;\n align-items: center;\n border-radius: var(--default-border-radius);\n transition: background 0.15s;\n\n &:hover {\n background: var(--background-accent-light);\n color: var(--text-color);\n }\n }\n }\n\n /* The entries pad themselves so a hover highlight reaches the panel edge. */\n &-toggle-panel {\n padding: 4px 0;\n }\n\n &-toggle {\n &-wrapper {\n position: absolute;\n right: 0;\n top: 0;\n bottom: 0;\n display: flex;\n align-items: center;\n padding: 0 4px;\n }\n\n /* Chrome (background, shadow, radius, positioning) is provided by the\n Popover panel this renders inside — keep only sizing here. */\n &-popover {\n display: flex;\n flex-direction: column;\n min-width: 160px;\n }\n\n &-item {\n padding: 4px 12px;\n white-space: nowrap;\n transition: background 0.1s;\n font-size: 14px;\n\n /* `:focus` as well, because the panel takes the focus when the keyboard\n opens it, and the checkbox itself is the focusable element. Not\n `--background-accent-light`: it is the same colour as that panel in the\n default theme. */\n &:hover,\n &:focus {\n background: var(--background-accent);\n }\n }\n\n /* Only rendered once a column has actually been resized, so it reads as an\n undo for something the user just did rather than a permanent option. */\n &-reset {\n margin-top: 4px;\n padding: 6px 12px;\n border: none;\n border-top: 1px solid var(--border-color);\n background: none;\n font: inherit;\n font-size: 13px;\n color: var(--text-color);\n text-align: left;\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition: background 0.1s;\n\n &:hover,\n &:focus {\n background: var(--background-accent);\n }\n }\n }\n\n /* Count badge on the column-toggle cog: how many columns the user has hidden.\n Positioned at the cog's upper-right corner; non-interactive. */\n &-column-chip {\n position: absolute;\n top: 0;\n right: 0;\n min-width: 16px;\n height: 16px;\n padding: 0 4px;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--primary-color);\n color: var(--text-on-primary);\n font-size: 10px;\n font-weight: 600;\n line-height: 1;\n border-radius: 8px;\n pointer-events: none;\n }\n\n &-select-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 0 4px;\n box-sizing: border-box;\n\n /* Only the header's select-all cell is focusable; the ones in rows are not. */\n &:focus-visible {\n outline: 1px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -1px;\n }\n }\n\n &-body {\n position: relative;\n box-sizing: border-box;\n\n /* Rows are absolute with left/right 0, so they inherit this width and stay\n aligned with the header when the table is wider than the viewport. */\n width: max(100%, var(--vl-total, 0px));\n }\n\n /* The list itself holds the tab stop, so the active row cannot use a\n `:focus-visible` of its own. Ringing the list instead would mark the whole\n table rather than the row the arrow keys are on, so the list drops its own\n outline and lends it to that row. Scoped to `:focus-visible`, a mouse user\n never sees it. */\n &:focus-visible {\n outline: none;\n\n & .virtualList-row-active {\n outline: 2px solid var(--focus-border-color, var(--primary-color));\n outline-offset: -2px;\n }\n }\n\n /* A resize drag travels over the rows, and their hover highlight following the\n pointer is noise. Inert rather than a `:hover` override, which would have to\n out-specify the selected-row rule to work. The drag itself is unaffected:\n pointer capture bypasses hit testing. */\n &-resizing &-body {\n pointer-events: none;\n }\n\n &-row {\n border-bottom: 1px solid var(--border-color);\n box-sizing: border-box;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n transition: background 0.1s;\n\n /* Isolate each row's layout/style so measuring or restyling one row can't force\n the whole list to re-layout — keeps fast scrolling smooth. */\n contain: layout style;\n\n &-no-divider {\n border-bottom: none;\n }\n\n &:hover {\n background: var(--background-accent-light);\n }\n\n /* Opt out of the hover highlight (e.g. when each row is a Card that handles\n its own hover). Declared before the `-selected` rule below, so a selected\n row's highlight still wins on hover. */\n &-no-hover:hover {\n background: transparent;\n }\n\n &-selected {\n background: var(--primary-lighter);\n\n &:hover {\n background: var(--primary-lighter);\n }\n }\n\n &-draggable {\n cursor: grab;\n\n /* Stop the mobile long-press from selecting text or showing the callout. */\n user-select: none;\n -webkit-touch-callout: none;\n\n &:active {\n cursor: grabbing;\n }\n }\n\n &-dragging {\n opacity: 0.4;\n }\n\n /* Tree-reorder `inside` drop: the dragged node becomes a child of this row,\n so highlight the whole row instead of drawing a between-rows line. */\n &-drop-inside {\n background: var(--primary-lighter);\n box-shadow: inset 0 0 0 2px var(--primary-color);\n }\n }\n\n /* Tree-reorder `before`/`after` drop line. Positioned inside the (relatively\n positioned absolute) row; `left` is set inline to the target's indent so it\n starts where the dropped content will. */\n &-drop-line {\n position: absolute;\n right: 0;\n height: 2px;\n background: var(--primary-color);\n pointer-events: none;\n z-index: 1;\n\n /* A small knob at the line's start, the usual tree drop-indicator look. */\n &::before {\n content: '';\n position: absolute;\n left: 0;\n top: 50%;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: var(--primary-color);\n transform: translate(-50%, -50%);\n }\n\n &-before {\n top: -1px;\n }\n\n &-after {\n bottom: -1px;\n }\n }\n\n /* While a row is being dragged, suppress hover backgrounds so the pointer\n moving across rows doesn't light up the wrong one, and stop text selection.\n `touch-action: none` stops the touch drag from also scrolling the list\n (only active once the long-press has armed the drag). */\n &-reordering {\n touch-action: none;\n\n .virtualList-row {\n user-select: none;\n\n &:hover {\n background: transparent;\n }\n\n &.virtualList-row-selected:hover {\n background: var(--primary-lighter);\n }\n\n &.virtualList-row-dragging {\n background: transparent;\n }\n }\n }\n\n &-cell {\n padding: 8px 12px;\n overflow: hidden;\n text-overflow: ellipsis;\n font-size: 14px;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n min-width: 0;\n\n /* `fit` columns are content-sized — keep their cells on a single line so the\n content can't wrap, and so the width measurement reads the true intrinsic\n width. (Other cells are free to wrap for variable-height rows.) */\n &[data-fit-key] {\n white-space: nowrap;\n }\n }\n\n &-loading {\n &-full {\n display: flex;\n justify-content: center;\n align-items: center;\n height: 100%;\n }\n\n &-indicator {\n display: flex;\n justify-content: center;\n align-items: center;\n padding: 8px 0;\n }\n }\n\n &-spinner {\n width: 32px;\n height: 32px;\n }\n\n /* Must stay after the declarations it undoes: both selectors are one class, so\n only source order separates them. `overflow` cannot be split per axis here\n either, because a container with `overflow-x` and `overflow-y: visible`\n computes the visible one to `auto` and hands the list back its own vertical\n scrollbar. So a frozen layout wider than the list overflows the page\n sideways instead of scrolling inside it. */\n &-windowed {\n height: auto;\n overflow: visible;\n scrollbar-gutter: auto;\n\n .virtualList-loading-full {\n height: auto;\n padding: 32px 0;\n }\n }\n}\n"],"mappings":""}
@@ -1 +1 @@
1
- "use client";import{AccordionStyleType as e}from"./common/accordion/accordion.types.mjs";import t from"./common/configProvider/configProvider.mjs";import{useComponentDefaults as n}from"./common/configProvider/useComponentDefaults.mjs";import{useLabels as r}from"./common/configProvider/useLabels.mjs";import{usePresentationDefault as i}from"./common/configProvider/usePresentationDefault.mjs";import{Presentation as a}from"./common/configProvider/presentation.types.mjs";import o from"./common/accordion/accordion.mjs";import s from"./common/loading/spinnerLoading/index.mjs";import{ButtonSize as c,ButtonStyleType as l,ButtonType as u}from"./common/button/button.types.mjs";import d from"./common/ripple/ripple.mjs";import f from"./common/button/button.mjs";import p from"./common/actionButtons/actionButtons.mjs";import m from"./common/interactableDiv/index.mjs";import h from"./common/actionIcon/actionIcon.mjs";import{AlertStyleType as g}from"./common/alert/alert.types.mjs";import _ from"./common/alert/alert.mjs";import v from"./common/animatedLogo/animatedLogo.mjs";import{AnimatedTextUnit as y,AnimatedTextVariant as b}from"./common/animatedText/animatedText.types.mjs";import x from"./common/animatedText/animatedText.mjs";import{BadgePosition as S,BadgeStyleType as C}from"./common/badge/badge.types.mjs";import w from"./common/badge/badge.mjs";import T from"./common/avatar/avatar.mjs";import E from"./common/avatar/avatarGroup.mjs";import D from"./common/bodyEnd/bodyEnd.mjs";import{PopoverAlign as O,PopoverPlacement as k,PopoverTrigger as A}from"./common/popover/popover.types.mjs";import j from"./common/popover/popover.mjs";import M from"./common/breadcrumb/breadcrumb.mjs";import{ButtonGroupOrientation as N}from"./common/buttonGroup/buttonGroup.types.mjs";import P from"./common/buttonGroup/buttonGroup.mjs";import{CardStyleType as F}from"./common/card/card.types.mjs";import I from"./common/card/Card.mjs";import L from"./common/card/CardHeader.mjs";import R from"./common/card/CardMedia.mjs";import z from"./common/card/CardContent.mjs";import B from"./common/card/CardActions.mjs";import V from"./common/carousel/carousel.mjs";import H from"./common/checkbox/checkbox.mjs";import{ChipStyleType as U}from"./common/chip/chip.types.mjs";import W from"./common/chip/chip.mjs";import G from"./common/chip/chipContainer.mjs";import K from"./common/tooltip/tooltip.mjs";import{InputIconPosition as q,InputType as J}from"./common/input/input.types.mjs";import Y from"./common/input/input.mjs";import X from"./common/colorPicker/colorPicker.mjs";import Z from"./common/modal/modal.mjs";import Q from"./common/confirmModal/confirmModal.mjs";import{getLocale as $,mapLocale as ee,setLocale as te}from"./services/localization.mjs";import ne from"./common/dropdown/dropdown.mjs";import re from"./common/datePicker/datePicker.mjs";import{DividerOrientation as ie,DividerTextPosition as ae}from"./common/divider/divider.types.mjs";import oe from"./common/divider/divider.mjs";import se from"./common/drawer/drawer.mjs";import{DropZoneReturnType as ce,DropZoneStatus as le}from"./common/dropZone/dropZone.types.mjs";import ue from"./common/dropZone/dropZone.mjs";import de from"./common/emptyState/emptyState.mjs";import{showToast as fe}from"./common/toast/toastService.mjs";import pe from"./common/toast/toastProvider.mjs";import me from"./common/errorBoundary/errorBoundary.mjs";import he from"./common/fab/fab.mjs";import ge from"./common/flip/flip.mjs";import{DoorSwing as _e,OpeningKind as ve,PlanIssueKind as ye,PlanUnitSystem as be,WallType as xe}from"./common/floorPlan/floorPlan.types.mjs";import{MIN_OPENING_WIDTH as Se,MIN_WALL_LENGTH as Ce}from"./common/floorPlan/floorPlan.geometry.mjs";import{DEFAULT_WALL_ATTRS as we,addOpening as Te,addRect as Ee,addWall as De,cleanupPlan as Oe,detachWall as ke,dissolveNode as Ae,mergeNodes as je,moveNode as Me,removeOpening as Ne,removeWall as Pe,scalePlan as Fe,splitWall as Ie,transformPlan as Le,updateOpening as Re,validatePlan as ze,wallLength as Be,weldShortWalls as Ve}from"./common/floorPlan/floorPlan.graph.mjs";import{MIN_FLOOR_HEIGHT as He,addFloor as Ue,duplicateFloor as We,emptyFloor as Ge,emptyFloorPlan as Ke,floorBelow as qe,floorBounds as Je,floorIndex as Ye,getFloor as Xe,moveFloor as Ze,removeFloor as Qe,renameFloor as $e,setFloor as et,setFloorHeight as tt,updateFloor as nt}from"./common/floorPlan/floorPlan.plan.mjs";import{deriveFaces as rt,nodeDegrees as it}from"./common/floorPlan/floorPlan.faces.mjs";import{commitDerivation as at,derivePlan as ot,pruneUnmatchedRooms as st,roomAtPoint as ct}from"./common/floorPlan/floorPlan.rooms.mjs";import{buildWallPaths as lt,openingGeometry as ut,wallCentrelineSpans as dt}from"./common/floorPlan/floorPlan.path.mjs";import{HIT_PX as ft,HIT_PX_COARSE as pt,hitTest as mt,selectionsEqual as ht,worldTolerances as gt}from"./common/floorPlan/floorPlan.hit.mjs";import{DEFAULT_SNAP_OPTIONS as _t,SnapKind as vt,clientToWorld as yt,resolveSnap as bt,screenScale as xt}from"./common/floorPlan/floorPlan.snap.mjs";import{PLAN_FORMAT_VERSION as St,planFromJson as Ct,planToJson as wt}from"./common/floorPlan/floorPlan.json.mjs";import{buildPlanMesh as Tt}from"./common/floorPlan/floorPlan.mesh.mjs";import{planFromLegacySvg as Et}from"./common/floorPlan/legacySvg.mjs";import Dt from"./common/iconPicker/iconPicker.mjs";import Ot from"./common/idleManager/idleManager.mjs";import kt from"./common/inputDropdown/inputDropdown.mjs";import At from"./common/kanbanBoard/KanbanColumn.mjs";import jt from"./common/kanbanBoard/KanbanBoard.mjs";import Mt from"./common/searchInput/searchInput.mjs";import Nt from"./common/klipyPicker/klipyPicker.mjs";import Pt from"./common/loading/iconLoading/index.mjs";import{isMenuAction as Ft,isMenuHeading as It,isMenuSeparator as Lt}from"./common/menu/menu.types.mjs";import Rt from"./common/menu/menu.mjs";import zt from"./common/multiSelect/multiSelect.mjs";import Bt from"./common/numberInput/index.mjs";import Vt from"./common/optionPicker/optionPicker.mjs";import{OtpInputCharset as Ht}from"./common/otpInput/otpInput.types.mjs";import Ut from"./common/otpInput/otpInput.mjs";import Wt from"./common/overscroll/overscroll.mjs";import Gt from"./common/pagination/pagination.mjs";import Kt from"./common/progressBar/progressBar.mjs";import{RadioGroupOrientation as qt}from"./common/radioGroup/radioGroup.types.mjs";import Jt from"./common/radioGroup/radioGroup.mjs";import Yt from"./common/rating/rating.mjs";import Xt from"./common/revealLens/revealLens.mjs";import Zt from"./common/roomDrawer/roomDrawer.mjs";import Qt from"./common/roomViewer/roomViewer.mjs";import{ScrollFadeAxis as $t}from"./common/scrollFade/scrollFade.types.mjs";import en from"./common/scrollFade/scrollFade.mjs";import tn from"./common/scrollbarProvider/index.mjs";import{SectionHeaderSize as nn}from"./common/sectionHeader/sectionHeader.types.mjs";import rn from"./common/sectionHeader/sectionHeader.mjs";import{SkeletonAnimation as an,SkeletonVariant as on}from"./common/skeleton/skeleton.types.mjs";import sn from"./common/skeleton/skeleton.mjs";import cn from"./common/slider/slider.mjs";import ln from"./common/splitButton/splitButton.mjs";import{StepperAlignment as un,StepperCollapse as dn,StepperLabelPlacement as fn,StepperStyleType as pn}from"./common/stepper/stepper.types.mjs";import mn from"./common/stepper/stepper.mjs";import hn from"./common/sticky/sticky.mjs";import gn from"./common/switch/switch.mjs";import _n from"./common/tabHeader/tabHeader.mjs";import vn from"./common/textarea/textarea.mjs";import{defaultTheme as yn}from"./common/themeProvider/defaultTheme.mjs";import bn from"./common/themeProvider/themeProvider.mjs";import{lightTheme as xn}from"./common/themeProvider/lightTheme.mjs";import{useTheme as Sn}from"./common/themeProvider/useTheme.mjs";import Cn,{TiltGroupContext as wn,useTiltGroup as Tn}from"./common/tiltGroup/tiltGroup.mjs";import{defaultTiltEasing as En}from"./common/tilt/tiltMath.mjs";import Dn from"./common/tilt/tilt.mjs";import On from"./common/timeInput/timeInput.mjs";import{TimelineAlignment as kn,TimelineItemStatus as An}from"./common/timeline/timeline.types.mjs";import jn from"./common/timeline/timeline.mjs";import Mn from"./common/timer/timer.mjs";import Nn from"./common/virtualList/virtualList.mjs";import{flattenTree as Pn}from"./common/tree/flattenTree.mjs";import Fn from"./common/tree/tree.mjs";import In from"./common/wizard/wizard.mjs";import Ln from"./services/formValidation/FormValidatorGroupWithForm.mjs";import Rn from"./services/formValidation/validatableComponent.mjs";import{useFormValidatorGroup as zn}from"./services/formValidation/useFormValidatorGroup.mjs";import{FormValidator as Bn,Validators as Vn,useFormValidator as Hn}from"./services/formValidation/index.mjs";import{hasEightPasswordChars as Un,hasOnePasswordDigit as Wn,hasOnePasswordLetter as Gn,isBic as Kn,isDate as qn,isEmail as Jn,isISODate as Yn,isNumber as Xn,isPassword as Zn,isURL as Qn}from"@ahrowe/form-validation";export{o as Accordion,e as AccordionStyleType,p as ActionButtons,h as ActionIcon,_ as Alert,g as AlertStyleType,v as AnimatedLogo,x as AnimatedText,y as AnimatedTextUnit,b as AnimatedTextVariant,T as Avatar,E as AvatarGroup,w as Badge,S as BadgePosition,C as BadgeStyleType,D as BodyEnd,M as Breadcrumb,f as Button,P as ButtonGroup,N as ButtonGroupOrientation,c as ButtonSize,l as ButtonStyleType,u as ButtonType,I as Card,B as CardActions,z as CardContent,L as CardHeader,R as CardMedia,F as CardStyleType,V as Carousel,H as Checkbox,W as Chip,G as ChipContainer,U as ChipStyleType,X as ColorPicker,t as ConfigProvider,Q as ConfirmModal,_t as DEFAULT_SNAP_OPTIONS,we as DEFAULT_WALL_ATTRS,re as DatePicker,oe as Divider,ie as DividerOrientation,ae as DividerTextPosition,_e as DoorSwing,se as Drawer,ue as DropZone,ce as DropZoneReturnType,le as DropZoneStatus,ne as Dropdown,de as EmptyState,me as ErrorBoundary,he as Fab,ge as Flip,Bn as FormValidator,Ln as FormValidatorGroup,ft as HIT_PX,pt as HIT_PX_COARSE,Pt as IconLoading,Dt as IconPicker,Ot as IdleManager,Y as Input,kt as InputDropdown,q as InputIconPosition,J as InputType,m as InteractableDiv,jt as KanbanBoard,At as KanbanColumn,Nt as KlipyPicker,He as MIN_FLOOR_HEIGHT,Se as MIN_OPENING_WIDTH,Ce as MIN_WALL_LENGTH,Rt as Menu,Z as Modal,zt as MultiSelect,Bt as NumberInput,ve as OpeningKind,Vt as OptionPicker,Ut as OtpInput,Ht as OtpInputCharset,Wt as Overscroll,St as PLAN_FORMAT_VERSION,Gt as Pagination,ye as PlanIssueKind,be as PlanUnitSystem,j as Popover,O as PopoverAlign,k as PopoverPlacement,A as PopoverTrigger,a as Presentation,Kt as ProgressBar,Jt as RadioGroup,qt as RadioGroupOrientation,Yt as Rating,Xt as RevealLens,d as Ripple,Zt as RoomDrawer,Qt as RoomViewer,en as ScrollFade,$t as ScrollFadeAxis,tn as ScrollbarProvider,Mt as SearchInput,rn as SectionHeader,nn as SectionHeaderSize,sn as Skeleton,an as SkeletonAnimation,on as SkeletonVariant,cn as Slider,vt as SnapKind,s as SpinnerLoading,ln as SplitButton,mn as Stepper,un as StepperAlignment,dn as StepperCollapse,fn as StepperLabelPlacement,pn as StepperStyleType,hn as Sticky,gn as Switch,_n as TabHeader,vn as Textarea,bn as ThemeProvider,Dn as Tilt,Cn as TiltGroup,wn as TiltGroupContext,On as TimeInput,jn as Timeline,kn as TimelineAlignment,An as TimelineItemStatus,Mn as Timer,pe as ToastProvider,K as Tooltip,Fn as Tree,Rn as ValidatableComponent,Vn as Validators,Nn as VirtualList,xe as WallType,In as Wizard,Ue as addFloor,Te as addOpening,Ee as addRect,De as addWall,Tt as buildPlanMesh,lt as buildWallPaths,Oe as cleanupPlan,yt as clientToWorld,at as commitDerivation,yn as defaultTheme,En as defaultTiltEasing,rt as deriveFaces,ot as derivePlan,ke as detachWall,Ae as dissolveNode,We as duplicateFloor,Ge as emptyFloor,Ke as emptyFloorPlan,Pn as flattenTree,qe as floorBelow,Je as floorBounds,Ye as floorIndex,Xe as getFloor,$ as getLocale,Un as hasEightPasswordChars,Wn as hasOnePasswordDigit,Gn as hasOnePasswordLetter,mt as hitTest,Kn as isBic,qn as isDate,Jn as isEmail,Yn as isISODate,Ft as isMenuAction,It as isMenuHeading,Lt as isMenuSeparator,Xn as isNumber,Zn as isPassword,Qn as isURL,xn as lightTheme,ee as mapLocale,je as mergeNodes,Ze as moveFloor,Me as moveNode,it as nodeDegrees,ut as openingGeometry,Ct as planFromJson,Et as planFromLegacySvg,wt as planToJson,st as pruneUnmatchedRooms,Qe as removeFloor,Ne as removeOpening,Pe as removeWall,$e as renameFloor,bt as resolveSnap,ct as roomAtPoint,Fe as scalePlan,xt as screenScale,ht as selectionsEqual,et as setFloor,tt as setFloorHeight,te as setLocale,fe as showToast,Ie as splitWall,Le as transformPlan,nt as updateFloor,Re as updateOpening,n as useComponentDefaults,Hn as useFormValidator,zn as useFormValidatorGroup,r as useLabels,i as usePresentationDefault,Sn as useTheme,Tn as useTiltGroup,ze as validatePlan,dt as wallCentrelineSpans,Be as wallLength,Ve as weldShortWalls,gt as worldTolerances};
1
+ "use client";import{AccordionStyleType as e}from"./common/accordion/accordion.types.mjs";import t from"./common/configProvider/configProvider.mjs";import{useComponentDefaults as n}from"./common/configProvider/useComponentDefaults.mjs";import{useLabels as r}from"./common/configProvider/useLabels.mjs";import{usePresentationDefault as i}from"./common/configProvider/usePresentationDefault.mjs";import{Presentation as a}from"./common/configProvider/presentation.types.mjs";import o from"./common/accordion/accordion.mjs";import s from"./common/loading/spinnerLoading/index.mjs";import{ButtonSize as c,ButtonStyleType as l,ButtonType as u}from"./common/button/button.types.mjs";import d from"./common/ripple/ripple.mjs";import f from"./common/button/button.mjs";import p from"./common/actionButtons/actionButtons.mjs";import m from"./common/interactableDiv/index.mjs";import h from"./common/actionIcon/actionIcon.mjs";import{AlertStyleType as g}from"./common/alert/alert.types.mjs";import _ from"./common/alert/alert.mjs";import v from"./common/animatedLogo/animatedLogo.mjs";import{AnimatedTextUnit as y,AnimatedTextVariant as b}from"./common/animatedText/animatedText.types.mjs";import x from"./common/animatedText/animatedText.mjs";import{BadgePosition as S,BadgeStyleType as C}from"./common/badge/badge.types.mjs";import w from"./common/badge/badge.mjs";import T from"./common/avatar/avatar.mjs";import E from"./common/avatar/avatarGroup.mjs";import D from"./common/bodyEnd/bodyEnd.mjs";import{PopoverAlign as O,PopoverPlacement as k,PopoverTrigger as A}from"./common/popover/popover.types.mjs";import{tabbablesIn as j}from"./common/hooks/useFocusBoundary.mjs";import M from"./common/hooks/useMediaQuery.mjs";import N from"./common/hooks/useCoarsePointer.mjs";import{useVisualViewportSize as P}from"./common/hooks/useVisualViewportSize.mjs";import{useOverlay as F}from"./common/hooks/useOverlay.mjs";import I from"./common/popover/popover.mjs";import L from"./common/breadcrumb/breadcrumb.mjs";import{ButtonGroupOrientation as R}from"./common/buttonGroup/buttonGroup.types.mjs";import z from"./common/buttonGroup/buttonGroup.mjs";import{CardStyleType as B}from"./common/card/card.types.mjs";import V from"./common/card/Card.mjs";import H from"./common/card/CardHeader.mjs";import U from"./common/card/CardMedia.mjs";import W from"./common/card/CardContent.mjs";import G from"./common/card/CardActions.mjs";import K from"./common/carousel/carousel.mjs";import q from"./common/checkbox/checkbox.mjs";import{ChipStyleType as J}from"./common/chip/chip.types.mjs";import Y from"./common/chip/chip.mjs";import X from"./common/chip/chipContainer.mjs";import Z from"./common/tooltip/tooltip.mjs";import{InputIconPosition as Q,InputType as $}from"./common/input/input.types.mjs";import ee from"./common/input/input.mjs";import te from"./common/colorPicker/colorPicker.mjs";import{useScrollEdges as ne}from"./common/hooks/useScrollEdges.mjs";import re from"./common/modal/modal.mjs";import ie from"./common/confirmModal/confirmModal.mjs";import{getLocale as ae,mapLocale as oe,setLocale as se}from"./services/localization.mjs";import ce from"./common/dropdown/dropdown.mjs";import le from"./common/datePicker/datePicker.mjs";import{DividerOrientation as ue,DividerTextPosition as de}from"./common/divider/divider.types.mjs";import fe from"./common/divider/divider.mjs";import pe from"./common/drawer/drawer.mjs";import{DropZoneReturnType as me,DropZoneStatus as he}from"./common/dropZone/dropZone.types.mjs";import ge from"./common/dropZone/dropZone.mjs";import _e from"./common/emptyState/emptyState.mjs";import{useSwipeDismiss as ve}from"./common/hooks/useSwipeDismiss.mjs";import{showToast as ye}from"./common/toast/toastService.mjs";import be from"./common/toast/toastProvider.mjs";import xe from"./common/errorBoundary/errorBoundary.mjs";import Se from"./common/fab/fab.mjs";import Ce from"./common/flip/flip.mjs";import{DoorSwing as we,OpeningKind as Te,PlanIssueKind as Ee,PlanUnitSystem as De,WallType as Oe}from"./common/floorPlan/floorPlan.types.mjs";import{MIN_OPENING_WIDTH as ke,MIN_WALL_LENGTH as Ae}from"./common/floorPlan/floorPlan.geometry.mjs";import{DEFAULT_WALL_ATTRS as je,addOpening as Me,addRect as Ne,addWall as Pe,cleanupPlan as Fe,detachWall as Ie,dissolveNode as Le,mergeNodes as Re,moveNode as ze,removeOpening as Be,removeWall as Ve,scalePlan as He,splitWall as Ue,transformPlan as We,updateOpening as Ge,validatePlan as Ke,wallLength as qe,weldShortWalls as Je}from"./common/floorPlan/floorPlan.graph.mjs";import{MIN_FLOOR_HEIGHT as Ye,addFloor as Xe,duplicateFloor as Ze,emptyFloor as Qe,emptyFloorPlan as $e,floorBelow as et,floorBounds as tt,floorIndex as nt,getFloor as rt,moveFloor as it,removeFloor as at,renameFloor as ot,setFloor as st,setFloorHeight as ct,updateFloor as lt}from"./common/floorPlan/floorPlan.plan.mjs";import{deriveFaces as ut,nodeDegrees as dt}from"./common/floorPlan/floorPlan.faces.mjs";import{commitDerivation as ft,derivePlan as pt,pruneUnmatchedRooms as mt,roomAtPoint as ht}from"./common/floorPlan/floorPlan.rooms.mjs";import{buildWallPaths as gt,openingGeometry as _t,wallCentrelineSpans as vt}from"./common/floorPlan/floorPlan.path.mjs";import{HIT_PX as yt,HIT_PX_COARSE as bt,hitTest as xt,selectionsEqual as St,worldTolerances as Ct}from"./common/floorPlan/floorPlan.hit.mjs";import{DEFAULT_SNAP_OPTIONS as wt,SnapKind as Tt,clientToWorld as Et,resolveSnap as Dt,screenScale as Ot}from"./common/floorPlan/floorPlan.snap.mjs";import{PLAN_FORMAT_VERSION as kt,planFromJson as At,planToJson as jt}from"./common/floorPlan/floorPlan.json.mjs";import{buildPlanMesh as Mt}from"./common/floorPlan/floorPlan.mesh.mjs";import{planFromLegacySvg as Nt}from"./common/floorPlan/legacySvg.mjs";import Pt from"./common/iconPicker/iconPicker.mjs";import Ft from"./common/idleManager/idleManager.mjs";import It from"./common/inputDropdown/inputDropdown.mjs";import Lt from"./common/kanbanBoard/KanbanColumn.mjs";import Rt from"./common/kanbanBoard/KanbanBoard.mjs";import zt from"./common/searchInput/searchInput.mjs";import Bt from"./common/klipyPicker/klipyPicker.mjs";import Vt,{formatHotkey as Ht}from"./common/hooks/useHotkeys.mjs";import{isMenuAction as Ut,isMenuHeading as Wt,isMenuSeparator as Gt}from"./common/menu/menu.types.mjs";import Kt from"./common/menu/menu.mjs";import{menuHotkeys as qt}from"./common/menu/menuHotkeys.mjs";import Jt from"./common/multiSelect/multiSelect.mjs";import Yt from"./common/numberInput/index.mjs";import Xt from"./common/optionPicker/optionPicker.mjs";import{OtpInputCharset as Zt}from"./common/otpInput/otpInput.types.mjs";import Qt from"./common/otpInput/otpInput.mjs";import $t from"./common/overscroll/overscroll.mjs";import en from"./common/pagination/pagination.mjs";import tn from"./common/progressBar/progressBar.mjs";import{RadioGroupOrientation as nn}from"./common/radioGroup/radioGroup.types.mjs";import rn from"./common/radioGroup/radioGroup.mjs";import an from"./common/rating/rating.mjs";import on from"./common/revealLens/revealLens.mjs";import sn from"./common/roomDrawer/roomDrawer.mjs";import cn from"./common/roomViewer/roomViewer.mjs";import{ScrollFadeAxis as ln}from"./common/scrollFade/scrollFade.types.mjs";import un from"./common/scrollFade/scrollFade.mjs";import dn from"./common/scrollbarProvider/index.mjs";import{SectionHeaderSize as fn}from"./common/sectionHeader/sectionHeader.types.mjs";import pn from"./common/sectionHeader/sectionHeader.mjs";import{SkeletonAnimation as mn,SkeletonVariant as hn}from"./common/skeleton/skeleton.types.mjs";import gn from"./common/skeleton/skeleton.mjs";import _n from"./common/slider/slider.mjs";import vn from"./common/splitButton/splitButton.mjs";import{StepperAlignment as yn,StepperCollapse as bn,StepperLabelPlacement as xn,StepperStyleType as Sn}from"./common/stepper/stepper.types.mjs";import Cn from"./common/stepper/stepper.mjs";import wn from"./common/sticky/sticky.mjs";import Tn from"./common/switch/switch.mjs";import En from"./common/tabHeader/tabHeader.mjs";import Dn from"./common/textarea/textarea.mjs";import{defaultTheme as On}from"./common/themeProvider/defaultTheme.mjs";import kn from"./common/themeProvider/themeProvider.mjs";import{lightTheme as An}from"./common/themeProvider/lightTheme.mjs";import{useTheme as jn}from"./common/themeProvider/useTheme.mjs";import Mn,{TiltGroupContext as Nn,useTiltGroup as Pn}from"./common/tiltGroup/tiltGroup.mjs";import{defaultTiltEasing as Fn}from"./common/tilt/tiltMath.mjs";import In from"./common/tilt/tilt.mjs";import Ln from"./common/timeInput/timeInput.mjs";import{TimelineAlignment as Rn,TimelineItemStatus as zn}from"./common/timeline/timeline.types.mjs";import Bn from"./common/timeline/timeline.mjs";import Vn from"./common/timer/timer.mjs";import Hn from"./common/virtualList/virtualList.mjs";import{flattenTree as Un}from"./common/tree/flattenTree.mjs";import Wn from"./common/tree/tree.mjs";import Gn from"./common/wizard/wizard.mjs";import Kn from"./services/formValidation/FormValidatorGroupWithForm.mjs";import qn from"./services/formValidation/validatableComponent.mjs";import{useFormValidatorGroup as Jn}from"./services/formValidation/useFormValidatorGroup.mjs";import{FormValidator as Yn,Validators as Xn,useFormValidator as Zn}from"./services/formValidation/index.mjs";import{hasEightPasswordChars as Qn,hasOnePasswordDigit as $n,hasOnePasswordLetter as er,isBic as tr,isDate as nr,isEmail as rr,isISODate as ir,isNumber as ar,isPassword as or,isURL as sr}from"@ahrowe/form-validation";export{o as Accordion,e as AccordionStyleType,p as ActionButtons,h as ActionIcon,_ as Alert,g as AlertStyleType,v as AnimatedLogo,x as AnimatedText,y as AnimatedTextUnit,b as AnimatedTextVariant,T as Avatar,E as AvatarGroup,w as Badge,S as BadgePosition,C as BadgeStyleType,D as BodyEnd,L as Breadcrumb,f as Button,z as ButtonGroup,R as ButtonGroupOrientation,c as ButtonSize,l as ButtonStyleType,u as ButtonType,V as Card,G as CardActions,W as CardContent,H as CardHeader,U as CardMedia,B as CardStyleType,K as Carousel,q as Checkbox,Y as Chip,X as ChipContainer,J as ChipStyleType,te as ColorPicker,t as ConfigProvider,ie as ConfirmModal,wt as DEFAULT_SNAP_OPTIONS,je as DEFAULT_WALL_ATTRS,le as DatePicker,fe as Divider,ue as DividerOrientation,de as DividerTextPosition,we as DoorSwing,pe as Drawer,ge as DropZone,me as DropZoneReturnType,he as DropZoneStatus,ce as Dropdown,_e as EmptyState,xe as ErrorBoundary,Se as Fab,Ce as Flip,Yn as FormValidator,Kn as FormValidatorGroup,yt as HIT_PX,bt as HIT_PX_COARSE,Pt as IconPicker,Ft as IdleManager,ee as Input,It as InputDropdown,Q as InputIconPosition,$ as InputType,m as InteractableDiv,Rt as KanbanBoard,Lt as KanbanColumn,Bt as KlipyPicker,Ye as MIN_FLOOR_HEIGHT,ke as MIN_OPENING_WIDTH,Ae as MIN_WALL_LENGTH,Kt as Menu,re as Modal,Jt as MultiSelect,Yt as NumberInput,Te as OpeningKind,Xt as OptionPicker,Qt as OtpInput,Zt as OtpInputCharset,$t as Overscroll,kt as PLAN_FORMAT_VERSION,en as Pagination,Ee as PlanIssueKind,De as PlanUnitSystem,I as Popover,O as PopoverAlign,k as PopoverPlacement,A as PopoverTrigger,a as Presentation,tn as ProgressBar,rn as RadioGroup,nn as RadioGroupOrientation,an as Rating,on as RevealLens,d as Ripple,sn as RoomDrawer,cn as RoomViewer,un as ScrollFade,ln as ScrollFadeAxis,dn as ScrollbarProvider,zt as SearchInput,pn as SectionHeader,fn as SectionHeaderSize,gn as Skeleton,mn as SkeletonAnimation,hn as SkeletonVariant,_n as Slider,Tt as SnapKind,s as SpinnerLoading,vn as SplitButton,Cn as Stepper,yn as StepperAlignment,bn as StepperCollapse,xn as StepperLabelPlacement,Sn as StepperStyleType,wn as Sticky,Tn as Switch,En as TabHeader,Dn as Textarea,kn as ThemeProvider,In as Tilt,Mn as TiltGroup,Nn as TiltGroupContext,Ln as TimeInput,Bn as Timeline,Rn as TimelineAlignment,zn as TimelineItemStatus,Vn as Timer,be as ToastProvider,Z as Tooltip,Wn as Tree,qn as ValidatableComponent,Xn as Validators,Hn as VirtualList,Oe as WallType,Gn as Wizard,Xe as addFloor,Me as addOpening,Ne as addRect,Pe as addWall,Mt as buildPlanMesh,gt as buildWallPaths,Fe as cleanupPlan,Et as clientToWorld,ft as commitDerivation,On as defaultTheme,Fn as defaultTiltEasing,ut as deriveFaces,pt as derivePlan,Ie as detachWall,Le as dissolveNode,Ze as duplicateFloor,Qe as emptyFloor,$e as emptyFloorPlan,Un as flattenTree,et as floorBelow,tt as floorBounds,nt as floorIndex,Ht as formatHotkey,rt as getFloor,ae as getLocale,Qn as hasEightPasswordChars,$n as hasOnePasswordDigit,er as hasOnePasswordLetter,xt as hitTest,tr as isBic,nr as isDate,rr as isEmail,ir as isISODate,Ut as isMenuAction,Wt as isMenuHeading,Gt as isMenuSeparator,ar as isNumber,or as isPassword,sr as isURL,An as lightTheme,oe as mapLocale,qt as menuHotkeys,Re as mergeNodes,it as moveFloor,ze as moveNode,dt as nodeDegrees,_t as openingGeometry,At as planFromJson,Nt as planFromLegacySvg,jt as planToJson,mt as pruneUnmatchedRooms,at as removeFloor,Be as removeOpening,Ve as removeWall,ot as renameFloor,Dt as resolveSnap,ht as roomAtPoint,He as scalePlan,Ot as screenScale,St as selectionsEqual,st as setFloor,ct as setFloorHeight,se as setLocale,ye as showToast,Ue as splitWall,j as tabbablesIn,We as transformPlan,lt as updateFloor,Ge as updateOpening,N as useCoarsePointer,n as useComponentDefaults,Zn as useFormValidator,Jn as useFormValidatorGroup,Vt as useHotkeys,r as useLabels,M as useMediaQuery,F as useOverlay,i as usePresentationDefault,ne as useScrollEdges,ve as useSwipeDismiss,jn as useTheme,Pn as useTiltGroup,P as useVisualViewportSize,Ke as validatePlan,vt as wallCentrelineSpans,qe as wallLength,Je as weldShortWalls,Ct as worldTolerances};