@pingux/astro 2.206.2-alpha.0 → 2.207.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/lib/cjs/components/Button/Buttons.styles.d.ts +14 -0
  2. package/lib/cjs/components/Button/Buttons.styles.js +11 -1
  3. package/lib/cjs/components/TableBase/TableBase.js +138 -32
  4. package/lib/cjs/components/TableBase/TableBase.mdx +46 -13
  5. package/lib/cjs/components/TableBase/TableBase.stories.js +52 -2
  6. package/lib/cjs/components/TableBase/TableBase.styles.d.ts +84 -9
  7. package/lib/cjs/components/TableBase/TableBase.styles.js +61 -10
  8. package/lib/cjs/components/TableBase/TableBase.test.js +585 -105
  9. package/lib/cjs/styles/themeOverrides/nextGenDarkMode/variants/variants.d.ts +14 -0
  10. package/lib/cjs/styles/themeOverrides/nextGenDarkMode/variants/variants.js +16 -2
  11. package/lib/cjs/styles/themes/next-gen/next-gen.d.ts +10 -0
  12. package/lib/cjs/styles/themes/next-gen/variants/button.d.ts +7 -0
  13. package/lib/cjs/styles/themes/next-gen/variants/button.js +4 -1
  14. package/lib/cjs/styles/themes/next-gen/variants/tableBase.d.ts +3 -0
  15. package/lib/cjs/styles/themes/next-gen/variants/tableBase.js +6 -1
  16. package/lib/cjs/styles/themes/next-gen/variants/variants.d.ts +3 -0
  17. package/lib/cjs/types/tableBase.d.ts +21 -5
  18. package/lib/components/Button/Buttons.styles.js +11 -1
  19. package/lib/components/TableBase/TableBase.js +139 -33
  20. package/lib/components/TableBase/TableBase.mdx +46 -13
  21. package/lib/components/TableBase/TableBase.stories.js +51 -1
  22. package/lib/components/TableBase/TableBase.styles.js +61 -10
  23. package/lib/components/TableBase/TableBase.test.js +585 -105
  24. package/lib/styles/themeOverrides/nextGenDarkMode/variants/variants.js +16 -2
  25. package/lib/styles/themes/next-gen/variants/button.js +4 -1
  26. package/lib/styles/themes/next-gen/variants/tableBase.js +6 -1
  27. package/package.json +1 -1
