@itwin/itwinui-react 3.15.4 → 3.16.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 (137) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/DEV-cjs/core/Breadcrumbs/Breadcrumbs.js +2 -2
  3. package/DEV-cjs/core/Checkbox/Checkbox.js +4 -6
  4. package/DEV-cjs/core/ComboBox/ComboBox.js +10 -6
  5. package/DEV-cjs/core/DatePicker/DatePicker.js +9 -1
  6. package/DEV-cjs/core/Dialog/Dialog.js +1 -1
  7. package/DEV-cjs/core/Panels/Panels.js +319 -0
  8. package/DEV-cjs/core/Panels/helpers.js +62 -0
  9. package/DEV-cjs/core/Radio/Radio.js +4 -6
  10. package/DEV-cjs/core/RadioTiles/RadioTileGroup.js +9 -2
  11. package/DEV-cjs/core/Select/SelectTag.js +9 -11
  12. package/DEV-cjs/core/Select/SelectTagContainer.js +2 -2
  13. package/DEV-cjs/core/Stepper/Stepper.js +1 -0
  14. package/DEV-cjs/core/Stepper/StepperStep.js +2 -1
  15. package/DEV-cjs/core/Table/Table.js +64 -47
  16. package/DEV-cjs/core/Table/TablePaginator.js +15 -3
  17. package/DEV-cjs/core/Table/actionHandlers/selectHandler.js +10 -7
  18. package/DEV-cjs/core/Table/columns/selectionColumn.js +6 -1
  19. package/DEV-cjs/core/Tree/Tree.js +1 -0
  20. package/DEV-cjs/index.js +4 -0
  21. package/DEV-cjs/styles.js +1 -1
  22. package/DEV-cjs/utils/components/MiddleTextTruncation.js +22 -4
  23. package/DEV-cjs/utils/components/OverflowContainer.js +170 -27
  24. package/DEV-cjs/utils/hooks/index.js +1 -1
  25. package/DEV-cjs/utils/hooks/useInstance.js +38 -0
  26. package/DEV-esm/core/Breadcrumbs/Breadcrumbs.js +2 -2
  27. package/DEV-esm/core/Checkbox/Checkbox.js +5 -10
  28. package/DEV-esm/core/ComboBox/ComboBox.js +10 -6
  29. package/DEV-esm/core/DatePicker/DatePicker.js +11 -1
  30. package/DEV-esm/core/Dialog/Dialog.js +1 -1
  31. package/DEV-esm/core/Panels/Panels.js +301 -0
  32. package/DEV-esm/core/Panels/helpers.js +42 -0
  33. package/DEV-esm/core/Radio/Radio.js +4 -9
  34. package/DEV-esm/core/RadioTiles/RadioTileGroup.js +8 -2
  35. package/DEV-esm/core/Select/SelectTag.js +9 -11
  36. package/DEV-esm/core/Select/SelectTagContainer.js +2 -2
  37. package/DEV-esm/core/Stepper/Stepper.js +1 -0
  38. package/DEV-esm/core/Stepper/StepperStep.js +2 -1
  39. package/DEV-esm/core/Table/Table.js +61 -47
  40. package/DEV-esm/core/Table/TablePaginator.js +16 -3
  41. package/DEV-esm/core/Table/actionHandlers/selectHandler.js +10 -7
  42. package/DEV-esm/core/Table/columns/selectionColumn.js +6 -1
  43. package/DEV-esm/core/Tree/Tree.js +1 -0
  44. package/DEV-esm/index.js +1 -0
  45. package/DEV-esm/styles.js +1 -1
  46. package/DEV-esm/utils/components/MiddleTextTruncation.js +22 -4
  47. package/DEV-esm/utils/components/OverflowContainer.js +143 -4
  48. package/DEV-esm/utils/hooks/index.js +1 -1
  49. package/DEV-esm/utils/hooks/useInstance.js +18 -0
  50. package/cjs/core/Breadcrumbs/Breadcrumbs.js +2 -2
  51. package/cjs/core/Checkbox/Checkbox.js +4 -6
  52. package/cjs/core/ComboBox/ComboBox.d.ts +13 -0
  53. package/cjs/core/ComboBox/ComboBox.js +10 -6
  54. package/cjs/core/DatePicker/DatePicker.d.ts +2 -2
  55. package/cjs/core/DatePicker/DatePicker.js +2 -1
  56. package/cjs/core/Dialog/Dialog.js +1 -1
  57. package/cjs/core/Dialog/DialogContext.d.ts +6 -2
  58. package/cjs/core/NonIdealState/NonIdealState.d.ts +15 -11
  59. package/cjs/core/Panels/Panels.d.ts +174 -0
  60. package/cjs/core/Panels/Panels.js +312 -0
  61. package/cjs/core/Panels/helpers.d.ts +23 -0
  62. package/cjs/core/Panels/helpers.js +61 -0
  63. package/cjs/core/Radio/Radio.js +4 -6
  64. package/cjs/core/RadioTiles/RadioTileGroup.d.ts +3 -1
  65. package/cjs/core/RadioTiles/RadioTileGroup.js +9 -2
  66. package/cjs/core/Select/SelectTag.d.ts +3 -1
  67. package/cjs/core/Select/SelectTag.js +9 -11
  68. package/cjs/core/Select/SelectTagContainer.js +2 -2
  69. package/cjs/core/Stepper/Stepper.d.ts +4 -0
  70. package/cjs/core/Stepper/Stepper.js +1 -0
  71. package/cjs/core/Stepper/StepperStep.d.ts +4 -0
  72. package/cjs/core/Stepper/StepperStep.js +2 -1
  73. package/cjs/core/Table/Table.d.ts +1 -0
  74. package/cjs/core/Table/Table.js +64 -47
  75. package/cjs/core/Table/TablePaginator.js +15 -3
  76. package/cjs/core/Table/actionHandlers/selectHandler.js +10 -7
  77. package/cjs/core/Table/columns/selectionColumn.js +6 -1
  78. package/cjs/core/Tree/Tree.js +1 -0
  79. package/cjs/index.d.ts +1 -0
  80. package/cjs/index.js +4 -0
  81. package/cjs/styles.js +1 -1
  82. package/cjs/utils/components/MiddleTextTruncation.d.ts +5 -7
  83. package/cjs/utils/components/MiddleTextTruncation.js +22 -4
  84. package/cjs/utils/components/OverflowContainer.d.ts +1 -0
  85. package/cjs/utils/components/OverflowContainer.js +170 -27
  86. package/cjs/utils/hooks/index.d.ts +1 -1
  87. package/cjs/utils/hooks/index.js +1 -1
  88. package/cjs/utils/hooks/useInstance.d.ts +22 -0
  89. package/cjs/utils/hooks/useInstance.js +38 -0
  90. package/esm/core/Breadcrumbs/Breadcrumbs.js +2 -2
  91. package/esm/core/Checkbox/Checkbox.js +5 -10
  92. package/esm/core/ComboBox/ComboBox.d.ts +13 -0
  93. package/esm/core/ComboBox/ComboBox.js +10 -6
  94. package/esm/core/DatePicker/DatePicker.d.ts +2 -2
  95. package/esm/core/DatePicker/DatePicker.js +4 -1
  96. package/esm/core/Dialog/Dialog.js +1 -1
  97. package/esm/core/Dialog/DialogContext.d.ts +6 -2
  98. package/esm/core/NonIdealState/NonIdealState.d.ts +15 -11
  99. package/esm/core/Panels/Panels.d.ts +174 -0
  100. package/esm/core/Panels/Panels.js +294 -0
  101. package/esm/core/Panels/helpers.d.ts +23 -0
  102. package/esm/core/Panels/helpers.js +41 -0
  103. package/esm/core/Radio/Radio.js +4 -9
  104. package/esm/core/RadioTiles/RadioTileGroup.d.ts +3 -1
  105. package/esm/core/RadioTiles/RadioTileGroup.js +8 -2
  106. package/esm/core/Select/SelectTag.d.ts +3 -1
  107. package/esm/core/Select/SelectTag.js +9 -11
  108. package/esm/core/Select/SelectTagContainer.js +2 -2
  109. package/esm/core/Stepper/Stepper.d.ts +4 -0
  110. package/esm/core/Stepper/Stepper.js +1 -0
  111. package/esm/core/Stepper/StepperStep.d.ts +4 -0
  112. package/esm/core/Stepper/StepperStep.js +2 -1
  113. package/esm/core/Table/Table.d.ts +1 -0
  114. package/esm/core/Table/Table.js +61 -47
  115. package/esm/core/Table/TablePaginator.js +16 -3
  116. package/esm/core/Table/actionHandlers/selectHandler.js +10 -7
  117. package/esm/core/Table/columns/selectionColumn.js +6 -1
  118. package/esm/core/Tree/Tree.js +1 -0
  119. package/esm/index.d.ts +1 -0
  120. package/esm/index.js +1 -0
  121. package/esm/styles.js +1 -1
  122. package/esm/utils/components/MiddleTextTruncation.d.ts +5 -7
  123. package/esm/utils/components/MiddleTextTruncation.js +22 -4
  124. package/esm/utils/components/OverflowContainer.d.ts +1 -0
  125. package/esm/utils/components/OverflowContainer.js +143 -4
  126. package/esm/utils/hooks/index.d.ts +1 -1
  127. package/esm/utils/hooks/index.js +1 -1
  128. package/esm/utils/hooks/useInstance.d.ts +22 -0
  129. package/esm/utils/hooks/useInstance.js +18 -0
  130. package/package.json +2 -2
  131. package/styles.css +8 -8
  132. package/DEV-cjs/utils/hooks/useOverflow.js +0 -76
  133. package/DEV-esm/utils/hooks/useOverflow.js +0 -63
  134. package/cjs/utils/hooks/useOverflow.d.ts +0 -23
  135. package/cjs/utils/hooks/useOverflow.js +0 -76
  136. package/esm/utils/hooks/useOverflow.d.ts +0 -23
  137. package/esm/utils/hooks/useOverflow.js +0 -63
