@itwin/itwinui-react 3.6.3 → 3.7.1

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 (34) hide show
  1. package/CHANGELOG.md +19 -1
  2. package/cjs/core/Alert/Alert.js +3 -1
  3. package/cjs/core/Breadcrumbs/Breadcrumbs.js +2 -1
  4. package/cjs/core/Footer/Footer.js +2 -1
  5. package/cjs/core/Table/SubRowExpander.d.ts +1 -0
  6. package/cjs/core/Table/SubRowExpander.js +2 -2
  7. package/cjs/core/Table/Table.js +37 -45
  8. package/cjs/core/Table/TableCell.js +3 -3
  9. package/cjs/core/Table/TableRowMemoized.d.ts +2 -2
  10. package/cjs/core/Table/TableRowMemoized.js +7 -6
  11. package/cjs/core/Table/cells/DefaultCell.d.ts +6 -0
  12. package/cjs/core/Table/cells/DefaultCell.js +9 -5
  13. package/cjs/core/Table/filters/FilterToggle.js +1 -1
  14. package/cjs/core/Toast/Toast.js +1 -1
  15. package/cjs/core/VisuallyHidden/VisuallyHidden.js +2 -1
  16. package/cjs/react-table/react-table.d.ts +10 -5
  17. package/esm/core/Alert/Alert.js +4 -2
  18. package/esm/core/Breadcrumbs/Breadcrumbs.js +2 -1
  19. package/esm/core/Footer/Footer.js +2 -1
  20. package/esm/core/Table/SubRowExpander.d.ts +1 -0
  21. package/esm/core/Table/SubRowExpander.js +2 -2
  22. package/esm/core/Table/Table.js +38 -46
  23. package/esm/core/Table/TableCell.js +3 -3
  24. package/esm/core/Table/TableRowMemoized.d.ts +2 -2
  25. package/esm/core/Table/TableRowMemoized.js +7 -6
  26. package/esm/core/Table/cells/DefaultCell.d.ts +6 -0
  27. package/esm/core/Table/cells/DefaultCell.js +10 -6
  28. package/esm/core/Table/filters/FilterToggle.js +1 -1
  29. package/esm/core/Toast/Toast.js +1 -1
  30. package/esm/core/VisuallyHidden/VisuallyHidden.js +3 -2
  31. package/esm/react-table/react-table.d.ts +10 -5
  32. package/package.json +1 -1
  33. package/react-table.d.ts +10 -5
  34. package/styles.css +13 -34
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 3.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#1934](https://github.com/iTwin/iTwinUI/pull/1934): Fixed the types for `Table` column `filter` to allow `"includesSome"`, which is an already-supported filter function. Also improved the types for the `filter` prop to improve TS autocompletion.
8
+ - [#1936](https://github.com/iTwin/iTwinUI/pull/1936): Fixed an issue in `ProgressRadial` where the visually hidden "Loading." text was sometimes incorrectly displayed for a short while in the beginning.
9
+
10
+ ## 3.7.0
11
+
12
+ ### Minor Changes
13
+
14
+ - [#1863](https://github.com/iTwin/iTwinUI/pull/1863): The filter button inside a `Table` will now always be visible, instead of only being shown on hover/focus.
15
+ - [#1891](https://github.com/iTwin/iTwinUI/pull/1891): The entire `Table` is now scrollable instead of just the `Table`'s body. This leads to a better scroll experience (e.g. the `Table`'s header now scrolls horizontally when hovering a dragged column near the header's left or right edges).
16
+ - [#1863](https://github.com/iTwin/iTwinUI/pull/1863): The responsive behavior of `Table` columns has been improved in a few different ways:
17
+ - All columns now have a non-zero default min-width. While we still recommend passing a custom min-width based on your data, this default will help prevent resizable columns from becoming too small.
18
+ - The filter and sort icons in a column header will now wrap to the next line, before the text starts wrapping.
19
+ - For cells that have a string value, the value will be automatically truncated after three lines.
20
+
3
21
  ## 3.6.3
4
22
 
5
23
  ### Patch Changes
@@ -24,7 +42,7 @@
24
42
 
25
43
  - [#1879](https://github.com/iTwin/iTwinUI/pull/1879): Added a new `native` prop to `Select` and `LabeledSelect`. When true, a native `<select>` element will be rendered.
26
44
  - [#1886](https://github.com/iTwin/iTwinUI/pull/1886): Native `Select` (`<Select native>`) offers a new `styleType` prop that accepts the values: `default` (pre-existing) and `borderless` (new).
27
- - [#1877](https://github.com/iTwin/iTwinUI/pull/1877): Fixed a bug in `LabeledSelect` where nested `<StatusMessage>`s were rendered when using `message={<StatusMessage>}`. As a result, non-string `message` values are no longer automatically wrapped in `<StatusMessage>`.
45
+ - [#1877](https://github.com/iTwin/iTwinUI/pull/1877): Fixed a bug in `InputGroup` where nested `<StatusMessage>`s were rendered when using `message={<StatusMessage>}`. As a result, non-string `message` values are no longer automatically wrapped in `<StatusMessage>`.
28
46
  - If you were passing a custom `ReactNode`, you might need to wrap it with `<StatusMessage>` for proper styling of `message`.
29
47
  - [#1881](https://github.com/iTwin/iTwinUI/pull/1881): Added a new subcomponent `InputWithDecorations.Icon` to replace direct usage of `Icon` inside `InputWithDecorations`.
30
48
  - Visual changes:
@@ -36,6 +36,7 @@ const React = __importStar(require("react"));
36
36
  const index_js_1 = require("../utils/index.js");
37
37
  const IconButton_js_1 = require("../Buttons/IconButton.js");
38
38
  const Icon_js_1 = require("../Icon/Icon.js");
39
+ const Anchor_js_1 = require("../Typography/Anchor.js");
39
40
  const AlertContext = React.createContext(undefined);
40
41
  // ----------------------------------------------------------------------------
41
42
  // Alert component
@@ -74,7 +75,8 @@ AlertMessage.displayName = 'Alert.Message';
74
75
  // Alert.Action component
75
76
  const AlertAction = React.forwardRef((props, ref) => {
76
77
  const { children, className, ...rest } = props;
77
- return (React.createElement(index_js_1.ButtonBase, { as: (!!props.href ? 'a' : 'button'), className: (0, classnames_1.default)('iui-alert-link', className), ref: ref, ...rest }, children));
78
+ const { type } = (0, index_js_1.useSafeContext)(AlertContext);
79
+ return (React.createElement(Anchor_js_1.Anchor, { as: (!!props.href ? 'a' : 'button'), className: (0, classnames_1.default)('iui-button-base', 'iui-alert-link', className), underline: true, "data-iui-status": type, ref: ref, ...rest }, children));
78
80
  });
79
81
  AlertAction.displayName = 'Alert.Action';
80
82
  // ----------------------------------------------------------------------------
@@ -35,6 +35,7 @@ const React = __importStar(require("react"));
35
35
  const classnames_1 = __importDefault(require("classnames"));
36
36
  const index_js_1 = require("../utils/index.js");
37
37
  const Button_js_1 = require("../Buttons/Button.js");
38
+ const Anchor_js_1 = require("../Typography/Anchor.js");
38
39
  const logWarningInDev = (0, index_js_1.createWarningLogger)();
39
40
  /**
40
41
  * A breadcrumb trail is used as a navigational aid to help users keep track
@@ -103,7 +104,7 @@ const Separator = ({ separator }) => (React.createElement(index_js_1.Box, { as:
103
104
  // ----------------------------------------------------------------------------
104
105
  const BreadcrumbsItem = React.forwardRef((props, forwardedRef) => {
105
106
  const { children: childrenProp, className, ...rest } = props;
106
- const defaultAs = !!props.href ? 'a' : !!props.onClick ? 'button' : 'span';
107
+ const defaultAs = !!props.href ? Anchor_js_1.Anchor : !!props.onClick ? 'button' : 'span';
107
108
  const children = defaultAs === 'button' ? React.createElement("span", null, childrenProp) : childrenProp;
108
109
  return (React.createElement(index_js_1.Box, { as: defaultAs, className: (0, classnames_1.default)('iui-breadcrumbs-content', className), ref: forwardedRef, ...rest }, children));
109
110
  });
@@ -37,6 +37,7 @@ const index_js_1 = require("../utils/index.js");
37
37
  const FooterItem_js_1 = require("./FooterItem.js");
38
38
  const FooterSeparator_js_1 = require("./FooterSeparator.js");
39
39
  const FooterList_js_1 = require("./FooterList.js");
40
+ const Anchor_js_1 = require("../Typography/Anchor.js");
40
41
  const footerTranslations = {
41
42
  cookies: 'Cookies',
42
43
  legalNotices: 'Legal notices',
@@ -111,7 +112,7 @@ exports.Footer = Object.assign((props) => {
111
112
  return (React.createElement(index_js_1.Box, { as: 'footer', className: (0, classnames_1.default)('iui-legal-footer', className), ...rest }, children ? (children) : (React.createElement(FooterList_js_1.FooterList, null, elements.map((element, index) => {
112
113
  return (React.createElement(React.Fragment, { key: element.key || `${element.title}-${index}` },
113
114
  index > 0 && React.createElement(FooterSeparator_js_1.FooterSeparator, null),
114
- React.createElement(FooterItem_js_1.FooterItem, null, element.url ? (React.createElement("a", { href: element.url, target: '_blank', rel: 'noreferrer' }, element.title)) : (element.title))));
115
+ React.createElement(FooterItem_js_1.FooterItem, null, element.url ? (React.createElement(Anchor_js_1.Anchor, { href: element.url, target: '_blank', rel: 'noreferrer' }, element.title)) : (element.title))));
115
116
  })))));
116
117
  }, {
117
118
  List: FooterList_js_1.FooterList,
@@ -6,5 +6,6 @@ export type SubRowExpanderProps<T extends Record<string, unknown>> = {
6
6
  isDisabled: boolean;
7
7
  cellProps: CellProps<T>;
8
8
  density?: 'default' | 'condensed' | 'extra-condensed';
9
+ [k: string]: unknown;
9
10
  };
10
11
  export declare const SubRowExpander: <T extends Record<string, unknown>>(props: SubRowExpanderProps<T>) => React.JSX.Element;
@@ -32,13 +32,13 @@ const React = __importStar(require("react"));
32
32
  const index_js_1 = require("../utils/index.js");
33
33
  const IconButton_js_1 = require("../Buttons/IconButton.js");
34
34
  const SubRowExpander = (props) => {
35
- const { cell, isDisabled, cellProps, expanderCell, density } = props;
35
+ const { cell, isDisabled, cellProps, expanderCell, density, ...rest } = props;
36
36
  return (React.createElement(React.Fragment, null, expanderCell ? (expanderCell(cellProps)) : (React.createElement(IconButton_js_1.IconButton, { "aria-label": 'Toggle sub row', "aria-expanded": cell.row.isExpanded ? 'true' : 'false', style: {
37
37
  marginInlineEnd: density === 'default' || density === undefined ? 8 : 4,
38
38
  }, className: 'iui-table-row-expander', styleType: 'borderless', size: 'small', onClick: (e) => {
39
39
  e.stopPropagation();
40
40
  cell.row.toggleRowExpanded();
41
- }, disabled: isDisabled }, React.createElement(index_js_1.SvgChevronRight, { style: {
41
+ }, disabled: isDisabled, ...rest }, React.createElement(index_js_1.SvgChevronRight, { style: {
42
42
  transform: cell.row.isExpanded ? 'rotate(90deg)' : undefined,
43
43
  } })))));
44
44
  };
@@ -48,6 +48,10 @@ const singleRowSelectedAction = 'singleRowSelected';
48
48
  const shiftRowSelectedAction = 'shiftRowSelected';
49
49
  exports.tableResizeStartAction = 'tableResizeStart';
50
50
  const tableResizeEndAction = 'tableResizeEnd';
51
+ const COLUMN_MIN_WIDTHS = {
52
+ default: 72,
53
+ withExpander: 108, // expander column should be wider to accommodate the expander icon
54
+ };
51
55
  const logWarningInDev = (0, index_js_1.createWarningLogger)();
52
56
  const flattenColumns = (columns) => {
53
57
  const flatColumns = [];
@@ -316,15 +320,11 @@ const Table = (props) => {
316
320
  instance.selectedFlatRows,
317
321
  selectionMode,
318
322
  ]);
319
- const headerRef = React.useRef(null);
320
- const bodyRef = React.useRef(null);
323
+ const tableRef = React.useRef(null);
321
324
  const { scrollToIndex, tableRowRef } = (0, index_js_3.useScrollToRow)({ ...props, page });
322
325
  const columnRefs = React.useRef({});
323
326
  const previousTableWidth = React.useRef(0);
324
327
  const onTableResize = React.useCallback(({ width }) => {
325
- // Handle header properties, regardless of whether the table is resizable
326
- setHeaderScrollWidth(headerRef.current?.scrollWidth ?? 0);
327
- setHeaderClientWidth(headerRef.current?.clientWidth ?? 0);
328
328
  // Handle table properties, but only when table is resizable
329
329
  if (!isResizable) {
330
330
  return;
@@ -354,8 +354,6 @@ const Table = (props) => {
354
354
  isResizable,
355
355
  ]);
356
356
  const [resizeRef] = (0, index_js_1.useResizeObserver)(onTableResize);
357
- const [headerScrollWidth, setHeaderScrollWidth] = React.useState(0);
358
- const [headerClientWidth, setHeaderClientWidth] = React.useState(0);
359
357
  // Flexbox handles columns resize so we take new column widths before browser repaints.
360
358
  (0, index_js_1.useLayoutEffect)(() => {
361
359
  if (state.isTableResizing) {
@@ -372,7 +370,7 @@ const Table = (props) => {
372
370
  const getPreparedRow = React.useCallback((index) => {
373
371
  const row = page[index];
374
372
  prepareRow(row);
375
- return (React.createElement(TableRowMemoized_js_1.TableRowMemoized, { row: row, rowProps: rowProps, isLast: index === page.length - 1, onRowInViewport: onRowInViewportRef, onBottomReached: onBottomReachedRef, intersectionMargin: intersectionMargin, state: state, key: row.getRowProps().key, onClick: onRowClickHandler, subComponent: subComponent, isDisabled: !!isRowDisabled?.(row.original), tableHasSubRows: hasAnySubRows, tableInstance: instance, expanderCell: expanderCell, bodyRef: bodyRef.current, tableRowRef: enableVirtualization ? undefined : tableRowRef(row), density: density }));
373
+ return (React.createElement(TableRowMemoized_js_1.TableRowMemoized, { row: row, rowProps: rowProps, isLast: index === page.length - 1, onRowInViewport: onRowInViewportRef, onBottomReached: onBottomReachedRef, intersectionMargin: intersectionMargin, state: state, key: row.getRowProps().key, onClick: onRowClickHandler, subComponent: subComponent, isDisabled: !!isRowDisabled?.(row.original), tableHasSubRows: hasAnySubRows, tableInstance: instance, expanderCell: expanderCell, scrollContainerRef: tableRef.current, tableRowRef: enableVirtualization ? undefined : tableRowRef(row), density: density }));
376
374
  }, [
377
375
  page,
378
376
  prepareRow,
@@ -391,18 +389,18 @@ const Table = (props) => {
391
389
  ]);
392
390
  const virtualizedItemRenderer = React.useCallback((index) => getPreparedRow(index), [getPreparedRow]);
393
391
  const updateStickyState = () => {
394
- if (!bodyRef.current || flatHeaders.every((header) => !header.sticky)) {
392
+ if (!tableRef.current || flatHeaders.every((header) => !header.sticky)) {
395
393
  return;
396
394
  }
397
- if (bodyRef.current.scrollLeft !== 0) {
395
+ if (tableRef.current.scrollLeft !== 0) {
398
396
  dispatch({ type: react_table_1.actions.setScrolledRight, value: true });
399
397
  }
400
398
  else {
401
399
  dispatch({ type: react_table_1.actions.setScrolledRight, value: false });
402
400
  }
403
401
  // If scrolled a bit to the left looking from the right side
404
- if (bodyRef.current.scrollLeft !==
405
- bodyRef.current.scrollWidth - bodyRef.current.clientWidth) {
402
+ if (tableRef.current.scrollLeft !==
403
+ tableRef.current.scrollWidth - tableRef.current.clientWidth) {
406
404
  dispatch({ type: react_table_1.actions.setScrolledLeft, value: true });
407
405
  }
408
406
  else {
@@ -416,16 +414,16 @@ const Table = (props) => {
416
414
  }, []);
417
415
  const isHeaderDirectClick = React.useRef(false);
418
416
  return (React.createElement(React.Fragment, null,
419
- React.createElement(index_js_1.Box, { ref: (element) => {
417
+ React.createElement(index_js_1.Box, { ref: (0, index_js_1.useMergedRefs)(tableRef, (element) => {
420
418
  ownerDocument.current = element?.ownerDocument;
421
419
  resizeRef(element);
422
- }, id: id, ...getTableProps({
420
+ }), id: id, ...getTableProps({
423
421
  className: (0, classnames_1.default)('iui-table', className),
424
422
  style: {
425
423
  minWidth: 0,
426
424
  ...style,
427
425
  },
428
- }), "data-iui-size": density === 'default' ? undefined : density, ...ariaDataAttributes },
426
+ }), onScroll: () => updateStickyState(), "data-iui-size": density === 'default' ? undefined : density, ...ariaDataAttributes },
429
427
  headerGroups.map((headerGroup) => {
430
428
  // There may be a better solution for this, but for now I'm filtering out the placeholder cells using header.id
431
429
  headerGroup.headers = headerGroup.headers.filter((header) => !header.id.includes('iui-table-checkbox-selector_placeholder') &&
@@ -433,18 +431,19 @@ const Table = (props) => {
433
431
  const headerGroupProps = headerGroup.getHeaderGroupProps({
434
432
  className: 'iui-table-row',
435
433
  });
436
- return (React.createElement(index_js_1.Box, { as: 'div', ref: headerRef, onScroll: () => {
437
- if (headerRef.current && bodyRef.current) {
438
- bodyRef.current.scrollLeft = headerRef.current.scrollLeft;
439
- updateStickyState();
440
- }
441
- }, key: headerGroupProps.key, ...headerWrapperProps, className: (0, classnames_1.default)('iui-table-header-wrapper', headerWrapperProps?.className) },
434
+ return (React.createElement(index_js_1.Box, { as: 'div', key: headerGroupProps.key, ...headerWrapperProps, className: (0, classnames_1.default)('iui-table-header-wrapper', headerWrapperProps?.className) },
442
435
  React.createElement(index_js_1.Box, { as: 'div', ...headerProps, className: (0, classnames_1.default)('iui-table-header', headerProps?.className) },
443
436
  React.createElement(index_js_1.Box, { ...headerGroupProps }, headerGroup.headers.map((column, index) => {
444
437
  const { onClick, ...restSortProps } = column.getSortByToggleProps();
445
438
  const columnHasExpanders = hasAnySubRows &&
446
439
  index ===
447
440
  headerGroup.headers.findIndex((c) => c.id !== index_js_5.SELECTION_CELL_ID);
441
+ // override "undefined" or zero min-width with default value
442
+ if ([undefined, 0].includes(column.minWidth)) {
443
+ column.minWidth = columnHasExpanders
444
+ ? COLUMN_MIN_WIDTHS.withExpander
445
+ : COLUMN_MIN_WIDTHS.default;
446
+ }
448
447
  const columnProps = column.getHeaderProps({
449
448
  ...restSortProps,
450
449
  className: (0, classnames_1.default)('iui-table-cell', {
@@ -456,7 +455,8 @@ const Table = (props) => {
456
455
  ...(0, utils_js_1.getCellStyle)(column, !!state.isTableResizing),
457
456
  ...(columnHasExpanders && (0, utils_js_1.getSubRowStyle)({ density })),
458
457
  ...(0, utils_js_1.getStickyStyle)(column, visibleColumns),
459
- flexWrap: 'unset',
458
+ flexWrap: 'wrap',
459
+ columnGap: 'var(--iui-size-xs)',
460
460
  },
461
461
  });
462
462
  return (React.createElement(index_js_1.Box, { ...columnProps, ...column.getDragAndDropProps(), key: columnProps.key, title: undefined, ref: (el) => {
@@ -478,56 +478,48 @@ const Table = (props) => {
478
478
  column.toggleSortBy();
479
479
  }
480
480
  } },
481
+ React.createElement(index_js_1.ShadowRoot, null,
482
+ typeof column.Header === 'string' ? (React.createElement(index_js_1.LineClamp, null,
483
+ React.createElement("slot", null))) : (React.createElement("slot", null)),
484
+ React.createElement("slot", { name: 'actions' }),
485
+ React.createElement("slot", { name: 'resizers' }),
486
+ React.createElement("slot", { name: 'shadows' })),
481
487
  column.render('Header'),
482
488
  (showFilterButton(column) ||
483
- showSortButton(column)) && (React.createElement(index_js_1.Box, { className: 'iui-table-header-actions-container', onKeyDown: (e) => e.stopPropagation() },
489
+ showSortButton(column)) && (React.createElement(index_js_1.Box, { className: 'iui-table-header-actions-container', onKeyDown: (e) => e.stopPropagation(), slot: 'actions' },
484
490
  showFilterButton(column) && (React.createElement(index_js_2.FilterToggle, { column: column })),
485
491
  showSortButton(column) && (React.createElement(index_js_1.Box, { className: 'iui-table-cell-end-icon' }, column.isSortedDesc ||
486
492
  (!column.isSorted && column.sortDescFirst) ? (React.createElement(index_js_1.SvgSortDown, { className: 'iui-table-sort', "aria-hidden": true })) : (React.createElement(index_js_1.SvgSortUp, { className: 'iui-table-sort', "aria-hidden": true })))))),
487
493
  isResizable &&
488
494
  column.isResizerVisible &&
489
495
  (index !== headerGroup.headers.length - 1 ||
490
- columnResizeMode === 'expand') && (React.createElement(index_js_1.Box, { ...column.getResizerProps(), className: 'iui-table-resizer' },
496
+ columnResizeMode === 'expand') && (React.createElement(index_js_1.Box, { ...column.getResizerProps(), className: 'iui-table-resizer', slot: 'resizers' },
491
497
  React.createElement(index_js_1.Box, { className: 'iui-table-resizer-bar' }))),
492
498
  enableColumnReordering &&
493
- !column.disableReordering && (React.createElement(index_js_1.Box, { className: 'iui-table-reorder-bar' })),
499
+ !column.disableReordering && (React.createElement(index_js_1.Box, { className: 'iui-table-reorder-bar', slot: 'resizers' })),
494
500
  column.sticky === 'left' &&
495
- state.sticky.isScrolledToRight && (React.createElement(index_js_1.Box, { className: 'iui-table-cell-shadow-right' })),
501
+ state.sticky.isScrolledToRight && (React.createElement(index_js_1.Box, { className: 'iui-table-cell-shadow-right', slot: 'shadows' })),
496
502
  column.sticky === 'right' &&
497
- state.sticky.isScrolledToLeft && (React.createElement(index_js_1.Box, { className: 'iui-table-cell-shadow-left' }))));
503
+ state.sticky.isScrolledToLeft && (React.createElement(index_js_1.Box, { className: 'iui-table-cell-shadow-left', slot: 'shadows' }))));
498
504
  })))));
499
505
  }),
500
- React.createElement(index_js_1.Box, { ...bodyProps, ...getTableBodyProps({
506
+ React.createElement(index_js_1.Box, { as: 'div', ...bodyProps, ...getTableBodyProps({
501
507
  className: (0, classnames_1.default)('iui-table-body', {
502
508
  'iui-zebra-striping': styleType === 'zebra-rows',
503
509
  }, bodyProps?.className),
504
510
  style: { outline: 0 },
505
- }), ref: bodyRef, onScroll: () => {
506
- if (headerRef.current && bodyRef.current) {
507
- headerRef.current.scrollLeft = bodyRef.current.scrollLeft;
508
- updateStickyState();
509
- }
510
- }, tabIndex: -1, "aria-multiselectable": (isSelectable && selectionMode === 'multi') || undefined },
511
- React.createElement(index_js_1.ShadowRoot, null,
512
- React.createElement("slot", null),
513
- rows.length === 0 && headerScrollWidth > headerClientWidth && (React.createElement("div", { "aria-hidden": true, style: {
514
- // This ensures that the table-body is always the same width as the table-header,
515
- // even if the table has no rows. See https://github.com/iTwin/iTwinUI/pull/1725
516
- width: headerScrollWidth,
517
- height: 0.1,
518
- } }))),
511
+ }), tabIndex: -1, "aria-multiselectable": (isSelectable && selectionMode === 'multi') || undefined },
519
512
  data.length !== 0 && (React.createElement(React.Fragment, null, enableVirtualization ? (React.createElement(VirtualScroll_js_1.VirtualScroll, { itemsLength: page.length, itemRenderer: virtualizedItemRenderer, scrollToIndex: scrollToIndex })) : (page.map((_, index) => getPreparedRow(index))))),
520
513
  isLoading && data.length === 0 && (React.createElement(index_js_1.Box, { as: 'div', ...emptyTableContentProps, className: (0, classnames_1.default)('iui-table-empty', emptyTableContentProps?.className) },
521
514
  React.createElement(ProgressRadial_js_1.ProgressRadial, { indeterminate: true }))),
522
- isLoading && data.length !== 0 && (React.createElement(index_js_1.Box, { className: 'iui-table-row', "data-iui-loading": 'true' },
523
- React.createElement(index_js_1.Box, { className: 'iui-table-cell' },
524
- React.createElement(ProgressRadial_js_1.ProgressRadial, { indeterminate: true, size: 'small' })))),
525
515
  !isLoading && data.length === 0 && !areFiltersSet && (React.createElement(index_js_1.Box, { as: 'div', ...emptyTableContentProps, className: (0, classnames_1.default)('iui-table-empty', emptyTableContentProps?.className) },
526
516
  React.createElement("div", null, emptyTableContent))),
527
517
  !isLoading &&
528
518
  (data.length === 0 || rows.length === 0) &&
529
519
  areFiltersSet && (React.createElement(index_js_1.Box, { as: 'div', ...emptyTableContentProps, className: (0, classnames_1.default)('iui-table-empty', emptyTableContentProps?.className) },
530
520
  React.createElement("div", null, emptyFilteredTableContent)))),
521
+ isLoading && data.length !== 0 && (React.createElement(index_js_1.Box, { className: 'iui-table-body-extra', "data-iui-loading": 'true' },
522
+ React.createElement(ProgressRadial_js_1.ProgressRadial, { indeterminate: true, size: 'small' }))),
531
523
  paginatorRenderer?.(paginatorRendererProps))));
532
524
  };
533
525
  exports.Table = Table;
@@ -63,7 +63,7 @@ const TableCell = (props) => {
63
63
  ...{ cell, row: cell.row, value: cell.value, column: cell.column },
64
64
  };
65
65
  const cellContent = (React.createElement(React.Fragment, null,
66
- tableHasSubRows && hasSubRowExpander && cell.row.canExpand && (React.createElement(SubRowExpander_js_1.SubRowExpander, { cell: cell, isDisabled: isDisabled, cellProps: cellProps, expanderCell: expanderCell, density: density })),
66
+ tableHasSubRows && hasSubRowExpander && cell.row.canExpand && (React.createElement(SubRowExpander_js_1.SubRowExpander, { cell: cell, isDisabled: isDisabled, cellProps: cellProps, expanderCell: expanderCell, density: density, slot: 'start' })),
67
67
  cell.render('Cell')));
68
68
  const cellRendererProps = {
69
69
  cellElementProps,
@@ -71,9 +71,9 @@ const TableCell = (props) => {
71
71
  children: (React.createElement(React.Fragment, null,
72
72
  cellContent,
73
73
  cell.column.sticky === 'left' &&
74
- tableInstance.state.sticky.isScrolledToRight && (React.createElement(index_js_3.Box, { className: 'iui-table-cell-shadow-right' })),
74
+ tableInstance.state.sticky.isScrolledToRight && (React.createElement(index_js_3.Box, { className: 'iui-table-cell-shadow-right', slot: 'shadows' })),
75
75
  cell.column.sticky === 'right' &&
76
- tableInstance.state.sticky.isScrolledToLeft && (React.createElement(index_js_3.Box, { className: 'iui-table-cell-shadow-left' })))),
76
+ tableInstance.state.sticky.isScrolledToLeft && (React.createElement(index_js_3.Box, { className: 'iui-table-cell-shadow-left', slot: 'shadows' })))),
77
77
  };
78
78
  return (React.createElement(React.Fragment, null, cell.column.cellRenderer ? (cell.column.cellRenderer({
79
79
  ...cellRendererProps,
@@ -23,7 +23,7 @@ export declare const TableRow: <T extends Record<string, unknown>>(props: {
23
23
  tableHasSubRows: boolean;
24
24
  tableInstance: TableInstance<T>;
25
25
  expanderCell?: ((cellProps: CellProps<T>) => React.ReactNode) | undefined;
26
- bodyRef: HTMLDivElement | null;
26
+ scrollContainerRef: HTMLDivElement | null;
27
27
  tableRowRef?: React.Ref<HTMLDivElement> | undefined;
28
28
  density?: "default" | "condensed" | "extra-condensed" | undefined;
29
29
  }) => React.JSX.Element;
@@ -44,7 +44,7 @@ export declare const TableRowMemoized: <T extends Record<string, unknown>>(props
44
44
  tableHasSubRows: boolean;
45
45
  tableInstance: TableInstance<T>;
46
46
  expanderCell?: ((cellProps: CellProps<T>) => React.ReactNode) | undefined;
47
- bodyRef: HTMLDivElement | null;
47
+ scrollContainerRef: HTMLDivElement | null;
48
48
  tableRowRef?: React.Ref<HTMLDivElement> | undefined;
49
49
  density?: "default" | "condensed" | "extra-condensed" | undefined;
50
50
  }) => React.JSX.Element;
@@ -42,20 +42,21 @@ const TableCell_js_1 = require("./TableCell.js");
42
42
  * When adding new features check whether it changes state that affects row. If it does then add equality check to `React.memo`.
43
43
  */
44
44
  const TableRow = (props) => {
45
- const { row, rowProps, isLast, onRowInViewport, onBottomReached, intersectionMargin, onClick, subComponent, isDisabled, tableHasSubRows, tableInstance, expanderCell, bodyRef, tableRowRef, density, } = props;
45
+ const { row, rowProps, isLast, onRowInViewport, onBottomReached, intersectionMargin, onClick, subComponent, isDisabled, tableHasSubRows, tableInstance, expanderCell, scrollContainerRef, tableRowRef, density, } = props;
46
46
  const onIntersect = React.useCallback(() => {
47
47
  onRowInViewport.current?.(row.original);
48
48
  isLast && onBottomReached.current?.();
49
49
  }, [isLast, onBottomReached, onRowInViewport, row.original]);
50
50
  const intersectionRoot = React.useMemo(() => {
51
- const isTableBodyScrollable = (bodyRef?.scrollHeight ?? 0) > (bodyRef?.offsetHeight ?? 0);
51
+ const isTableScrollable = (scrollContainerRef?.scrollHeight ?? 0) >
52
+ (scrollContainerRef?.offsetHeight ?? 0);
52
53
  // If table body is scrollable, make it the intersection root
53
- if (isTableBodyScrollable) {
54
- return bodyRef;
54
+ if (isTableScrollable) {
55
+ return scrollContainerRef;
55
56
  }
56
57
  // Otherwise, make the viewport the intersection root
57
58
  return undefined;
58
- }, [bodyRef]);
59
+ }, [scrollContainerRef]);
59
60
  const intersectionRef = (0, index_js_1.useIntersection)(onIntersect, {
60
61
  rootMargin: `${intersectionMargin}px`,
61
62
  root: intersectionRoot,
@@ -114,7 +115,7 @@ exports.TableRowMemoized = React.memo(exports.TableRow, (prevProp, nextProp) =>
114
115
  prevProp.rowProps === nextProp.rowProps &&
115
116
  prevProp.expanderCell === nextProp.expanderCell &&
116
117
  prevProp.tableHasSubRows === nextProp.tableHasSubRows &&
117
- prevProp.bodyRef === nextProp.bodyRef &&
118
+ prevProp.scrollContainerRef === nextProp.scrollContainerRef &&
118
119
  prevProp.state.columnOrder === nextProp.state.columnOrder &&
119
120
  !nextProp.state.columnResizing.isResizingColumn &&
120
121
  prevProp.state.isTableResizing === nextProp.state.isTableResizing &&
@@ -13,6 +13,12 @@ export type DefaultCellProps<T extends Record<string, unknown>> = {
13
13
  * Status of the cell.
14
14
  */
15
15
  status?: 'positive' | 'negative' | 'warning';
16
+ /**
17
+ * Should the contents of the cell be clamped after a certain number of lines?
18
+ *
19
+ * Will be enabled by default if the cell content is a string.
20
+ */
21
+ clamp?: boolean;
16
22
  } & CellRendererProps<T> & React.ComponentPropsWithoutRef<'div'>;
17
23
  /**
18
24
  * Default cell.
@@ -46,12 +46,16 @@ const index_js_1 = require("../../utils/index.js");
46
46
  * }
47
47
  */
48
48
  const DefaultCell = (props) => {
49
- // Omitting `cellProps`
50
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
51
- const { cellElementProps: { className: cellElementClassName, style: cellElementStyle, ...cellElementProps }, children, startIcon, endIcon, cellProps, isDisabled, className, style, status, ...rest } = props;
49
+ const { cellElementProps: { className: cellElementClassName, style: cellElementStyle, ...cellElementProps }, children, startIcon, endIcon, cellProps, isDisabled, className, style, status, clamp = typeof cellProps.value === 'string', ...rest } = props;
52
50
  return (React.createElement(index_js_1.Box, { ...cellElementProps, ...rest, className: (0, classnames_1.default)(cellElementClassName, className), "aria-disabled": isDisabled?.(cellProps.row.original) || undefined, "data-iui-status": status, style: { ...cellElementStyle, ...style } },
53
- startIcon && (React.createElement(index_js_1.Box, { className: 'iui-table-cell-start-icon' }, startIcon)),
51
+ React.createElement(index_js_1.ShadowRoot, null,
52
+ React.createElement("slot", { name: 'start' }),
53
+ clamp ? (React.createElement(index_js_1.LineClamp, null,
54
+ React.createElement("slot", null))) : (React.createElement("slot", null)),
55
+ React.createElement("slot", { name: 'end' }),
56
+ React.createElement("slot", { name: 'shadows' })),
57
+ startIcon && (React.createElement(index_js_1.Box, { className: 'iui-table-cell-start-icon', slot: 'start' }, startIcon)),
54
58
  children,
55
- endIcon && React.createElement(index_js_1.Box, { className: 'iui-table-cell-end-icon' }, endIcon)));
59
+ endIcon && (React.createElement(index_js_1.Box, { className: 'iui-table-cell-end-icon', slot: 'end' }, endIcon))));
56
60
  };
57
61
  exports.DefaultCell = DefaultCell;
@@ -57,6 +57,6 @@ const FilterToggle = (props) => {
57
57
  React.createElement(IconButton_js_1.IconButton, { styleType: 'borderless', isActive: isVisible || isColumnFiltered, className: (0, classnames_1.default)('iui-table-filter-button', className), "aria-label": 'Filter', onClick: (e) => {
58
58
  // Prevents from triggering sort
59
59
  e.stopPropagation();
60
- }, ...rest }, isColumnFiltered ? React.createElement(index_js_1.SvgFilter, null) : React.createElement(index_js_1.SvgFilterHollow, null))))));
60
+ }, "data-iui-shift": 'left', ...rest }, isColumnFiltered ? React.createElement(index_js_1.SvgFilter, null) : React.createElement(index_js_1.SvgFilterHollow, null))))));
61
61
  };