@@ -644,10 +644,21 @@ declare const _default: {
644
644
  '&.is-last-column-sticky': {
645
645
  'thead tr th:last-of-type': {
646
646
  backgroundColor: string;
647
+ '&::after': {
648
+ backgroundColor: any;
649
+ };
647
650
  };
648
651
  'tbody tr': {
652
+ '&:nth-of-type(odd) td:last-of-type': {
653
+ '&::after': {
654
+ backgroundColor: any;
655
+ };
656
+ };
649
657
  '&:nth-of-type(even) td:last-of-type': {
650
658
  backgroundColor: string;
659
+ '&::after': {
660
+ backgroundColor: any;
661
+ };
651
662
  };
652
663
  };
653
664
  };
@@ -666,6 +677,9 @@ declare const _default: {
666
677
  data: {
667
678
  color: string;
668
679
  };
680
+ resizer: {
681
+ backgroundColor: any;
682
+ };
669
683
  };
670
684
  statusIcon: {
671
685
  base: {
@@ -241,11 +241,22 @@ var _default = exports["default"] = {
241
241
  container: {
242
242
  '&.is-last-column-sticky': {
243
243
  'thead tr th:last-of-type': {
244
- backgroundColor: 'backgroundBase'
244
+ backgroundColor: 'backgroundBase',
245
+ '&::after': {
246
+ backgroundColor: _onyxTokens.astroTokensDark.color.common['border-dark']
247
+ }
245
248
  },
246
249
  'tbody tr': {
250
+ '&:nth-of-type(odd) td:last-of-type': {
251
+ '&::after': {
252
+ backgroundColor: _onyxTokens.astroTokensDark.color.common['border-dark']
253
+ }
254
+ },
247
255
  '&:nth-of-type(even) td:last-of-type': {
248
- backgroundColor: 'backgroundBase'
256
+ backgroundColor: 'backgroundBase',
257
+ '&::after': {
258
+ backgroundColor: _onyxTokens.astroTokensDark.color.common['border-dark']
259
+ }
249
260
  }
250
261
  }
251
262
  }
@@ -263,6 +274,9 @@ var _default = exports["default"] = {
263
274
  },
264
275
  data: {
265
276
  color: 'text.secondary'
277
+ },
278
+ resizer: {
279
+ backgroundColor: _onyxTokens.astroTokensDark.color.common['border-dark']
266
280
  }
267
281
  },
268
282
  statusIcon: statusIcon,
@@ -1826,6 +1826,13 @@ declare const _default: {
1826
1826
  fontSize: string;
1827
1827
  py: string;
1828
1828
  };
1829
+ tableHeaderButton: {
1830
+ '&.is-focused': {
1831
+ outline: string;
1832
+ outlineColor: string;
1833
+ outlineOffset: string;
1834
+ };
1835
+ };
1829
1836
  };
1830
1837
  forms: {
1831
1838
  input: import("theme-ui").ThemeUICSSObject;
@@ -6921,6 +6928,9 @@ declare const _default: {
6921
6928
  outlineColor: string;
6922
6929
  };
6923
6930
  };
6931
+ resizer: {
6932
+ backgroundColor: any;
6933
+ };
6924
6934
  };
6925
6935
  tabs: {
6926
6936
  '&.is-horizontal': {
@@ -1522,5 +1522,12 @@ declare const buttons: {
1522
1522
  fontSize: string;
1523
1523
  py: string;
1524
1524
  };
1525
+ tableHeaderButton: {
1526
+ '&.is-focused': {
1527
+ outline: string;
1528
+ outlineColor: string;
1529
+ outlineOffset: string;
1530
+ };
1531
+ };
1525
1532
  };
1526
1533
  export default buttons;
@@ -648,6 +648,9 @@ var buttons = {
648
648
  aiChat: aiChat,
649
649
  paginationMenu: paginationMenu,
650
650
  ButtonInputGroupContentRight: ButtonInputGroupContentRight,
651
- selectLink: selectLink
651
+ selectLink: selectLink,
652
+ tableHeaderButton: {
653
+ '&.is-focused': _objectSpread({}, defaultFocus)
654
+ }
652
655
  };
653
656
  var _default = exports["default"] = buttons;
@@ -113,4 +113,7 @@ export declare const tableBase: {
113
113
  outlineColor: string;
114
114
  };
115
115
  };
116
+ resizer: {
117
+ backgroundColor: any;
118
+ };
116
119
  };
@@ -14,6 +14,7 @@ _Object$defineProperty(exports, "__esModule", {
14
14
  });
15
15
  exports.tableBase = void 0;
16
16
  var _defineProperty2 = _interopRequireDefault(require("@babel/runtime-corejs3/helpers/defineProperty"));
17
+ var _onyxTokens = require("@pingux/onyx-tokens");
17
18
  var _colors = _interopRequireDefault(require("../colors/colors"));
18
19
  var _button = require("./button");
19
20
  function ownKeys(e, r) { var t = _Object$keys(e); if (_Object$getOwnPropertySymbols) { var o = _Object$getOwnPropertySymbols(e); r && (o = _filterInstanceProperty(o).call(o, function (r) { return _Object$getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
@@ -92,6 +93,9 @@ var thead = {
92
93
  boxShadow: "0 1px 0 ".concat(_colors["default"].border.base)
93
94
  }
94
95
  };
96
+ var resizer = {
97
+ backgroundColor: _onyxTokens.astroTokens.color.common['border-dark']
98
+ };
95
99
  var head = {
96
100
  px: 'lg',
97
101
  py: 'sm',
@@ -126,5 +130,6 @@ var tableBase = exports.tableBase = {
126
130
  thead: thead,
127
131
  head: head,
128
132
  tbody: tbody,
129
- data: data
133
+ data: data,
134
+ resizer: resizer
130
135
  };
@@ -2207,6 +2207,9 @@ declare const _default: {
2207
2207
  outlineColor: string;
2208
2208
  };
2209
2209
  };
2210
+ resizer: {
2211
+ backgroundColor: any;
2212
+ };
2210
2213
  };
2211
2214
  tabs: {
2212
2215
  '&.is-horizontal': {
@@ -1,15 +1,16 @@
1
- import { ReactNode } from 'react';
1
+ import { ReactNode, RefObject } from 'react';
2
2
  import type { TableState } from '@react-stately/table';
3
3
  import { TableColumnResizeState } from '@react-stately/table';
4
4
  import type { GridNode } from '@react-types/grid';
5
- import type { Node } from '@react-types/shared';
6
- import type { TableProps } from '@react-types/table';
5
+ import type { FocusableElement, Key, Node } from '@react-types/shared';
6
+ import type { ColumnSize, TableProps } from '@react-types/table';
7
7
  import { TestingAttributes } from './shared/test';
8
8
  import { BoxProps } from './box';
9
9
  import { DOMAttributes } from './shared';
10
+ export type ResizeHandler = (widths: Map<Key, ColumnSize>) => void;
10
11
  export interface BaseProp extends BoxProps, TestingAttributes, DOMAttributes {
11
12
  }
12
- export interface TableBaseProps<T extends object> extends TableProps<T>, Omit<BaseProp, 'children'> {
13
+ export interface TableBaseProps<T extends object> extends TableProps<T>, Omit<BaseProp, 'children' | 'onResize'> {
13
14
  'aria-label'?: string;
14
15
  selectionMode?: 'none' | 'single' | 'multiple';
15
16
  selectionBehavior?: 'replace' | 'toggle';
@@ -18,6 +19,9 @@ export interface TableBaseProps<T extends object> extends TableProps<T>, Omit<Ba
18
19
  caption?: ReactNode | string;
19
20
  isStickyHeader?: boolean;
20
21
  isLastColumnSticky?: boolean;
22
+ onResizeStart?: ResizeHandler;
23
+ onResize?: ResizeHandler;
24
+ onResizeEnd?: ResizeHandler;
21
25
  }
22
26
  export interface TableRowGroupProps extends BaseProp {
23
27
  type: 'thead' | 'tbody';
@@ -30,17 +34,29 @@ export interface TableHeaderRowProps<T> extends BaseProp {
30
34
  children: ReactNode;
31
35
  className?: string;
32
36
  }
33
- export interface TableColumnHeaderProps<T> extends BaseProp {
37
+ export interface ResizerProps<T> {
38
+ column: GridNode<T>;
39
+ layoutState: TableColumnResizeState<T>;
40
+ triggerRef?: RefObject<FocusableElement | null>;
41
+ onResizeStart?: (widths: Map<Key, ColumnSize>) => void;
42
+ onResize?: (widths: Map<Key, ColumnSize>) => void;
43
+ onResizeEnd?: (widths: Map<Key, ColumnSize>) => void;
44
+ }
45
+ export interface TableColumnHeaderProps<T> extends Omit<BaseProp, 'onResize'> {
34
46
  column: GridNode<T>;
35
47
  state: TableState<T>;
36
48
  className?: string;
37
49
  layoutState: TableColumnResizeState<T>;
50
+ onResizeStart?: ResizeHandler;
51
+ onResize?: ResizeHandler;
52
+ onResizeEnd?: ResizeHandler;
38
53
  }
39
54
  export interface TableRowProps<T> extends BaseProp {
40
55
  item: Node<T>;
41
56
  state: TableState<T>;
42
57
  children: ReactNode;
43
58
  className?: string;
59
+ hasSelectionCheckboxes?: boolean;
44
60
  }
45
61
  export interface TableCellProps<T> extends BaseProp {
46
62
  cell: GridNode<T>;
@@ -342,5 +342,15 @@ export default {
342
342
  withIcon: withIcon,
343
343
  filter: filter,
344
344
  searchNavTabLabel: tabLabelButton,
345
- selectLink: selectLink
345
+ selectLink: selectLink,
346
+ tableHeaderButton: {
347
+ fontSize: 'md',
348
+ fontWeight: '1',
349
+ color: 'text.primary',
350
+ lineHeight: 'body',
351
+ backgroundColor: 'transparent',
352
+ borderRadius: '0px',
353
+ textAlign: 'left',
354
+ '&.is-focused': _objectSpread({}, defaultFocus)
355
+ }
346
356
  };
@@ -10,7 +10,7 @@ import _extends from "@babel/runtime-corejs3/helpers/esm/extends";
10
10
  import _defineProperty from "@babel/runtime-corejs3/helpers/esm/defineProperty";
11
11
  import _slicedToArray from "@babel/runtime-corejs3/helpers/esm/slicedToArray";
12
12
  import _objectWithoutProperties from "@babel/runtime-corejs3/helpers/esm/objectWithoutProperties";
13
- var _excluded = ["caption", "selectionMode", "selectionBehavior", "hasSelectionCheckboxes", "isStickyHeader", "className", "isLastColumnSticky"],
13
+ var _excluded = ["caption", "selectionMode", "selectionBehavior", "hasSelectionCheckboxes", "isStickyHeader", "className", "isLastColumnSticky", "onResizeStart", "onResize", "onResizeEnd"],
14
14
  _excluded2 = ["caption"],
15
15
  _excluded3 = ["type", "children", "className", "isSticky"];
16
16
  import _mapInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/map";
@@ -20,7 +20,7 @@ function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var _co
20
20
  import React, { forwardRef, useCallback, useLayoutEffect, useRef, useState } from 'react';
21
21
  import { useFocusRing } from '@react-aria/focus';
22
22
  import { useHover, usePress } from '@react-aria/interactions';
23
- import { useTable, useTableCell, useTableColumnHeader, useTableHeaderRow, useTableRow, useTableRowGroup, useTableSelectAllCheckbox, useTableSelectionCheckbox } from '@react-aria/table';
23
+ import { useTable, useTableCell, useTableColumnHeader, useTableColumnResize, useTableHeaderRow, useTableRow, useTableRowGroup, useTableSelectAllCheckbox, useTableSelectionCheckbox } from '@react-aria/table';
24
24
  import { mergeProps, useResizeObserver } from '@react-aria/utils';
25
25
  import { VisuallyHidden } from '@react-aria/visually-hidden';
26
26
  import { useTableColumnResizeState, useTableState } from '@react-stately/table';
@@ -56,6 +56,9 @@ var TableBase = /*#__PURE__*/forwardRef(function (props, ref) {
56
56
  isStickyHeader = _props$isStickyHeader === void 0 ? false : _props$isStickyHeader,
57
57
  className = props.className,
58
58
  isLastColumnSticky = props.isLastColumnSticky,
59
+ onResizeStart = props.onResizeStart,
60
+ onResize = props.onResize,
61
+ onResizeEnd = props.onResizeEnd,
59
62
  others = _objectWithoutProperties(props, _excluded);
60
63
  var tableRef = useLocalOrForwardRef(ref);
61
64
  var headerRef = useRef(null);
@@ -136,7 +139,10 @@ var TableBase = /*#__PURE__*/forwardRef(function (props, ref) {
136
139
  key: column.key,
137
140
  column: column,
138
141
  state: state,
139
- layoutState: layoutState
142
+ layoutState: layoutState,
143
+ onResizeStart: onResizeStart,
144
+ onResize: onResize,
145
+ onResizeEnd: onResizeEnd
140
146
  });
141
147
  }));
142
148
  })), ___EmotionJSX(TableRowGroup, _extends({
@@ -154,12 +160,13 @@ var TableBase = /*#__PURE__*/forwardRef(function (props, ref) {
154
160
  }, ___EmotionJSX(Loader, {
155
161
  variant: "loader.withinDataTable"
156
162
  })), _mapInstanceProperty(_context3 = _Array$from(collection.body.childNodes)).call(_context3, function (row) {
157
- var _context4;
163
+ var _context4, _collection$getChildr, _collection$getChildr2;
158
164
  return ___EmotionJSX(TableRow, {
159
165
  key: row.key,
160
166
  item: row,
161
- state: state
162
- }, _mapInstanceProperty(_context4 = _Array$from(collection.getChildren(row.key))).call(_context4, function (cell) {
167
+ state: state,
168
+ hasSelectionCheckboxes: hasSelectionCheckboxes
169
+ }, _mapInstanceProperty(_context4 = _Array$from((_collection$getChildr = (_collection$getChildr2 = collection.getChildren) === null || _collection$getChildr2 === void 0 ? void 0 : _collection$getChildr2.call(collection, row.key)) !== null && _collection$getChildr !== void 0 ? _collection$getChildr : [])).call(_context4, function (cell) {
163
170
  return cell.props.isSelectionCell ? ___EmotionJSX(TableCheckboxCell, {
164
171
  key: cell.key,
165
172
  cell: cell,
@@ -225,39 +232,87 @@ function TableHeaderRow(props) {
225
232
  ref: ref
226
233
  }), children);
227
234
  }
235
+ function Resizer(props) {
236
+ var column = props.column,
237
+ layoutState = props.layoutState,
238
+ triggerRef = props.triggerRef,
239
+ onResizeStart = props.onResizeStart,
240
+ onResize = props.onResize,
241
+ onResizeEnd = props.onResizeEnd;
242
+ var ref = useRef(null);
243
+ var _useTableColumnResize = useTableColumnResize({
244
+ column: column,
245
+ 'aria-label': "".concat(column.textValue, " column width"),
246
+ triggerRef: triggerRef,
247
+ onResizeStart: onResizeStart,
248
+ onResize: onResize,
249
+ onResizeEnd: onResizeEnd
250
+ }, layoutState, ref),
251
+ resizerProps = _useTableColumnResize.resizerProps,
252
+ inputProps = _useTableColumnResize.inputProps;
253
+ var _useFocusRing = useFocusRing(),
254
+ isFocusVisible = _useFocusRing.isFocusVisible,
255
+ focusProps = _useFocusRing.focusProps;
256
+
257
+ // The input must be nested inside the Box that carries resizerProps so that
258
+ // keyboard events fired on the input (Enter to start resize, Escape/Tab to
259
+ // end it) bubble up to the element with the keyboard handlers.
260
+
261
+ var _useStatusClasses3 = useStatusClasses('', {
262
+ isFocused: isFocusVisible
263
+ }),
264
+ classNames = _useStatusClasses3.classNames;
265
+ return ___EmotionJSX(Box, _extends({
266
+ role: "presentation",
267
+ variant: "tableBase.resizer",
268
+ className: classNames
269
+ }, resizerProps), ___EmotionJSX("input", _extends({
270
+ ref: ref
271
+ }, mergeProps(inputProps, focusProps))));
272
+ }
228
273
  function TableColumnHeader(props) {
229
- var _column$props;
274
+ var _column$props, _column$props2;
230
275
  var column = props.column,
231
276
  state = props.state,
232
277
  className = props.className,
233
- layoutState = props.layoutState;
278
+ layoutState = props.layoutState,
279
+ onResizeStart = props.onResizeStart,
280
+ onResize = props.onResize,
281
+ onResizeEnd = props.onResizeEnd;
234
282
  var ref = useRef(null);
235
283
  var _useTableColumnHeader = useTableColumnHeader({
236
284
  node: column
237
285
  }, state, ref),
238
286
  columnHeaderProps = _useTableColumnHeader.columnHeaderProps;
239
- var _useFocusRing = useFocusRing(),
240
- isFocusVisible = _useFocusRing.isFocusVisible,
241
- focusProps = _useFocusRing.focusProps;
287
+
288
+ // within: true catches keyboard focus on the resizer <input> child so the
289
+ // <th> shows its focus ring even when the child is the active element.
290
+ var _useFocusRing2 = useFocusRing({
291
+ within: true
292
+ }),
293
+ isFocusVisible = _useFocusRing2.isFocusVisible,
294
+ focusProps = _useFocusRing2.focusProps;
242
295
  var allowsSorting = (_column$props = column.props) === null || _column$props === void 0 ? void 0 : _column$props.allowsSorting;
296
+ var allowsResizing = (_column$props2 = column.props) === null || _column$props2 === void 0 ? void 0 : _column$props2.allowsResizing;
243
297
  var _useGetTheme = useGetTheme(),
244
298
  icons = _useGetTheme.icons;
245
299
  var Ascending = icons.Ascending,
246
300
  Descending = icons.Descending;
247
301
  var sortDescriptor = state.sortDescriptor;
248
- var arrowIcon = (sortDescriptor === null || sortDescriptor === void 0 ? void 0 : sortDescriptor.column) === column.key && (sortDescriptor === null || sortDescriptor === void 0 ? void 0 : sortDescriptor.direction) === 'ascending' ? Ascending : Descending;
302
+ var isSortedAscending = (sortDescriptor === null || sortDescriptor === void 0 ? void 0 : sortDescriptor.column) === column.key && (sortDescriptor === null || sortDescriptor === void 0 ? void 0 : sortDescriptor.direction) === 'ascending';
303
+ var arrowIcon = isSortedAscending ? Ascending : Descending;
249
304
  var sortIcon = ___EmotionJSX(Icon, {
250
305
  icon: arrowIcon,
251
306
  size: "xs",
252
307
  "aria-hidden": "true",
253
308
  title: {
254
- name: (sortDescriptor === null || sortDescriptor === void 0 ? void 0 : sortDescriptor.direction) === 'ascending' ? 'Sort ascending' : 'Sort descending'
309
+ name: isSortedAscending ? 'Sort ascending' : 'Sort descending'
255
310
  }
256
311
  });
257
- var _useStatusClasses3 = useStatusClasses(className, {
312
+ var _useStatusClasses4 = useStatusClasses(className, {
258
313
  isFocused: isFocusVisible
259
314
  }),
260
- classNames = _useStatusClasses3.classNames;
315
+ classNames = _useStatusClasses4.classNames;
261
316
  useHandleFocusRef(ref);
262
317
  return ___EmotionJSX(Box, _extends({
263
318
  as: "th",
@@ -266,19 +321,27 @@ function TableColumnHeader(props) {
266
321
  variant: "tableBase.head",
267
322
  className: classNames,
268
323
  sx: _objectSpread({
269
- width: layoutState === null || layoutState === void 0 ? void 0 : layoutState.getColumnWidth(column.key)
324
+ width: layoutState === null || layoutState === void 0 ? void 0 : layoutState.getColumnWidth(column.key),
325
+ position: 'relative'
270
326
  }, column.props.sx)
271
327
  }, mergeProps(columnHeaderProps, focusProps, column.props)), ___EmotionJSX(Box, {
272
328
  isRow: true,
273
329
  gap: "sm",
274
330
  alignItems: "center"
275
- }, ___EmotionJSX(Text, null, column.rendered), allowsSorting && sortIcon));
331
+ }, ___EmotionJSX(Text, null, column.rendered), allowsSorting && sortIcon), allowsResizing && ___EmotionJSX(Resizer, {
332
+ column: column,
333
+ layoutState: layoutState,
334
+ onResizeStart: onResizeStart,
335
+ onResize: onResize,
336
+ onResizeEnd: onResizeEnd
337
+ }));
276
338
  }
277
339
  function TableRow(props) {
278
340
  var item = props.item,
279
341
  state = props.state,
280
342
  children = props.children,
281
- className = props.className;
343
+ className = props.className,
344
+ hasSelectionCheckboxes = props.hasSelectionCheckboxes;
282
345
  var ref = useRef(null);
283
346
  var _useTableRow = useTableRow({
284
347
  node: item
@@ -286,31 +349,50 @@ function TableRow(props) {
286
349
  rowProps = _useTableRow.rowProps;
287
350
  var isSelected = state.selectionManager.isSelected(item.key);
288
351
  var isDisabled = state.disabledKeys.has(item.key);
289
- var _useFocusRing2 = useFocusRing(),
290
- isFocusVisible = _useFocusRing2.isFocusVisible,
291
- focusProps = _useFocusRing2.focusProps;
352
+ var _useFocusRing3 = useFocusRing(),
353
+ isFocusVisible = _useFocusRing3.isFocusVisible,
354
+ focusProps = _useFocusRing3.focusProps;
292
355
  var _useHover = useHover({}),
293
356
  hoverProps = _useHover.hoverProps,
294
357
  isHovered = _useHover.isHovered;
358
+
359
+ // allowTextSelectionOnPress: true prevents the press handler from suppressing
360
+ // native text-selection drags (user-select: none is not applied on pointer down).
361
+ // When hasSelectionCheckboxes is true, isDisabled: true disables the visual
362
+ // isPressed state — selection is handled solely by the checkbox cells.
295
363
  var _usePress = usePress({
296
- ref: ref
364
+ ref: ref,
365
+ allowTextSelectionOnPress: true,
366
+ isDisabled: !!hasSelectionCheckboxes
297
367
  }),
298
368
  pressProps = _usePress.pressProps,
299
369
  isPressed = _usePress.isPressed;
300
- var _useStatusClasses4 = useStatusClasses(className, {
370
+
371
+ // React Aria's useSelectableItem only maps Space to selection (not Enter).
372
+ // Enter is reserved for "action" which is unused here, so we manually toggle
373
+ // selection on Enter to match expected keyboard behavior.
374
+ var enterKeyProps = useCallback(function (e) {
375
+ if (e.key !== 'Enter') return;
376
+ if (isDisabled || state.selectionManager.selectionMode === 'none') return;
377
+ e.preventDefault();
378
+ state.selectionManager.toggleSelection(item.key);
379
+ }, [isDisabled, item.key, state.selectionManager]);
380
+ var _useStatusClasses5 = useStatusClasses(className, {
301
381
  isSelected: isSelected,
302
382
  isHovered: isHovered,
303
383
  isPressed: isPressed,
304
384
  isFocused: isFocusVisible,
305
385
  isDisabled: isDisabled
306
386
  }),
307
- classNames = _useStatusClasses4.classNames;
387
+ classNames = _useStatusClasses5.classNames;
308
388
  return ___EmotionJSX(Box, _extends({
309
389
  as: "tr",
310
390
  display: "table-row",
311
391
  className: classNames,
312
392
  variant: "tableBase.row"
313
- }, mergeProps(rowProps, focusProps, hoverProps, pressProps), {
393
+ }, mergeProps(rowProps, focusProps, hoverProps, pressProps, {
394
+ onKeyDown: enterKeyProps
395
+ }), {
314
396
  ref: ref
315
397
  }), children);
316
398
  }
@@ -324,14 +406,19 @@ function TableCell(props) {
324
406
  node: cell
325
407
  }, state, ref),
326
408
  gridCellProps = _useTableCell.gridCellProps;
327
- var _useFocusRing3 = useFocusRing(),
328
- isFocusVisible = _useFocusRing3.isFocusVisible,
329
- focusProps = _useFocusRing3.focusProps;
330
- var _useStatusClasses5 = useStatusClasses(className, {
409
+ var _useFocusRing4 = useFocusRing(),
410
+ isFocusVisible = _useFocusRing4.isFocusVisible,
411
+ focusProps = _useFocusRing4.focusProps;
412
+ var _useStatusClasses6 = useStatusClasses(className, {
331
413
  isFocused: isFocusVisible
332
414
  }),
333
- classNames = _useStatusClasses5.classNames;
415
+ classNames = _useStatusClasses6.classNames;
334
416
  useHandleFocusRef(ref);
417
+
418
+ // Prevents pointer events reaching the row's press handler, allowing native text-selection drags.
419
+ var stopPointerPropagation = useCallback(function (e) {
420
+ e.stopPropagation();
421
+ }, []);
335
422
  return ___EmotionJSX(Box, _extends({
336
423
  as: "td",
337
424
  display: "table-cell",
@@ -341,8 +428,22 @@ function TableCell(props) {
341
428
  sx: _objectSpread({
342
429
  width: layoutState === null || layoutState === void 0 ? void 0 : layoutState.getColumnWidth(cell.column.key)
343
430
  }, cell.props.sx)
344
- }, mergeProps(gridCellProps, focusProps, cell.props)), cell.rendered);
431
+ }, mergeProps({
432
+ onPointerDown: stopPointerPropagation,
433
+ onMouseDown: stopPointerPropagation
434
+ }, gridCellProps, focusProps, cell.props)), cell.rendered);
345
435
  }
436
+
437
+ // Native checkboxes only toggle on Space, not Enter. This handler makes Enter
438
+ // behave the same as Space by calling the checkbox's onChange callback.
439
+ var handleCheckboxEnterKey = function handleCheckboxEnterKey(onChange, isSelected) {
440
+ return function (e) {
441
+ if (e.key === 'Enter') {
442
+ e.preventDefault();
443
+ onChange === null || onChange === void 0 || onChange(!isSelected);
444
+ }
445
+ };
446
+ };
346
447
  function TableSelectAllCell(props) {
347
448
  var column = props.column,
348
449
  state = props.state,
@@ -365,7 +466,8 @@ function TableSelectAllCell(props) {
365
466
  ref: ref
366
467
  }), state.selectionManager.selectionMode === 'single' ? ___EmotionJSX(VisuallyHidden, null, checkboxProps['aria-label']) : ___EmotionJSX(CheckboxField, _extends({
367
468
  checkBoxProps: {
368
- 'data-testid': 'select-all-checkbox'
469
+ 'data-testid': 'select-all-checkbox',
470
+ onKeyDown: handleCheckboxEnterKey(checkboxProps.onChange, checkboxProps.isSelected)
369
471
  }
370
472
  }, checkboxProps)));
371
473
  }
@@ -391,6 +493,10 @@ function TableCheckboxCell(props) {
391
493
  }, cell.props.sx)
392
494
  }, gridCellProps, {
393
495
  ref: ref
394
- }), ___EmotionJSX(CheckboxField, checkboxProps));
496
+ }), ___EmotionJSX(CheckboxField, _extends({
497
+ checkBoxProps: {
498
+ onKeyDown: handleCheckboxEnterKey(checkboxProps.onChange, checkboxProps.isSelected)
499
+ }
500
+ }, checkboxProps)));
395
501
  }
396
502
  export default TableBase;
@@ -3,29 +3,62 @@ import * as TableBaseStories from './TableBase.stories';
3
3
 
4
4
  <Meta of={TableBaseStories} isTemplate />
5
5
 
6
- # Table
6
+ # TableBase
7
7
 
8
- Data tables display information in a grid-like format of rows and columns.
9
- They organize information in a way thats easy to scan so that users can look for patterns and develop insights from data.
8
+ Data tables display information in a grid-like format of rows and columns.
9
+ They organize information in a way that's easy to scan so that users can look for patterns and develop insights from data.
10
10
 
11
- Column header names describe the type of content displayed in each column.
11
+ Column header names describe the type of content displayed in each column.
12
12
  Each row contains data related to a single entity.
13
13
 
14
- Tables should:
14
+ TableBase should:
15
15
  - Have consistently aligned content.
16
16
  - Use multiple heading rows for higher-level grouping of the columns.
17
17
  - Use column dividers sparingly.
18
18
 
19
- Tables shouldn’t use different indicators to represent empty fields.
19
+ This basic component is rendered as an HTML `<table>`, which accepts the `<TBody>` component and the `<THead>` as children.
20
20
 
21
- This basic component is rendered as an HTML `<table>`, which accepts the `<TableBody>` component and the `<TableHead>` as children.
21
+ ### When to Use
22
+
23
+ Use TableBase when you need a fully custom table implementation that goes beyond what higher-level table components provide. Common use cases include:
24
+
25
+ - Sticky columns (e.g., pinning the last column during horizontal scroll)
26
+ - Custom row or column grouping
27
+ - Non-standard selection behaviors
28
+ - Bespoke cell rendering or layout that requires direct control over the table structure
29
+
30
+ TableBase is the lowest-level table primitive in Astro and gives you direct access to the underlying React Aria/Stately table hooks. Use it when you need that level of control.
22
31
 
23
32
  ### Required Components
24
33
 
25
- This component requires these additional components:
34
+ This component requires these additional components:
35
+
36
+ - **TableCaption** — renders an accessible `<caption>` element for the table
37
+ - **THead** — wraps the header section (`<thead>`)
38
+ - **Column** — defines each column header cell (`<th>`)
39
+ - **TBody** — wraps the body section (`<tbody>`)
40
+ - **Row** — defines each data row (`<tr>`)
41
+ - **Cell** — defines each data cell (`<td>`)
42
+
43
+ ### Accessibility
44
+
45
+ #### Keyboard Navigation
46
+
47
+ These keys provide additional functionality to the component.
48
+
49
+ | Keys | Functions |
50
+ | ---- | --------- |
51
+ | Tab | Moves focus to the next focusable component. All focusable components in the table are included in the page tab sequence. |
52
+ | Shift + Tab | Moves focus to the previous focusable component. |
53
+ | Space or Enter | Focuses the component and triggers an action if one is associated with it. |
54
+ | Arrow keys | Can be used to navigate between the components in the table. |
55
+ | Home or Page Up | Shifts the focus to the first row. |
56
+ | End or Page Down | Shifts the focus to the last visible row. |
57
+
58
+ #### Screen Readers
26
59
 
27
- - TableCaption
28
- - TableHead
29
- - TableRow
30
- - TableCell
31
- - TableBody
60
+ This component uses the following attributes to assist screen readers:
61
+ - The **`aria-label`** attribute should be provided on the `TableBase` to give the table an accessible name. When a visible `TableCaption` is present, use **`aria-labelledby`** to associate the caption with the table instead.
62
+ - The **`aria-rowcount`** and **`aria-columncount`** attributes are used to indicate the total number of rows and columns in the grid structure.
63
+ - Each `Row` uses the **`aria-rowindex`** attribute to enumerate its position within the table.
64
+ - Each `Column` and `Cell` uses the **`aria-colindex`** attribute to enumerate its position within its row.