@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
@@ -17,6 +17,7 @@ export const StepperStep = React.forwardRef((props, forwardedRef) => {
17
17
  trackContentProps,
18
18
  circleProps,
19
19
  nameProps,
20
+ stepContent,
20
21
  ...rest
21
22
  } = props;
22
23
  let isPast = currentStepNumber > index;
@@ -74,7 +75,7 @@ export const StepperStep = React.forwardRef((props, forwardedRef) => {
74
75
  ...circleProps,
75
76
  className: cx('iui-stepper-circle', circleProps?.className),
76
77
  },
77
- index + 1,
78
+ stepContent ? stepContent() : index + 1,
78
79
  ),
79
80
  ),
80
81
  'default' === type &&
@@ -3,6 +3,7 @@ import type { CellProps, TableOptions, Row, TableState } from '../../react-table
3
3
  import type { CommonProps } from '../../utils/index.js';
4
4
  import type { TableFilterValue } from './filters/index.js';
5
5
  export declare const tableResizeStartAction = "tableResizeStart";
6
+ export declare const iuiId: unique symbol;
6
7
  export type TablePaginatorRendererProps = {
7
8
  /**
8
9
  * The zero-based index of the current page.
@@ -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),
@@ -461,51 +478,52 @@ export const Table = (props) => {
461
478
  (index, virtualItem, virtualizer) => {
462
479
  let row = page[index];
463
480
  prepareRow(row);
464
- if (row.subRows.length > 0 || !subComponent)
465
- return React.createElement(TableRowMemoized, {
466
- row: row,
467
- rowProps: rowProps,
468
- isLast: index === page.length - 1,
469
- onRowInViewport: onRowInViewportRef,
470
- onBottomReached: onBottomReachedRef,
471
- intersectionMargin: intersectionMargin,
472
- state: state,
473
- key: row.getRowProps().key,
474
- onClick: onRowClickHandler,
475
- subComponent: subComponent,
476
- isDisabled: !!isRowDisabled?.(row.original),
477
- tableHasSubRows: hasAnySubRows,
478
- tableInstance: instance,
479
- expanderCell: expanderCell,
480
- scrollContainerRef: tableRef.current,
481
- tableRowRef: enableVirtualization ? void 0 : tableRowRef(row),
482
- density: density,
483
- virtualItem: virtualItem,
484
- virtualizer: virtualizer,
485
- });
486
- return React.createElement(
487
- TableExpandableContentMemoized,
488
- {
489
- key: row.getRowProps().key,
490
- virtualItem: virtualItem,
491
- ref: enableVirtualization
492
- ? virtualizer?.measureElement
493
- : tableRowRef(row),
494
- isDisabled: !!isRowDisabled?.(row.original),
495
- },
496
- subComponent(row),
497
- );
481
+ let isRowASubComponent = !!row.original[iuiId] && !!subComponent;
482
+ if (isRowASubComponent)
483
+ return React.createElement(
484
+ TableExpandableContentMemoized,
485
+ {
486
+ key: row.getRowProps().key,
487
+ virtualItem: virtualItem,
488
+ ref: enableVirtualization
489
+ ? virtualizer?.measureElement
490
+ : tableRowRef(row),
491
+ isDisabled: !!isRowDisabled?.(row.original),
492
+ },
493
+ subComponent(row),
494
+ );
495
+ return React.createElement(TableRowMemoized, {
496
+ row: row,
497
+ rowProps: rowProps,
498
+ isLast: index === page.length - 1,
499
+ onRowInViewport: onRowInViewportRef,
500
+ onBottomReached: onBottomReachedRef,
501
+ intersectionMargin: intersectionMargin,
502
+ state: state,
503
+ key: row.getRowProps().key,
504
+ onClick: onRowClickHandler,
505
+ subComponent: subComponent,
506
+ isDisabled: !!isRowDisabled?.(row.original),
507
+ tableHasSubRows: hasAnySubRows,
508
+ tableInstance: instance,
509
+ expanderCell: expanderCell,
510
+ scrollContainerRef: tableRef.current,
511
+ tableRowRef: enableVirtualization ? void 0 : tableRowRef(row),
512
+ density: density,
513
+ virtualItem: virtualItem,
514
+ virtualizer: virtualizer,
515
+ });
498
516
  },
499
517
  [
500
518
  page,
501
519
  prepareRow,
520
+ subComponent,
502
521
  rowProps,
503
522
  onRowInViewportRef,
504
523
  onBottomReachedRef,
505
524
  intersectionMargin,
506
525
  state,
507
526
  onRowClickHandler,
508
- subComponent,
509
527
  isRowDisabled,
510
528
  hasAnySubRows,
511
529
  instance,
@@ -639,11 +657,7 @@ export const Table = (props) => {
639
657
  },
640
658
  bodyProps?.className,
641
659
  ),
642
- style: {
643
- outline: 0,
644
- },
645
660
  }),
646
- tabIndex: -1,
647
661
  'aria-multiselectable':
648
662
  (isSelectable && 'multi' === selectionMode) || void 0,
649
663
  },
@@ -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/esm/index.d.ts CHANGED
@@ -62,6 +62,7 @@ export { ModalContent } from './core/Modal/ModalContent.js';
62
62
  export { ModalButtonBar } from './core/Modal/ModalButtonBar.js';
63
63
  export { NotificationMarker } from './core/NotificationMarker/NotificationMarker.js';
64
64
  export { Overlay } from './core/Overlay/Overlay.js';
65
+ export { Panels as unstable_Panels } from './core/Panels/Panels.js';
65
66
  export { ProgressLinear } from './core/ProgressIndicators/ProgressLinear.js';
66
67
  export { ProgressRadial } from './core/ProgressIndicators/ProgressRadial.js';
67
68
  export { Radio } from './core/Radio/Radio.js';
package/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/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,6 +1,6 @@
1
1
  import * as React from 'react';
2
- import type { CommonProps } from '../props.js';
3
- export type MiddleTextTruncationProps = {
2
+ import type { PolymorphicForwardRefComponent } from '../props.js';
3
+ type MiddleTextTruncationProps = {
4
4
  /**
5
5
  * Text to truncate.
6
6
  */
@@ -14,7 +14,7 @@ export type MiddleTextTruncationProps = {
14
14
  * Custom renderer for the truncated text.
15
15
  */
16
16
  textRenderer?: (truncatedText: string, originalText: string) => React.ReactNode;
17
- } & CommonProps;
17
+ };
18
18
  /**
19
19
  * Truncates text with the ellipsis in the middle,
20
20
  * leaving defined number of chars at the end.
@@ -31,7 +31,5 @@ export type MiddleTextTruncationProps = {
31
31
  * )}
32
32
  * />
33
33
  */