62
62
  exports.FilterToggle = FilterToggle;
@@ -152,7 +152,7 @@ exports.ToastPresentation = React.forwardRef((props, forwardedRef) => {
152
152
  return (React.createElement(index_js_1.Box, { className: (0, classnames_1.default)(`iui-toast iui-${category}`, className), ref: forwardedRef, ...rest },
153
153
  React.createElement(index_js_1.Box, { className: 'iui-status-area' }, React.createElement(StatusIcon, { className: 'iui-icon' })),
154
154
  React.createElement(index_js_1.Box, { as: 'div', ...contentProps, className: (0, classnames_1.default)('iui-message', contentProps?.className) }, content),
155
- link && (React.createElement(index_js_1.ButtonBase, { className: 'iui-toast-anchor', ...link, title: undefined }, link.title)),
155
+ link && (React.createElement(index_js_1.ButtonBase, { ...link, className: (0, classnames_1.default)('iui-anchor', 'iui-toast-anchor', link.className), title: undefined, "data-iui-status": category, "data-iui-underline": true }, link.title)),
156
156
  (type === 'persisting' || hasCloseButton) && (React.createElement(IconButton_js_1.IconButton, { size: 'small', styleType: 'borderless', onClick: onClose, "aria-label": 'Close' },
157
157
  React.createElement(index_js_1.SvgCloseSmall, null)))));
158
158
  });
