@urbanos/react-discovery-ui 2.1.49 → 2.1.51

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.
@@ -43,20 +43,32 @@ var DataTable = function DataTable(_ref) {
43
43
  _useState2 = _slicedToArray(_useState, 2),
44
44
  pagination = _useState2[0],
45
45
  setPagination = _useState2[1];
46
- var _useState3 = (0, _react.useState)({
47
- row: 0,
48
- col: 0
49
- }),
46
+ var _useState3 = (0, _react.useState)({}),
50
47
  _useState4 = _slicedToArray(_useState3, 2),
51
- focusedCell = _useState4[0],
52
- setFocusedCell = _useState4[1];
53
- var focusedCellRef = (0, _react.useRef)({
54
- row: 0,
55
- col: 0
56
- });
48
+ columnSizing = _useState4[0],
49
+ setColumnSizing = _useState4[1];
57
50
  var totalRowsRef = (0, _react.useRef)(1);
58
51
  var totalColsRef = (0, _react.useRef)(columns.length);
59
52
  var tableRef = (0, _react.useRef)(null);
53
+
54
+ // Stable per-instance prefix so header IDs are unique if multiple tables are on the page
55
+ var tableId = (0, _react.useRef)("dv-".concat(Math.random().toString(36).slice(2, 7))).current;
56
+ var colHeaderId = function colHeaderId(colIndex) {
57
+ return "".concat(tableId, "-col-").concat(colIndex);
58
+ };
59
+ var rowHeaderId = function rowHeaderId(rowIndex) {
60
+ return "".concat(tableId, "-row-").concat(rowIndex);
61
+ };
62
+ var dataCellId = function dataCellId(rowIndex, colIndex) {
63
+ return "".concat(tableId, "-cell-").concat(rowIndex, "-").concat(colIndex);
64
+ };
65
+
66
+ // aria-activedescendant: track currently active cell by ID
67
+ var _useState5 = (0, _react.useState)(colHeaderId(0)),
68
+ _useState6 = _slicedToArray(_useState5, 2),
69
+ activeCellId = _useState6[0],
70
+ setActiveCellId = _useState6[1];
71
+ var activeCellIdRef = (0, _react.useRef)(colHeaderId(0));
60
72
  (0, _react.useEffect)(function () {
61
73
  setPagination(function (prev) {
62
74
  return _objectSpread(_objectSpread({}, prev), {}, {
@@ -68,7 +80,8 @@ var DataTable = function DataTable(_ref) {
68
80
  data: data || [],
69
81
  columns: columns,
70
82
  state: {
71
- pagination: pagination
83
+ pagination: pagination,
84
+ columnSizing: columnSizing
72
85
  },
73
86
  onPaginationChange: function onPaginationChange(updater) {
74
87
  var newPagination = typeof updater === 'function' ? updater(pagination) : updater;
@@ -77,6 +90,9 @@ var DataTable = function DataTable(_ref) {
77
90
  onNextPageClicked(newPagination.pageIndex);
78
91
  }
79
92
  },
93
+ onColumnSizingChange: setColumnSizing,
94
+ columnResizeMode: 'onChange',
95
+ enableColumnResizing: true,
80
96
  getCoreRowModel: (0, _reactTable.getCoreRowModel)(),
81
97
  getPaginationRowModel: (0, _reactTable.getPaginationRowModel)()
82
98
  });
@@ -85,15 +101,54 @@ var DataTable = function DataTable(_ref) {
85
101
  totalRowsRef.current = table.getRowModel().rows.length + 1; // row 0 = header
86
102
  totalColsRef.current = columns.length;
87
103
 
88
- // Native keydown listenerfires before React's document-level delegation,
89
- // so e.preventDefault() reliably cancels browser arrow-key scroll.
104
+ // Resolve cell ID from (row, col) coordinates row 0 = header, rows 1+ = data rows
105
+ var resolveId = function resolveId(row, col) {
106
+ if (row === 0) return colHeaderId(col);
107
+ var dataRowIndex = row - 1;
108
+ return col === 0 ? rowHeaderId(dataRowIndex) : dataCellId(dataRowIndex, col);
109
+ };
110
+
111
+ // Parse (row, col) from a cell element's data attributes
112
+ var parseCoords = function parseCoords(el) {
113
+ var row = parseInt(el.getAttribute('data-row'), 10);
114
+ var col = parseInt(el.getAttribute('data-col'), 10);
115
+ return isNaN(row) || isNaN(col) ? null : {
116
+ row: row,
117
+ col: col
118
+ };
119
+ };
120
+ var activateCell = function activateCell(row, col) {
121
+ var id = resolveId(row, col);
122
+ activeCellIdRef.current = id;
123
+ setActiveCellId(id);
124
+ var tableEl = tableRef.current;
125
+ if (!tableEl) return;
126
+ var cell = tableEl.querySelector("[data-row=\"".concat(row, "\"][data-col=\"").concat(col, "\"]"));
127
+ if (cell) {
128
+ cell.scrollIntoView({
129
+ block: 'nearest',
130
+ inline: 'nearest'
131
+ });
132
+ }
133
+ };
134
+
135
+ // Native keydown listener on the table container — reliable preventDefault for arrow keys
90
136
  (0, _react.useEffect)(function () {
91
137
  var tableEl = tableRef.current;
92
138
  if (!tableEl) return;
93
139
  var handleKeyDown = function handleKeyDown(e) {
94
- var _focusedCellRef$curre = focusedCellRef.current,
95
- row = _focusedCellRef$curre.row,
96
- col = _focusedCellRef$curre.col;
140
+ // Parse current coords from active cell ID via DOM lookup
141
+ var activeEl = tableEl.querySelector("#".concat(CSS.escape(activeCellIdRef.current)));
142
+ var coords = activeEl ? parseCoords(activeEl) : {
143
+ row: 0,
144
+ col: 0
145
+ };
146
+ var _ref2 = coords || {
147
+ row: 0,
148
+ col: 0
149
+ },
150
+ row = _ref2.row,
151
+ col = _ref2.col;
97
152
  var moves = {
98
153
  ArrowUp: [row - 1, col],
99
154
  ArrowDown: [row + 1, col],
@@ -104,44 +159,24 @@ var DataTable = function DataTable(_ref) {
104
159
  e.preventDefault();
105
160
  var r = Math.max(0, Math.min(totalRowsRef.current - 1, moves[e.key][0]));
106
161
  var c = Math.max(0, Math.min(totalColsRef.current - 1, moves[e.key][1]));
107
- focusedCellRef.current = {
108
- row: r,
109
- col: c
110
- };
111
- setFocusedCell({
112
- row: r,
113
- col: c
114
- });
115
- var cell = tableEl.querySelector("[data-row=\"".concat(r, "\"][data-col=\"").concat(c, "\"]"));
116
- if (cell) {
117
- cell.focus({
118
- preventScroll: true
119
- });
120
- cell.scrollIntoView({
121
- block: 'nearest',
122
- inline: 'nearest'
123
- });
124
- }
162
+ activateCell(r, c);
125
163
  };
126
164
  tableEl.addEventListener('keydown', handleKeyDown);
127
165
  return function () {
128
166
  return tableEl.removeEventListener('keydown', handleKeyDown);
129
167
  };
130
- }, []); // empty deps — all values accessed via refs
168
+ }, []); // empty deps — all values accessed via refs or DOM
131
169
 
132
- var tabIndex = function tabIndex(row, col) {
133
- return focusedCell.row === row && focusedCell.col === col ? 0 : -1;
134
- };
135
- var onCellFocus = function onCellFocus(row, col) {
136
- focusedCellRef.current = {
137
- row: row,
138
- col: col
139
- };
140
- setFocusedCell({
141
- row: row,
142
- col: col
143
- });
170
+ // Click-to-activate via event delegation on the table
171
+ var handleTableClick = function handleTableClick(e) {
172
+ var cell = e.target.closest('[data-row][data-col]');
173
+ if (!cell) return;
174
+ var coords = parseCoords(cell);
175
+ if (coords) activateCell(coords.row, coords.col);
144
176
  };
177
+ var dataRows = table.getRowModel().rows;
178
+ var pageIndex = pagination.pageIndex;
179
+ var pageSize = pagination.pageSize;
145
180
  return /*#__PURE__*/_react.default.createElement("div", {
146
181
  style: {
147
182
  height: '400px',
@@ -149,54 +184,77 @@ var DataTable = function DataTable(_ref) {
149
184
  }
150
185
  }, /*#__PURE__*/_react.default.createElement("table", {
151
186
  ref: tableRef,
187
+ role: "grid",
188
+ tabIndex: 0,
189
+ "aria-activedescendant": activeCellId,
190
+ "aria-rowcount": dataRows.length + 1,
191
+ "aria-colcount": columns.length,
152
192
  style: {
153
- minWidth: "".concat(columns.length * 120, "px")
154
- }
193
+ width: table.getTotalSize()
194
+ },
195
+ onClick: handleTableClick
155
196
  }, /*#__PURE__*/_react.default.createElement("caption", null, datasetName ? "".concat(datasetName, " Dataset Preview") : 'Dataset Preview'), /*#__PURE__*/_react.default.createElement("thead", null, table.getHeaderGroups().map(function (headerGroup) {
156
197
  return /*#__PURE__*/_react.default.createElement("tr", {
157
- key: headerGroup.id
198
+ key: headerGroup.id,
199
+ role: "row",
200
+ "aria-rowindex": 1
158
201
  }, headerGroup.headers.map(function (header, colIndex) {
159
202
  var _header$column$column;
160
203
  return /*#__PURE__*/_react.default.createElement("th", {
161
204
  key: header.id,
205
+ id: colHeaderId(colIndex),
206
+ role: "columnheader",
162
207
  scope: "col",
163
- tabIndex: tabIndex(0, colIndex),
164
208
  "data-row": 0,
165
209
  "data-col": colIndex,
166
- onFocus: function onFocus() {
167
- return onCellFocus(0, colIndex);
168
- },
169
- className: ((_header$column$column = header.column.columnDef.meta) === null || _header$column$column === void 0 ? void 0 : _header$column$column.headerClassName) || 'table-header'
170
- }, header.isPlaceholder ? null : (0, _reactTable.flexRender)(header.column.columnDef.header, header.getContext()));
210
+ "aria-colindex": colIndex + 1,
211
+ className: "".concat(((_header$column$column = header.column.columnDef.meta) === null || _header$column$column === void 0 ? void 0 : _header$column$column.headerClassName) || 'table-header').concat(activeCellId === colHeaderId(colIndex) ? ' active-cell' : ''),
212
+ style: {
213
+ width: header.getSize(),
214
+ position: 'relative'
215
+ }
216
+ }, header.isPlaceholder ? null : (0, _reactTable.flexRender)(header.column.columnDef.header, header.getContext()), header.column.getCanResize() && /*#__PURE__*/_react.default.createElement("div", {
217
+ className: "resizer".concat(header.column.getIsResizing() ? ' isResizing' : ''),
218
+ onMouseDown: header.getResizeHandler(),
219
+ onTouchStart: header.getResizeHandler(),
220
+ "aria-hidden": "true"
221
+ }));
171
222
  }));
172
- })), /*#__PURE__*/_react.default.createElement("tbody", null, table.getRowModel().rows.length === 0 ? /*#__PURE__*/_react.default.createElement("tr", null, /*#__PURE__*/_react.default.createElement("td", {
223
+ })), /*#__PURE__*/_react.default.createElement("tbody", null, dataRows.length === 0 ? /*#__PURE__*/_react.default.createElement("tr", {
224
+ role: "row"
225
+ }, /*#__PURE__*/_react.default.createElement("td", {
226
+ role: "gridcell",
173
227
  colSpan: columns.length,
174
228
  className: "no-data-message"
175
- }, "No rows found")) : table.getRowModel().rows.map(function (row, rowIndex) {
229
+ }, "No rows found")) : dataRows.map(function (row, rowIndex) {
176
230
  var tableRow = rowIndex + 1;
231
+ var ariaRowIndex = pageIndex * pageSize + rowIndex + 2; // +1 for header, +1 for 1-based
177
232
  return /*#__PURE__*/_react.default.createElement("tr", {
178
233
  key: row.id,
234
+ role: "row",
235
+ "aria-rowindex": ariaRowIndex,
179
236
  className: rowIndex % 2 === 0 ? 'striped-row' : ''
180
237
  }, row.getVisibleCells().map(function (cell, colIndex) {
181
238
  var content = (0, _reactTable.flexRender)(cell.column.columnDef.cell, cell.getContext());
182
239
  return colIndex === 0 ? /*#__PURE__*/_react.default.createElement("th", {
183
240
  key: cell.id,
241
+ id: rowHeaderId(rowIndex),
242
+ role: "rowheader",
184
243
  scope: "row",
185
- tabIndex: tabIndex(tableRow, colIndex),
244
+ headers: colHeaderId(0),
186
245
  "data-row": tableRow,
187
246
  "data-col": colIndex,
188
- onFocus: function onFocus() {
189
- return onCellFocus(tableRow, colIndex);
190
- },
191
- className: "row-header"
247
+ "aria-colindex": 1,
248
+ className: "row-header".concat(activeCellId === rowHeaderId(rowIndex) ? ' active-cell' : '')
192
249
  }, content) : /*#__PURE__*/_react.default.createElement("td", {
193
250
  key: cell.id,
194
- tabIndex: tabIndex(tableRow, colIndex),
251
+ id: dataCellId(rowIndex, colIndex),
252
+ role: "gridcell",
253
+ headers: "".concat(colHeaderId(colIndex), " ").concat(rowHeaderId(rowIndex)),
195
254
  "data-row": tableRow,
196
255
  "data-col": colIndex,
197
- onFocus: function onFocus() {
198
- return onCellFocus(tableRow, colIndex);
199
- }
256
+ "aria-colindex": colIndex + 1,
257
+ className: activeCellId === dataCellId(rowIndex, colIndex) ? 'active-cell' : undefined
200
258
  }, content);
201
259
  }));
202
260
  }))), /*#__PURE__*/_react.default.createElement("div", {
@@ -277,10 +335,10 @@ var cleanseField = function cleanseField(value) {
277
335
  }
278
336
  };
279
337
  var cleanseRow = function cleanseRow(row) {
280
- var reconstructedObject = Object.assign.apply(Object, [{}].concat(_toConsumableArray(Object.entries(row).map(function (_ref2) {
281
- var _ref3 = _slicedToArray(_ref2, 2),
282
- k = _ref3[0],
283
- v = _ref3[1];
338
+ var reconstructedObject = Object.assign.apply(Object, [{}].concat(_toConsumableArray(Object.entries(row).map(function (_ref3) {
339
+ var _ref4 = _slicedToArray(_ref3, 2),
340
+ k = _ref4[0],
341
+ v = _ref4[1];
284
342
  return _defineProperty({}, k, cleanseField(v));
285
343
  }))));
286
344
  return reconstructedObject;
@@ -292,10 +350,10 @@ var cleanseData = function cleanseData(data) {
292
350
  });
293
351
  };
294
352
  var _default = exports.default = function _default(props) {
295
- var _useState5 = (0, _react.useState)(0),
296
- _useState6 = _slicedToArray(_useState5, 2),
297
- index = _useState6[0],
298
- setIndex = _useState6[1];
353
+ var _useState7 = (0, _react.useState)(0),
354
+ _useState8 = _slicedToArray(_useState7, 2),
355
+ index = _useState8[0],
356
+ setIndex = _useState8[1];
299
357
  var isGeojson = props.format === 'geojson';
300
358
  var cleanData = isGeojson ? undefined : props.data ? cleanseData(props.data) : props.data;
301
359
  var columns = (0, _react.useMemo)(function () {
@@ -306,6 +364,8 @@ var _default = exports.default = function _default(props) {
306
364
  accessorFn: function accessorFn(row) {
307
365
  return row[column];
308
366
  },
367
+ size: 120,
368
+ minSize: 60,
309
369
  meta: {
310
370
  headerClassName: 'table-header'
311
371
  }
@@ -11,7 +11,7 @@
11
11
  color: black;
12
12
  }
13
13
 
14
- tbody tr:nth-child(even).striped-row {
14
+ tbody tr.striped-row {
15
15
  background-color: rgba(0, 0, 0, 0.03);
16
16
  }
17
17
 
@@ -32,10 +32,32 @@
32
32
 
33
33
  table {
34
34
  table-layout: fixed;
35
- width: 100%;
36
35
  border-collapse: collapse;
37
36
  }
38
37
 
38
+ .resizer {
39
+ position: absolute;
40
+ right: 0;
41
+ top: 0;
42
+ height: 100%;
43
+ width: 4px;
44
+ cursor: col-resize;
45
+ user-select: none;
46
+ touch-action: none;
47
+ background: rgba(0, 0, 0, 0.15);
48
+ opacity: 0;
49
+
50
+ &:hover,
51
+ &.isResizing {
52
+ opacity: 1;
53
+ background: #005fcc;
54
+ }
55
+ }
56
+
57
+ th:hover .resizer {
58
+ opacity: 1;
59
+ }
60
+
39
61
  th, td {
40
62
  overflow: hidden;
41
63
  text-overflow: ellipsis;
@@ -53,6 +75,16 @@
53
75
  font-weight: normal;
54
76
  text-align: left;
55
77
  }
78
+
79
+ .active-cell {
80
+ outline: 2px solid #005fcc;
81
+ outline-offset: -2px;
82
+ }
83
+
84
+ table:focus {
85
+ outline: 2px solid #005fcc;
86
+ outline-offset: 2px;
87
+ }
56
88
  }
57
89
 
58
90
  #data-view-raw {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@urbanos/react-discovery-ui",
3
- "version": "2.1.49",
3
+ "version": "2.1.51",
4
4
  "description": "React component for dataset discovery UI",
5
5
  "main": "./lib/ReactDiscoveryUI.js",
6
6
  "repository": {