@vuu-ui/vuu-table 0.8.94 → 0.8.96

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 (108) hide show
  1. package/cjs/Row.css.js +1 -1
  2. package/cjs/Row.js +1 -1
  3. package/cjs/Row.js.map +1 -1
  4. package/cjs/Table.css.js +1 -1
  5. package/cjs/Table.js +113 -31
  6. package/cjs/Table.js.map +1 -1
  7. package/cjs/cell-block/CellBlock.css.js +6 -0
  8. package/cjs/cell-block/CellBlock.css.js.map +1 -0
  9. package/cjs/cell-block/CellBlock.js +41 -0
  10. package/cjs/cell-block/CellBlock.js.map +1 -0
  11. package/cjs/cell-block/cellblock-utils.js +140 -0
  12. package/cjs/cell-block/cellblock-utils.js.map +1 -0
  13. package/cjs/cell-block/useCellBlockSelection.js +232 -0
  14. package/cjs/cell-block/useCellBlockSelection.js.map +1 -0
  15. package/cjs/index.js +5 -0
  16. package/cjs/index.js.map +1 -1
  17. package/cjs/moving-window.js +21 -9
  18. package/cjs/moving-window.js.map +1 -1
  19. package/cjs/pagination/PaginationControl.css.js +6 -0
  20. package/cjs/pagination/PaginationControl.css.js.map +1 -0
  21. package/cjs/pagination/PaginationControl.js +38 -0
  22. package/cjs/pagination/PaginationControl.js.map +1 -0
  23. package/cjs/pagination/usePagination.js +38 -0
  24. package/cjs/pagination/usePagination.js.map +1 -0
  25. package/cjs/table-dom-utils.js +67 -10
  26. package/cjs/table-dom-utils.js.map +1 -1
  27. package/cjs/table-header/useTableHeader.js +1 -1
  28. package/cjs/table-header/useTableHeader.js.map +1 -1
  29. package/cjs/useCellFocus.js +56 -0
  30. package/cjs/useCellFocus.js.map +1 -0
  31. package/cjs/useControlledTableNavigation.js +3 -1
  32. package/cjs/useControlledTableNavigation.js.map +1 -1
  33. package/cjs/useDataSource.js +13 -0
  34. package/cjs/useDataSource.js.map +1 -1
  35. package/cjs/useKeyboardNavigation.js +8 -82
  36. package/cjs/useKeyboardNavigation.js.map +1 -1
  37. package/cjs/useMeasuredHeight.js +5 -5
  38. package/cjs/useMeasuredHeight.js.map +1 -1
  39. package/cjs/useTable.js +63 -16
  40. package/cjs/useTable.js.map +1 -1
  41. package/cjs/useTableContextMenu.js +2 -1
  42. package/cjs/useTableContextMenu.js.map +1 -1
  43. package/cjs/useTableScroll.js.map +1 -1
  44. package/cjs/useTableViewport.js +31 -13
  45. package/cjs/useTableViewport.js.map +1 -1
  46. package/esm/Row.css.js +1 -1
  47. package/esm/Row.js +1 -1
  48. package/esm/Row.js.map +1 -1
  49. package/esm/Table.css.js +1 -1
  50. package/esm/Table.js +115 -33
  51. package/esm/Table.js.map +1 -1
  52. package/esm/cell-block/CellBlock.css.js +4 -0
  53. package/esm/cell-block/CellBlock.css.js.map +1 -0
  54. package/esm/cell-block/CellBlock.js +39 -0
  55. package/esm/cell-block/CellBlock.js.map +1 -0
  56. package/esm/cell-block/cellblock-utils.js +131 -0
  57. package/esm/cell-block/cellblock-utils.js.map +1 -0
  58. package/esm/cell-block/useCellBlockSelection.js +230 -0
  59. package/esm/cell-block/useCellBlockSelection.js.map +1 -0
  60. package/esm/index.js +2 -1
  61. package/esm/index.js.map +1 -1
  62. package/esm/moving-window.js +21 -9
  63. package/esm/moving-window.js.map +1 -1
  64. package/esm/pagination/PaginationControl.css.js +4 -0
  65. package/esm/pagination/PaginationControl.css.js.map +1 -0
  66. package/esm/pagination/PaginationControl.js +36 -0
  67. package/esm/pagination/PaginationControl.js.map +1 -0
  68. package/esm/pagination/usePagination.js +36 -0
  69. package/esm/pagination/usePagination.js.map +1 -0
  70. package/esm/table-dom-utils.js +64 -10
  71. package/esm/table-dom-utils.js.map +1 -1
  72. package/esm/table-header/useTableHeader.js +1 -1
  73. package/esm/table-header/useTableHeader.js.map +1 -1
  74. package/esm/useCellFocus.js +54 -0
  75. package/esm/useCellFocus.js.map +1 -0
  76. package/esm/useControlledTableNavigation.js +3 -2
  77. package/esm/useControlledTableNavigation.js.map +1 -1
  78. package/esm/useDataSource.js +13 -0
  79. package/esm/useDataSource.js.map +1 -1
  80. package/esm/useKeyboardNavigation.js +8 -82
  81. package/esm/useKeyboardNavigation.js.map +1 -1
  82. package/esm/useMeasuredHeight.js +5 -5
  83. package/esm/useMeasuredHeight.js.map +1 -1
  84. package/esm/useTable.js +63 -16
  85. package/esm/useTable.js.map +1 -1
  86. package/esm/useTableContextMenu.js +2 -1
  87. package/esm/useTableContextMenu.js.map +1 -1
  88. package/esm/useTableScroll.js.map +1 -1
  89. package/esm/useTableViewport.js +31 -13
  90. package/esm/useTableViewport.js.map +1 -1
  91. package/package.json +9 -9
  92. package/types/Table.d.ts +40 -0
  93. package/types/cell-block/CellBlock.d.ts +6 -0
  94. package/types/cell-block/cellblock-utils.d.ts +37 -0
  95. package/types/cell-block/useCellBlockSelection.d.ts +16 -0
  96. package/types/index.d.ts +1 -0
  97. package/types/moving-window.d.ts +3 -1
  98. package/types/pagination/PaginationControl.d.ts +6 -0
  99. package/types/pagination/index.d.ts +1 -0
  100. package/types/pagination/usePagination.d.ts +9 -0
  101. package/types/table-dom-utils.d.ts +6 -1
  102. package/types/useCellFocus.d.ts +13 -0
  103. package/types/useControlledTableNavigation.d.ts +1 -0
  104. package/types/useKeyboardNavigation.d.ts +5 -5
  105. package/types/useMeasuredHeight.d.ts +2 -2
  106. package/types/useTable.d.ts +13 -11
  107. package/types/useTableScroll.d.ts +1 -0
  108. package/types/useTableViewport.d.ts +3 -2
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+
5
+ const getPageCount = (dataSource) => {
6
+ const { range, size } = dataSource;
7
+ const pageSize = range.to - range.from;
8
+ if (pageSize > 0) {
9
+ return Math.ceil(size / pageSize);
10
+ } else {
11
+ return 0;
12
+ }
13
+ };
14
+ const usePagination = ({ dataSource }) => {
15
+ const [pageCount, setPageCount] = react.useState(getPageCount(dataSource));
16
+ react.useMemo(() => {
17
+ dataSource.on("page-count", (n) => setPageCount(n));
18
+ }, [dataSource]);
19
+ const handlePageChange = react.useCallback(
20
+ (_evt, page) => {
21
+ const { range } = dataSource;
22
+ const pageSize = range.to - range.from;
23
+ const firstRow = pageSize * (page - 1);
24
+ console.log(
25
+ `set range ${JSON.stringify({ from: firstRow, to: firstRow + pageSize })}`
26
+ );
27
+ dataSource.range = { from: firstRow, to: firstRow + pageSize };
28
+ },
29
+ [dataSource]
30
+ );
31
+ return {
32
+ onPageChange: handlePageChange,
33
+ pageCount
34
+ };
35
+ };
36
+
37
+ exports.usePagination = usePagination;
38
+ //# sourceMappingURL=usePagination.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"usePagination.js","sources":["../../src/pagination/usePagination.ts"],"sourcesContent":["import { SyntheticEvent, useCallback, useMemo, useState } from \"react\";\nimport { DataSource } from \"@vuu-ui/vuu-data-types\";\n\nexport interface PaginationHookProps {\n dataSource: DataSource;\n}\n\nconst getPageCount = (dataSource: DataSource) => {\n const { range, size } = dataSource;\n const pageSize = range.to - range.from;\n if (pageSize > 0) {\n return Math.ceil(size / pageSize);\n } else {\n return 0;\n }\n};\n\nexport const usePagination = ({ dataSource }: PaginationHookProps) => {\n const [pageCount, setPageCount] = useState<number>(getPageCount(dataSource));\n\n useMemo(() => {\n dataSource.on(\"page-count\", (n: number) => setPageCount(n));\n }, [dataSource]);\n\n const handlePageChange = useCallback(\n (_evt: SyntheticEvent, page: number) => {\n const { range } = dataSource;\n const pageSize = range.to - range.from;\n const firstRow = pageSize * (page - 1);\n console.log(\n `set range ${JSON.stringify({ from: firstRow, to: firstRow + pageSize })}`,\n );\n dataSource.range = { from: firstRow, to: firstRow + pageSize };\n },\n [dataSource],\n );\n\n return {\n onPageChange: handlePageChange,\n pageCount,\n };\n};\n"],"names":["useState","useMemo","useCallback"],"mappings":";;;;AAOA,MAAM,YAAA,GAAe,CAAC,UAA2B,KAAA;AAC/C,EAAM,MAAA,EAAE,KAAO,EAAA,IAAA,EAAS,GAAA,UAAA,CAAA;AACxB,EAAM,MAAA,QAAA,GAAW,KAAM,CAAA,EAAA,GAAK,KAAM,CAAA,IAAA,CAAA;AAClC,EAAA,IAAI,WAAW,CAAG,EAAA;AAChB,IAAO,OAAA,IAAA,CAAK,IAAK,CAAA,IAAA,GAAO,QAAQ,CAAA,CAAA;AAAA,GAC3B,MAAA;AACL,IAAO,OAAA,CAAA,CAAA;AAAA,GACT;AACF,CAAA,CAAA;AAEO,MAAM,aAAgB,GAAA,CAAC,EAAE,UAAA,EAAsC,KAAA;AACpE,EAAA,MAAM,CAAC,SAAW,EAAA,YAAY,IAAIA,cAAiB,CAAA,YAAA,CAAa,UAAU,CAAC,CAAA,CAAA;AAE3E,EAAAC,aAAA,CAAQ,MAAM;AACZ,IAAA,UAAA,CAAW,GAAG,YAAc,EAAA,CAAC,CAAc,KAAA,YAAA,CAAa,CAAC,CAAC,CAAA,CAAA;AAAA,GAC5D,EAAG,CAAC,UAAU,CAAC,CAAA,CAAA;AAEf,EAAA,MAAM,gBAAmB,GAAAC,iBAAA;AAAA,IACvB,CAAC,MAAsB,IAAiB,KAAA;AACtC,MAAM,MAAA,EAAE,OAAU,GAAA,UAAA,CAAA;AAClB,MAAM,MAAA,QAAA,GAAW,KAAM,CAAA,EAAA,GAAK,KAAM,CAAA,IAAA,CAAA;AAClC,MAAM,MAAA,QAAA,GAAW,YAAY,IAAO,GAAA,CAAA,CAAA,CAAA;AACpC,MAAQ,OAAA,CAAA,GAAA;AAAA,QACN,CAAA,UAAA,EAAa,IAAK,CAAA,SAAA,CAAU,EAAE,IAAA,EAAM,UAAU,EAAI,EAAA,QAAA,GAAW,QAAS,EAAC,CAAC,CAAA,CAAA;AAAA,OAC1E,CAAA;AACA,MAAA,UAAA,CAAW,QAAQ,EAAE,IAAA,EAAM,QAAU,EAAA,EAAA,EAAI,WAAW,QAAS,EAAA,CAAA;AAAA,KAC/D;AAAA,IACA,CAAC,UAAU,CAAA;AAAA,GACb,CAAA;AAEA,EAAO,OAAA;AAAA,IACL,YAAc,EAAA,gBAAA;AAAA,IACd,SAAA;AAAA,GACF,CAAA;AACF;;;;"}
@@ -1,7 +1,8 @@
1
1
  'use strict';