@@ -46,11 +46,12 @@ const index_js_1 = require("../utils/index.js");
46
46
  */
47
47
  exports.VisuallyHidden = React.forwardRef((props, ref) => {
48
48
  const { as: asProp = 'span', className, unhideOnFocus = true, children: childrenProp, ...rest } = props;
49
+ const isMounted = (0, index_js_1.useIsClient)();
49
50
  // ShadowRoot is not supported on all elements, so we only use it for few common ones.
50
51
  const children = !['div', 'span', 'p'].includes(asProp) ? (childrenProp) : (React.createElement(React.Fragment, null,
51
52
  React.createElement(index_js_1.ShadowRoot, { css: css },
52
53
  React.createElement("slot", null)),
53
- childrenProp));
54
+ isMounted && childrenProp));
54
55
  return (React.createElement(index_js_1.Box, { as: asProp, className: (0, classnames_1.default)('iui-visually-hidden', className), "data-iui-unhide-on-focus": unhideOnFocus ? true : undefined, ref: ref, ...rest }, children));
55
56
  });
56
57
  // ----------------------------------------------------------------------------
@@ -1,4 +1,9 @@
1
1
  import type { ChangeEvent, ComponentType, CSSProperties, DependencyList, EffectCallback, MouseEvent, ReactElement, ReactFragment, ReactNode } from 'react';