@@ -53,6 +53,7 @@ let singleRowSelectedAction = 'singleRowSelected';
53
53
  let shiftRowSelectedAction = 'shiftRowSelected';
54
54
  export const tableResizeStartAction = 'tableResizeStart';
55
55
  let tableResizeEndAction = 'tableResizeEnd';
56
+ export const iuiId = Symbol('iui-id');
56
57
  let flattenColumns = (columns) => {
57
58
  let flatColumns = [];
58
59
  columns.forEach((column) => {
@@ -105,6 +106,7 @@ export const Table = (props) => {
105
106
  headerProps,
106
107
  bodyProps,
107
108
  emptyTableContentProps,
109
+ getRowId,
108
110
  ...rest
109
111
  } = props;
110
112
  useGlobals();
@@ -240,16 +242,30 @@ export const Table = (props) => {
240
242
  ),
241
243
  [data, getSubRows],
242
244
  );
243
- let [rowHasParent] = React.useState(() => new WeakSet());
244
245
  let getSubRowsWithSubComponents = React.useCallback(
245
- (originalRow) => {
246
- if (!rowHasParent.has(originalRow)) {
247
- rowHasParent.add(originalRow);
248
- return [originalRow];
249
- }
250
- return originalRow.subRows || [];
246
+ (originalRow, relativeIndex) => {
247
+ if (originalRow[iuiId]) return [];
248
+ if (originalRow.subRows) return originalRow.subRows;
249
+ return [
250
+ {
251
+ [iuiId]: `subcomponent-${relativeIndex}`,
252
+ ...originalRow,
253
+ },
254
+ ];
251
255
  },
252
- [rowHasParent],
256
+ [],
257
+ );
258
+ let getRowIdWithSubComponents = React.useCallback(
259
+ (originalRow, relativeIndex, parent) => {
260
+ let defaultRowId = parent
261
+ ? `${parent.id}.${relativeIndex}`
262
+ : `${relativeIndex}`;
263
+ let rowIdFromUser = getRowId?.(originalRow, relativeIndex, parent);
264
+ if (void 0 !== rowIdFromUser && originalRow[iuiId])
265
+ return `${rowIdFromUser}-${defaultRowId}`;
266
+ return rowIdFromUser ?? defaultRowId;
267
+ },
268
+ [getRowId],
253
269
  );
254
270
  let instance = useTable(
255
271
  {
@@ -269,6 +285,7 @@ export const Table = (props) => {
269
285
  ...props.initialState,
270
286
  },
271
287
  columnResizeMode,
288
+ getRowId: subComponent ? getRowIdWithSubComponents : getRowId,
272
289
  },
273
290
  useFlexLayout,
274
291
  useResizeColumns(ownerDocument),
@@ -469,51 +486,52 @@ export const Table = (props) => {
469
486
  (index, virtualItem, virtualizer) => {
470
487
  let row = page[index];
471
488
  prepareRow(row);
472
- if (row.subRows.length > 0 || !subComponent)
473
- return React.createElement(TableRowMemoized, {
474
- row: row,
475
- rowProps: rowProps,
476
- isLast: index === page.length - 1,
477
- onRowInViewport: onRowInViewportRef,
478
- onBottomReached: onBottomReachedRef,
479
- intersectionMargin: intersectionMargin,
480
- state: state,
481
- key: row.getRowProps().key,
482
- onClick: onRowClickHandler,
483
- subComponent: subComponent,
484
- isDisabled: !!isRowDisabled?.(row.original),
485
- tableHasSubRows: hasAnySubRows,
486
- tableInstance: instance,
487
- expanderCell: expanderCell,
488
- scrollContainerRef: tableRef.current,
489
- tableRowRef: enableVirtualization ? void 0 : tableRowRef(row),
490
- density: density,
491
- virtualItem: virtualItem,
492
- virtualizer: virtualizer,
493
- });
494
- return React.createElement(
495
- TableExpandableContentMemoized,
496
- {
497
- key: row.getRowProps().key,
498
- virtualItem: virtualItem,
499
- ref: enableVirtualization
500
- ? virtualizer?.measureElement
501
- : tableRowRef(row),
502
- isDisabled: !!isRowDisabled?.(row.original),
503
- },
504
- subComponent(row),
505
- );
489
+ let isRowASubComponent = !!row.original[iuiId] && !!subComponent;
490
+ if (isRowASubComponent)
491
+ return React.createElement(
492
+ TableExpandableContentMemoized,
493
+ {
494
+ key: row.getRowProps().key,
495
+ virtualItem: virtualItem,
496
+ ref: enableVirtualization
497
+ ? virtualizer?.measureElement
498
+ : tableRowRef(row),
499
+ isDisabled: !!isRowDisabled?.(row.original),
500
+ },
501
+ subComponent(row),
502
+ );
503
+ return React.createElement(TableRowMemoized, {
504
+ row: row,
505
+ rowProps: rowProps,
506
+ isLast: index === page.length - 1,
507
+ onRowInViewport: onRowInViewportRef,
508
+ onBottomReached: onBottomReachedRef,
509
+ intersectionMargin: intersectionMargin,
510
+ state: state,
511
+ key: row.getRowProps().key,
512
+ onClick: onRowClickHandler,
513
+ subComponent: subComponent,
514
+ isDisabled: !!isRowDisabled?.(row.original),
515
+ tableHasSubRows: hasAnySubRows,
516
+ tableInstance: instance,
517
+ expanderCell: expanderCell,
518
+ scrollContainerRef: tableRef.current,
519
+ tableRowRef: enableVirtualization ? void 0 : tableRowRef(row),
520
+ density: density,
521
+ virtualItem: virtualItem,
522
+ virtualizer: virtualizer,
523
+ });
506
524
  },
507
525
  [
508
526
  page,
509
527
  prepareRow,
528
+ subComponent,
510
529
  rowProps,
511
530
  onRowInViewportRef,
512
531
  onBottomReachedRef,
513
532
  intersectionMargin,
514
533
  state,
515
534
  onRowClickHandler,
516
- subComponent,
517
535
  isRowDisabled,
518
536
  hasAnySubRows,
519
537
  instance,
@@ -647,11 +665,7 @@ export const Table = (props) => {
647
665
  },
648
666
  bodyProps?.className,
649
667
  ),
650
- style: {
651
- outline: 0,
652
- },
653
668
  }),
654
- tabIndex: -1,
655
669
  'aria-multiselectable':
656
670
  (isSelectable && 'multi' === selectionMode) || void 0,
657
671
  },
@@ -13,6 +13,7 @@ import {
13
13
  SvgChevronRight,
14
14
  Box,
15
15
  OverflowContainer,
16
+ useLayoutEffect,
16
17
  } from '../../utils/index.js';
17
18
  import { styles } from '../../styles.js';
18
19
  let defaultLocalization = {
@@ -56,7 +57,7 @@ export const TablePaginator = (props) => {
56
57
  );
57
58
  let pageListRef = React.useRef(null);
58
59
  let [focusedIndex, setFocusedIndex] = React.useState(currentPage);
59
- React.useLayoutEffect(() => {
60
+ useLayoutEffect(() => {
60
61
  setFocusedIndex(currentPage);
61
62
  }, [currentPage]);
62
63
  let needFocus = React.useRef(false);
@@ -178,6 +179,7 @@ export const TablePaginator = (props) => {
178
179
  : React.createElement(TablePaginatorPageButtons, {
179
180
  size: size,
180
181
  focusedIndex: focusedIndex,
182
+ setFocusedIndex: setFocusedIndex,
181
183
  totalPagesCount: totalPagesCount,
182
184
  onPageChange: onPageChange,
183
185
  currentPage: currentPage,
@@ -251,6 +253,7 @@ export const TablePaginator = (props) => {
251
253
  let TablePaginatorPageButtons = (props) => {
252
254
  let {
253
255
  focusedIndex,
256
+ setFocusedIndex,
254
257
  totalPagesCount,
255
258
  onPageChange,
256
259
  currentPage,
@@ -270,14 +273,24 @@ let TablePaginatorPageButtons = (props) => {
270
273
  styleType: 'borderless',
271
274
  size: buttonSize,
272
275
  'data-iui-active': index === currentPage,
273
- onClick: () => onPageChange(index),
276
+ onClick: () => {
277
+ setFocusedIndex(index);
278
+ onPageChange(index);
279
+ },
274
280
  'aria-current': index === currentPage,
275
281
  'aria-label': localization.goToPageLabel?.(index + 1),
276
282
  tabIndex: tabIndex,
277
283
  },
278
284
  index + 1,
279
285
  ),
280
- [focusedIndex, currentPage, localization, buttonSize, onPageChange],
286
+ [
287
+ focusedIndex,
288
+ buttonSize,
289
+ currentPage,
290
+ localization,
291
+ setFocusedIndex,
292
+ onPageChange,
293
+ ],
281
294
  );
282
295
  let pageList = React.useMemo(
283
296
  () =>
@@ -1,3 +1,4 @@
1
+ import { iuiId } from '../Table.js';
1
2
  let onSelectHandler = (newState, instance, onSelect, isRowDisabled) => {
2
3
  if (!instance?.rows.length) {
3
4
  onSelect?.([], newState);
@@ -7,14 +8,16 @@ let onSelectHandler = (newState, instance, onSelect, isRowDisabled) => {
7
8
  let handleRow = (row) => {
8
9
  if (isRowDisabled?.(row.original)) return false;
9
10
  let isAllSubSelected = true;
10
- row.initialSubRows.forEach((subRow) => {
11
- let result = handleRow(subRow);
12
- if (!result) isAllSubSelected = false;
13
- });
11
+ if (row.initialSubRows[0]?.original[iuiId] === void 0)
12
+ row.initialSubRows.forEach((subRow) => {
13
+ let result = handleRow(subRow);
14
+ if (!result) isAllSubSelected = false;
15
+ });
14
16
  if (
15
- (!instance.selectSubRows && newState.selectedRowIds[row.id]) ||
16
- (!row.initialSubRows.length && newState.selectedRowIds[row.id]) ||
17
- (row.initialSubRows.length && isAllSubSelected)
17
+ newState.selectedRowIds[row.id] &&
18
+ (!instance.selectSubRows ||
19
+ !row.initialSubRows.length ||
20
+ isAllSubSelected)
18
21
  )
19
22
  newSelectedRowIds[row.id] = true;
20
23
  return !!newSelectedRowIds[row.id];
@@ -1,6 +1,7 @@
1
1
  import * as React from 'react';
2
2
  import { Checkbox } from '../../Checkbox/Checkbox.js';
3
3
  import { DefaultCell } from '../cells/index.js';
4
+ import { iuiId } from '../Table.js';
4
5
  export const SELECTION_CELL_ID = 'iui-table-checkbox-selector';
5
6
  export const SelectionColumn = (props = {}) => {
6
7
  let { isDisabled, density } = props;
@@ -50,7 +51,11 @@ export const SelectionColumn = (props = {}) => {
50
51
  disabled: isDisabled?.(row.original),
51
52
  onClick: (e) => e.stopPropagation(),
52
53
  onChange: () => {
53
- if (row.subRows.length > 0 && selectSubRows)
54
+ if (
55
+ row.subRows.length > 0 &&
56
+ selectSubRows &&
57
+ void 0 === row.initialSubRows[0].original[iuiId]
58
+ )
54
59
  row.toggleRowSelected(
55
60
  !row.subRows.every(
56
61
  (subRow) => subRow.isSelected || isDisabled?.(subRow.original),
@@ -164,6 +164,7 @@ export const Tree = (props) => {
164
164
  onKeyDown: handleKeyDown,
165
165
  ref: treeRef,
166
166
  className: className,
167
+ 'data-iui-size': 'small' === size ? 'small' : void 0,
167
168
  style: style,
168
169
  ...rest,
169
170
  })
package/DEV-esm/index.js CHANGED
@@ -64,6 +64,7 @@ export { ModalContent } from './core/Modal/ModalContent.js';
64
64
  export { ModalButtonBar } from './core/Modal/ModalButtonBar.js';
65
65
  export { NotificationMarker } from './core/NotificationMarker/NotificationMarker.js';
66
66
  export { Overlay } from './core/Overlay/Overlay.js';
67
+ export { Panels as unstable_Panels } from './core/Panels/Panels.js';
67
68
  export { ProgressLinear } from './core/ProgressIndicators/ProgressLinear.js';
68
69
  export { ProgressRadial } from './core/ProgressIndicators/ProgressRadial.js';
69
70
  export { Radio } from './core/Radio/Radio.js';
package/DEV-esm/styles.js CHANGED
@@ -1,4 +1,4 @@
1
- const t = '3.15.4';
1
+ const t = '3.16.0';
2
2
  const u = new Proxy(
3
3
  {},
4
4
  {
@@ -1,8 +1,10 @@
1
1
  import * as React from 'react';
2
2
  import { OverflowContainer } from './OverflowContainer.js';
3
+ import { VisuallyHidden } from '../../core/VisuallyHidden/VisuallyHidden.js';
4
+ import { ShadowRoot } from './ShadowRoot.js';
3
5
  let ELLIPSIS_CHAR = '…';
4
- export const MiddleTextTruncation = (props) => {
5
- let { text, style, ...rest } = props;
6
+ export const MiddleTextTruncation = React.forwardRef((props, forwardedRef) => {
7
+ let { text, endCharsCount, textRenderer, style, ...rest } = props;
6
8
  return React.createElement(
7
9
  OverflowContainer,
8
10
  {
@@ -16,10 +18,26 @@ export const MiddleTextTruncation = (props) => {
16
18
  },
17
19
  itemsCount: text.length,
18
20
  ...rest,
21
+ ref: forwardedRef,
19
22
  },
20
- React.createElement(MiddleTextTruncationContent, props),
23
+ React.createElement(
24
+ ShadowRoot,
25
+ null,
26
+ React.createElement(VisuallyHidden, null, text),
27
+ React.createElement('slot', {
28
+ 'aria-hidden': true,
29
+ style: {
30
+ pointerEvents: 'none',
31
+ },
32
+ }),
33
+ ),
34
+ React.createElement(MiddleTextTruncationContent, {
35
+ text: text,
36
+ endCharsCount: endCharsCount,
37
+ textRenderer: textRenderer,
38
+ }),
21
39
  );
22
- };
40
+ });
23
41
  MiddleTextTruncation.displayName = 'MiddleTextTruncation';
24
42
  let MiddleTextTruncationContent = (props) => {
25
43
  let { text, endCharsCount = 6, textRenderer } = props;
@@ -1,13 +1,14 @@
1
1
  import React from 'react';
2
2
  import { useMergedRefs } from '../hooks/useMergedRefs.js';
3
3
  import { Box } from './Box.js';
4
- import { useOverflow } from '../hooks/useOverflow.js';
4
+ import { useLayoutEffect } from '../hooks/useIsomorphicLayoutEffect.js';
5
5
  import { useSafeContext } from '../hooks/useSafeContext.js';
6
- let OverflowContainerComponent = React.forwardRef((props, ref) => {
6
+ import { isUnitTest } from '../functions/dev.js';
7
+ import { useResizeObserver } from '../hooks/useResizeObserver.js';
8
+ let OverflowContainerMain = React.forwardRef((props, forwardedRef) => {
7
9
  let { itemsCount, children, overflowOrientation, ...rest } = props;
8
10
  let [containerRef, visibleCount] = useOverflow(
9
11
  itemsCount,
10
- false,
11
12
  overflowOrientation,
12
13
  );
13
14
  let overflowContainerContextValue = React.useMemo(
@@ -25,7 +26,7 @@ let OverflowContainerComponent = React.forwardRef((props, ref) => {
25
26
  React.createElement(
26
27
  Box,
27
28
  {
28
- ref: useMergedRefs(ref, containerRef),
29
+ ref: useMergedRefs(forwardedRef, containerRef),
29
30
  ...rest,
30
31
  },
31
32
  children,
@@ -38,12 +39,150 @@ let OverflowContainerOverflowNode = (props) => {
38
39
  let isOverflowing = visibleCount < itemsCount;
39
40
  return isOverflowing ? children : null;
40
41
  };
42
+ let OverflowContainerComponent = React.forwardRef((props, forwardedRef) => {
43
+ let { itemsCount, overflowOrientation = 'horizontal', ...rest } = props;
44
+ let [size, setSize] = React.useState(null);
45
+ let [resizeRef] = useResizeObserver(setSize);
46
+ let ref = useMergedRefs(resizeRef, forwardedRef);
47
+ let key = `${itemsCount}${
48
+ 'vertical' === overflowOrientation ? size?.height : size?.width
49
+ }`;
50
+ return React.createElement(OverflowContainerMain, {
51
+ ...rest,
52
+ key: key,
53
+ ref: ref,
54
+ itemsCount: itemsCount,
55
+ overflowOrientation: overflowOrientation,
56
+ });
57
+ });
41
58
  export const OverflowContainer = Object.assign(OverflowContainerComponent, {
42
59
  OverflowNode: OverflowContainerOverflowNode,
43
60
  useContext: useOverflowContainerContext,
44
61
  });
45
62
  let OverflowContainerContext = React.createContext(void 0);
46
63
  OverflowContainerContext.displayName = 'OverflowContainerContext';
64
+ let useOverflow = (itemsCount, orientation = 'horizontal') => {
65
+ let [guessState, dispatch] = React.useReducer(
66
+ overflowGuessReducer,
67
+ {
68
+ itemsCount,
69
+ },
70
+ overflowGuessReducerInitialState,
71
+ );
72
+ let containerRef = React.useRef(null);
73
+ let isGuessing = React.useRef(false);
74
+ useLayoutEffect(() => {
75
+ let { minGuess, maxGuess, isStabilized, visibleCount } = guessState;
76
+ if (isStabilized) return;
77
+ guessVisibleCount();
78
+ function guessVisibleCount() {
79
+ if (isStabilized || isGuessing.current || isUnitTest) return;
80
+ try {
81
+ isGuessing.current = true;
82
+ if (null == containerRef.current) return;
83
+ let dimension = 'horizontal' === orientation ? 'Width' : 'Height';
84
+ let availableSize = containerRef.current[`offset${dimension}`];
85
+ let requiredSize = containerRef.current[`scroll${dimension}`];
86
+ let isOverflowing = availableSize < requiredSize;
87
+ if (
88
+ 0 === itemsCount ||
89
+ (1 === visibleCount && isOverflowing) ||
90
+ (visibleCount === itemsCount && !isOverflowing) ||
91
+ (maxGuess - minGuess === 1 && visibleCount === minGuess)
92
+ ) {
93
+ dispatch({
94
+ type: 'stabilize',
95
+ });
96
+ return;
97
+ }
98
+ if (maxGuess === visibleCount && !isOverflowing) {
99
+ dispatch({
100
+ type: 'shiftGuessRangeForward',
101
+ });
102
+ return;
103
+ }
104
+ isOverflowing
105
+ ? dispatch({
106
+ type: 'decreaseMaxGuess',
107
+ currentState: guessState,
108
+ })
109
+ : dispatch({
110
+ type: 'increaseMinGuess',
111
+ currentState: guessState,
112
+ });
113
+ } finally {
114
+ isGuessing.current = false;
115
+ }
116
+ }
117
+ }, [guessState, itemsCount, orientation]);
118
+ return [containerRef, guessState.visibleCount];
119
+ };
120
+ let STARTING_MAX_ITEMS_COUNT = 32;
121
+ let overflowGuessReducerInitialState = ({ itemsCount }) => {
122
+ let initialVisibleCount = Math.min(itemsCount, STARTING_MAX_ITEMS_COUNT);
123
+ return isUnitTest
124
+ ? {
125
+ isStabilized: true,
126
+ minGuess: null,
127
+ maxGuess: null,
128
+ itemsCount,
129
+ visibleCount: itemsCount,
130
+ }
131
+ : {
132
+ isStabilized: false,
133
+ minGuess: 0,
134
+ maxGuess: initialVisibleCount,
135
+ itemsCount,
136
+ visibleCount: initialVisibleCount,
137
+ };
138
+ };
139
+ let overflowGuessReducer = (state, action) => {
140
+ let getSafeVisibleCount = ({ visibleCount, itemsCount }) =>
141
+ Math.min(itemsCount, visibleCount);
142
+ switch (action.type) {
143
+ case 'decreaseMaxGuess':
144
+ case 'increaseMinGuess':
145
+ if (state.isStabilized) return state;
146
+ let newMinGuess = state.minGuess;
147
+ let newMaxGuess = state.maxGuess;
148
+ if ('decreaseMaxGuess' === action.type)
149
+ newMaxGuess = action.currentState.visibleCount;
150
+ else newMinGuess = action.currentState.visibleCount;
151
+ let newVisibleCount = Math.floor((newMinGuess + newMaxGuess) / 2);
152
+ return {
153
+ ...state,
154
+ isStabilized: false,
155
+ minGuess: newMinGuess,
156
+ maxGuess: newMaxGuess,
157
+ visibleCount: getSafeVisibleCount({
158
+ visibleCount: newVisibleCount,
159
+ itemsCount: state.itemsCount,
160
+ }),
161
+ };
162
+ case 'shiftGuessRangeForward':
163
+ if (state.isStabilized) return state;
164
+ let doubleOfMaxGuess = 2 * state.maxGuess;
165
+ return {
166
+ ...state,
167
+ isStabilized: false,
168
+ minGuess: state.maxGuess,
169
+ maxGuess: doubleOfMaxGuess,
170
+ visibleCount: getSafeVisibleCount({
171
+ visibleCount: doubleOfMaxGuess,
172
+ itemsCount: state.itemsCount,
173
+ }),
174
+ };
175
+ case 'stabilize':
176
+ return {
177
+ ...state,
178
+ isStabilized: true,
179
+ minGuess: null,
180
+ maxGuess: null,
181
+ };
182
+ default:
183
+ return state;
184
+ }
185
+ };
47
186
  function useOverflowContainerContext() {
48
187
  let overflowContainerContext = useSafeContext(OverflowContainerContext);
49
188
  return overflowContainerContext;
@@ -1,6 +1,5 @@
1
1
  export * from './useEventListener.js';
2
2
  export * from './useMergedRefs.js';
3
- export * from './useOverflow.js';
4
3
  export * from './useResizeObserver.js';
5
4
  export * from './useContainerWidth.js';
6
5
  export * from './useGlobals.js';
@@ -14,4 +13,5 @@ export * from './useId.js';
14
13
  export * from './useControlledState.js';
15
14
  export * from './useSyncExternalStore.js';
16
15
  export * from './useVirtualScroll.js';
16
+ export * from './useInstance.js';
17
17
  export * from './useWarningLogger.js';
@@ -0,0 +1,18 @@
1
+ import * as React from 'react';
2
+ import { useSyncExternalStore } from './useSyncExternalStore.js';
3
+ class Instance {}
4
+ export const useInstance = () => React.useMemo(() => new Instance(), []);
5
+ export const useSynchronizeInstance = (instance, properties) => {
6
+ let synchronize = React.useCallback(() => {
7
+ if (!(instance instanceof Instance)) return () => {};
8
+ Object.assign(instance, properties);
9
+ return () => {
10
+ for (let key in properties) delete instance[key];
11
+ };
12
+ }, [instance, properties]);
13
+ return useSyncExternalStore(
14
+ synchronize,
15
+ () => instance,
16
+ () => instance,
17
+ );
18
+ };
@@ -15,7 +15,7 @@ const _classnames = _interop_require_default._(require('classnames'));
15
15
  const _index = require('../../utils/index.js');
16
16
  const _Button = require('../Buttons/Button.js');
17
17
  const _Anchor = require('../Typography/Anchor.js');
18
- const BreadcrumbsComponent = _react.forwardRef((props, ref) => {
18
+ const BreadcrumbsComponent = _react.forwardRef((props, forwardedRef) => {
19
19
  let {
20
20
  children: childrenProp,
21
21
  currentIndex = _react.Children.count(childrenProp) - 1,
@@ -33,7 +33,7 @@ const BreadcrumbsComponent = _react.forwardRef((props, ref) => {
33
33
  {
34
34
  as: 'nav',
35
35
  className: (0, _classnames.default)('iui-breadcrumbs', className),
36
- ref: ref,
36
+ ref: forwardedRef,
37
37
  'aria-label': 'Breadcrumb',
38
38
  ...rest,
39
39
  },
@@ -46,11 +46,11 @@ const Checkbox = _react.forwardRef((props, ref) => {
46
46
  'iui-checkbox',
47
47
  {
48
48
  'iui-checkbox-visibility': 'eyeball' === variant,
49
- 'iui-loading': isLoading,
50
49
  },
51
50
  className,
52
51
  ),
53
52
  style: style,
53
+ 'data-iui-loading': isLoading ? 'true' : void 0,
54
54
  disabled: disabled || isLoading,
55
55
  type: 'checkbox',
56
56
  ref: refs,
@@ -71,13 +71,11 @@ const Checkbox = _react.forwardRef((props, ref) => {
71
71
  as: 'label',
72
72
  className: (0, _classnames.default)(
73
73
  'iui-checkbox-wrapper',
74
- {
75
- 'iui-disabled': disabled,
76
- [`iui-${status}`]: !!status,
77
- 'iui-loading': isLoading,
78
- },
79
74
  wrapperClassName,
80
75
  ),
76
+ 'data-iui-disabled': disabled ? 'true' : void 0,
77
+ 'data-iui-status': status,
78
+ 'data-iui-loading': isLoading ? 'true' : void 0,
81
79
  ...restWrapperProps,
82
80
  },
83
81
  checkbox,
@@ -33,11 +33,24 @@ export type ComboboxMultipleTypeProps<T> = {
33
33
  * Callback fired when selected value changes.
34
34
  */
35
35
  onChange?: (value: T) => void;
36
+ /**
37
+ * Only applicable when `multiple` is enabled.
38
+ *
39
+ * If `true`, toggling an option will clear the filter.
40
+ * Useful when users would likely want to re-filter after toggling an option.
41
+ *
42
+ * If `false`, the filter will remain as-is after toggling an option.
43
+ * Useful when users would likely want to toggle multiple options from the _same_ filtered results.
44
+ *
45
+ * @default true
46
+ */
47
+ clearFilterOnOptionToggle?: never;
36
48
  } | {
37
49
  multiple: true;
38
50
  value?: T[] | null | undefined;
39
51
  defaultValue?: T[] | null;
40
52
  onChange?: (value: T[], event: MultipleOnChangeProps<T>) => void;
53
+ clearFilterOnOptionToggle?: boolean;
41
54
  };
42
55
  export type ComboBoxProps<T> = {
43
56
  /**
@@ -51,6 +51,7 @@ const ComboBox = _react.forwardRef((props, forwardedRef) => {
51
51
  onHide: onHideProp,
52
52
  id = inputProps?.id ? `iui-${inputProps.id}-cb` : idPrefix,
53
53
  defaultValue,
54
+ clearFilterOnOptionToggle = true,
54
55
  ...rest
55
56
  } = props;
56
57
  let inputRef = _react.useRef(null);
@@ -204,7 +205,6 @@ const ComboBox = _react.forwardRef((props, forwardedRef) => {
204
205
  preventScroll: true,
205
206
  });
206
207
  if (optionsRef.current[__originalIndex]?.disabled) return;
207
- setIsInputDirty(false);
208
208
  if (multiple) {
209
209
  let actionType = isMenuItemSelected(__originalIndex)
210
210
  ? 'removed'
@@ -222,7 +222,10 @@ const ComboBox = _react.forwardRef((props, forwardedRef) => {
222
222
  .filter(Boolean)
223
223
  .join(', '),
224
224
  );
225
- setInputValue('');
225
+ if (clearFilterOnOptionToggle) {
226
+ setInputValue('');
227
+ setIsInputDirty(false);
228
+ }
226
229
  } else {
227
230
  setSelectedIndexes(__originalIndex);
228
231
  hide();
@@ -230,13 +233,14 @@ const ComboBox = _react.forwardRef((props, forwardedRef) => {
230
233
  }
231
234
  },
232
235
  [
233
- selectedChangeHandler,
234
- isMenuItemSelected,
236
+ optionsRef,
235
237
  multiple,
238
+ isMenuItemSelected,
239
+ selectedChangeHandler,
240
+ setSelectedIndexes,
236
241
  onChangeHandler,
237
- optionsRef,
242
+ clearFilterOnOptionToggle,
238
243
  hide,
239
- setSelectedIndexes,
240
244
  ],
241
245
  );
242
246
  let getMenuItem = _react.useCallback(