@vuu-ui/vuu-table 0.8.95 → 0.8.97

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 (71) hide show
  1. package/cjs/Table.css.js +1 -1
  2. package/cjs/Table.js +19 -3
  3. package/cjs/Table.js.map +1 -1
  4. package/cjs/cell-block/CellBlock.css.js +1 -1
  5. package/cjs/cell-block/CellBlock.js +12 -2
  6. package/cjs/cell-block/CellBlock.js.map +1 -1
  7. package/cjs/cell-block/cellblock-utils.js +71 -0
  8. package/cjs/cell-block/cellblock-utils.js.map +1 -1
  9. package/cjs/cell-block/useCellBlockSelection.js +69 -74
  10. package/cjs/cell-block/useCellBlockSelection.js.map +1 -1
  11. package/cjs/cell-renderers/checkbox-row-selector/CheckboxRowSelectorCell.css.js +1 -1
  12. package/cjs/table-cell/TableCell.css.js +1 -1
  13. package/cjs/table-dom-utils.js +67 -10
  14. package/cjs/table-dom-utils.js.map +1 -1
  15. package/cjs/table-header/useTableHeader.js +1 -1
  16. package/cjs/table-header/useTableHeader.js.map +1 -1
  17. package/cjs/useCellFocus.js +98 -0
  18. package/cjs/useCellFocus.js.map +1 -0
  19. package/cjs/useKeyboardNavigation.js +13 -83
  20. package/cjs/useKeyboardNavigation.js.map +1 -1
  21. package/cjs/useMeasuredHeight.js +5 -5
  22. package/cjs/useMeasuredHeight.js.map +1 -1
  23. package/cjs/useTable.js +49 -7
  24. package/cjs/useTable.js.map +1 -1
  25. package/cjs/useTableContextMenu.js +2 -1
  26. package/cjs/useTableContextMenu.js.map +1 -1
  27. package/cjs/useTableModel.js +7 -7
  28. package/cjs/useTableModel.js.map +1 -1
  29. package/cjs/useTableScroll.js +115 -62
  30. package/cjs/useTableScroll.js.map +1 -1
  31. package/esm/Table.css.js +1 -1
  32. package/esm/Table.js +19 -3
  33. package/esm/Table.js.map +1 -1
  34. package/esm/cell-block/CellBlock.css.js +1 -1
  35. package/esm/cell-block/CellBlock.js +13 -3
  36. package/esm/cell-block/CellBlock.js.map +1 -1
  37. package/esm/cell-block/cellblock-utils.js +68 -1
  38. package/esm/cell-block/cellblock-utils.js.map +1 -1
  39. package/esm/cell-block/useCellBlockSelection.js +68 -73
  40. package/esm/cell-block/useCellBlockSelection.js.map +1 -1
  41. package/esm/cell-renderers/checkbox-row-selector/CheckboxRowSelectorCell.css.js +1 -1
  42. package/esm/table-cell/TableCell.css.js +1 -1
  43. package/esm/table-dom-utils.js +64 -10
  44. package/esm/table-dom-utils.js.map +1 -1
  45. package/esm/table-header/useTableHeader.js +1 -1
  46. package/esm/table-header/useTableHeader.js.map +1 -1
  47. package/esm/useCellFocus.js +96 -0
  48. package/esm/useCellFocus.js.map +1 -0
  49. package/esm/useKeyboardNavigation.js +13 -83
  50. package/esm/useKeyboardNavigation.js.map +1 -1
  51. package/esm/useMeasuredHeight.js +5 -5
  52. package/esm/useMeasuredHeight.js.map +1 -1
  53. package/esm/useTable.js +49 -7
  54. package/esm/useTable.js.map +1 -1
  55. package/esm/useTableContextMenu.js +2 -1
  56. package/esm/useTableContextMenu.js.map +1 -1
  57. package/esm/useTableModel.js +7 -7
  58. package/esm/useTableModel.js.map +1 -1
  59. package/esm/useTableScroll.js +115 -62
  60. package/esm/useTableScroll.js.map +1 -1
  61. package/package.json +9 -9
  62. package/types/Table.d.ts +3 -0
  63. package/types/cell-block/CellBlock.d.ts +2 -1
  64. package/types/cell-block/cellblock-utils.d.ts +20 -0
  65. package/types/cell-block/useCellBlockSelection.d.ts +7 -2
  66. package/types/table-dom-utils.d.ts +7 -5
  67. package/types/useCellFocus.d.ts +16 -0
  68. package/types/useKeyboardNavigation.d.ts +9 -7
  69. package/types/useMeasuredHeight.d.ts +2 -2
  70. package/types/useTable.d.ts +4 -1
  71. package/types/useTableScroll.d.ts +15 -5
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCellFocus.js","sources":["../src/useCellFocus.ts"],"sourcesContent":["import {\n KeyboardEventHandler,\n MutableRefObject,\n RefCallback,\n RefObject,\n useCallback,\n} from \"react\";\nimport {\n dataCellQuery,\n getTableCell,\n headerCellQuery,\n} from \"./table-dom-utils\";\nimport { ScrollRequestHandler } from \"./useTableScroll\";\nimport { isArrowKey, queryClosest } from \"@vuu-ui/vuu-utils\";\nimport { CellFocusState, CellPos } from \"@vuu-ui/vuu-table-types\";\n\nexport interface CellFocusHookProps {\n cellFocusStateRef: MutableRefObject<CellFocusState>;\n containerRef: RefObject<HTMLElement>;\n disableFocus?: boolean;\n requestScroll?: ScrollRequestHandler;\n}\n\nconst getCellPosition = (el: HTMLElement) => {\n const top = parseInt(el.parentElement?.style.top ?? \"-1\");\n return { top };\n};\n\nexport type FocusCell = (cellPos: CellPos, fromKeyboard?: boolean) => void;\n\nexport const useCellFocus = ({\n cellFocusStateRef,\n containerRef,\n disableFocus = false,\n requestScroll,\n}: CellFocusHookProps) => {\n const focusCellPlaceholderRef = useCallback<RefCallback<HTMLDivElement>>(\n (el) => (cellFocusStateRef.current.placeholderEl = el),\n [cellFocusStateRef],\n );\n\n const focusCell = useCallback<FocusCell>(\n (cellPos, fromKeyboard = false) => {\n if (containerRef.current) {\n const { current: state } = cellFocusStateRef;\n\n if (fromKeyboard && state.outsideViewport) {\n state.cellPos = cellPos;\n } else {\n const activeCell = getTableCell(containerRef, cellPos);\n if (activeCell) {\n if (activeCell !== state.el) {\n state.el?.removeAttribute(\"tabindex\");\n activeCell.setAttribute(\"tabindex\", \"0\");\n\n // TODO no need to measure if we're navigating horizontally\n state.cellPos = cellPos;\n state.el = activeCell;\n state.pos = getCellPosition(activeCell);\n state.outsideViewport = false;\n\n if (state.placeholderEl) {\n state.placeholderEl.style.top = `${state.pos.top}px`;\n }\n }\n // TODO needs to be scroll cell to accommodate horizontal virtualization\n requestScroll?.({ type: \"scroll-row\", rowIndex: cellPos[0] });\n activeCell.focus({ preventScroll: true });\n }\n }\n }\n },\n [cellFocusStateRef, containerRef, requestScroll],\n );\n\n const tableBodyRef = useCallback<RefCallback<HTMLDivElement>>(\n (el) => {\n if (el) {\n const { current: state } = cellFocusStateRef;\n const table = queryClosest<HTMLDivElement>(el, \".vuuTable\");\n if (table) {\n if (state.el === null && !disableFocus) {\n const cell =\n table.querySelector<HTMLDivElement>(headerCellQuery(0)) ||\n table.querySelector<HTMLDivElement>(dataCellQuery(0, 0));\n if (cell) {\n cell.setAttribute(\"tabindex\", \"0\");\n state.cellPos = [0, 0];\n state.el = cell;\n state.pos = { top: -20 };\n\n if (state.placeholderEl) {\n state.placeholderEl.style.top = `-20px`;\n }\n }\n }\n }\n }\n },\n [cellFocusStateRef, disableFocus],\n );\n\n const focusCellPlaceholderKeyDown = useCallback<KeyboardEventHandler>(\n (evt) => {\n const { outsideViewport, pos } = cellFocusStateRef.current;\n if (pos && isArrowKey(evt.key)) {\n // TODO depends on whether we're scrolling up or down\n if (outsideViewport === \"above\") {\n requestScroll?.({ type: \"scroll-top\", scrollPos: pos.top });\n } else if (outsideViewport === \"below\") {\n requestScroll?.({ type: \"scroll-bottom\", scrollPos: pos.top });\n } else {\n throw Error(\n `cellFocusPlaceholder should not have focus if inside viewport`,\n );\n }\n }\n },\n [cellFocusStateRef, requestScroll],\n );\n\n return {\n focusCell,\n focusCellPlaceholderKeyDown,\n focusCellPlaceholderRef,\n tableBodyRef,\n };\n};\n"],"names":["useCallback","getTableCell","queryClosest","headerCellQuery","dataCellQuery","isArrowKey"],"mappings":"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"}
@@ -28,43 +28,13 @@ const isNavigationKey = (key, navigationStyle) => {
28
28
  };