34
- export declare const MiddleTextTruncation: {
35
- (props: MiddleTextTruncationProps): React.JSX.Element;
36
- displayName: string;
37
- };
34
+ export declare const MiddleTextTruncation: PolymorphicForwardRefComponent<"span", MiddleTextTruncationProps>;
35
+ export {};
@@ -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
  let MiddleTextTruncationContent = (props) => {
24
42
  let { text, endCharsCount = 6, textRenderer } = props;
25
43
  let { visibleCount } = OverflowContainer.useContext();
@@ -15,6 +15,7 @@ type OverflowContainerOverflowNodeProps = {
15
15
  children: React.ReactNode;
16
16
  };
17
17
  /**
18
+ * @private
18
19
  * Wrapper over `useOverflow`.
19
20
  *
20
21
  * - Use `OverflowContainer.useContext()` to get overflow related properties.
@@ -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,11 +39,149 @@ 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);
63
+ let useOverflow = (itemsCount, orientation = 'horizontal') => {
64
+ let [guessState, dispatch] = React.useReducer(
65
+ overflowGuessReducer,
66
+ {
67
+ itemsCount,
68
+ },
69
+ overflowGuessReducerInitialState,
70
+ );
71
+ let containerRef = React.useRef(null);
72
+ let isGuessing = React.useRef(false);
73
+ useLayoutEffect(() => {
74
+ let { minGuess, maxGuess, isStabilized, visibleCount } = guessState;
75
+ if (isStabilized) return;
76
+ guessVisibleCount();
77
+ function guessVisibleCount() {
78
+ if (isStabilized || isGuessing.current || isUnitTest) return;
79
+ try {
80
+ isGuessing.current = true;
81
+ if (null == containerRef.current) return;
82
+ let dimension = 'horizontal' === orientation ? 'Width' : 'Height';
83
+ let availableSize = containerRef.current[`offset${dimension}`];
84
+ let requiredSize = containerRef.current[`scroll${dimension}`];
85
+ let isOverflowing = availableSize < requiredSize;
86
+ if (
87
+ 0 === itemsCount ||
88
+ (1 === visibleCount && isOverflowing) ||
89
+ (visibleCount === itemsCount && !isOverflowing) ||
90
+ (maxGuess - minGuess === 1 && visibleCount === minGuess)
91
+ ) {
92
+ dispatch({
93
+ type: 'stabilize',
94
+ });
95
+ return;
96
+ }
97
+ if (maxGuess === visibleCount && !isOverflowing) {
98
+ dispatch({
99
+ type: 'shiftGuessRangeForward',
100
+ });
101
+ return;
102
+ }
103
+ isOverflowing
104
+ ? dispatch({
105
+ type: 'decreaseMaxGuess',
106
+ currentState: guessState,
107
+ })
108
+ : dispatch({
109
+ type: 'increaseMinGuess',
110
+ currentState: guessState,
111
+ });
112
+ } finally {
113
+ isGuessing.current = false;
114
+ }
115
+ }
116
+ }, [guessState, itemsCount, orientation]);
117
+ return [containerRef, guessState.visibleCount];
118
+ };
119
+ let STARTING_MAX_ITEMS_COUNT = 32;
120
+ let overflowGuessReducerInitialState = ({ itemsCount }) => {
121
+ let initialVisibleCount = Math.min(itemsCount, STARTING_MAX_ITEMS_COUNT);
122
+ return isUnitTest
123
+ ? {
124
+ isStabilized: true,
125
+ minGuess: null,
126
+ maxGuess: null,
127
+ itemsCount,
128
+ visibleCount: itemsCount,
129
+ }
130
+ : {
131
+ isStabilized: false,
132
+ minGuess: 0,
133
+ maxGuess: initialVisibleCount,
134
+ itemsCount,
135
+ visibleCount: initialVisibleCount,
136
+ };
137
+ };
138
+ let overflowGuessReducer = (state, action) => {
139
+ let getSafeVisibleCount = ({ visibleCount, itemsCount }) =>
140
+ Math.min(itemsCount, visibleCount);
141
+ switch (action.type) {
142
+ case 'decreaseMaxGuess':
143
+ case 'increaseMinGuess':
144
+ if (state.isStabilized) return state;
145
+ let newMinGuess = state.minGuess;
146
+ let newMaxGuess = state.maxGuess;
147
+ if ('decreaseMaxGuess' === action.type)
148
+ newMaxGuess = action.currentState.visibleCount;
149
+ else newMinGuess = action.currentState.visibleCount;
150
+ let newVisibleCount = Math.floor((newMinGuess + newMaxGuess) / 2);
151
+ return {
152
+ ...state,
153
+ isStabilized: false,
154
+ minGuess: newMinGuess,
155
+ maxGuess: newMaxGuess,
156
+ visibleCount: getSafeVisibleCount({
157
+ visibleCount: newVisibleCount,
158
+ itemsCount: state.itemsCount,
159
+ }),
160
+ };
161
+ case 'shiftGuessRangeForward':
162
+ if (state.isStabilized) return state;
163
+ let doubleOfMaxGuess = 2 * state.maxGuess;
164
+ return {
165
+ ...state,
166
+ isStabilized: false,
167
+ minGuess: state.maxGuess,
168
+ maxGuess: doubleOfMaxGuess,
169
+ visibleCount: getSafeVisibleCount({
170
+ visibleCount: doubleOfMaxGuess,
171
+ itemsCount: state.itemsCount,
172
+ }),
173
+ };
174
+ case 'stabilize':
175
+ return {
176
+ ...state,
177
+ isStabilized: true,
178
+ minGuess: null,
179
+ maxGuess: null,
180
+ };
181
+ default:
182
+ return state;
183
+ }
184
+ };
46
185
  function useOverflowContainerContext() {
47
186
  let overflowContainerContext = useSafeContext(OverflowContainerContext);
48
187
  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';
@@ -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,22 @@
1
+ declare class Instance {
2
+ }
3
+ export declare const useInstance: () => Instance;
4
+ /**
5
+ * Synchronizes the instance with the provided properties.
6
+ *
7
+ * @param instance Instance created by `useInstance`.
8
+ * @param properties Memoized object containing properties to be synchronized.
9
+ *
10
+ * @example
11
+ * const instance = useInstance();
12
+ *
13
+ * const properties = React.useMemo(() => ({
14
+ * show: () => console.log('show'),
15
+ * }), []);
16
+ *
17
+ * useSynchronizeInstance(instance, properties);
18
+ *
19
+ * instance.show(); // logs 'show'
20
+ */
21
+ export declare const useSynchronizeInstance: <T>(instance: T, properties: T) => T;
22
+ export {};
@@ -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
+ };