2
2
 
3
- const headerCellQuery = (colIdx) => `.vuuTable-col-headers .vuuTableHeaderCell:nth-child(${colIdx})`;
4
- const dataCellQuery = (rowIdx, colIdx) => `.vuuTable-body > [aria-rowindex='${rowIdx + 1}'] > [role='cell']:nth-child(${colIdx + 1})`;
3
+ const NULL_CELL_POS = [-1, -1];
4
+ const headerCellQuery = (colIdx) => `.vuuTable-col-headers .vuuTableHeaderCell[aria-colindex='${colIdx + 1}']`;
5
+ const dataCellQuery = (rowIdx, colIdx) => `.vuuTable-body > [aria-rowindex='${rowIdx + 1}'] > [aria-colindex='${colIdx + 1}']`;
5
6
  const getTableCell = (containerRef, [rowIdx, colIdx]) => {
6
7
  const cssQuery = rowIdx === -1 ? headerCellQuery(colIdx) : dataCellQuery(rowIdx, colIdx);
7
8
  const cell = containerRef.current?.querySelector(
@@ -22,17 +23,70 @@ const cellDropdownShowing = (cell) => {
22
23
  return false;
23
24
  };
24
25
  const cellIsTextInput = (cell) => cell.querySelector(".vuuTableInputCell") !== null;
25
- function getRowIndex(rowEl) {
26
- if (rowEl) {
27
- const idx = rowEl.ariaRowIndex;
28
- if (idx !== null) {
29
- return parseInt(idx, 10) - 1;
26
+ const getIndexFromRowElement = (rowElement) => {
27
+ const rowIndex = rowElement?.ariaRowIndex;
28
+ if (rowIndex != null) {
29
+ const index = parseInt(rowIndex) - 1;
30
+ if (!isNaN(index)) {
31
+ return index;
30
32
  }
31
33
  }
32
34
  return -1;
33
- }
35
+ };
36
+ const getIndexFromCellElement = (cellElement) => {
37
+ const colIndex = cellElement?.ariaColIndex;
38
+ if (colIndex != null) {
39
+ const index = parseInt(colIndex) - 1;
40
+ if (!isNaN(index)) {
41
+ return index;
42
+ }
43
+ }
44
+ return -1;
45
+ };
46
+ const getTableCellPos = (tableCell) => {
47
+ const colIdx = getIndexFromCellElement(tableCell);
48
+ if (tableCell.role === "columnHeader") {
49
+ return [-1, colIdx];
50
+ } else {
51
+ const focusedRow = tableCell.closest("[role='row']");
52
+ if (focusedRow) {
53
+ return [getIndexFromRowElement(focusedRow), colIdx];
54
+ }
55
+ }
56
+ return NULL_CELL_POS;
57
+ };
34
58
  const closestRow = (el) => el.closest('[role="row"]');
35
- const closestRowIndex = (el) => getRowIndex(closestRow(el));
59
+ const closestRowIndex = (el) => getIndexFromRowElement(closestRow(el));
60
+ function getNextCellPos(key, [rowIdx, colIdx], columnCount, rowCount) {
61
+ if (key === "ArrowUp") {
62
+ if (rowIdx > -1) {
63
+ return [rowIdx - 1, colIdx];
64
+ } else {
65
+ return [rowIdx, colIdx];
66
+ }
67
+ } else if (key === "ArrowDown") {
68
+ if (rowIdx === -1) {
69
+ return [0, colIdx];
70
+ } else if (rowIdx === rowCount - 1) {
71
+ return [rowIdx, colIdx];
72
+ } else {
73
+ return [rowIdx + 1, colIdx];
74
+ }
75
+ } else if (key === "ArrowRight") {
76
+ if (colIdx < columnCount - 1) {
77
+ return [rowIdx, colIdx + 1];
78
+ } else {
79
+ return [rowIdx, colIdx];
80
+ }
81
+ } else if (key === "ArrowLeft") {
82
+ if (colIdx > 0) {
83
+ return [rowIdx, colIdx - 1];
84
+ } else {
85
+ return [rowIdx, colIdx];
86
+ }
87
+ }
88
+ return [rowIdx, colIdx];
89
+ }
36
90
  const NO_SCROLL_NECESSARY = [void 0, void 0];
37
91
  const howFarIsRowOutsideViewport = (rowEl, totalHeaderHeight, contentContainer = rowEl.closest(".vuuTable-contentContainer")) => {
38
92
  if (contentContainer) {
@@ -60,8 +114,11 @@ exports.cellIsEditable = cellIsEditable;
60
114
  exports.cellIsTextInput = cellIsTextInput;
61
115
  exports.closestRowIndex = closestRowIndex;
62
116
  exports.dataCellQuery = dataCellQuery;
63
- exports.getRowIndex = getRowIndex;
117
+ exports.getIndexFromCellElement = getIndexFromCellElement;
118
+ exports.getIndexFromRowElement = getIndexFromRowElement;
119
+ exports.getNextCellPos = getNextCellPos;
64
120
  exports.getTableCell = getTableCell;
121
+ exports.getTableCellPos = getTableCellPos;
65
122
  exports.headerCellQuery = headerCellQuery;
66
123
  exports.howFarIsRowOutsideViewport = howFarIsRowOutsideViewport;
67
124
  //# sourceMappingURL=table-dom-utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"table-dom-utils.js","sources":["../src/table-dom-utils.ts"],"sourcesContent":["import { RefObject } from \"react\";\nimport { ScrollDirection } from \"./useTableScroll\";\n\n/**\n * [rowIndex, colIndex\n */\nexport type CellPos = [number, number];\n\nexport const headerCellQuery = (colIdx: number) =>\n `.vuuTable-col-headers .vuuTableHeaderCell:nth-child(${colIdx})`;\n\nexport const dataCellQuery = (rowIdx: number, colIdx: number) =>\n `.vuuTable-body > [aria-rowindex='${rowIdx + 1}'] > [role='cell']:nth-child(${\n colIdx + 1\n })`;\n\nexport const getTableCell = (\n containerRef: RefObject<HTMLElement>,\n\n [rowIdx, colIdx]: CellPos\n) => {\n const cssQuery =\n rowIdx === -1 ? headerCellQuery(colIdx) : dataCellQuery(rowIdx, colIdx);\n const cell = containerRef.current?.querySelector(\n cssQuery\n ) as HTMLTableCellElement;\n\n if (cellIsEditable(cell)) {\n // Dropdown gets focus, Input does not\n const focusableContent = cell.querySelector(\"button\") as HTMLElement;\n return focusableContent || cell;\n } else {\n return cell;\n }\n};\n\nexport const cellIsEditable = (cell: HTMLDivElement | null) =>\n cell?.classList.contains(\"vuuTableCell-editable\");\n\nexport const cellDropdownShowing = (cell: HTMLDivElement | null) => {\n if (cellIsEditable(cell)) {\n return cell?.querySelector('.saltDropdown[aria-expanded=\"true\"]') !== null;\n }\n return false;\n};\n\nexport const cellIsTextInput = (cell: HTMLElement) =>\n cell.querySelector(\".vuuTableInputCell\") !== null;\n\nexport function getRowIndex(rowEl?: HTMLElement) {\n if (rowEl) {\n const idx: string | null = rowEl.ariaRowIndex;\n if (idx !== null) {\n return parseInt(idx, 10) - 1;\n }\n }\n return -1;\n}\n\nconst closestRow = (el: HTMLElement) =>\n el.closest('[role=\"row\"]') as HTMLElement;\n\nexport const closestRowIndex = (el: HTMLElement) => getRowIndex(closestRow(el));\n\nconst NO_SCROLL_NECESSARY = [undefined, undefined] as const;\n\nexport const howFarIsRowOutsideViewport = (\n rowEl: HTMLElement,\n totalHeaderHeight: number,\n contentContainer = rowEl.closest(\".vuuTable-contentContainer\")\n): readonly [ScrollDirection | undefined, number | undefined] => {\n //TODO lots of scope for optimisation here\n if (contentContainer) {\n // TODO take totalHeaderHeight into consideration\n const viewport = contentContainer?.getBoundingClientRect();\n const upperBoundary = viewport.top + totalHeaderHeight;\n const row = rowEl.getBoundingClientRect();\n if (row) {\n if (row.bottom > viewport.bottom) {\n return [\"down\", row.bottom - viewport.bottom];\n } else if (row.top < upperBoundary) {\n return [\"up\", row.top - upperBoundary];\n } else {\n return NO_SCROLL_NECESSARY;\n }\n } else {\n throw Error(\"Whats going on, row not found\");\n }\n } else {\n throw Error(\"Whats going on, scrollbar container not found\");\n }\n};\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"table-dom-utils.js","sources":["../src/table-dom-utils.ts"],"sourcesContent":["import { RefObject } from \"react\";\nimport { ScrollDirection } from \"./useTableScroll\";\nimport type { ArrowKey, PageKey } from \"@vuu-ui/vuu-utils\";\n\nconst NULL_CELL_POS: CellPos = [-1, -1];\n\n/**\n * [rowIndex, colIndex\n */\nexport type CellPos = [number, number];\n\nexport type NavigationKey = PageKey | ArrowKey;\n\nexport const headerCellQuery = (colIdx: number) =>\n `.vuuTable-col-headers .vuuTableHeaderCell[aria-colindex='${colIdx + 1}']`;\n\nexport const dataCellQuery = (rowIdx: number, colIdx: number) =>\n `.vuuTable-body > [aria-rowindex='${rowIdx + 1}'] > [aria-colindex='${colIdx + 1}']`;\n\nexport const getTableCell = (\n containerRef: RefObject<HTMLElement>,\n [rowIdx, colIdx]: CellPos,\n) => {\n const cssQuery =\n rowIdx === -1 ? headerCellQuery(colIdx) : dataCellQuery(rowIdx, colIdx);\n const cell = containerRef.current?.querySelector(\n cssQuery,\n ) as HTMLTableCellElement;\n\n if (cellIsEditable(cell)) {\n // Dropdown gets focus, Input does not\n const focusableContent = cell.querySelector(\"button\") as HTMLElement;\n return focusableContent || cell;\n } else {\n return cell;\n }\n};\n\nexport const cellIsEditable = (cell: HTMLDivElement | null) =>\n cell?.classList.contains(\"vuuTableCell-editable\");\n\nexport const cellDropdownShowing = (cell: HTMLDivElement | null) => {\n if (cellIsEditable(cell)) {\n return cell?.querySelector('.saltDropdown[aria-expanded=\"true\"]') !== null;\n }\n return false;\n};\n\nexport const cellIsTextInput = (cell: HTMLElement) =>\n cell.querySelector(\".vuuTableInputCell\") !== null;\n\nexport const getIndexFromRowElement = (rowElement: HTMLElement | null) => {\n const rowIndex = rowElement?.ariaRowIndex;\n if (rowIndex != null) {\n const index = parseInt(rowIndex) - 1;\n if (!isNaN(index)) {\n return index;\n }\n }\n return -1;\n};\n\nexport const getIndexFromCellElement = (cellElement: HTMLElement | null) => {\n const colIndex = cellElement?.ariaColIndex;\n if (colIndex != null) {\n const index = parseInt(colIndex) - 1;\n if (!isNaN(index)) {\n return index;\n }\n }\n return -1;\n};\n\nexport const getTableCellPos = (tableCell: HTMLDivElement): CellPos => {\n const colIdx = getIndexFromCellElement(tableCell);\n if (tableCell.role === \"columnHeader\") {\n return [-1, colIdx];\n } else {\n const focusedRow = tableCell.closest(\"[role='row']\") as HTMLElement;\n if (focusedRow) {\n return [getIndexFromRowElement(focusedRow), colIdx];\n }\n }\n return NULL_CELL_POS;\n};\n\nconst closestRow = (el: HTMLElement) =>\n el.closest('[role=\"row\"]') as HTMLElement;\n\nexport const closestRowIndex = (el: HTMLElement) =>\n getIndexFromRowElement(closestRow(el));\n\nexport function getNextCellPos(\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 if (colIdx < columnCount - 1) {\n return [rowIdx, colIdx + 1];\n } else {\n return [rowIdx, colIdx];\n }\n } else if (key === \"ArrowLeft\") {\n if (colIdx > 0) {\n return [rowIdx, colIdx - 1];\n } else {\n return [rowIdx, colIdx];\n }\n }\n return [rowIdx, colIdx];\n}\n\nconst NO_SCROLL_NECESSARY = [undefined, undefined] as const;\n\nexport const howFarIsRowOutsideViewport = (\n rowEl: HTMLElement,\n totalHeaderHeight: number,\n contentContainer = rowEl.closest(\".vuuTable-contentContainer\"),\n): readonly [ScrollDirection | undefined, number | undefined] => {\n //TODO lots of scope for optimisation here\n if (contentContainer) {\n // TODO take totalHeaderHeight into consideration\n const viewport = contentContainer?.getBoundingClientRect();\n const upperBoundary = viewport.top + totalHeaderHeight;\n const row = rowEl.getBoundingClientRect();\n if (row) {\n if (row.bottom > viewport.bottom) {\n return [\"down\", row.bottom - viewport.bottom];\n } else if (row.top < upperBoundary) {\n return [\"up\", row.top - upperBoundary];\n } else {\n return NO_SCROLL_NECESSARY;\n }\n } else {\n throw Error(\"Whats going on, row not found\");\n }\n } else {\n throw Error(\"Whats going on, scrollbar container not found\");\n }\n};\n"],"names":[],"mappings":";;AAIA,MAAM,aAAA,GAAyB,CAAC,CAAA,CAAA,EAAI,CAAE,CAAA,CAAA,CAAA;AAS/B,MAAM,eAAkB,GAAA,CAAC,MAC9B,KAAA,CAAA,yDAAA,EAA4D,SAAS,CAAC,CAAA,EAAA,EAAA;AAE3D,MAAA,aAAA,GAAgB,CAAC,MAAgB,EAAA,MAAA,KAC5C,oCAAoC,MAAS,GAAA,CAAC,CAAwB,qBAAA,EAAA,MAAA,GAAS,CAAC,CAAA,EAAA,EAAA;AAE3E,MAAM,eAAe,CAC1B,YAAA,EACA,CAAC,MAAA,EAAQ,MAAM,CACZ,KAAA;AACH,EAAM,MAAA,QAAA,GACJ,WAAW,CAAK,CAAA,GAAA,eAAA,CAAgB,MAAM,CAAI,GAAA,aAAA,CAAc,QAAQ,MAAM,CAAA,CAAA;AACxE,EAAM,MAAA,IAAA,GAAO,aAAa,OAAS,EAAA,aAAA;AAAA,IACjC,QAAA;AAAA,GACF,CAAA;AAEA,EAAI,IAAA,cAAA,CAAe,IAAI,CAAG,EAAA;AAExB,IAAM,MAAA,gBAAA,GAAmB,IAAK,CAAA,aAAA,CAAc,QAAQ,CAAA,CAAA;AACpD,IAAA,OAAO,gBAAoB,IAAA,IAAA,CAAA;AAAA,GACtB,MAAA;AACL,IAAO,OAAA,IAAA,CAAA;AAAA,GACT;AACF,EAAA;AAEO,MAAM,iBAAiB,CAAC,IAAA,KAC7B,IAAM,EAAA,SAAA,CAAU,SAAS,uBAAuB,EAAA;AAErC,MAAA,mBAAA,GAAsB,CAAC,IAAgC,KAAA;AAClE,EAAI,IAAA,cAAA,CAAe,IAAI,CAAG,EAAA;AACxB,IAAO,OAAA,IAAA,EAAM,aAAc,CAAA,qCAAqC,CAAM,KAAA,IAAA,CAAA;AAAA,GACxE;AACA,EAAO,OAAA,KAAA,CAAA;AACT,EAAA;AAEO,MAAM,kBAAkB,CAAC,IAAA,KAC9B,IAAK,CAAA,aAAA,CAAc,oBAAoB,CAAM,KAAA,KAAA;AAElC,MAAA,sBAAA,GAAyB,CAAC,UAAmC,KAAA;AACxE,EAAA,MAAM,WAAW,UAAY,EAAA,YAAA,CAAA;AAC7B,EAAA,IAAI,YAAY,IAAM,EAAA;AACpB,IAAM,MAAA,KAAA,GAAQ,QAAS,CAAA,QAAQ,CAAI,GAAA,CAAA,CAAA;AACnC,IAAI,IAAA,CAAC,KAAM,CAAA,KAAK,CAAG,EAAA;AACjB,MAAO,OAAA,KAAA,CAAA;AAAA,KACT;AAAA,GACF;AACA,EAAO,OAAA,CAAA,CAAA,CAAA;AACT,EAAA;AAEa,MAAA,uBAAA,GAA0B,CAAC,WAAoC,KAAA;AAC1E,EAAA,MAAM,WAAW,WAAa,EAAA,YAAA,CAAA;AAC9B,EAAA,IAAI,YAAY,IAAM,EAAA;AACpB,IAAM,MAAA,KAAA,GAAQ,QAAS,CAAA,QAAQ,CAAI,GAAA,CAAA,CAAA;AACnC,IAAI,IAAA,CAAC,KAAM,CAAA,KAAK,CAAG,EAAA;AACjB,MAAO,OAAA,KAAA,CAAA;AAAA,KACT;AAAA,GACF;AACA,EAAO,OAAA,CAAA,CAAA,CAAA;AACT,EAAA;AAEa,MAAA,eAAA,GAAkB,CAAC,SAAuC,KAAA;AACrE,EAAM,MAAA,MAAA,GAAS,wBAAwB,SAAS,CAAA,CAAA;AAChD,EAAI,IAAA,SAAA,CAAU,SAAS,cAAgB,EAAA;AACrC,IAAO,OAAA,CAAC,IAAI,MAAM,CAAA,CAAA;AAAA,GACb,MAAA;AACL,IAAM,MAAA,UAAA,GAAa,SAAU,CAAA,OAAA,CAAQ,cAAc,CAAA,CAAA;AACnD,IAAA,IAAI,UAAY,EAAA;AACd,MAAA,OAAO,CAAC,sBAAA,CAAuB,UAAU,CAAA,EAAG,MAAM,CAAA,CAAA;AAAA,KACpD;AAAA,GACF;AACA,EAAO,OAAA,aAAA,CAAA;AACT,EAAA;AAEA,MAAM,UAAa,GAAA,CAAC,EAClB,KAAA,EAAA,CAAG,QAAQ,cAAc,CAAA,CAAA;AAEpB,MAAM,kBAAkB,CAAC,EAAA,KAC9B,sBAAuB,CAAA,UAAA,CAAW,EAAE,CAAC,EAAA;AAEhC,SAAS,eACd,GACA,EAAA,CAAC,QAAQ,MAAM,CAAA,EACf,aACA,QACS,EAAA;AACT,EAAA,IAAI,QAAQ,SAAW,EAAA;AACrB,IAAA,IAAI,SAAS,CAAI,CAAA,EAAA;AACf,MAAO,OAAA,CAAC,MAAS,GAAA,CAAA,EAAG,MAAM,CAAA,CAAA;AAAA,KACrB,MAAA;AACL,MAAO,OAAA,CAAC,QAAQ,MAAM,CAAA,CAAA;AAAA,KACxB;AAAA,GACF,MAAA,IAAW,QAAQ,WAAa,EAAA;AAC9B,IAAA,IAAI,WAAW,CAAI,CAAA,EAAA;AACjB,MAAO,OAAA,CAAC,GAAG,MAAM,CAAA,CAAA;AAAA,KACnB,MAAA,IAAW,MAAW,KAAA,QAAA,GAAW,CAAG,EAAA;AAClC,MAAO,OAAA,CAAC,QAAQ,MAAM,CAAA,CAAA;AAAA,KACjB,MAAA;AACL,MAAO,OAAA,CAAC,MAAS,GAAA,CAAA,EAAG,MAAM,CAAA,CAAA;AAAA,KAC5B;AAAA,GACF,MAAA,IAAW,QAAQ,YAAc,EAAA;AAC/B,IAAI,IAAA,MAAA,GAAS,cAAc,CAAG,EAAA;AAC5B,MAAO,OAAA,CAAC,MAAQ,EAAA,MAAA,GAAS,CAAC,CAAA,CAAA;AAAA,KACrB,MAAA;AACL,MAAO,OAAA,CAAC,QAAQ,MAAM,CAAA,CAAA;AAAA,KACxB;AAAA,GACF,MAAA,IAAW,QAAQ,WAAa,EAAA;AAC9B,IAAA,IAAI,SAAS,CAAG,EAAA;AACd,MAAO,OAAA,CAAC,MAAQ,EAAA,MAAA,GAAS,CAAC,CAAA,CAAA;AAAA,KACrB,MAAA;AACL,MAAO,OAAA,CAAC,QAAQ,MAAM,CAAA,CAAA;AAAA,KACxB;AAAA,GACF;AACA,EAAO,OAAA,CAAC,QAAQ,MAAM,CAAA,CAAA;AACxB,CAAA;AAEA,MAAM,mBAAA,GAAsB,CAAC,KAAA,CAAA,EAAW,KAAS,CAAA,CAAA,CAAA;AAEpC,MAAA,0BAAA,GAA6B,CACxC,KACA,EAAA,iBAAA,EACA,mBAAmB,KAAM,CAAA,OAAA,CAAQ,4BAA4B,CACE,KAAA;AAE/D,EAAA,IAAI,gBAAkB,EAAA;AAEpB,IAAM,MAAA,QAAA,GAAW,kBAAkB,qBAAsB,EAAA,CAAA;AACzD,IAAM,MAAA,aAAA,GAAgB,SAAS,GAAM,GAAA,iBAAA,CAAA;AACrC,IAAM,MAAA,GAAA,GAAM,MAAM,qBAAsB,EAAA,CAAA;AACxC,IAAA,IAAI,GAAK,EAAA;AACP,MAAI,IAAA,GAAA,CAAI,MAAS,GAAA,QAAA,CAAS,MAAQ,EAAA;AAChC,QAAA,OAAO,CAAC,MAAA,EAAQ,GAAI,CAAA,MAAA,GAAS,SAAS,MAAM,CAAA,CAAA;AAAA,OAC9C,MAAA,IAAW,GAAI,CAAA,GAAA,GAAM,aAAe,EAAA;AAClC,QAAA,OAAO,CAAC,IAAA,EAAM,GAAI,CAAA,GAAA,GAAM,aAAa,CAAA,CAAA;AAAA,OAChC,MAAA;AACL,QAAO,OAAA,mBAAA,CAAA;AAAA,OACT;AAAA,KACK,MAAA;AACL,MAAA,MAAM,MAAM,+BAA+B,CAAA,CAAA;AAAA,KAC7C;AAAA,GACK,MAAA;AACL,IAAA,MAAM,MAAM,+CAA+C,CAAA,CAAA;AAAA,GAC7D;AACF;;;;;;;;;;;;;;;"}
@@ -16,7 +16,7 @@ const useTableHeader = ({
16
16
  }) => {
17
17
  const containerRef = react.useRef(null);
18
18
  const scrollingContainerRef = react.useRef(null);
19
- const { rowRef } = useMeasuredHeight.useMeasuredHeight({
19
+ const { measuredRef: rowRef } = useMeasuredHeight.useMeasuredHeight({
20
20
  onHeightMeasured
21
21
  });
22
22
  const setContainerRef = react.useCallback((el) => {
@@ -1 +1 @@
1
- {"version":3,"file":"useTableHeader.js","sources":["../../src/table-header/useTableHeader.ts"],"sourcesContent":["import { ColumnDescriptor } from \"@vuu-ui/vuu-table-types\";\nimport {\n DropOptions,\n useDragDrop as useDragDrop,\n} from \"@vuu-ui/vuu-ui-controls\";\nimport {\n moveColumnTo,\n queryClosest,\n visibleColumnAtIndex,\n} from \"@vuu-ui/vuu-utils\";\nimport { RefCallback, useCallback, useRef } from \"react\";\nimport { TableHeaderProps } from \"./TableHeader\";\nimport { useMeasuredHeight } from \"../useMeasuredHeight\";\nimport { useForkRef } from \"@salt-ds/core\";\n\nexport interface TableHeaderHookProps\n extends Pick<\n TableHeaderProps,\n | \"allowDragColumnHeader\"\n | \"columns\"\n | \"onMoveColumn\"\n | \"onSortColumn\"\n | \"tableConfig\"\n > {\n label?: string;\n onHeightMeasured: (height: number) => void;\n onMoveColumn: (columns: ColumnDescriptor[]) => void;\n onSortColumn: (column: ColumnDescriptor, addToExistingSort: boolean) => void;\n}\n\nexport const useTableHeader = ({\n allowDragColumnHeader,\n columns,\n onHeightMeasured,\n onMoveColumn,\n onSortColumn,\n tableConfig,\n}: TableHeaderHookProps) => {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const scrollingContainerRef = useRef<HTMLDivElement | null>(null);\n const { rowRef } = useMeasuredHeight({\n onHeightMeasured,\n });\n\n const setContainerRef = useCallback<RefCallback<HTMLDivElement>>((el) => {\n containerRef.current = el;\n if (el) {\n scrollingContainerRef.current = el.closest(\".vuuTable-contentContainer\");\n } else {\n scrollingContainerRef.current = null;\n }\n }, []);\n\n const handleDropColumnHeader = useCallback(\n ({ fromIndex: moveFrom, toIndex: moveTo }: DropOptions) => {\n const column = columns[moveFrom];\n // columns are what get rendered, so these are the columns that\n // the drop operation relates to. We must translate these into\n // columns within the table config. Grouping complicates this\n // as the group columns are not present in columns but ARE in\n // config.columns\n const orderedColumns = moveColumnTo(columns, column, moveTo);\n\n const ofColumn =\n ({ name }: ColumnDescriptor) =>\n (col: ColumnDescriptor) =>\n col.name === name;\n\n const targetIndex = orderedColumns.findIndex(ofColumn(column));\n const nextColumn = orderedColumns[targetIndex + 1];\n const insertPos = nextColumn\n ? tableConfig.columns.findIndex(ofColumn(nextColumn))\n : -1;\n\n if (moveTo > moveFrom && insertPos !== -1) {\n onMoveColumn(moveColumnTo(tableConfig.columns, column, insertPos - 1));\n } else {\n onMoveColumn(moveColumnTo(tableConfig.columns, column, insertPos));\n }\n },\n [columns, onMoveColumn, tableConfig.columns],\n );\n\n const handleColumnHeaderClick = useCallback(\n (evt: React.MouseEvent | React.KeyboardEvent) => {\n const headerCell = queryClosest(evt.target, \".vuuTableHeaderCell\");\n const colIdx = parseInt(headerCell?.dataset.index ?? \"-1\");\n const column = visibleColumnAtIndex(columns, colIdx);\n const isAdditive = evt.shiftKey;\n column && onSortColumn(column, isAdditive);\n },\n [columns, onSortColumn],\n );\n\n // Drag Drop column headers\n const {\n onMouseDown: columnHeaderDragMouseDown,\n draggable: draggableColumn,\n ...dragDropHook\n } = useDragDrop({\n allowDragDrop: allowDragColumnHeader,\n containerRef,\n draggableClassName: `vuuTable`,\n itemQuery: \".vuuTableHeaderCell\",\n onDrop: handleDropColumnHeader,\n orientation: \"horizontal\",\n scrollingContainerRef,\n });\n\n return {\n draggableColumn,\n draggedColumnIndex: dragDropHook.draggedItemIndex,\n onClick: handleColumnHeaderClick,\n onMouseDown: columnHeaderDragMouseDown,\n setContainerRef: useForkRef(setContainerRef, rowRef),\n };\n};\n"],"names":["useRef","useMeasuredHeight","useCallback","moveColumnTo","queryClosest","visibleColumnAtIndex","useDragDrop","useForkRef"],"mappings":"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"}
1
+ {"version":3,"file":"useTableHeader.js","sources":["../../src/table-header/useTableHeader.ts"],"sourcesContent":["import { ColumnDescriptor } from \"@vuu-ui/vuu-table-types\";\nimport {\n DropOptions,\n useDragDrop as useDragDrop,\n} from \"@vuu-ui/vuu-ui-controls\";\nimport {\n moveColumnTo,\n queryClosest,\n visibleColumnAtIndex,\n} from \"@vuu-ui/vuu-utils\";\nimport { RefCallback, useCallback, useRef } from \"react\";\nimport { TableHeaderProps } from \"./TableHeader\";\nimport { useMeasuredHeight } from \"../useMeasuredHeight\";\nimport { useForkRef } from \"@salt-ds/core\";\n\nexport interface TableHeaderHookProps\n extends Pick<\n TableHeaderProps,\n | \"allowDragColumnHeader\"\n | \"columns\"\n | \"onMoveColumn\"\n | \"onSortColumn\"\n | \"tableConfig\"\n > {\n label?: string;\n onHeightMeasured: (height: number) => void;\n onMoveColumn: (columns: ColumnDescriptor[]) => void;\n onSortColumn: (column: ColumnDescriptor, addToExistingSort: boolean) => void;\n}\n\nexport const useTableHeader = ({\n allowDragColumnHeader,\n columns,\n onHeightMeasured,\n onMoveColumn,\n onSortColumn,\n tableConfig,\n}: TableHeaderHookProps) => {\n const containerRef = useRef<HTMLDivElement | null>(null);\n const scrollingContainerRef = useRef<HTMLDivElement | null>(null);\n const { measuredRef: rowRef } = useMeasuredHeight({\n onHeightMeasured,\n });\n\n const setContainerRef = useCallback<RefCallback<HTMLDivElement>>((el) => {\n containerRef.current = el;\n if (el) {\n scrollingContainerRef.current = el.closest(\".vuuTable-contentContainer\");\n } else {\n scrollingContainerRef.current = null;\n }\n }, []);\n\n const handleDropColumnHeader = useCallback(\n ({ fromIndex: moveFrom, toIndex: moveTo }: DropOptions) => {\n const column = columns[moveFrom];\n // columns are what get rendered, so these are the columns that\n // the drop operation relates to. We must translate these into\n // columns within the table config. Grouping complicates this\n // as the group columns are not present in columns but ARE in\n // config.columns\n const orderedColumns = moveColumnTo(columns, column, moveTo);\n\n const ofColumn =\n ({ name }: ColumnDescriptor) =>\n (col: ColumnDescriptor) =>\n col.name === name;\n\n const targetIndex = orderedColumns.findIndex(ofColumn(column));\n const nextColumn = orderedColumns[targetIndex + 1];\n const insertPos = nextColumn\n ? tableConfig.columns.findIndex(ofColumn(nextColumn))\n : -1;\n\n if (moveTo > moveFrom && insertPos !== -1) {\n onMoveColumn(moveColumnTo(tableConfig.columns, column, insertPos - 1));\n } else {\n onMoveColumn(moveColumnTo(tableConfig.columns, column, insertPos));\n }\n },\n [columns, onMoveColumn, tableConfig.columns],\n );\n\n const handleColumnHeaderClick = useCallback(\n (evt: React.MouseEvent | React.KeyboardEvent) => {\n const headerCell = queryClosest(evt.target, \".vuuTableHeaderCell\");\n const colIdx = parseInt(headerCell?.dataset.index ?? \"-1\");\n const column = visibleColumnAtIndex(columns, colIdx);\n const isAdditive = evt.shiftKey;\n column && onSortColumn(column, isAdditive);\n },\n [columns, onSortColumn],\n );\n\n // Drag Drop column headers\n const {\n onMouseDown: columnHeaderDragMouseDown,\n draggable: draggableColumn,\n ...dragDropHook\n } = useDragDrop({\n allowDragDrop: allowDragColumnHeader,\n containerRef,\n draggableClassName: `vuuTable`,\n itemQuery: \".vuuTableHeaderCell\",\n onDrop: handleDropColumnHeader,\n orientation: \"horizontal\",\n scrollingContainerRef,\n });\n\n return {\n draggableColumn,\n draggedColumnIndex: dragDropHook.draggedItemIndex,\n onClick: handleColumnHeaderClick,\n onMouseDown: columnHeaderDragMouseDown,\n setContainerRef: useForkRef(setContainerRef, rowRef),\n };\n};\n"],"names":["useRef","useMeasuredHeight","useCallback","moveColumnTo","queryClosest","visibleColumnAtIndex","useDragDrop","useForkRef"],"mappings":"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"}
@@ -0,0 +1,56 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var tableDomUtils = require('./table-dom-utils.js');
5
+ var vuuUtils = require('@vuu-ui/vuu-utils');
6
+
7
+ const useCellFocus = ({
8
+ containerRef,
9
+ disableFocus = false,
10
+ requestScroll
11
+ }) => {
12
+ const focusableCell = react.useRef();
13
+ const focusCell = react.useCallback(
14
+ (cellPos) => {
15
+ if (containerRef.current) {
16
+ const activeCell = tableDomUtils.getTableCell(containerRef, cellPos);
17
+ if (activeCell) {
18
+ if (activeCell !== focusableCell.current) {
19
+ focusableCell.current?.removeAttribute("tabindex");
20
+ focusableCell.current = activeCell;
21
+ activeCell.setAttribute("tabindex", "0");
22
+ }
23
+ requestScroll?.({ type: "scroll-row", rowIndex: cellPos[0] });
24
+ activeCell.focus({ preventScroll: true });
25
+ }
26
+ }
27
+ },
28
+ // TODO we recreate this function whenever viewportRange changes, which will
29
+ // be often whilst scrolling - store range in a a ref ?
30
+ [containerRef, requestScroll]
31
+ );
32
+ const tableBodyRef = react.useCallback(
33
+ (el) => {
34
+ if (el) {
35
+ const table = vuuUtils.queryClosest(el, ".vuuTable");
36
+ if (table) {
37
+ if (focusableCell.current === void 0 && !disableFocus) {
38
+ const cell = table.querySelector(tableDomUtils.headerCellQuery(0)) || table.querySelector(tableDomUtils.dataCellQuery(0, 0));
39
+ if (cell) {
40
+ cell.setAttribute("tabindex", "0");
41
+ focusableCell.current = cell;
42
+ }
43
+ }
44
+ }
45
+ }
46
+ },
47
+ [disableFocus]
48
+ );
49
+ return {
50
+ focusCell,
51
+ tableBodyRef
52
+ };
53
+ };
54
+
55
+ exports.useCellFocus = useCellFocus;
56
+ //# sourceMappingURL=useCellFocus.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useCellFocus.js","sources":["../src/useCellFocus.ts"],"sourcesContent":["import { RefCallback, RefObject, useCallback, useRef } from \"react\";\nimport {\n CellPos,\n dataCellQuery,\n getTableCell,\n headerCellQuery,\n} from \"./table-dom-utils\";\nimport { ScrollRequestHandler } from \"./useTableScroll\";\nimport { queryClosest } from \"@vuu-ui/vuu-utils\";\n\nexport interface CellFocusHookProps {\n containerRef: RefObject<HTMLElement>;\n disableFocus?: boolean;\n requestScroll?: ScrollRequestHandler;\n}\n\nexport type FocusCell = (cellPos: CellPos) => void;\n\nexport const useCellFocus = ({\n containerRef,\n disableFocus = false,\n requestScroll,\n}: CellFocusHookProps) => {\n const focusableCell = useRef<HTMLElement>();\n\n const focusCell = useCallback<FocusCell>(\n (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 tableBodyRef = useCallback<RefCallback<HTMLDivElement>>(\n (el) => {\n if (el) {\n const table = queryClosest<HTMLDivElement>(el, \".vuuTable\");\n if (table) {\n if (focusableCell.current === undefined && !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 focusableCell.current = cell;\n }\n }\n }\n }\n },\n [disableFocus],\n );\n\n return {\n focusCell,\n tableBodyRef,\n };\n};\n"],"names":["useRef","useCallback","getTableCell","queryClosest","headerCellQuery","dataCellQuery"],"mappings":";;;;;;AAkBO,MAAM,eAAe,CAAC;AAAA,EAC3B,YAAA;AAAA,EACA,YAAe,GAAA,KAAA;AAAA,EACf,aAAA;AACF,CAA0B,KAAA;AACxB,EAAA,MAAM,gBAAgBA,YAAoB,EAAA,CAAA;AAE1C,EAAA,MAAM,SAAY,GAAAC,iBAAA;AAAA,IAChB,CAAC,OAAY,KAAA;AACX,MAAA,IAAI,aAAa,OAAS,EAAA;AACxB,QAAM,MAAA,UAAA,GAAaC,0BAAa,CAAA,YAAA,EAAc,OAAO,CAAA,CAAA;AACrD,QAAA,IAAI,UAAY,EAAA;AACd,UAAI,IAAA,UAAA,KAAe,cAAc,OAAS,EAAA;AACxC,YAAc,aAAA,CAAA,OAAA,EAAS,gBAAgB,UAAU,CAAA,CAAA;AACjD,YAAA,aAAA,CAAc,OAAU,GAAA,UAAA,CAAA;AACxB,YAAW,UAAA,CAAA,YAAA,CAAa,YAAY,GAAG,CAAA,CAAA;AAAA,WACzC;AAEA,UAAA,aAAA,GAAgB,EAAE,IAAM,EAAA,YAAA,EAAc,UAAU,OAAQ,CAAA,CAAC,GAAG,CAAA,CAAA;AAC5D,UAAA,UAAA,CAAW,KAAM,CAAA,EAAE,aAAe,EAAA,IAAA,EAAM,CAAA,CAAA;AAAA,SAC1C;AAAA,OACF;AAAA,KACF;AAAA;AAAA;AAAA,IAGA,CAAC,cAAc,aAAa,CAAA;AAAA,GAC9B,CAAA;AAEA,EAAA,MAAM,YAAe,GAAAD,iBAAA;AAAA,IACnB,CAAC,EAAO,KAAA;AACN,MAAA,IAAI,EAAI,EAAA;AACN,QAAM,MAAA,KAAA,GAAQE,qBAA6B,CAAA,EAAA,EAAI,WAAW,CAAA,CAAA;AAC1D,QAAA,IAAI,KAAO,EAAA;AACT,UAAA,IAAI,aAAc,CAAA,OAAA,KAAY,KAAa,CAAA,IAAA,CAAC,YAAc,EAAA;AACxD,YAAA,MAAM,IACJ,GAAA,KAAA,CAAM,aAA8B,CAAAC,6BAAA,CAAgB,CAAC,CAAC,CACtD,IAAA,KAAA,CAAM,aAA8B,CAAAC,2BAAA,CAAc,CAAG,EAAA,CAAC,CAAC,CAAA,CAAA;AACzD,YAAA,IAAI,IAAM,EAAA;AACR,cAAK,IAAA,CAAA,YAAA,CAAa,YAAY,GAAG,CAAA,CAAA;AACjC,cAAA,aAAA,CAAc,OAAU,GAAA,IAAA,CAAA;AAAA,aAC1B;AAAA,WACF;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAAA,IACA,CAAC,YAAY,CAAA;AAAA,GACf,CAAA;AAEA,EAAO,OAAA;AAAA,IACL,SAAA;AAAA,IACA,YAAA;AAAA,GACF,CAAA;AACF;;;;"}
@@ -4,6 +4,7 @@ var vuuUiControls = require('@vuu-ui/vuu-ui-controls');
4
4
  var vuuUtils = require('@vuu-ui/vuu-utils');
5
5
  var react = require('react');
6
6
 
7
+ const isRowSelectionKey = (key) => key === "Enter" || key === " ";
7
8
  const useControlledTableNavigation = (initialValue, rowCount) => {
8
9
  const tableRef = react.useRef(null);
9
10
  const [highlightedIndexRef, setHighlightedIndex] = vuuUiControls.useStateRef(initialValue);
@@ -13,7 +14,7 @@ const useControlledTableNavigation = (initialValue, rowCount) => {
13
14
  setHighlightedIndex((index = -1) => Math.min(rowCount - 1, index + 1));
14
15
  } else if (e.key === "ArrowUp") {
15
16
  setHighlightedIndex((index = -1) => Math.max(0, index - 1));
16
- } else if (e.key === "Enter" || e.key === " ") {
17
+ } else if (isRowSelectionKey(e.key)) {
17
18
  const { current: rowIdx } = highlightedIndexRef;
18
19
  if (typeof rowIdx === "number") {
19
20
  const rowEl = tableRef.current?.querySelector(
@@ -41,5 +42,6 @@ const useControlledTableNavigation = (initialValue, rowCount) => {
41
42
  };
42
43
  };
43
44
 
45
+ exports.isRowSelectionKey = isRowSelectionKey;
44
46
  exports.useControlledTableNavigation = useControlledTableNavigation;
45
47
  //# sourceMappingURL=useControlledTableNavigation.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useControlledTableNavigation.js","sources":["../src/useControlledTableNavigation.ts"],"sourcesContent":["import { useStateRef } from \"@vuu-ui/vuu-ui-controls\";\nimport { dispatchMouseEvent } from \"@vuu-ui/vuu-utils\";\nimport { KeyboardEventHandler, useCallback, useRef } from \"react\";\n\nexport const useControlledTableNavigation = (\n initialValue: number,\n rowCount: number,\n) => {\n const tableRef = useRef<HTMLDivElement>(null);\n\n const [highlightedIndexRef, setHighlightedIndex] = useStateRef<\n number | undefined\n >(initialValue);\n\n const handleKeyDown = useCallback<KeyboardEventHandler>(\n (e) => {\n if (e.key === \"ArrowDown\") {\n setHighlightedIndex((index = -1) => Math.min(rowCount - 1, index + 1));\n } else if (e.key === \"ArrowUp\") {\n setHighlightedIndex((index = -1) => Math.max(0, index - 1));\n } else if (e.key === \"Enter\" || e.key === \" \") {\n const { current: rowIdx } = highlightedIndexRef;\n // induce an onSelect event by 'clicking' the row\n if (typeof rowIdx === \"number\") {\n const rowEl = tableRef.current?.querySelector(\n `[aria-rowindex=\"${rowIdx + 1}\"]`,\n ) as HTMLElement;\n if (rowEl) {\n dispatchMouseEvent(rowEl, \"click\");\n }\n }\n }\n },\n [highlightedIndexRef, rowCount, setHighlightedIndex],\n );\n\n const handleHighlight = useCallback(\n (idx: number) => {\n setHighlightedIndex(idx);\n },\n [setHighlightedIndex],\n );\n\n return {\n highlightedIndexRef,\n onHighlight: handleHighlight,\n onKeyDown: handleKeyDown,\n tableRef,\n };\n};\n"],"names":["useRef","useStateRef","useCallback","dispatchMouseEvent"],"mappings":";;;;;;AAIa,MAAA,4BAAA,GAA+B,CAC1C,YAAA,EACA,QACG,KAAA;AACH,EAAM,MAAA,QAAA,GAAWA,aAAuB,IAAI,CAAA,CAAA;AAE5C,EAAA,MAAM,CAAC,mBAAA,EAAqB,mBAAmB,CAAA,GAAIC,0BAEjD,YAAY,CAAA,CAAA;AAEd,EAAA,MAAM,aAAgB,GAAAC,iBAAA;AAAA,IACpB,CAAC,CAAM,KAAA;AACL,MAAI,IAAA,CAAA,CAAE,QAAQ,WAAa,EAAA;AACzB,QAAoB,mBAAA,CAAA,CAAC,QAAQ,CAAO,CAAA,KAAA,IAAA,CAAK,IAAI,QAAW,GAAA,CAAA,EAAG,KAAQ,GAAA,CAAC,CAAC,CAAA,CAAA;AAAA,OACvE,MAAA,IAAW,CAAE,CAAA,GAAA,KAAQ,SAAW,EAAA;AAC9B,QAAoB,mBAAA,CAAA,CAAC,QAAQ,CAAO,CAAA,KAAA,IAAA,CAAK,IAAI,CAAG,EAAA,KAAA,GAAQ,CAAC,CAAC,CAAA,CAAA;AAAA,iBACjD,CAAE,CAAA,GAAA,KAAQ,OAAW,IAAA,CAAA,CAAE,QAAQ,GAAK,EAAA;AAC7C,QAAM,MAAA,EAAE,OAAS,EAAA,MAAA,EAAW,GAAA,mBAAA,CAAA;AAE5B,QAAI,IAAA,OAAO,WAAW,QAAU,EAAA;AAC9B,UAAM,MAAA,KAAA,GAAQ,SAAS,OAAS,EAAA,aAAA;AAAA,YAC9B,CAAA,gBAAA,EAAmB,SAAS,CAAC,CAAA,EAAA,CAAA;AAAA,WAC/B,CAAA;AACA,UAAA,IAAI,KAAO,EAAA;AACT,YAAAC,2BAAA,CAAmB,OAAO,OAAO,CAAA,CAAA;AAAA,WACnC;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAAA,IACA,CAAC,mBAAqB,EAAA,QAAA,EAAU,mBAAmB,CAAA;AAAA,GACrD,CAAA;AAEA,EAAA,MAAM,eAAkB,GAAAD,iBAAA;AAAA,IACtB,CAAC,GAAgB,KAAA;AACf,MAAA,mBAAA,CAAoB,GAAG,CAAA,CAAA;AAAA,KACzB;AAAA,IACA,CAAC,mBAAmB,CAAA;AAAA,GACtB,CAAA;AAEA,EAAO,OAAA;AAAA,IACL,mBAAA;AAAA,IACA,WAAa,EAAA,eAAA;AAAA,IACb,SAAW,EAAA,aAAA;AAAA,IACX,QAAA;AAAA,GACF,CAAA;AACF;;;;"}
1
+ {"version":3,"file":"useControlledTableNavigation.js","sources":["../src/useControlledTableNavigation.ts"],"sourcesContent":["import { useStateRef } from \"@vuu-ui/vuu-ui-controls\";\nimport { dispatchMouseEvent } from \"@vuu-ui/vuu-utils\";\nimport { KeyboardEventHandler, useCallback, useRef } from \"react\";\n\nexport const isRowSelectionKey = (key: string) =>\n key === \"Enter\" || key === \" \";\n\nexport const useControlledTableNavigation = (\n initialValue: number,\n rowCount: number,\n) => {\n const tableRef = useRef<HTMLDivElement>(null);\n\n const [highlightedIndexRef, setHighlightedIndex] = useStateRef<\n number | undefined\n >(initialValue);\n\n const handleKeyDown = useCallback<KeyboardEventHandler>(\n (e) => {\n if (e.key === \"ArrowDown\") {\n setHighlightedIndex((index = -1) => Math.min(rowCount - 1, index + 1));\n } else if (e.key === \"ArrowUp\") {\n setHighlightedIndex((index = -1) => Math.max(0, index - 1));\n } else if (isRowSelectionKey(e.key)) {\n const { current: rowIdx } = highlightedIndexRef;\n // induce an onSelect event by 'clicking' the row\n if (typeof rowIdx === \"number\") {\n const rowEl = tableRef.current?.querySelector(\n `[aria-rowindex=\"${rowIdx + 1}\"]`,\n ) as HTMLElement;\n if (rowEl) {\n dispatchMouseEvent(rowEl, \"click\");\n }\n }\n }\n },\n [highlightedIndexRef, rowCount, setHighlightedIndex],\n );\n\n const handleHighlight = useCallback(\n (idx: number) => {\n setHighlightedIndex(idx);\n },\n [setHighlightedIndex],\n );\n\n return {\n highlightedIndexRef,\n onHighlight: handleHighlight,\n onKeyDown: handleKeyDown,\n tableRef,\n };\n};\n"],"names":["useRef","useStateRef","useCallback","dispatchMouseEvent"],"mappings":";;;;;;AAIO,MAAM,iBAAoB,GAAA,CAAC,GAChC,KAAA,GAAA,KAAQ,WAAW,GAAQ,KAAA,IAAA;AAEhB,MAAA,4BAAA,GAA+B,CAC1C,YAAA,EACA,QACG,KAAA;AACH,EAAM,MAAA,QAAA,GAAWA,aAAuB,IAAI,CAAA,CAAA;AAE5C,EAAA,MAAM,CAAC,mBAAA,EAAqB,mBAAmB,CAAA,GAAIC,0BAEjD,YAAY,CAAA,CAAA;AAEd,EAAA,MAAM,aAAgB,GAAAC,iBAAA;AAAA,IACpB,CAAC,CAAM,KAAA;AACL,MAAI,IAAA,CAAA,CAAE,QAAQ,WAAa,EAAA;AACzB,QAAoB,mBAAA,CAAA,CAAC,QAAQ,CAAO,CAAA,KAAA,IAAA,CAAK,IAAI,QAAW,GAAA,CAAA,EAAG,KAAQ,GAAA,CAAC,CAAC,CAAA,CAAA;AAAA,OACvE,MAAA,IAAW,CAAE,CAAA,GAAA,KAAQ,SAAW,EAAA;AAC9B,QAAoB,mBAAA,CAAA,CAAC,QAAQ,CAAO,CAAA,KAAA,IAAA,CAAK,IAAI,CAAG,EAAA,KAAA,GAAQ,CAAC,CAAC,CAAA,CAAA;AAAA,OACjD,MAAA,IAAA,iBAAA,CAAkB,CAAE,CAAA,GAAG,CAAG,EAAA;AACnC,QAAM,MAAA,EAAE,OAAS,EAAA,MAAA,EAAW,GAAA,mBAAA,CAAA;AAE5B,QAAI,IAAA,OAAO,WAAW,QAAU,EAAA;AAC9B,UAAM,MAAA,KAAA,GAAQ,SAAS,OAAS,EAAA,aAAA;AAAA,YAC9B,CAAA,gBAAA,EAAmB,SAAS,CAAC,CAAA,EAAA,CAAA;AAAA,WAC/B,CAAA;AACA,UAAA,IAAI,KAAO,EAAA;AACT,YAAAC,2BAAA,CAAmB,OAAO,OAAO,CAAA,CAAA;AAAA,WACnC;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAAA,IACA,CAAC,mBAAqB,EAAA,QAAA,EAAU,mBAAmB,CAAA;AAAA,GACrD,CAAA;AAEA,EAAA,MAAM,eAAkB,GAAAD,iBAAA;AAAA,IACtB,CAAC,GAAgB,KAAA;AACf,MAAA,mBAAA,CAAoB,GAAG,CAAA,CAAA;AAAA,KACzB;AAAA,IACA,CAAC,mBAAmB,CAAA;AAAA,GACtB,CAAA;AAEA,EAAO,OAAA;AAAA,IACL,mBAAA;AAAA,IACA,WAAa,EAAA,eAAA;AAAA,IACb,SAAW,EAAA,aAAA;AAAA,IACX,QAAA;AAAA,GACF,CAAA;AACF;;;;;"}
@@ -20,6 +20,14 @@ const useDataSource = ({
20
20
  // eslint-disable-next-line react-hooks/exhaustive-deps
21
21
  []
22
22
  );
23
+ react.useMemo(() => {
24
+ dataSource.on("resumed", () => {
25
+ const { range } = dataSource;
26
+ if (range.to !== 0) {
27
+ dataWindow.setRange(dataSource.range);
28
+ }
29
+ });
30
+ }, [dataSource, dataWindow]);
23
31
  const setData = react.useCallback(
24
32
  (updates) => {
25
33
  for (const row of updates) {
@@ -46,6 +54,11 @@ const useDataSource = ({
46
54
  }
47
55
  }
48
56
  if (message.rows) {
57
+ if (message.range) {
58
+ if (message.range.to !== dataWindow.range.to) {
59
+ dataWindow.setRange(message.range);
60
+ }
61
+ }
49
62
  setData(message.rows);
50
63
  } else if (message.size === 0) {
51
64
  setData([]);
@@ -1 +1 @@
1
- {"version":3,"file":"useDataSource.js","sources":["../src/useDataSource.ts"],"sourcesContent":["import {\n DataSource,\n DataSourceRow,\n DataSourceSubscribedMessage,\n SubscribeCallback,\n} from \"@vuu-ui/vuu-data-types\";\nimport { VuuRange } from \"@vuu-ui/vuu-protocol-types\";\nimport { getFullRange, NULL_RANGE, rangesAreSame } from \"@vuu-ui/vuu-utils\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { MovingWindow } from \"./moving-window\";\n\nexport interface DataSourceHookProps {\n dataSource: DataSource;\n onSizeChange: (size: number) => void;\n onSubscribed: (subscription: DataSourceSubscribedMessage) => void;\n renderBufferSize?: number;\n}\n\nexport const useDataSource = ({\n dataSource,\n onSizeChange,\n onSubscribed,\n renderBufferSize = 0,\n}: DataSourceHookProps) => {\n const [, forceUpdate] = useState<unknown>(null);\n const data = useRef<DataSourceRow[]>([]);\n const isMounted = useRef(true);\n const hasUpdated = useRef(false);\n const rangeRef = useRef<VuuRange>(NULL_RANGE);\n\n const dataWindow = useMemo(\n () => new MovingWindow(getFullRange(NULL_RANGE, renderBufferSize)),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n );\n\n const setData = useCallback(\n (updates: DataSourceRow[]) => {\n for (const row of updates) {\n dataWindow.add(row);\n }\n data.current = dataWindow.data;\n if (isMounted.current) {\n // TODO do we ever need to worry about missing updates here ?\n forceUpdate({});\n }\n },\n [dataWindow],\n );\n\n const datasourceMessageHandler: SubscribeCallback = useCallback(\n (message) => {\n if (message.type === \"subscribed\") {\n onSubscribed?.(message);\n } else if (message.type === \"viewport-update\") {\n if (typeof message.size === \"number\") {\n onSizeChange?.(message.size);\n const size = dataWindow.data.length;\n dataWindow.setRowCount(message.size);\n if (dataWindow.data.length < size) {\n forceUpdate({});\n }\n }\n if (message.rows) {\n setData(message.rows);\n } else if (message.size === 0) {\n setData([]);\n } else if (typeof message.size === \"number\") {\n data.current = dataWindow.data;\n hasUpdated.current = true;\n }\n } else if (message.type === \"viewport-clear\") {\n onSizeChange?.(0);\n dataWindow.setRowCount(0);\n setData([]);\n forceUpdate({});\n } else {\n console.log(`useDataSource unexpected message ${message.type}`);\n }\n },\n [dataWindow, onSizeChange, onSubscribed, setData],\n );\n\n const getSelectedRows = useCallback(() => {\n return dataWindow.getSelectedRows();\n }, [dataWindow]);\n\n useEffect(() => {\n isMounted.current = true;\n if (dataSource.status !== \"initialising\") {\n dataSource.resume?.(datasourceMessageHandler);\n }\n return () => {\n isMounted.current = false;\n dataSource.suspend?.();\n };\n }, [dataSource, datasourceMessageHandler]);\n\n useEffect(() => {\n if (dataSource.status === \"disabled\") {\n dataSource.enable?.(datasourceMessageHandler);\n }\n }, [dataSource, datasourceMessageHandler, renderBufferSize]);\n\n const setRange = useCallback(\n (range: VuuRange) => {\n if (!rangesAreSame(range, rangeRef.current)) {\n const fullRange = getFullRange(range, renderBufferSize);\n dataWindow.setRange(fullRange);\n\n if (dataSource.status !== \"subscribed\") {\n dataSource?.subscribe({ range: fullRange }, datasourceMessageHandler);\n } else {\n dataSource.range = rangeRef.current = fullRange;\n }\n // emit a range event omitting the renderBufferSize\n // This isn't great, we're using the dataSource as a conduit to emit a\n // message that has nothing to do with the dataSource itself. Client\n // is the DataSourceState component.\n // WHY CANT THIS BE DONE WITHIN DataSOurce ?\n dataSource.emit(\"range\", range);\n }\n },\n [dataSource, dataWindow, datasourceMessageHandler, renderBufferSize],\n );\n\n return {\n data: data.current,\n dataRef: data,\n getSelectedRows,\n range: rangeRef.current,\n setRange,\n };\n};\n"],"names":["useState","useRef","NULL_RANGE","useMemo","MovingWindow","getFullRange","useCallback","useEffect","rangesAreSame"],"mappings":"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"}
1
+ {"version":3,"file":"useDataSource.js","sources":["../src/useDataSource.ts"],"sourcesContent":["import {\n DataSource,\n DataSourceRow,\n DataSourceSubscribedMessage,\n SubscribeCallback,\n} from \"@vuu-ui/vuu-data-types\";\nimport { VuuRange } from \"@vuu-ui/vuu-protocol-types\";\nimport { getFullRange, NULL_RANGE, rangesAreSame } from \"@vuu-ui/vuu-utils\";\nimport { useCallback, useEffect, useMemo, useRef, useState } from \"react\";\nimport { MovingWindow } from \"./moving-window\";\n\nexport interface DataSourceHookProps {\n dataSource: DataSource;\n onSizeChange: (size: number) => void;\n onSubscribed: (subscription: DataSourceSubscribedMessage) => void;\n renderBufferSize?: number;\n}\n\nexport const useDataSource = ({\n dataSource,\n onSizeChange,\n onSubscribed,\n renderBufferSize = 0,\n}: DataSourceHookProps) => {\n const [, forceUpdate] = useState<unknown>(null);\n const data = useRef<DataSourceRow[]>([]);\n const isMounted = useRef(true);\n const hasUpdated = useRef(false);\n const rangeRef = useRef<VuuRange>(NULL_RANGE);\n\n const dataWindow = useMemo(\n () => new MovingWindow(getFullRange(NULL_RANGE, renderBufferSize)),\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [],\n );\n\n useMemo(() => {\n dataSource.on(\"resumed\", () => {\n // When we resume a dataSource (after switching tabs etc)\n // client will receive rows. We may not have received any\n // setRange calls at this point so dataWindow range will\n //not yet be set. If the dataWindow range is already set,\n // this is a no-op.\n const { range } = dataSource;\n if (range.to !== 0) {\n dataWindow.setRange(dataSource.range);\n }\n });\n }, [dataSource, dataWindow]);\n\n const setData = useCallback(\n (updates: DataSourceRow[]) => {\n for (const row of updates) {\n dataWindow.add(row);\n }\n data.current = dataWindow.data;\n if (isMounted.current) {\n // TODO do we ever need to worry about missing updates here ?\n forceUpdate({});\n }\n },\n [dataWindow],\n );\n\n const datasourceMessageHandler: SubscribeCallback = useCallback(\n (message) => {\n if (message.type === \"subscribed\") {\n onSubscribed?.(message);\n } else if (message.type === \"viewport-update\") {\n if (typeof message.size === \"number\") {\n onSizeChange?.(message.size);\n const size = dataWindow.data.length;\n dataWindow.setRowCount(message.size);\n if (dataWindow.data.length < size) {\n forceUpdate({});\n }\n }\n if (message.rows) {\n if (message.range) {\n if (message.range.to !== dataWindow.range.to) {\n dataWindow.setRange(message.range);\n }\n }\n setData(message.rows);\n } else if (message.size === 0) {\n setData([]);\n } else if (typeof message.size === \"number\") {\n data.current = dataWindow.data;\n hasUpdated.current = true;\n }\n } else if (message.type === \"viewport-clear\") {\n onSizeChange?.(0);\n dataWindow.setRowCount(0);\n setData([]);\n forceUpdate({});\n } else {\n console.log(`useDataSource unexpected message ${message.type}`);\n }\n },\n [dataWindow, onSizeChange, onSubscribed, setData],\n );\n\n const getSelectedRows = useCallback(() => {\n return dataWindow.getSelectedRows();\n }, [dataWindow]);\n\n useEffect(() => {\n isMounted.current = true;\n if (dataSource.status !== \"initialising\") {\n dataSource.resume?.(datasourceMessageHandler);\n }\n return () => {\n isMounted.current = false;\n dataSource.suspend?.();\n };\n }, [dataSource, datasourceMessageHandler]);\n\n useEffect(() => {\n if (dataSource.status === \"disabled\") {\n dataSource.enable?.(datasourceMessageHandler);\n }\n }, [dataSource, datasourceMessageHandler, renderBufferSize]);\n\n const setRange = useCallback(\n (range: VuuRange) => {\n if (!rangesAreSame(range, rangeRef.current)) {\n const fullRange = getFullRange(range, renderBufferSize);\n dataWindow.setRange(fullRange);\n\n if (dataSource.status !== \"subscribed\") {\n dataSource?.subscribe({ range: fullRange }, datasourceMessageHandler);\n } else {\n dataSource.range = rangeRef.current = fullRange;\n }\n // emit a range event omitting the renderBufferSize\n // This isn't great, we're using the dataSource as a conduit to emit a\n // message that has nothing to do with the dataSource itself. Client\n // is the DataSourceState component.\n // WHY CANT THIS BE DONE WITHIN DataSource ?\n dataSource.emit(\"range\", range);\n }\n },\n [dataSource, dataWindow, datasourceMessageHandler, renderBufferSize],\n );\n\n return {\n data: data.current,\n dataRef: data,\n getSelectedRows,\n range: rangeRef.current,\n setRange,\n };\n};\n"],"names":["useState","useRef","NULL_RANGE","useMemo","MovingWindow","getFullRange","useCallback","useEffect","rangesAreSame"],"mappings":"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"}
@@ -28,43 +28,12 @@ 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 = ({
63
32
  columnCount = 0,
64
33
  containerRef,
65
- disableFocus = false,
66
34
  defaultHighlightedIndex,
67
35
  disableHighlightOnFocus,
36
+ focusCell,
68
37
  highlightedIndex: highlightedIndexProp,
69
38
  navigationStyle,
70
39
  requestScroll,
@@ -73,7 +42,6 @@ const useKeyboardNavigation = ({
73
42
  viewportRowCount
74
43
  }) => {
75
44
  const focusedCellPos = react.useRef([-1, -1]);
76
- const focusableCell = react.useRef();
77
45
  const activeCellPos = react.useRef([-1, 0]);
78
46
  const highlightedIndexRef = react.useRef();
79
47
  const [highlightedIndex, setHighlightedIdx] = core.useControlled({
@@ -92,39 +60,6 @@ const useKeyboardNavigation = ({
92
60
  const getFocusedCell = (element) => element?.closest(
93
61
  "[role='columnHeader'],[role='cell']"
94
62
  );
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
63
  const setActiveCell = react.useCallback(
129
64
  (rowIdx, colIdx, fromKeyboard = false) => {
130
65
  const pos = [rowIdx, colIdx];
@@ -184,7 +119,7 @@ const useKeyboardNavigation = ({
184
119
  if (containerRef.current?.contains(document.activeElement)) {
185
120
  const focusedCell = getFocusedCell(document.activeElement);
186
121
  if (focusedCell) {
187
- focusedCellPos.current = getTableCellPos(focusedCell);
122
+ focusedCellPos.current = tableDomUtils.getTableCellPos(focusedCell);
188
123
  if (navigationStyle === "row") {
189
124
  setHighlightedIdx(focusedCellPos.current[0]);
190
125
  }
@@ -199,7 +134,7 @@ const useKeyboardNavigation = ({
199
134
  ]);
200
135
  const navigateChildItems = react.useCallback(
201
136
  async (key) => {
202
- const [nextRowIdx, nextColIdx] = isPagingKey(key) ? await nextPageItemIdx(key, activeCellPos.current) : nextCellPos(key, activeCellPos.current, columnCount, rowCount);
137
+ const [nextRowIdx, nextColIdx] = isPagingKey(key) ? await nextPageItemIdx(key, activeCellPos.current) : tableDomUtils.getNextCellPos(key, activeCellPos.current, columnCount, rowCount);
203
138
  const [rowIdx, colIdx] = activeCellPos.current;
204
139
  if (nextRowIdx !== rowIdx || nextColIdx !== colIdx) {
205
140
  setActiveCell(nextRowIdx, nextColIdx, true);
@@ -216,7 +151,7 @@ const useKeyboardNavigation = ({
216
151
  const moveHighlightedRow = react.useCallback(
217
152
  async (key) => {
218
153
  const { current: highlighted } = highlightedIndexRef;
219
- const [nextRowIdx] = isPagingKey(key) ? await nextPageItemIdx(key, [highlighted ?? -1, 0]) : nextCellPos(key, [highlighted ?? -1, 0], columnCount, rowCount);
154
+ const [nextRowIdx] = isPagingKey(key) ? await nextPageItemIdx(key, [highlighted ?? -1, 0]) : tableDomUtils.getNextCellPos(key, [highlighted ?? -1, 0], columnCount, rowCount);
220
155
  if (nextRowIdx !== highlighted) {
221
156
  setHighlightedIndex(nextRowIdx);
222
157
  scrollRowIntoViewIfNecessary(nextRowIdx);
@@ -232,7 +167,9 @@ const useKeyboardNavigation = ({
232
167
  );
233
168
  react.useEffect(() => {
234
169
  if (highlightedIndexProp !== void 0 && highlightedIndexProp !== -1) {
235
- scrollRowIntoViewIfNecessary(highlightedIndexProp);
170
+ requestAnimationFrame(() => {
171
+ scrollRowIntoViewIfNecessary(highlightedIndexProp);
172
+ });
236
173
  }
237
174
  }, [highlightedIndexProp, scrollRowIntoViewIfNecessary]);
238
175
  const handleKeyDown = react.useCallback(
@@ -259,7 +196,7 @@ const useKeyboardNavigation = ({
259
196
  const target = evt.target;
260
197
  const focusedCell = getFocusedCell(target);
261
198
  if (focusedCell) {
262
- const [rowIdx, colIdx] = getTableCellPos(focusedCell);
199
+ const [rowIdx, colIdx] = tableDomUtils.getTableCellPos(focusedCell);
263
200
  setActiveCell(rowIdx, colIdx);
264
201
  }
265
202
  },
@@ -280,17 +217,6 @@ const useKeyboardNavigation = ({
280
217
  const navigate = react.useCallback(() => {
281
218
  navigateChildItems("ArrowDown");
282
219
  }, [navigateChildItems]);
283
- const fullyRendered = containerRef.current?.firstChild != null;
284
- react.useEffect(() => {
285
- if (fullyRendered && focusableCell.current === void 0 && !disableFocus) {
286
- const { current: container } = containerRef;
287
- const cell = container?.querySelector(tableDomUtils.headerCellQuery(0)) || container?.querySelector(tableDomUtils.dataCellQuery(0, 0));
288
- if (cell) {
289
- cell.setAttribute("tabindex", "0");
290
- focusableCell.current = cell;
291
- }
292
- }
293
- }, [containerRef, disableFocus, fullyRendered]);
294
220
  return {
295
221
  highlightedIndexRef,
296
222
  navigate,