29
29
  const PageKeys = ["Home", "End", "PageUp", "PageDown"];
30
30
  const isPagingKey = (key) => PageKeys.includes(key);
31
- const NULL_CELL_POS = [-1, -1];
32
- function nextCellPos(key, [rowIdx, colIdx], columnCount, rowCount) {
33
- if (key === "ArrowUp") {
34
- if (rowIdx > -1) {
35
- return [rowIdx - 1, colIdx];
36
- } else {
37
- return [rowIdx, colIdx];
38
- }
39
- } else if (key === "ArrowDown") {
40
- if (rowIdx === -1) {
41
- return [0, colIdx];
42
- } else if (rowIdx === rowCount - 1) {
43
- return [rowIdx, colIdx];
44
- } else {
45
- return [rowIdx + 1, colIdx];
46
- }
47
- } else if (key === "ArrowRight") {
48
- if (colIdx < columnCount) {
49
- return [rowIdx, colIdx + 1];
50
- } else {
51
- return [rowIdx, colIdx];
52
- }
53
- } else if (key === "ArrowLeft") {
54
- if (colIdx > 1) {
55
- return [rowIdx, colIdx - 1];
56
- } else {
57
- return [rowIdx, colIdx];
58
- }
59
- }
60
- return [rowIdx, colIdx];
61
- }
62
31
  const useKeyboardNavigation = ({
32
+ cellFocusStateRef,
63
33
  columnCount = 0,
64
34
  containerRef,
65
- disableFocus = false,
66
35
  defaultHighlightedIndex,
67
36
  disableHighlightOnFocus,
37
+ focusCell,
68
38
  highlightedIndex: highlightedIndexProp,
69
39
  navigationStyle,
70
40
  requestScroll,
@@ -73,7 +43,6 @@ const useKeyboardNavigation = ({
73
43
  viewportRowCount
74
44
  }) => {
75
45
  const focusedCellPos = react.useRef([-1, -1]);
76
- const focusableCell = react.useRef();
77
46
  const activeCellPos = react.useRef([-1, 0]);
78
47
  const highlightedIndexRef = react.useRef();
79
48
  const [highlightedIndex, setHighlightedIdx] = core.useControlled({
@@ -83,7 +52,7 @@ const useKeyboardNavigation = ({
83
52
  });
84
53
  highlightedIndexRef.current = highlightedIndex;
85
54
  const setHighlightedIndex = react.useCallback(
86
- (idx, fromKeyboard = false) => {
55
+ (idx) => {
87
56
  onHighlight?.(idx);
88
57
  setHighlightedIdx(idx);
89
58
  },
@@ -92,39 +61,6 @@ const useKeyboardNavigation = ({
92
61
  const getFocusedCell = (element) => element?.closest(
93
62
  "[role='columnHeader'],[role='cell']"
94
63
  );
95
- const getTableCellPos = (tableCell) => {
96
- if (tableCell.role === "columnHeader") {
97
- const colIdx = parseInt(tableCell.dataset.idx ?? "-1", 10);
98
- return [-1, colIdx];
99
- } else {
100
- const focusedRow = tableCell.closest("[role='row']");
101
- if (focusedRow) {
102
- const rowIdx = vuuUtils.getIndexFromRowElement(focusedRow);
103
- const colIdx = Array.from(focusedRow.childNodes).indexOf(tableCell);
104
- return [rowIdx, colIdx];
105
- }
106
- }
107
- return NULL_CELL_POS;
108
- };
109
- const focusCell = react.useCallback(
110
- (cellPos) => {
111
- if (containerRef.current) {
112
- const activeCell = tableDomUtils.getTableCell(containerRef, cellPos);
113
- if (activeCell) {
114
- if (activeCell !== focusableCell.current) {
115
- focusableCell.current?.removeAttribute("tabindex");
116
- focusableCell.current = activeCell;
117
- activeCell.setAttribute("tabindex", "0");
118
- }
119
- requestScroll?.({ type: "scroll-row", rowIndex: cellPos[0] });
120
- activeCell.focus({ preventScroll: true });
121
- }
122
- }
123
- },
124
- // TODO we recreate this function whenever viewportRange changes, which will
125
- // be often whilst scrolling - store range in a a ref ?
126
- [containerRef, requestScroll]
127
- );
128
64
  const setActiveCell = react.useCallback(
129
65
  (rowIdx, colIdx, fromKeyboard = false) => {
130
66
  const pos = [rowIdx, colIdx];
@@ -132,7 +68,7 @@ const useKeyboardNavigation = ({
132
68
  if (navigationStyle === "row") {
133
69
  setHighlightedIdx(rowIdx);
134
70
  } else {
135
- focusCell(pos);
71
+ focusCell(pos, fromKeyboard);
136
72
  }
137
73
  if (fromKeyboard) {
138
74
  focusedCellPos.current = pos;
@@ -143,10 +79,12 @@ const useKeyboardNavigation = ({
143
79
  const nextPageItemIdx = react.useCallback(
144
80
  (key, [rowIdx, colIdx]) => new Promise((resolve) => {
145
81
  let newRowIdx = rowIdx;
82
+ const { current: focusState } = cellFocusStateRef;
146
83
  switch (key) {
147
84
  case "PageDown": {
148
85
  newRowIdx = Math.min(rowCount - 1, rowIdx + viewportRowCount);
149
86
  if (newRowIdx !== rowIdx) {
87
+ focusState.cellPos = [newRowIdx, colIdx];
150
88
  requestScroll?.({ type: "scroll-page", direction: "down" });
151
89
  }
152
90
  break;
@@ -154,6 +92,7 @@ const useKeyboardNavigation = ({
154
92
  case "PageUp": {
155
93
  newRowIdx = Math.max(0, rowIdx - viewportRowCount);
156
94
  if (newRowIdx !== rowIdx) {
95
+ focusState.cellPos = [newRowIdx, colIdx];
157
96
  requestScroll?.({ type: "scroll-page", direction: "up" });
158
97
  }
159
98
  break;
@@ -161,6 +100,7 @@ const useKeyboardNavigation = ({
161
100
  case "Home": {
162
101
  newRowIdx = 0;
163
102
  if (newRowIdx !== rowIdx) {
103
+ focusState.cellPos = [0, colIdx];
164
104
  requestScroll?.({ type: "scroll-end", direction: "home" });
165
105
  }
166
106
  break;
@@ -168,6 +108,7 @@ const useKeyboardNavigation = ({
168
108
  case "End": {
169
109
  newRowIdx = rowCount - 1;
170
110
  if (newRowIdx !== rowIdx) {
111
+ focusState.cellPos = [newRowIdx, colIdx];
171
112
  requestScroll?.({ type: "scroll-end", direction: "end" });
172
113
  }
173
114
  break;
@@ -184,7 +125,7 @@ const useKeyboardNavigation = ({
184
125
  if (containerRef.current?.contains(document.activeElement)) {
185
126
  const focusedCell = getFocusedCell(document.activeElement);
186
127
  if (focusedCell) {
187
- focusedCellPos.current = getTableCellPos(focusedCell);
128
+ focusedCellPos.current = tableDomUtils.getTableCellPos(focusedCell);
188
129
  if (navigationStyle === "row") {
189
130
  setHighlightedIdx(focusedCellPos.current[0]);
190
131
  }
@@ -199,7 +140,7 @@ const useKeyboardNavigation = ({
199
140
  ]);
200
141
  const navigateChildItems = react.useCallback(
201
142
  async (key) => {
202
- const [nextRowIdx, nextColIdx] = isPagingKey(key) ? await nextPageItemIdx(key, activeCellPos.current) : nextCellPos(key, activeCellPos.current, columnCount, rowCount);
143
+ const [nextRowIdx, nextColIdx] = isPagingKey(key) ? await nextPageItemIdx(key, activeCellPos.current) : tableDomUtils.getNextCellPos(key, activeCellPos.current, columnCount, rowCount);
203
144
  const [rowIdx, colIdx] = activeCellPos.current;
204
145
  if (nextRowIdx !== rowIdx || nextColIdx !== colIdx) {
205
146
  setActiveCell(nextRowIdx, nextColIdx, true);
@@ -216,7 +157,7 @@ const useKeyboardNavigation = ({
216
157
  const moveHighlightedRow = react.useCallback(
217
158
  async (key) => {
218
159
  const { current: highlighted } = highlightedIndexRef;
219
- const [nextRowIdx] = isPagingKey(key) ? await nextPageItemIdx(key, [highlighted ?? -1, 0]) : nextCellPos(key, [highlighted ?? -1, 0], columnCount, rowCount);
160
+ const [nextRowIdx] = isPagingKey(key) ? await nextPageItemIdx(key, [highlighted ?? -1, 0]) : tableDomUtils.getNextCellPos(key, [highlighted ?? -1, 0], columnCount, rowCount);
220
161
  if (nextRowIdx !== highlighted) {
221
162
  setHighlightedIndex(nextRowIdx);
222
163
  scrollRowIntoViewIfNecessary(nextRowIdx);
@@ -261,7 +202,7 @@ const useKeyboardNavigation = ({
261
202
  const target = evt.target;
262
203
  const focusedCell = getFocusedCell(target);
263
204
  if (focusedCell) {
264
- const [rowIdx, colIdx] = getTableCellPos(focusedCell);
205
+ const [rowIdx, colIdx] = tableDomUtils.getTableCellPos(focusedCell);
265
206
  setActiveCell(rowIdx, colIdx);
266
207
  }
267
208
  },
@@ -282,17 +223,6 @@ const useKeyboardNavigation = ({
282
223
  const navigate = react.useCallback(() => {
283
224
  navigateChildItems("ArrowDown");
284
225
  }, [navigateChildItems]);
285
- const fullyRendered = containerRef.current?.firstChild != null;
286
- react.useEffect(() => {
287
- if (fullyRendered && focusableCell.current === void 0 && !disableFocus) {
288
- const { current: container } = containerRef;
289
- const cell = container?.querySelector(tableDomUtils.headerCellQuery(0)) || container?.querySelector(tableDomUtils.dataCellQuery(0, 0));
290
- if (cell) {
291
- cell.setAttribute("tabindex", "0");
292
- focusableCell.current = cell;
293
- }
294
- }
295
- }, [containerRef, disableFocus, fullyRendered]);
296
226
  return {
297
227
  highlightedIndexRef,
298
228
  navigate,
@@ -1 +1 @@
1
- {"version":3,"file":"useKeyboardNavigation.js","sources":["../src/useKeyboardNavigation.ts"],"sourcesContent":["import { VuuRange } from \"@vuu-ui/vuu-protocol-types\";\nimport { getIndexFromRowElement, queryClosest } from \"@vuu-ui/vuu-utils\";\nimport { useControlled } from \"@salt-ds/core\";\nimport {\n KeyboardEvent,\n MouseEvent,\n RefObject,\n useCallback,\n useEffect,\n useRef,\n} from \"react\";\nimport { TableNavigationStyle } from \"./Table\";\nimport {\n CellPos,\n cellDropdownShowing,\n closestRowIndex,\n dataCellQuery,\n getTableCell,\n headerCellQuery,\n} from \"./table-dom-utils\";\nimport { ScrollRequestHandler } from \"./useTableScroll\";\n\nconst rowNavigationKeys = new Set<NavigationKey>([\n \"Home\",\n \"End\",\n \"PageUp\",\n \"PageDown\",\n \"ArrowDown\",\n \"ArrowUp\",\n]);\n\nconst cellNavigationKeys = new Set(rowNavigationKeys);\ncellNavigationKeys.add(\"ArrowLeft\");\ncellNavigationKeys.add(\"ArrowRight\");\n\nexport const isNavigationKey = (\n key: string,\n navigationStyle: TableNavigationStyle,\n): key is NavigationKey => {\n switch (navigationStyle) {\n case \"cell\":\n return cellNavigationKeys.has(key as NavigationKey);\n case \"row\":\n return rowNavigationKeys.has(key as NavigationKey);\n default:\n return false;\n }\n};\n\ntype ArrowKey = \"ArrowUp\" | \"ArrowDown\" | \"ArrowLeft\" | \"ArrowRight\";\ntype PageKey = \"Home\" | \"End\" | \"PageUp\" | \"PageDown\";\ntype NavigationKey = PageKey | ArrowKey;\n\nconst PageKeys = [\"Home\", \"End\", \"PageUp\", \"PageDown\"];\nexport const isPagingKey = (key: string): key is PageKey =>\n PageKeys.includes(key);\n\nconst NULL_CELL_POS: CellPos = [-1, -1];\n\nfunction nextCellPos(\n key: ArrowKey,\n [rowIdx, colIdx]: CellPos,\n columnCount: number,\n rowCount: number,\n): CellPos {\n if (key === \"ArrowUp\") {\n if (rowIdx > -1) {\n return [rowIdx - 1, colIdx];\n } else {\n return [rowIdx, colIdx];\n }\n } else if (key === \"ArrowDown\") {\n if (rowIdx === -1) {\n return [0, colIdx];\n } else if (rowIdx === rowCount - 1) {\n return [rowIdx, colIdx];\n } else {\n return [rowIdx + 1, colIdx];\n }\n } else if (key === \"ArrowRight\") {\n // The colIdx is 1 based, because of the selection decorator\n if (colIdx < columnCount) {\n return [rowIdx, colIdx + 1];\n } else {\n return [rowIdx, colIdx];\n }\n } else if (key === \"ArrowLeft\") {\n if (colIdx > 1) {\n return [rowIdx, colIdx - 1];\n } else {\n return [rowIdx, colIdx];\n }\n }\n return [rowIdx, colIdx];\n}\n\nexport interface NavigationHookProps {\n containerRef: RefObject<HTMLElement>;\n columnCount?: number;\n defaultHighlightedIndex?: number;\n disableFocus?: boolean;\n disableHighlightOnFocus?: boolean;\n highlightedIndex?: number;\n label?: string;\n navigationStyle: TableNavigationStyle;\n viewportRange: VuuRange;\n onHighlight?: (idx: number) => void;\n requestScroll?: ScrollRequestHandler;\n restoreLastFocus?: boolean;\n rowCount?: number;\n selected?: unknown;\n viewportRowCount: number;\n}\n\nexport const useKeyboardNavigation = ({\n columnCount = 0,\n containerRef,\n disableFocus = false,\n defaultHighlightedIndex,\n disableHighlightOnFocus,\n highlightedIndex: highlightedIndexProp,\n navigationStyle,\n requestScroll,\n onHighlight,\n rowCount = 0,\n viewportRowCount,\n}: // viewportRange,\nNavigationHookProps) => {\n // const { from: viewportFirstRow, to: viewportLastRow } = viewportRange;\n const focusedCellPos = useRef<CellPos>([-1, -1]);\n const focusableCell = useRef<HTMLElement>();\n const activeCellPos = useRef<CellPos>([-1, 0]);\n // Keep this in sync with state value. This can be used by functions that need\n // to reference highlightedIndex at call time but do not need to be regenerated\n // every time it changes (i.e keep highlightedIndex out of their dependency\n // arrays, as it can update frequently)\n const highlightedIndexRef = useRef<number | undefined>();\n\n const [highlightedIndex, setHighlightedIdx] = useControlled({\n controlled: highlightedIndexProp,\n default: defaultHighlightedIndex,\n name: \"UseKeyboardNavigation\",\n });\n highlightedIndexRef.current = highlightedIndex;\n const setHighlightedIndex = useCallback(\n (idx: number, fromKeyboard = false) => {\n onHighlight?.(idx);\n setHighlightedIdx(idx);\n if (fromKeyboard) {\n // lastFocus.current = idx;\n }\n },\n [onHighlight, setHighlightedIdx],\n );\n\n const getFocusedCell = (element: HTMLElement | Element | null) =>\n element?.closest(\n \"[role='columnHeader'],[role='cell']\",\n ) as HTMLDivElement | null;\n\n const getTableCellPos = (tableCell: HTMLDivElement): CellPos => {\n if (tableCell.role === \"columnHeader\") {\n const colIdx = parseInt(tableCell.dataset.idx ?? \"-1\", 10);\n return [-1, colIdx];\n } else {\n const focusedRow = tableCell.closest(\"[role='row']\") as HTMLElement;\n if (focusedRow) {\n const rowIdx = getIndexFromRowElement(focusedRow);\n // TODO will get trickier when we introduce horizontal virtualisation\n const colIdx = Array.from(focusedRow.childNodes).indexOf(tableCell);\n return [rowIdx, colIdx];\n }\n }\n return NULL_CELL_POS;\n };\n\n const focusCell = useCallback(\n (cellPos: CellPos) => {\n if (containerRef.current) {\n const activeCell = getTableCell(containerRef, cellPos);\n if (activeCell) {\n if (activeCell !== focusableCell.current) {\n focusableCell.current?.removeAttribute(\"tabindex\");\n focusableCell.current = activeCell;\n activeCell.setAttribute(\"tabindex\", \"0\");\n }\n // TODO needs to be scroll cell\n requestScroll?.({ type: \"scroll-row\", rowIndex: cellPos[0] });\n activeCell.focus({ preventScroll: true });\n }\n }\n },\n // TODO we recreate this function whenever viewportRange changes, which will\n // be often whilst scrolling - store range in a a ref ?\n [containerRef, requestScroll],\n );\n\n const setActiveCell = useCallback(\n (rowIdx: number, colIdx: number, fromKeyboard = false) => {\n const pos: CellPos = [rowIdx, colIdx];\n activeCellPos.current = pos;\n if (navigationStyle === \"row\") {\n setHighlightedIdx(rowIdx);\n } else {\n focusCell(pos);\n }\n if (fromKeyboard) {\n focusedCellPos.current = pos;\n }\n },\n [focusCell, navigationStyle, setHighlightedIdx],\n );\n\n const nextPageItemIdx = useCallback(\n (\n key: \"PageDown\" | \"PageUp\" | \"Home\" | \"End\",\n [rowIdx, colIdx]: CellPos,\n ): Promise<CellPos> =>\n new Promise((resolve) => {\n let newRowIdx = rowIdx;\n switch (key) {\n case \"PageDown\": {\n newRowIdx = Math.min(rowCount - 1, rowIdx + viewportRowCount);\n if (newRowIdx !== rowIdx) {\n requestScroll?.({ type: \"scroll-page\", direction: \"down\" });\n }\n break;\n }\n case \"PageUp\": {\n newRowIdx = Math.max(0, rowIdx - viewportRowCount);\n if (newRowIdx !== rowIdx) {\n requestScroll?.({ type: \"scroll-page\", direction: \"up\" });\n }\n break;\n }\n case \"Home\": {\n newRowIdx = 0;\n if (newRowIdx !== rowIdx) {\n requestScroll?.({ type: \"scroll-end\", direction: \"home\" });\n }\n break;\n }\n case \"End\": {\n newRowIdx = rowCount - 1;\n if (newRowIdx !== rowIdx) {\n requestScroll?.({ type: \"scroll-end\", direction: \"end\" });\n }\n break;\n }\n }\n // Introduce a delay to allow the scroll operation to complete,\n // which will trigger a range reset and rerender of rows. We\n // might need to tweak how this works. If we introduce too big\n // a delay, we risk seeing the newly rendered rows, with the focus\n // still on the old cell, which will be apparent as a brief flash\n // of the old cell focus before switching to correct cell. If we were\n // to change the way re assign keys such that we can guarantee that\n // when we page down, rows in same position get same keys, then same\n // cell would be focussed in new page as previous and issue would not\n // arise.\n setTimeout(() => {\n resolve([newRowIdx, colIdx]);\n }, 35);\n }),\n [requestScroll, rowCount, viewportRowCount],\n );\n\n const handleFocus = useCallback(() => {\n if (disableHighlightOnFocus !== true) {\n if (containerRef.current?.contains(document.activeElement)) {\n // IF focus arrives via keyboard, a cell will have received focus,\n // we handle that here. If focus arrives via click on a cell with\n // no tabindex (i.e all cells except one) we leave that to the\n // click handler.\n const focusedCell = getFocusedCell(document.activeElement);\n if (focusedCell) {\n focusedCellPos.current = getTableCellPos(focusedCell);\n if (navigationStyle === \"row\") {\n setHighlightedIdx(focusedCellPos.current[0]);\n }\n }\n }\n }\n }, [\n disableHighlightOnFocus,\n containerRef,\n navigationStyle,\n setHighlightedIdx,\n ]);\n\n const navigateChildItems = useCallback(\n async (key: NavigationKey) => {\n const [nextRowIdx, nextColIdx] = isPagingKey(key)\n ? await nextPageItemIdx(key, activeCellPos.current)\n : nextCellPos(key, activeCellPos.current, columnCount, rowCount);\n const [rowIdx, colIdx] = activeCellPos.current;\n if (nextRowIdx !== rowIdx || nextColIdx !== colIdx) {\n setActiveCell(nextRowIdx, nextColIdx, true);\n }\n },\n [columnCount, nextPageItemIdx, rowCount, setActiveCell],\n );\n\n const scrollRowIntoViewIfNecessary = useCallback(\n (rowIndex: number) => {\n requestScroll?.({ type: \"scroll-row\", rowIndex });\n },\n [requestScroll],\n );\n\n const moveHighlightedRow = useCallback(\n async (key: NavigationKey) => {\n const { current: highlighted } = highlightedIndexRef;\n const [nextRowIdx] = isPagingKey(key)\n ? await nextPageItemIdx(key, [highlighted ?? -1, 0])\n : nextCellPos(key, [highlighted ?? -1, 0], columnCount, rowCount);\n if (nextRowIdx !== highlighted) {\n setHighlightedIndex(nextRowIdx);\n // TO(DO make this a scroll request)\n scrollRowIntoViewIfNecessary(nextRowIdx);\n }\n },\n [\n columnCount,\n nextPageItemIdx,\n rowCount,\n scrollRowIntoViewIfNecessary,\n setHighlightedIndex,\n ],\n );\n\n useEffect(() => {\n if (highlightedIndexProp !== undefined && highlightedIndexProp !== -1) {\n requestAnimationFrame(() => {\n // deferred call, ensuring table has fully rendered\n scrollRowIntoViewIfNecessary(highlightedIndexProp);\n });\n }\n }, [highlightedIndexProp, scrollRowIntoViewIfNecessary]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n const cell = queryClosest<HTMLDivElement>(e.target, \".vuuTableCell\");\n if (cellDropdownShowing(cell)) {\n return;\n }\n if (rowCount > 0 && isNavigationKey(e.key, navigationStyle)) {\n e.preventDefault();\n e.stopPropagation();\n if (navigationStyle === \"row\") {\n moveHighlightedRow(e.key);\n } else {\n void navigateChildItems(e.key);\n }\n }\n },\n [rowCount, navigationStyle, moveHighlightedRow, navigateChildItems],\n );\n\n const handleClick = useCallback(\n // Might not be a cell e.g the Settings button\n (evt: MouseEvent) => {\n const target = evt.target as HTMLElement;\n const focusedCell = getFocusedCell(target);\n if (focusedCell) {\n const [rowIdx, colIdx] = getTableCellPos(focusedCell);\n setActiveCell(rowIdx, colIdx);\n }\n },\n [setActiveCell],\n );\n\n const handleMouseLeave = useCallback(() => {\n setHighlightedIndex(-1);\n }, [setHighlightedIndex]);\n\n const handleMouseMove = useCallback(\n (evt: MouseEvent) => {\n const idx = closestRowIndex(evt.target as HTMLElement);\n if (idx !== -1 && idx !== highlightedIndexRef.current) {\n setHighlightedIndex(idx);\n }\n },\n [setHighlightedIndex],\n );\n\n const navigate = useCallback(() => {\n navigateChildItems(\"ArrowDown\");\n }, [navigateChildItems]);\n\n // First render will only render the outer container when explicit\n // sizing has not been provided. Outer container is measured and\n // only then, on second render, is content rendered.\n const fullyRendered = containerRef.current?.firstChild != null;\n useEffect(() => {\n if (fullyRendered && focusableCell.current === undefined && !disableFocus) {\n const { current: container } = containerRef;\n const cell = (container?.querySelector(headerCellQuery(0)) ||\n container?.querySelector(dataCellQuery(0, 0))) as HTMLElement;\n if (cell) {\n cell.setAttribute(\"tabindex\", \"0\");\n focusableCell.current = cell;\n }\n }\n }, [containerRef, disableFocus, fullyRendered]);\n\n return {\n highlightedIndexRef,\n navigate,\n onClick: handleClick,\n onFocus: handleFocus,\n onKeyDown: handleKeyDown,\n onMouseLeave: navigationStyle === \"row\" ? handleMouseLeave : undefined,\n onMouseMove: navigationStyle === \"row\" ? handleMouseMove : undefined,\n };\n};\n"],"names":["useRef","useControlled","useCallback","getIndexFromRowElement","getTableCell","useEffect","queryClosest","cellDropdownShowing","closestRowIndex","headerCellQuery","dataCellQuery"],"mappings":"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"}
1
+ {"version":3,"file":"useKeyboardNavigation.js","sources":["../src/useKeyboardNavigation.ts"],"sourcesContent":["import { VuuRange } from \"@vuu-ui/vuu-protocol-types\";\nimport { PageKey, queryClosest } from \"@vuu-ui/vuu-utils\";\nimport { useControlled } from \"@salt-ds/core\";\nimport {\n KeyboardEvent,\n MouseEvent,\n MutableRefObject,\n RefObject,\n useCallback,\n useEffect,\n useRef,\n} from \"react\";\nimport { TableNavigationStyle } from \"./Table\";\nimport {\n NavigationKey,\n cellDropdownShowing,\n closestRowIndex,\n getTableCellPos,\n getNextCellPos,\n} from \"./table-dom-utils\";\nimport { ScrollRequestHandler } from \"./useTableScroll\";\nimport { FocusCell } from \"./useCellFocus\";\nimport { CellFocusState, CellPos } from \"@vuu-ui/vuu-table-types\";\n\nconst rowNavigationKeys = new Set<NavigationKey>([\n \"Home\",\n \"End\",\n \"PageUp\",\n \"PageDown\",\n \"ArrowDown\",\n \"ArrowUp\",\n]);\n\nconst cellNavigationKeys = new Set(rowNavigationKeys);\ncellNavigationKeys.add(\"ArrowLeft\");\ncellNavigationKeys.add(\"ArrowRight\");\n\nexport const isNavigationKey = (\n key: string,\n navigationStyle: TableNavigationStyle,\n): key is NavigationKey => {\n switch (navigationStyle) {\n case \"cell\":\n return cellNavigationKeys.has(key as NavigationKey);\n case \"row\":\n return rowNavigationKeys.has(key as NavigationKey);\n default:\n return false;\n }\n};\n\nconst PageKeys = [\"Home\", \"End\", \"PageUp\", \"PageDown\"];\nexport const isPagingKey = (key: string): key is PageKey =>\n PageKeys.includes(key);\n\nexport interface NavigationHookProps {\n cellFocusStateRef: MutableRefObject<CellFocusState>;\n containerRef: RefObject<HTMLElement>;\n columnCount?: number;\n defaultHighlightedIndex?: number;\n disableFocus?: boolean;\n disableHighlightOnFocus?: boolean;\n focusCell: FocusCell;\n highlightedIndex?: number;\n label?: string;\n navigationStyle: TableNavigationStyle;\n viewportRange: VuuRange;\n onHighlight?: (idx: number) => void;\n requestScroll?: ScrollRequestHandler;\n restoreLastFocus?: boolean;\n rowCount?: number;\n selected?: unknown;\n viewportRowCount: number;\n}\n\nexport const useKeyboardNavigation = ({\n cellFocusStateRef,\n columnCount = 0,\n containerRef,\n defaultHighlightedIndex,\n disableHighlightOnFocus,\n focusCell,\n highlightedIndex: highlightedIndexProp,\n navigationStyle,\n requestScroll,\n onHighlight,\n rowCount = 0,\n viewportRowCount,\n}: NavigationHookProps) => {\n const focusedCellPos = useRef<CellPos>([-1, -1]);\n const activeCellPos = useRef<CellPos>([-1, 0]);\n // Keep this in sync with state value. This can be used by functions that need\n // to reference highlightedIndex at call time but do not need to be regenerated\n // every time it changes (i.e keep highlightedIndex out of their dependency\n // arrays, as it can update frequently)\n const highlightedIndexRef = useRef<number | undefined>();\n\n const [highlightedIndex, setHighlightedIdx] = useControlled({\n controlled: highlightedIndexProp,\n default: defaultHighlightedIndex,\n name: \"UseKeyboardNavigation\",\n });\n highlightedIndexRef.current = highlightedIndex;\n const setHighlightedIndex = useCallback(\n (idx: number) => {\n onHighlight?.(idx);\n setHighlightedIdx(idx);\n },\n [onHighlight, setHighlightedIdx],\n );\n\n const getFocusedCell = (element: HTMLElement | Element | null) =>\n element?.closest(\n \"[role='columnHeader'],[role='cell']\",\n ) as HTMLDivElement | null;\n\n const setActiveCell = useCallback(\n (rowIdx: number, colIdx: number, fromKeyboard = false) => {\n const pos: CellPos = [rowIdx, colIdx];\n // TODO do we still need this when we have cellFocusStateRef ?\n activeCellPos.current = pos;\n if (navigationStyle === \"row\") {\n setHighlightedIdx(rowIdx);\n } else {\n focusCell(pos, fromKeyboard);\n }\n if (fromKeyboard) {\n focusedCellPos.current = pos;\n }\n },\n [focusCell, navigationStyle, setHighlightedIdx],\n );\n\n const nextPageItemIdx = useCallback(\n (\n key: \"PageDown\" | \"PageUp\" | \"Home\" | \"End\",\n [rowIdx, colIdx]: CellPos,\n ): Promise<CellPos> =>\n new Promise((resolve) => {\n let newRowIdx = rowIdx;\n const { current: focusState } = cellFocusStateRef;\n switch (key) {\n case \"PageDown\": {\n newRowIdx = Math.min(rowCount - 1, rowIdx + viewportRowCount);\n if (newRowIdx !== rowIdx) {\n focusState.cellPos = [newRowIdx, colIdx];\n requestScroll?.({ type: \"scroll-page\", direction: \"down\" });\n }\n break;\n }\n case \"PageUp\": {\n newRowIdx = Math.max(0, rowIdx - viewportRowCount);\n if (newRowIdx !== rowIdx) {\n focusState.cellPos = [newRowIdx, colIdx];\n requestScroll?.({ type: \"scroll-page\", direction: \"up\" });\n }\n break;\n }\n case \"Home\": {\n newRowIdx = 0;\n if (newRowIdx !== rowIdx) {\n focusState.cellPos = [0, colIdx];\n requestScroll?.({ type: \"scroll-end\", direction: \"home\" });\n }\n break;\n }\n case \"End\": {\n newRowIdx = rowCount - 1;\n if (newRowIdx !== rowIdx) {\n focusState.cellPos = [newRowIdx, colIdx];\n requestScroll?.({ type: \"scroll-end\", direction: \"end\" });\n }\n break;\n }\n }\n // Introduce a delay to allow the scroll operation to complete,\n // which will trigger a range reset and rerender of rows. We\n // might need to tweak how this works. If we introduce too big\n // a delay, we risk seeing the newly rendered rows, with the focus\n // still on the old cell, which will be apparent as a brief flash\n // of the old cell focus before switching to correct cell. If we were\n // to change the way re assign keys such that we can guarantee that\n // when we page down, rows in same position get same keys, then same\n // cell would be focussed in new page as previous and issue would not\n // arise.\n setTimeout(() => {\n resolve([newRowIdx, colIdx]);\n }, 35);\n }),\n [requestScroll, rowCount, viewportRowCount],\n );\n\n const handleFocus = useCallback(() => {\n if (disableHighlightOnFocus !== true) {\n if (containerRef.current?.contains(document.activeElement)) {\n // IF focus arrives via keyboard, a cell will have received focus,\n // we handle that here. If focus arrives via click on a cell with\n // no tabindex (i.e all cells except one) we leave that to the\n // click handler.\n const focusedCell = getFocusedCell(document.activeElement);\n if (focusedCell) {\n focusedCellPos.current = getTableCellPos(focusedCell);\n if (navigationStyle === \"row\") {\n setHighlightedIdx(focusedCellPos.current[0]);\n }\n }\n }\n }\n }, [\n disableHighlightOnFocus,\n containerRef,\n navigationStyle,\n setHighlightedIdx,\n ]);\n\n const navigateChildItems = useCallback(\n async (key: NavigationKey) => {\n const [nextRowIdx, nextColIdx] = isPagingKey(key)\n ? await nextPageItemIdx(key, activeCellPos.current)\n : getNextCellPos(key, activeCellPos.current, columnCount, rowCount);\n const [rowIdx, colIdx] = activeCellPos.current;\n if (nextRowIdx !== rowIdx || nextColIdx !== colIdx) {\n setActiveCell(nextRowIdx, nextColIdx, true);\n }\n },\n [columnCount, nextPageItemIdx, rowCount, setActiveCell],\n );\n\n const scrollRowIntoViewIfNecessary = useCallback(\n (rowIndex: number) => {\n requestScroll?.({ type: \"scroll-row\", rowIndex });\n },\n [requestScroll],\n );\n\n const moveHighlightedRow = useCallback(\n async (key: NavigationKey) => {\n const { current: highlighted } = highlightedIndexRef;\n const [nextRowIdx] = isPagingKey(key)\n ? await nextPageItemIdx(key, [highlighted ?? -1, 0])\n : getNextCellPos(key, [highlighted ?? -1, 0], columnCount, rowCount);\n if (nextRowIdx !== highlighted) {\n setHighlightedIndex(nextRowIdx);\n // TO(DO make this a scroll request)\n scrollRowIntoViewIfNecessary(nextRowIdx);\n }\n },\n [\n columnCount,\n nextPageItemIdx,\n rowCount,\n scrollRowIntoViewIfNecessary,\n setHighlightedIndex,\n ],\n );\n\n useEffect(() => {\n if (highlightedIndexProp !== undefined && highlightedIndexProp !== -1) {\n requestAnimationFrame(() => {\n // deferred call, ensuring table has fully rendered\n scrollRowIntoViewIfNecessary(highlightedIndexProp);\n });\n }\n }, [highlightedIndexProp, scrollRowIntoViewIfNecessary]);\n\n const handleKeyDown = useCallback(\n (e: KeyboardEvent) => {\n const cell = queryClosest<HTMLDivElement>(e.target, \".vuuTableCell\");\n if (cellDropdownShowing(cell)) {\n return;\n }\n if (rowCount > 0 && isNavigationKey(e.key, navigationStyle)) {\n e.preventDefault();\n e.stopPropagation();\n if (navigationStyle === \"row\") {\n moveHighlightedRow(e.key);\n } else {\n void navigateChildItems(e.key);\n }\n }\n },\n [rowCount, navigationStyle, moveHighlightedRow, navigateChildItems],\n );\n\n const handleClick = useCallback(\n // Might not be a cell e.g the Settings button\n (evt: MouseEvent) => {\n const target = evt.target as HTMLElement;\n const focusedCell = getFocusedCell(target);\n if (focusedCell) {\n const [rowIdx, colIdx] = getTableCellPos(focusedCell);\n setActiveCell(rowIdx, colIdx);\n }\n },\n [setActiveCell],\n );\n\n const handleMouseLeave = useCallback(() => {\n setHighlightedIndex(-1);\n }, [setHighlightedIndex]);\n\n const handleMouseMove = useCallback(\n (evt: MouseEvent) => {\n const idx = closestRowIndex(evt.target as HTMLElement);\n if (idx !== -1 && idx !== highlightedIndexRef.current) {\n setHighlightedIndex(idx);\n }\n },\n [setHighlightedIndex],\n );\n\n const navigate = useCallback(() => {\n navigateChildItems(\"ArrowDown\");\n }, [navigateChildItems]);\n\n return {\n highlightedIndexRef,\n navigate,\n onClick: handleClick,\n onFocus: handleFocus,\n onKeyDown: handleKeyDown,\n onMouseLeave: navigationStyle === \"row\" ? handleMouseLeave : undefined,\n onMouseMove: navigationStyle === \"row\" ? handleMouseMove : undefined,\n };\n};\n"],"names":["useRef","useControlled","useCallback","getTableCellPos","getNextCellPos","useEffect","queryClosest","cellDropdownShowing","closestRowIndex"],"mappings":"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"}
@@ -7,26 +7,26 @@ const useMeasuredHeight = ({
7
7
  onHeightMeasured,
8
8
  height: heightProp = 0
9
9
  }) => {
10
- const [rowHeight, setRowHeight] = react.useState(heightProp);
10
+ const [measuredHeight, setMeasuredHeight] = react.useState(heightProp);
11
11
  const resizeObserver = react.useMemo(() => {
12
12
  return new ResizeObserver((entries) => {
13
13
  for (const entry of entries) {
14
14
  const [{ blockSize: measuredSize }] = entry.borderBoxSize;
15
15
  const newHeight = Math.round(measuredSize);
16
16
  if (vuuUtils.isValidNumber(newHeight)) {
17
- setRowHeight(newHeight);
17
+ setMeasuredHeight(newHeight);
18
18
  onHeightMeasured?.(newHeight);
19
19
  }
20
20
  }
21
21
  });
22
22
  }, [onHeightMeasured]);
23
- const rowRef = react.useCallback(
23
+ const measuredRef = react.useCallback(
24
24
  (el) => {
25
25
  if (el) {
26
26
  if (heightProp === 0) {
27
27
  const { height } = el.getBoundingClientRect();
28
28
  resizeObserver.observe(el);
29
- setRowHeight(height);
29
+ setMeasuredHeight(Math.round(height));
30
30
  }
31
31
  } else {
32
32
  resizeObserver.disconnect();
@@ -34,7 +34,7 @@ const useMeasuredHeight = ({
34
34
  },
35
35
  [resizeObserver, heightProp]
36
36
  );
37
- return { rowHeight, rowRef };
37
+ return { measuredHeight, measuredRef };
38
38
  };
39
39
 
40
40
  exports.useMeasuredHeight = useMeasuredHeight;
@@ -1 +1 @@
1
- {"version":3,"file":"useMeasuredHeight.js","sources":["../src/useMeasuredHeight.ts"],"sourcesContent":["import { isValidNumber } from \"@vuu-ui/vuu-utils\";\nimport { RefCallback, useCallback, useMemo, useState } from \"react\";\n\ninterface MeasuredHeightHookProps {\n onHeightMeasured?: (height: number) => void;\n height?: number;\n}\n\nexport const useMeasuredHeight = ({\n onHeightMeasured,\n height: heightProp = 0,\n}: MeasuredHeightHookProps) => {\n const [rowHeight, setRowHeight] = useState(heightProp);\n\n const resizeObserver = useMemo(() => {\n return new ResizeObserver((entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const [{ blockSize: measuredSize }] = entry.borderBoxSize;\n const newHeight = Math.round(measuredSize);\n if (isValidNumber(newHeight)) {\n setRowHeight(newHeight);\n onHeightMeasured?.(newHeight);\n }\n }\n });\n }, [onHeightMeasured]);\n\n const rowRef = useCallback<RefCallback<HTMLDivElement>>(\n (el) => {\n if (el) {\n if (heightProp === 0) {\n const { height } = el.getBoundingClientRect();\n resizeObserver.observe(el);\n setRowHeight(height);\n }\n } else {\n resizeObserver.disconnect();\n }\n },\n [resizeObserver, heightProp],\n );\n return { rowHeight, rowRef };\n};\n"],"names":["useState","useMemo","isValidNumber","useCallback"],"mappings":";;;;;AAQO,MAAM,oBAAoB,CAAC;AAAA,EAChC,gBAAA;AAAA,EACA,QAAQ,UAAa,GAAA,CAAA;AACvB,CAA+B,KAAA;AAC7B,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAIA,eAAS,UAAU,CAAA,CAAA;AAErD,EAAM,MAAA,cAAA,GAAiBC,cAAQ,MAAM;AACnC,IAAO,OAAA,IAAI,cAAe,CAAA,CAAC,OAAmC,KAAA;AAC5D,MAAA,KAAA,MAAW,SAAS,OAAS,EAAA;AAC3B,QAAA,MAAM,CAAC,EAAE,SAAA,EAAW,YAAa,EAAC,IAAI,KAAM,CAAA,aAAA,CAAA;AAC5C,QAAM,MAAA,SAAA,GAAY,IAAK,CAAA,KAAA,CAAM,YAAY,CAAA,CAAA;AACzC,QAAI,IAAAC,sBAAA,CAAc,SAAS,CAAG,EAAA;AAC5B,UAAA,YAAA,CAAa,SAAS,CAAA,CAAA;AACtB,UAAA,gBAAA,GAAmB,SAAS,CAAA,CAAA;AAAA,SAC9B;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH,EAAG,CAAC,gBAAgB,CAAC,CAAA,CAAA;AAErB,EAAA,MAAM,MAAS,GAAAC,iBAAA;AAAA,IACb,CAAC,EAAO,KAAA;AACN,MAAA,IAAI,EAAI,EAAA;AACN,QAAA,IAAI,eAAe,CAAG,EAAA;AACpB,UAAA,MAAM,EAAE,MAAA,EAAW,GAAA,EAAA,CAAG,qBAAsB,EAAA,CAAA;AAC5C,UAAA,cAAA,CAAe,QAAQ,EAAE,CAAA,CAAA;AACzB,UAAA,YAAA,CAAa,MAAM,CAAA,CAAA;AAAA,SACrB;AAAA,OACK,MAAA;AACL,QAAA,cAAA,CAAe,UAAW,EAAA,CAAA;AAAA,OAC5B;AAAA,KACF;AAAA,IACA,CAAC,gBAAgB,UAAU,CAAA;AAAA,GAC7B,CAAA;AACA,EAAO,OAAA,EAAE,WAAW,MAAO,EAAA,CAAA;AAC7B;;;;"}
1
+ {"version":3,"file":"useMeasuredHeight.js","sources":["../src/useMeasuredHeight.ts"],"sourcesContent":["import { isValidNumber } from \"@vuu-ui/vuu-utils\";\nimport { RefCallback, useCallback, useMemo, useState } from \"react\";\n\ninterface MeasuredHeightHookProps {\n onHeightMeasured?: (height: number) => void;\n height?: number;\n}\n\nexport const useMeasuredHeight = ({\n onHeightMeasured,\n height: heightProp = 0,\n}: MeasuredHeightHookProps) => {\n const [measuredHeight, setMeasuredHeight] = useState(heightProp);\n\n const resizeObserver = useMemo(() => {\n return new ResizeObserver((entries: ResizeObserverEntry[]) => {\n for (const entry of entries) {\n const [{ blockSize: measuredSize }] = entry.borderBoxSize;\n const newHeight = Math.round(measuredSize);\n if (isValidNumber(newHeight)) {\n setMeasuredHeight(newHeight);\n onHeightMeasured?.(newHeight);\n }\n }\n });\n }, [onHeightMeasured]);\n\n const measuredRef = useCallback<RefCallback<HTMLDivElement>>(\n (el) => {\n if (el) {\n if (heightProp === 0) {\n const { height } = el.getBoundingClientRect();\n resizeObserver.observe(el);\n // avoids tiny sub-pixel discrepancies\n setMeasuredHeight(Math.round(height));\n }\n } else {\n resizeObserver.disconnect();\n }\n },\n [resizeObserver, heightProp],\n );\n return { measuredHeight, measuredRef };\n};\n"],"names":["useState","useMemo","isValidNumber","useCallback"],"mappings":";;;;;AAQO,MAAM,oBAAoB,CAAC;AAAA,EAChC,gBAAA;AAAA,EACA,QAAQ,UAAa,GAAA,CAAA;AACvB,CAA+B,KAAA;AAC7B,EAAA,MAAM,CAAC,cAAA,EAAgB,iBAAiB,CAAA,GAAIA,eAAS,UAAU,CAAA,CAAA;AAE/D,EAAM,MAAA,cAAA,GAAiBC,cAAQ,MAAM;AACnC,IAAO,OAAA,IAAI,cAAe,CAAA,CAAC,OAAmC,KAAA;AAC5D,MAAA,KAAA,MAAW,SAAS,OAAS,EAAA;AAC3B,QAAA,MAAM,CAAC,EAAE,SAAA,EAAW,YAAa,EAAC,IAAI,KAAM,CAAA,aAAA,CAAA;AAC5C,QAAM,MAAA,SAAA,GAAY,IAAK,CAAA,KAAA,CAAM,YAAY,CAAA,CAAA;AACzC,QAAI,IAAAC,sBAAA,CAAc,SAAS,CAAG,EAAA;AAC5B,UAAA,iBAAA,CAAkB,SAAS,CAAA,CAAA;AAC3B,UAAA,gBAAA,GAAmB,SAAS,CAAA,CAAA;AAAA,SAC9B;AAAA,OACF;AAAA,KACD,CAAA,CAAA;AAAA,GACH,EAAG,CAAC,gBAAgB,CAAC,CAAA,CAAA;AAErB,EAAA,MAAM,WAAc,GAAAC,iBAAA;AAAA,IAClB,CAAC,EAAO,KAAA;AACN,MAAA,IAAI,EAAI,EAAA;AACN,QAAA,IAAI,eAAe,CAAG,EAAA;AACpB,UAAA,MAAM,EAAE,MAAA,EAAW,GAAA,EAAA,CAAG,qBAAsB,EAAA,CAAA;AAC5C,UAAA,cAAA,CAAe,QAAQ,EAAE,CAAA,CAAA;AAEzB,UAAkB,iBAAA,CAAA,IAAA,CAAK,KAAM,CAAA,MAAM,CAAC,CAAA,CAAA;AAAA,SACtC;AAAA,OACK,MAAA;AACL,QAAA,cAAA,CAAe,UAAW,EAAA,CAAA;AAAA,OAC5B;AAAA,KACF;AAAA,IACA,CAAC,gBAAgB,UAAU,CAAA;AAAA,GAC7B,CAAA;AACA,EAAO,OAAA,EAAE,gBAAgB,WAAY,EAAA,CAAA;AACvC;;;;"}
package/cjs/useTable.js CHANGED
@@ -3,24 +3,33 @@
3
3
  var vuuUiControls = require('@vuu-ui/vuu-ui-controls');
4
4
  var vuuUtils = require('@vuu-ui/vuu-utils');
5
5
  var react = require('react');
6
+ var useCellBlockSelection = require('./cell-block/useCellBlockSelection.js');
6
7
  var buildContextMenuDescriptors = require('./context-menu/buildContextMenuDescriptors.js');
7
8
  var useHandleTableContextMenu = require('./context-menu/useHandleTableContextMenu.js');
8
9
  var tableConfig = require('./table-config.js');
10
+ var tableDomUtils = require('./table-dom-utils.js');
9
11
  var useCellEditing = require('./useCellEditing.js');
12
+ var useCellFocus = require('./useCellFocus.js');
10
13
  var useDataSource = require('./useDataSource.js');
11
14
  var useKeyboardNavigation = require('./useKeyboardNavigation.js');
15
+ var useRowClassNameGenerators = require('./useRowClassNameGenerators.js');
12
16
  var useSelection = require('./useSelection.js');
17
+ var useTableAndColumnSettings = require('./useTableAndColumnSettings.js');
13
18
  var useTableContextMenu = require('./useTableContextMenu.js');
14
19
  var useTableModel = require('./useTableModel.js');
15
20
  var useTableScroll = require('./useTableScroll.js');
16
21
  var useTableViewport = require('./useTableViewport.js');
17
- var useTableAndColumnSettings = require('./useTableAndColumnSettings.js');
18
- var useRowClassNameGenerators = require('./useRowClassNameGenerators.js');
19
- var useCellBlockSelection = require('./cell-block/useCellBlockSelection.js');
20
22
 
21
23
  const stripInternalProperties = (tableConfig) => {
22
24
  return tableConfig;
23
25
  };
26
+ const NullCellFocusState = {
27
+ cellPos: void 0,
28
+ el: null,
29
+ outsideViewport: false,
30
+ placeholderEl: null,
31
+ pos: void 0
32
+ };
24
33
  const { KEY, IS_EXPANDED, IS_LEAF } = vuuUtils.metadataKeys;
25
34
  const NULL_DRAG_DROP = {
26
35
  draggable: void 0,
@@ -63,6 +72,8 @@ const useTable = ({
63
72
  react.useMemo(() => {
64
73
  tableConfigRef.current = config;
65
74
  }, [config]);
75
+ const cellFocusStateRef = react.useRef(NullCellFocusState);
76
+ const focusCellRef = react.useRef();
66
77
  const [headerHeight, setHeaderHeight] = react.useState(showColumnHeaders ? -1 : 0);
67
78
  const [rowCount, setRowCount] = react.useState(dataSource.size);
68
79
  if (dataSource === void 0) {
@@ -155,7 +166,9 @@ const useTable = ({
155
166
  onSubscribed
156
167
  });
157
168
  const { requestScroll, ...scrollProps } = useTableScroll.useTableScroll({
169
+ cellFocusStateRef,
158
170
  columns,
171
+ focusCell: focusCellRef.current,
159
172
  getRowAtPosition,
160
173
  rowHeight,
161
174
  scrollingApiRef,
@@ -382,6 +395,19 @@ const useTable = ({
382
395
  },
383
396
  [columnMap, columns, dataSource, dispatchTableModelAction]
384
397
  );
398
+ const {
399
+ focusCell,
400
+ focusCellPlaceholderKeyDown,
401
+ focusCellPlaceholderRef,
402
+ tableBodyRef
403
+ } = useCellFocus.useCellFocus({
404
+ cellFocusStateRef,
405
+ containerRef,
406
+ disableFocus,
407
+ requestScroll
408
+ });
409
+ focusCellRef.current = focusCell;
410
+ const columnCount = columns.filter((c) => c.hidden !== true).length;
385
411
  const {
386
412
  highlightedIndexRef,
387
413
  navigate,
@@ -389,13 +415,15 @@ const useTable = ({
389
415
  onKeyDown: navigationKeyDown,
390
416
  ...containerProps
391
417
  } = useKeyboardNavigation.useKeyboardNavigation({
392
- columnCount: columns.filter((c) => c.hidden !== true).length,
418
+ cellFocusStateRef,
419
+ columnCount,
393
420
  containerRef,
394
421
  disableFocus,
422
+ focusCell,
395
423
  highlightedIndex: highlightedIndexProp,
396
424
  navigationStyle,
397
425
  requestScroll,
398
- rowCount: dataSource?.size,
426
+ rowCount,
399
427
  onHighlight,
400
428
  viewportRange: range,
401
429
  viewportRowCount: viewportMeasurements.rowCount
@@ -467,13 +495,24 @@ const useTable = ({
467
495
  onSelectionChange: handleSelectionChange,
468
496
  selectionModel
469
497
  });
498
+ const handleSelectCellBlock = react.useCallback(
499
+ (cellBlock2) => {
500
+ handleSelectionChange([]);
501
+ onSelectCellBlock?.(cellBlock2);
502
+ },
503
+ [handleSelectionChange, onSelectCellBlock]
504
+ );
470
505
  const {
471
506
  onMouseDown: cellBlockHookMouseDown,
472
507
  cellBlock,
473
508
  onKeyDown: cellBlockSelectionKeyDown
474
509
  } = useCellBlockSelection.useCellBlockSelection({
475
510
  allowCellBlockSelection,
476
- onSelectCellBlock
511
+ columnCount,
512
+ containerRef,
513
+ focusCell,
514
+ onSelectCellBlock: handleSelectCellBlock,
515
+ rowCount
477
516
  });
478
517
  const handleRowClick = react.useCallback(
479
518
  (evt, row, rangeSelect, keepExistingSelection) => {
@@ -540,7 +579,7 @@ const useTable = ({
540
579
  const handleDragStartRow = react.useCallback(
541
580
  (dragDropState) => {
542
581
  const { initialDragElement } = dragDropState;
543
- const rowIndex = vuuUtils.getIndexFromRowElement(initialDragElement);
582
+ const rowIndex = tableDomUtils.getIndexFromRowElement(initialDragElement);
544
583
  const row = dataRef.current.find((row2) => row2[0] === rowIndex);
545
584
  if (row) {
546
585
  dragDropState.setPayload(row);
@@ -579,6 +618,8 @@ const useTable = ({
579
618
  columns,
580
619
  data,
581
620
  draggableRow,
621
+ focusCellPlaceholderKeyDown,
622
+ focusCellPlaceholderRef,
582
623
  getRowOffset,
583
624
  handleContextMenuAction,
584
625
  headerHeight,
@@ -604,6 +645,7 @@ const useTable = ({
604
645
  scrollProps,
605
646
  // TODO don't think we need these ...
606
647
  tableAttributes,
648
+ tableBodyRef,
607
649
  tableConfig: tableConfig$1,
608
650
  viewportMeasurements
609
651
  };