2
+ /**
3
+ * This allows custom strings and keeps intellisense for string unions.
4
+ * See https://github.com/Microsoft/TypeScript/issues/29729
5
+ */
6
+ type AnyString = string & {};
2
7
  export type FieldType = 'text' | 'number' | 'date' | string;
3
8
  export type CellRendererProps<D extends Record<string, unknown> = {}> = {
4
9
  /**
@@ -109,10 +114,10 @@ export interface ColumnInterface<D extends Record<string, unknown> = {}> extends
109
114
  Filter?: Renderer<FilterProps<D>> | Renderer<TableFilterProps<D>>;
110
115
  /**
111
116
  * String value or custom function to use for filtering.
112
- * Possible string values: `text`, `exactText`, `exactTextCase`, `includes`, `includesAll`, `exact`, `equals`, `between`.
113
- * More info about these filters: https://github.com/tannerlinsley/react-table/blob/master/src/filterTypes.js
117
+ * Possible string values: `text`, `exactText`, `exactTextCase`, `includes`, `includesAll`, `includesSome`, `exact`, `equals`, `between`.
118
+ * More info about these filters: https://github.com/TanStack/table/blob/v7/src/filterTypes.js
114
119
  */
115
- filter?: FilterType<D> | DefaultFilterTypes | string;
120
+ filter?: FilterType<D> | DefaultFilterTypes | AnyString;
116
121
  /**
117
122
  * Function that should return whole cell element not only the content.
118
123
  * Must be memoized.
@@ -428,7 +433,7 @@ export type UseFiltersColumnOptions<D extends Record<string, unknown>> = Partial
428
433
  Filter: Renderer<FilterProps<D>>;
429
434
  disableFilters: boolean;
430
435
  defaultCanFilter: boolean;
431
- filter: FilterType<D> | DefaultFilterTypes | string;
436
+ filter: FilterType<D> | DefaultFilterTypes | AnyString;
432
437
  }>;
433
438
  export interface UseFiltersInstanceProps<D extends Record<string, unknown>> {
434
439
  preFilteredRows: Array<Row<D>>;
@@ -457,7 +462,7 @@ export type Filters<D extends Record<string, unknown>> = Array<{
457
462
  value: FilterValue;
458
463
  }>;
459
464
  export type FilterTypes<D extends Record<string, unknown>> = Record<string, FilterType<D>>;
460
- export type DefaultFilterTypes = 'text' | 'exactText' | 'exactTextCase' | 'includes' | 'includesAll' | 'exact' | 'equals' | 'between';
465
+ export type DefaultFilterTypes = 'text' | 'exactText' | 'exactTextCase' | 'includes' | 'includesAll' | 'includesSome' | 'exact' | 'equals' | 'between';
461
466
  export interface FilterType<D extends Record<string, unknown>> {
462
467
  (rows: Array<Row<D>>, columnIds: Array<IdType<D>>, filterValue: FilterValue): Array<Row<D>>;
463
468
  autoRemove?: ((filterValue: FilterValue) => boolean) | undefined;
@@ -4,9 +4,10 @@
4
4
  *--------------------------------------------------------------------------------------------*/
5
5
  import cx from 'classnames';
6
6
  import * as React from 'react';
7
- import { useSafeContext, polymorphic, StatusIconMap, SvgCloseSmall, Box, ButtonBase, } from '../utils/index.js';
7
+ import { useSafeContext, polymorphic, StatusIconMap, SvgCloseSmall, Box, } from '../utils/index.js';
8
8
  import { IconButton } from '../Buttons/IconButton.js';
9
9
  import { Icon } from '../Icon/Icon.js';
10
+ import { Anchor } from '../Typography/Anchor.js';
10
11
  const AlertContext = React.createContext(undefined);
11
12
  // ----------------------------------------------------------------------------
12
13
  // Alert component
@@ -45,7 +46,8 @@ AlertMessage.displayName = 'Alert.Message';
45
46
  // Alert.Action component
46
47
  const AlertAction = React.forwardRef((props, ref) => {
47
48
  const { children, className, ...rest } = props;
48
- return (React.createElement(ButtonBase, { as: (!!props.href ? 'a' : 'button'), className: cx('iui-alert-link', className), ref: ref, ...rest }, children));
49
+ const { type } = useSafeContext(AlertContext);
50
+ return (React.createElement(Anchor, { as: (!!props.href ? 'a' : 'button'), className: cx('iui-button-base', 'iui-alert-link', className), underline: true, "data-iui-status": type, ref: ref, ...rest }, children));
49
51
  });
50
52
  AlertAction.displayName = 'Alert.Action';
51
53
  // ----------------------------------------------------------------------------
@@ -6,6 +6,7 @@ import * as React from 'react';
6
6
  import cx from 'classnames';
7
7
  import { useMergedRefs, useOverflow, SvgChevronRight, Box, createWarningLogger, } from '../utils/index.js';
8
8
  import { Button } from '../Buttons/Button.js';
9
+ import { Anchor } from '../Typography/Anchor.js';
9
10
  const logWarningInDev = createWarningLogger();
10
11
  /**
11
12
  * A breadcrumb trail is used as a navigational aid to help users keep track
@@ -74,7 +75,7 @@ const Separator = ({ separator }) => (React.createElement(Box, { as: 'li', class
74
75
  // ----------------------------------------------------------------------------
75
76
  const BreadcrumbsItem = React.forwardRef((props, forwardedRef) => {
76
77
  const { children: childrenProp, className, ...rest } = props;
77
- const defaultAs = !!props.href ? 'a' : !!props.onClick ? 'button' : 'span';
78
+ const defaultAs = !!props.href ? Anchor : !!props.onClick ? 'button' : 'span';
78
79
  const children = defaultAs === 'button' ? React.createElement("span", null, childrenProp) : childrenProp;
79
80
  return (React.createElement(Box, { as: defaultAs, className: cx('iui-breadcrumbs-content', className), ref: forwardedRef, ...rest }, children));
80
81
  });
@@ -8,6 +8,7 @@ import { Box } from '../utils/index.js';
8
8
  import { FooterItem } from './FooterItem.js';
9
9
  import { FooterSeparator } from './FooterSeparator.js';
10
10
  import { FooterList } from './FooterList.js';
11
+ import { Anchor } from '../Typography/Anchor.js';
11
12
  const footerTranslations = {
12
13
  cookies: 'Cookies',
13
14
  legalNotices: 'Legal notices',
@@ -82,7 +83,7 @@ export const Footer = Object.assign((props) => {
82
83
  return (React.createElement(Box, { as: 'footer', className: cx('iui-legal-footer', className), ...rest }, children ? (children) : (React.createElement(FooterList, null, elements.map((element, index) => {
83
84
  return (React.createElement(React.Fragment, { key: element.key || `${element.title}-${index}` },
84
85
  index > 0 && React.createElement(FooterSeparator, null),
85
- React.createElement(FooterItem, null, element.url ? (React.createElement("a", { href: element.url, target: '_blank', rel: 'noreferrer' }, element.title)) : (element.title))));
86
+ React.createElement(FooterItem, null, element.url ? (React.createElement(Anchor, { href: element.url, target: '_blank', rel: 'noreferrer' }, element.title)) : (element.title))));
86
87
  })))));
87
88
  }, {
88
89
  List: FooterList,
@@ -6,5 +6,6 @@ export type SubRowExpanderProps<T extends Record<string, unknown>> = {
6
6
  isDisabled: boolean;
7
7
  cellProps: CellProps<T>;
8
8
  density?: 'default' | 'condensed' | 'extra-condensed';
9
+ [k: string]: unknown;
9
10
  };
10
11
  export declare const SubRowExpander: <T extends Record<string, unknown>>(props: SubRowExpanderProps<T>) => React.JSX.Element;
@@ -6,13 +6,13 @@ import * as React from 'react';
6
6
  import { SvgChevronRight } from '../utils/index.js';
7
7
  import { IconButton } from '../Buttons/IconButton.js';
8
8
  export const SubRowExpander = (props) => {
9
- const { cell, isDisabled, cellProps, expanderCell, density } = props;
9
+ const { cell, isDisabled, cellProps, expanderCell, density, ...rest } = props;
10
10
  return (React.createElement(React.Fragment, null, expanderCell ? (expanderCell(cellProps)) : (React.createElement(IconButton, { "aria-label": 'Toggle sub row', "aria-expanded": cell.row.isExpanded ? 'true' : 'false', style: {
11
11
  marginInlineEnd: density === 'default' || density === undefined ? 8 : 4,
12
12
  }, className: 'iui-table-row-expander', styleType: 'borderless', size: 'small', onClick: (e) => {
13
13
  e.stopPropagation();
14
14
  cell.row.toggleRowExpanded();
15
- }, disabled: isDisabled }, React.createElement(SvgChevronRight, { style: {
15
+ }, disabled: isDisabled, ...rest }, React.createElement(SvgChevronRight, { style: {
16
16
  transform: cell.row.isExpanded ? 'rotate(90deg)' : undefined,
17
17
  } })))));
18
18
  };