@itwin/itwinui-react 1.29.3 → 1.30.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.30.0](https://www.github.com/iTwin/iTwinUI-react/compare/v1.29.3...v1.30.0) (2022-02-08)
4
+
5
+ ### What's new
6
+
7
+ * **ButtonGroup:** Add new `overflowPlacement` prop to allow placing `overflowButton` at start ([#527](https://www.github.com/iTwin/iTwinUI-react/issues/527)) ([442ba87](https://www.github.com/iTwin/iTwinUI-react/commit/442ba87f8ba45240f07e8cacd9463cd95e5ec1e7))
8
+ * **Table:** Added new `enableVirtualization` prop for row virtualization ([#236](https://www.github.com/iTwin/iTwinUI-react/issues/236)) ([178bb75](https://www.github.com/iTwin/iTwinUI-react/commit/178bb756bf14fd0f780433ca33ea2a151d4c1436))
9
+
10
+ ### Fixes
11
+
12
+ * **Popover:** Prevent from adding 5px padding ([#534](https://www.github.com/iTwin/iTwinUI-react/issues/534)) ([fea4782](https://www.github.com/iTwin/iTwinUI-react/commit/fea47823ae730b3e9b2bab2e2ec75b1ba8101b27))
13
+ * **Slider:** Handle pointermove events only when thumb is active ([#528](https://www.github.com/iTwin/iTwinUI-react/issues/528)) ([e41b53b](https://www.github.com/iTwin/iTwinUI-react/commit/e41b53b7ccda245f298bb33c6984f2431d917932))
14
+
3
15
  ### [1.29.3](https://www.github.com/iTwin/iTwinUI-react/compare/v1.29.2...v1.29.3) (2022-01-26)
4
16
 
5
17
  ### Fixes
@@ -6,13 +6,20 @@ export declare type ButtonGroupProps = {
6
6
  */
7
7
  children: React.ReactNode;
8
8
  /**
9
- * If specified, this prop will be used to show a custom button as the last button
10
- * when overflow happens, i.e. when there is not enough space to fit all the buttons.
9
+ * If specified, this prop will be used to show a custom button when overflow happens,
10
+ * i.e. when there is not enough space to fit all the buttons.
11
11
  *
12
12
  * Expects a function that takes the index of the first button that is overflowing (i.e. hidden)
13
13
  * and returns the `ReactNode` to render.
14
+ *
15
+ * The placement of this button can be controlled using the `overflowPlacement` prop.
14
16
  */
15
17
  overflowButton?: (firstOverflowingIndex: number) => React.ReactNode;
18
+ /**
19
+ * If `overflowButton` is specified, should it placed at the start or the end?
20
+ * @default 'end'
21
+ */
22
+ overflowPlacement?: 'start' | 'end';
16
23
  } & React.ComponentPropsWithRef<'div'>;
17
24
  /**
18
25
  * Group buttons together for common actions.
@@ -42,5 +49,5 @@ export declare type ButtonGroupProps = {
42
49
  * {buttons}
43
50
  * </ButtonGroup>
44
51
  */
45
- export declare const ButtonGroup: React.ForwardRefExoticComponent<Pick<ButtonGroupProps, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "overflowButton"> & React.RefAttributes<HTMLDivElement>>;
52
+ export declare const ButtonGroup: React.ForwardRefExoticComponent<Pick<ButtonGroupProps, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "overflowButton" | "overflowPlacement"> & React.RefAttributes<HTMLDivElement>>;
46
53
  export default ButtonGroup;
@@ -63,13 +63,14 @@ require("@itwin/itwinui-css/css/button.css");
63
63
  * </ButtonGroup>
64
64
  */
65
65
  exports.ButtonGroup = react_1.default.forwardRef(function (props, ref) {
66
- var children = props.children, className = props.className, style = props.style, overflowButton = props.overflowButton, rest = __rest(props, ["children", "className", "style", "overflowButton"]);
66
+ var children = props.children, className = props.className, style = props.style, overflowButton = props.overflowButton, _a = props.overflowPlacement, overflowPlacement = _a === void 0 ? 'end' : _a, rest = __rest(props, ["children", "className", "style", "overflowButton", "overflowPlacement"]);
67
67
  var items = react_1.default.useMemo(function () { var _a; return (_a = react_1.default.Children.map(children, function (child) { return react_1.default.createElement("div", null, child); })) !== null && _a !== void 0 ? _a : []; }, [children]);
68
68
  (0, utils_1.useTheme)();
69
- var _a = (0, utils_1.useOverflow)(items, !overflowButton), overflowRef = _a[0], visibleCount = _a[1];
69
+ var _b = (0, utils_1.useOverflow)(items, !overflowButton), overflowRef = _b[0], visibleCount = _b[1];
70
70
  var refs = (0, utils_1.useMergedRefs)(overflowRef, ref);
71
71
  return (react_1.default.createElement("div", __assign({ className: (0, classnames_1.default)('iui-button-group', className), style: __assign(__assign({}, (!!overflowButton && { width: '100%' })), style), ref: refs }, rest), !!overflowButton && visibleCount < items.length ? (react_1.default.createElement(react_1.default.Fragment, null,
72
+ overflowButton && overflowPlacement === 'start' && (react_1.default.createElement("div", null, overflowButton(visibleCount))),
72
73
  items.slice(0, visibleCount - 1),
73
- overflowButton(visibleCount))) : (items)));
74
+ overflowButton && overflowPlacement === 'end' && (react_1.default.createElement("div", null, overflowButton(visibleCount))))) : (items)));
74
75
  });
75
76
  exports.default = exports.ButtonGroup;
@@ -138,22 +138,31 @@ exports.ColorBuilder = react_1.default.forwardRef(function (props, ref) {
138
138
  }
139
139
  }, [colorDotActive, updateColorDot]);
140
140
  var handleSquarePointerUp = react_1.default.useCallback(function (event) {
141
+ if (!colorDotActive) {
142
+ return;
143
+ }
141
144
  updateSquareValue(event, 'onChange');
142
145
  setColorDotActive(false);
143
146
  event.preventDefault();
144
147
  event.stopPropagation();
145
- }, [updateSquareValue]);
148
+ }, [colorDotActive, updateSquareValue]);
146
149
  (0, utils_1.useEventListener)('pointerup', handleSquarePointerUp, (_k = builderRef.current) === null || _k === void 0 ? void 0 : _k.ownerDocument);
147
150
  var handleSquarePointerMove = react_1.default.useCallback(function (event) {
151
+ if (!colorDotActive) {
152
+ return;
153
+ }
148
154
  event.preventDefault();
149
155
  event.stopPropagation();
150
156
  updateSquareValue(event, 'onUpdate');
151
- }, [updateSquareValue]);
157
+ }, [colorDotActive, updateSquareValue]);
152
158
  (0, utils_1.useEventListener)('pointermove', handleSquarePointerMove, (_l = builderRef.current) === null || _l === void 0 ? void 0 : _l.ownerDocument);
153
159
  var handleSquarePointerLeave = react_1.default.useCallback(function (event) {
160
+ if (!colorDotActive) {
161
+ return;
162
+ }
154
163
  updateSquareValue(event, 'onChange');
155
164
  setColorDotActive(false);
156
- }, [updateSquareValue]);
165
+ }, [colorDotActive, updateSquareValue]);
157
166
  (0, utils_1.useEventListener)('pointerleave', handleSquarePointerLeave, (_m = builderRef.current) === null || _m === void 0 ? void 0 : _m.ownerDocument);
158
167
  var keysPressed = react_1.default.useRef({}); // keep track of which keys are currently pressed
159
168
  // Arrow key navigation for color dot
@@ -164,10 +164,13 @@ exports.Slider = react_1.default.forwardRef(function (props, ref) {
164
164
  onChange,
165
165
  ]);
166
166
  var handlePointerMove = react_1.default.useCallback(function (event) {
167
+ if (activeThumbIndex === undefined) {
168
+ return;
169
+ }
167
170
  event.preventDefault();
168
171
  event.stopPropagation();
169
172
  updateThumbValue(event, 'onUpdate');
170
- }, [updateThumbValue]);
173
+ }, [activeThumbIndex, updateThumbValue]);
171
174
  // function called by Thumb keyboard processing
172
175
  var onThumbValueChanged = react_1.default.useCallback(function (index, value) {
173
176
  if (currentValues[index] === value) {
@@ -182,11 +185,14 @@ exports.Slider = react_1.default.forwardRef(function (props, ref) {
182
185
  setActiveThumbIndex(index);
183
186
  }, []);
184
187
  var handlePointerUp = react_1.default.useCallback(function (event) {
188
+ if (activeThumbIndex === undefined) {
189
+ return;
190
+ }
185
191
  updateThumbValue(event, 'onChange');
186
192
  setActiveThumbIndex(undefined);
187
193
  event.preventDefault();
188
194
  event.stopPropagation();
189
- }, [updateThumbValue]);
195
+ }, [activeThumbIndex, updateThumbValue]);
190
196
  var handlePointerDownOnSlider = react_1.default.useCallback(function (event) {
191
197
  if (containerRef.current) {
192
198
  var percent = getPercentageOfRectangle(containerRef.current.getBoundingClientRect(), event.clientX);
@@ -56,6 +56,7 @@ export declare type TableProps<T extends Record<string, unknown> = Record<string
56
56
  isSelectable?: boolean;
57
57
  /**
58
58
  * Handler for rows selection. Must be memoized.
59
+ * This is triggered only by user initiated actions (i.e. data change will not call it).
59
60
  */
60
61
  onSelect?: (selectedData: T[] | undefined, tableState?: TableState<T>) => void;
61
62
  /**
@@ -160,6 +161,15 @@ export declare type TableProps<T extends Record<string, unknown> = Record<string
160
161
  * @default 'default'
161
162
  */
162
163
  styleType?: 'default' | 'zebra-rows';
164
+ /**
165
+ * Virtualization is used for the scrollable table body.
166
+ * Height on the table is required for virtualization to work.
167
+ * @example
168
+ * <Table enableVirtualization style={{height: 400}} {...} />
169
+ * @default false
170
+ * @beta
171
+ */
172
+ enableVirtualization?: boolean;
163
173
  } & Omit<CommonProps, 'title'>;
164
174
  /**
165
175
  * Table based on [react-table](https://react-table.tanstack.com/docs/api/overview).
@@ -46,6 +46,7 @@ var hooks_1 = require("./hooks");
46
46
  var actionHandlers_1 = require("./actionHandlers");
47
47
  var selectHandler_1 = require("./actionHandlers/selectHandler");
48
48
  var resizeHandler_1 = require("./actionHandlers/resizeHandler");
49
+ var VirtualScroll_1 = __importDefault(require("../utils/components/VirtualScroll"));
49
50
  var singleRowSelectedAction = 'singleRowSelected';
50
51
  var tableResizeStartAction = 'tableResizeStart';
51
52
  var tableResizeEndAction = 'tableResizeEnd';
@@ -93,9 +94,9 @@ var tableResizeEndAction = 'tableResizeEnd';
93
94
  */
94
95
  var Table = function (props) {
95
96
  var _a;
96
- var data = props.data, columns = props.columns, _b = props.isLoading, isLoading = _b === void 0 ? false : _b, emptyTableContent = props.emptyTableContent, className = props.className, style = props.style, id = props.id, _c = props.isSelectable, isSelectable = _c === void 0 ? false : _c, onSelect = props.onSelect, onRowClick = props.onRowClick, _d = props.isSortable, isSortable = _d === void 0 ? false : _d, onSort = props.onSort, stateReducer = props.stateReducer, onBottomReached = props.onBottomReached, onRowInViewport = props.onRowInViewport, _e = props.intersectionMargin, intersectionMargin = _e === void 0 ? 300 : _e, subComponent = props.subComponent, onExpand = props.onExpand, onFilter = props.onFilter, emptyFilteredTableContent = props.emptyFilteredTableContent, filterFunctions = props.filterTypes, expanderCell = props.expanderCell, isRowDisabled = props.isRowDisabled, rowProps = props.rowProps, _f = props.density, density = _f === void 0 ? 'default' : _f, _g = props.selectSubRows, selectSubRows = _g === void 0 ? true : _g, getSubRows = props.getSubRows, _h = props.selectRowOnClick, selectRowOnClick = _h === void 0 ? true : _h, paginatorRenderer = props.paginatorRenderer, _j = props.pageSize, pageSize = _j === void 0 ? 25 : _j, _k = props.isResizable, isResizable = _k === void 0 ? false : _k, _l = props.styleType, styleType = _l === void 0 ? 'default' : _l, rest = __rest(props, ["data", "columns", "isLoading", "emptyTableContent", "className", "style", "id", "isSelectable", "onSelect", "onRowClick", "isSortable", "onSort", "stateReducer", "onBottomReached", "onRowInViewport", "intersectionMargin", "subComponent", "onExpand", "onFilter", "emptyFilteredTableContent", "filterTypes", "expanderCell", "isRowDisabled", "rowProps", "density", "selectSubRows", "getSubRows", "selectRowOnClick", "paginatorRenderer", "pageSize", "isResizable", "styleType"]);
97
+ var data = props.data, columns = props.columns, _b = props.isLoading, isLoading = _b === void 0 ? false : _b, emptyTableContent = props.emptyTableContent, className = props.className, style = props.style, id = props.id, _c = props.isSelectable, isSelectable = _c === void 0 ? false : _c, onSelect = props.onSelect, onRowClick = props.onRowClick, _d = props.isSortable, isSortable = _d === void 0 ? false : _d, onSort = props.onSort, stateReducer = props.stateReducer, onBottomReached = props.onBottomReached, onRowInViewport = props.onRowInViewport, _e = props.intersectionMargin, intersectionMargin = _e === void 0 ? 300 : _e, subComponent = props.subComponent, onExpand = props.onExpand, onFilter = props.onFilter, emptyFilteredTableContent = props.emptyFilteredTableContent, filterFunctions = props.filterTypes, expanderCell = props.expanderCell, isRowDisabled = props.isRowDisabled, rowProps = props.rowProps, _f = props.density, density = _f === void 0 ? 'default' : _f, _g = props.selectSubRows, selectSubRows = _g === void 0 ? true : _g, getSubRows = props.getSubRows, _h = props.selectRowOnClick, selectRowOnClick = _h === void 0 ? true : _h, paginatorRenderer = props.paginatorRenderer, _j = props.pageSize, pageSize = _j === void 0 ? 25 : _j, _k = props.isResizable, isResizable = _k === void 0 ? false : _k, _l = props.styleType, styleType = _l === void 0 ? 'default' : _l, _m = props.enableVirtualization, enableVirtualization = _m === void 0 ? false : _m, rest = __rest(props, ["data", "columns", "isLoading", "emptyTableContent", "className", "style", "id", "isSelectable", "onSelect", "onRowClick", "isSortable", "onSort", "stateReducer", "onBottomReached", "onRowInViewport", "intersectionMargin", "subComponent", "onExpand", "onFilter", "emptyFilteredTableContent", "filterTypes", "expanderCell", "isRowDisabled", "rowProps", "density", "selectSubRows", "getSubRows", "selectRowOnClick", "paginatorRenderer", "pageSize", "isResizable", "styleType", "enableVirtualization"]);
97
98
  (0, utils_1.useTheme)();
98
- var _m = react_1.default.useState(), ownerDocument = _m[0], setOwnerDocument = _m[1];
99
+ var _o = react_1.default.useState(), ownerDocument = _o[0], setOwnerDocument = _o[1];
99
100
  var defaultColumn = react_1.default.useMemo(function () { return ({
100
101
  maxWidth: 0,
101
102
  minWidth: 0,
@@ -233,6 +234,23 @@ var Table = function (props) {
233
234
  });
234
235
  var headerRef = react_1.default.useRef(null);
235
236
  var bodyRef = react_1.default.useRef(null);
237
+ var getPreparedRow = react_1.default.useCallback(function (row) {
238
+ prepareRow(row);
239
+ return (react_1.default.createElement(TableRowMemoized_1.TableRowMemoized, { row: row, rowProps: rowProps, isLast: row.index === data.length - 1, onRowInViewport: onRowInViewportRef, onBottomReached: onBottomReachedRef, intersectionMargin: intersectionMargin, state: state, key: row.getRowProps().key, onClick: onRowClickHandler, subComponent: subComponent, isDisabled: !!(isRowDisabled === null || isRowDisabled === void 0 ? void 0 : isRowDisabled(row.original)), tableHasSubRows: hasAnySubRows, tableInstance: instance, expanderCell: expanderCell }));
240
+ }, [
241
+ data.length,
242
+ expanderCell,
243
+ hasAnySubRows,
244
+ instance,
245
+ intersectionMargin,
246
+ isRowDisabled,
247
+ onRowClickHandler,
248
+ prepareRow,
249
+ rowProps,
250
+ state,
251
+ subComponent,
252
+ ]);
253
+ var virtualizedItemRenderer = react_1.default.useCallback(function (index) { return getPreparedRow(page[index]); }, [getPreparedRow, page]);
236
254
  return (react_1.default.createElement(react_1.default.Fragment, null,
237
255
  react_1.default.createElement("div", __assign({ ref: function (element) {
238
256
  setOwnerDocument(element === null || element === void 0 ? void 0 : element.ownerDocument);
@@ -269,16 +287,13 @@ var Table = function (props) {
269
287
  className: (0, classnames_1.default)('iui-table-body', {
270
288
  'iui-zebra-striping': styleType === 'zebra-rows',
271
289
  }),
290
+ style: { outline: 0 },
272
291
  }), { ref: bodyRef, onScroll: function () {
273
292
  if (headerRef.current && bodyRef.current) {
274
293
  headerRef.current.scrollLeft = bodyRef.current.scrollLeft;
275
294
  }
276
- } }),
277
- data.length !== 0 &&
278
- page.map(function (row) {
279
- prepareRow(row);
280
- return (react_1.default.createElement(TableRowMemoized_1.TableRowMemoized, { row: row, rowProps: rowProps, isLast: row.index === data.length - 1, onRowInViewport: onRowInViewportRef, onBottomReached: onBottomReachedRef, intersectionMargin: intersectionMargin, state: state, key: row.getRowProps().key, onClick: onRowClickHandler, subComponent: subComponent, isDisabled: !!(isRowDisabled === null || isRowDisabled === void 0 ? void 0 : isRowDisabled(row.original)), tableHasSubRows: hasAnySubRows, tableInstance: instance, expanderCell: expanderCell }));
281
- }),
295
+ }, tabIndex: -1 }),
296
+ data.length !== 0 && (react_1.default.createElement(react_1.default.Fragment, null, enableVirtualization ? (react_1.default.createElement(VirtualScroll_1.default, { itemsLength: page.length, itemRenderer: virtualizedItemRenderer })) : (page.map(function (row) { return getPreparedRow(row); })))),
282
297
  isLoading && data.length === 0 && (react_1.default.createElement("div", { className: 'iui-table-empty' },
283
298
  react_1.default.createElement(ProgressIndicators_1.ProgressRadial, { indeterminate: true }))),
284
299
  isLoading && data.length !== 0 && (react_1.default.createElement("div", { className: 'iui-row' },
@@ -63,28 +63,28 @@ var Tile = function (props) {
63
63
  var showMenu = react_1.default.useCallback(function () { return setIsMenuVisible(true); }, []);
64
64
  var hideMenu = react_1.default.useCallback(function () { return setIsMenuVisible(false); }, []);
65
65
  return (react_1.default.createElement("div", __assign({ className: (0, classnames_1.default)('iui-tile', { 'iui-folder': variant === 'folder' }, { 'iui-new': isNew }, { 'iui-selected': isSelected }, className) }, rest),
66
- thumbnail && (react_1.default.createElement("div", { className: 'iui-thumbnail' },
67
- typeof thumbnail === 'string' ? (react_1.default.createElement("div", { className: 'iui-picture', style: { backgroundImage: "url(" + thumbnail + ")" } })) : thumbnail && thumbnail.type === 'img' ? (react_1.default.cloneElement(thumbnail, {
68
- className: 'iui-picture',
66
+ thumbnail && (react_1.default.createElement("div", { className: 'iui-tile-thumbnail' },
67
+ typeof thumbnail === 'string' ? (react_1.default.createElement("div", { className: 'iui-tile-thumbnail-picture', style: { backgroundImage: "url(" + thumbnail + ")" } })) : thumbnail && thumbnail.type === 'img' ? (react_1.default.cloneElement(thumbnail, {
68
+ className: 'iui-tile-thumbnail-picture',
69
69
  })) : react_1.default.isValidElement(thumbnail) ? (react_1.default.cloneElement(thumbnail, {
70
70
  className: (0, classnames_1.default)('iui-thumbnail-icon', thumbnail.props.className),
71
71
  })) : (thumbnail),
72
72
  leftIcon &&
73
73
  react_1.default.cloneElement(leftIcon, {
74
- className: 'iui-small iui-type-indicator',
74
+ className: 'iui-small iui-tile-thumbnail-type-indicator',
75
75
  }),
76
76
  rightIcon &&
77
77
  react_1.default.cloneElement(rightIcon, {
78
- className: 'iui-small iui-quick-action',
78
+ className: 'iui-small iui-tile-thumbnail-quick-action',
79
79
  }),
80
- badge && react_1.default.createElement("div", { className: 'iui-badge-container' }, badge))),
81
- react_1.default.createElement("div", { className: 'iui-content' },
82
- react_1.default.createElement("div", { className: 'iui-name' },
80
+ badge && (react_1.default.createElement("div", { className: 'iui-tile-thumbnail-badge-container' }, badge)))),
81
+ react_1.default.createElement("div", { className: 'iui-tile-content' },
82
+ react_1.default.createElement("div", { className: 'iui-tile-name' },
83
83
  isSelected && (react_1.default.createElement(Checkmark_1.default, { className: (0, classnames_1.default)('iui-tile-status-icon', 'iui-informational'), "aria-hidden": true })),
84
84
  isNew && (react_1.default.createElement(New_1.default, { className: (0, classnames_1.default)('iui-tile-status-icon', 'iui-positive'), "aria-hidden": true })),
85
- react_1.default.createElement("span", { className: 'iui-name-label' }, name)),
86
- description != undefined && (react_1.default.createElement("div", { className: 'iui-description' }, description)),
87
- metadata != undefined && (react_1.default.createElement("div", { className: 'iui-metadata' }, metadata)),
85
+ react_1.default.createElement("span", { className: 'iui-tile-name-label' }, name)),
86
+ description != undefined && (react_1.default.createElement("div", { className: 'iui-tile-description' }, description)),
87
+ metadata != undefined && (react_1.default.createElement("div", { className: 'iui-tile-metadata' }, metadata)),
88
88
  moreOptions && (react_1.default.createElement(DropdownMenu_1.DropdownMenu, { onShow: showMenu, onHide: hideMenu, menuItems: function (close) {
89
89
  return moreOptions.map(function (option) {
90
90
  return react_1.default.cloneElement(option, {
@@ -97,16 +97,12 @@ var Tile = function (props) {
97
97
  });
98
98
  });
99
99
  } },
100
- react_1.default.createElement(Buttons_1.IconButton, { styleType: 'borderless', size: 'small', className: (0, classnames_1.default)('iui-more-options', {
100
+ react_1.default.createElement(Buttons_1.IconButton, { styleType: 'borderless', size: 'small', className: (0, classnames_1.default)('iui-tile-more-options', {
101
101
  'iui-visible': isMenuVisible,
102
102
  }), "aria-label": 'More options' },
103
103
  react_1.default.createElement(More_1.default, null)))),
104
104
  children),
105
- buttons && (react_1.default.createElement("div", { className: 'iui-tile-buttons' }, buttons.map(function (button) {
106
- return react_1.default.cloneElement(button, {
107
- className: (0, classnames_1.default)('iui-tile-button', button.props.className),
108
- });
109
- })))));
105
+ buttons && react_1.default.createElement("div", { className: 'iui-tile-buttons' }, buttons)));
110
106
  };
111
107
  exports.Tile = Tile;
112
108
  exports.default = exports.Tile;
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
  import { TippyProps } from '@tippyjs/react';
3
- import { Placement, Instance } from 'tippy.js';
3
+ import type { Placement, Instance } from 'tippy.js';
4
4
  export declare type PopoverInstance = Instance;
5
+ import '@itwin/itwinui-css/css/popover.css';
5
6
  export declare type PopoverProps = {
6
7
  /**
7
8
  * Controlled flag for whether the popover is visible.
@@ -29,15 +29,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
30
  exports.hideOnEscOrTab = exports.Popover = void 0;
31
31
  var react_1 = __importDefault(require("react"));
32
+ var classnames_1 = __importDefault(require("classnames"));
32
33
  var react_2 = __importDefault(require("@tippyjs/react"));
33
34
  var useMergedRefs_1 = require("../hooks/useMergedRefs");
35
+ require("@itwin/itwinui-css/css/popover.css");
34
36
  /**
35
37
  * Wrapper around [tippy.js](https://atomiks.github.io/tippyjs)
36
38
  * with pre-configured props and plugins (e.g. lazy mounting, focus, etc).
37
39
  * @private
38
40
  */
39
41
  exports.Popover = react_1.default.forwardRef(function (props, ref) {
40
- var _a = react_1.default.useState(false), mounted = _a[0], setMounted = _a[1];
42
+ var _a;
43
+ var _b = react_1.default.useState(false), mounted = _b[0], setMounted = _b[1];
41
44
  var tippyRef = react_1.default.useRef(null);
42
45
  var refs = (0, useMergedRefs_1.useMergedRefs)(tippyRef, ref);
43
46
  // Plugin to allow lazy mounting. See https://github.com/atomiks/tippyjs-react#lazy-mounting
@@ -56,14 +59,14 @@ exports.Popover = react_1.default.forwardRef(function (props, ref) {
56
59
  },
57
60
  }); },
58
61
  };
59
- var computedProps = __assign(__assign({ allowHTML: true, animation: false, appendTo: 'parent', arrow: false, duration: 0, interactive: true, popperOptions: {
60
- strategy: 'fixed',
61
- modifiers: [{ name: 'flip' }],
62
- }, role: undefined, offset: [0, 0], maxWidth: '' }, props), { plugins: __spreadArray([
62
+ var computedProps = __assign(__assign({ allowHTML: true, animation: false, appendTo: 'parent', arrow: false, duration: 0, interactive: true, role: undefined, offset: [0, 0], maxWidth: '' }, props), { className: (0, classnames_1.default)('iui-popover', props.className), plugins: __spreadArray([
63
63
  lazyLoad,
64
64
  removeTabIndex,
65
65
  exports.hideOnEscOrTab
66
- ], (props.plugins || []), true) });
66
+ ], (props.plugins || []), true), popperOptions: __assign(__assign({ strategy: 'fixed' }, props.popperOptions), { modifiers: __spreadArray([
67
+ { name: 'flip' },
68
+ { name: 'preventOverflow', options: { padding: 0 } }
69
+ ], (((_a = props.popperOptions) === null || _a === void 0 ? void 0 : _a.modifiers) || []), true) }) });
67
70
  if (props.render) {
68
71
  var render_1 = props.render;
69
72
  computedProps.render = function () {
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ export declare type VirtualScrollProps = {
3
+ /**
4
+ * Length of the items to virtualize.
5
+ */
6
+ itemsLength: number;
7
+ /**
8
+ * Single item render function, which gives index of the item (0 based) in the data array
9
+ * and expects to get the JSX of that element to render.
10
+ * Recommended to memoize the reference of the function.
11
+ */
12
+ itemRenderer: (index: number) => JSX.Element;
13
+ /**
14
+ * Number of items to be rendered at the start and the end.
15
+ * Not recommended to go lower than the visible items in viewport.
16
+ * @default 10
17
+ */
18
+ bufferSize?: number;
19
+ } & React.ComponentPropsWithRef<'div'>;
20
+ /**
21
+ * `VirtualScroll` component is used to render a huge amount of items in the DOM. It renders only the ones which are visible
22
+ * and the amount provided through `bufferSize` prop at the start and the end. Can be used inside other components like `Table`.
23
+ *
24
+ * It has two wrapper elements, so DOM will be changed. One is used for setting full expected height in the scrollable container
25
+ * and other is for transformation (translateY) to show the correct part of the list.
26
+ *
27
+ * Currently it works only with the direct vertically scrollable parent element. It does not work with body scroll.
28
+ * It supports only static (same) height rows virtualization. Expect some issues, if list consists of different height elements.
29
+ * @example
30
+ * const itemRenderer = React.useCallback(() => (
31
+ * <div key={index}>
32
+ * This is my item #{index}
33
+ * </div>
34
+ * ), [])
35
+ * <VirtualScroll
36
+ * itemsLength={1000}
37
+ * itemRenderer={itemRenderer}
38
+ * />
39
+ * @private
40
+ */
41
+ export declare const VirtualScroll: React.ForwardRefExoticComponent<Pick<VirtualScrollProps, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "itemsLength" | "itemRenderer" | "bufferSize"> & React.RefAttributes<HTMLDivElement>>;
42
+ export default VirtualScroll;
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __assign = (this && this.__assign) || function () {
3
+ __assign = Object.assign || function(t) {
4
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
5
+ s = arguments[i];
6
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
7
+ t[p] = s[p];
8
+ }
9
+ return t;
10
+ };
11
+ return __assign.apply(this, arguments);
12
+ };
13
+ var __rest = (this && this.__rest) || function (s, e) {
14
+ var t = {};
15
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
16
+ t[p] = s[p];
17
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
18
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
19
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
20
+ t[p[i]] = s[p[i]];
21
+ }
22
+ return t;
23
+ };
24
+ var __importDefault = (this && this.__importDefault) || function (mod) {
25
+ return (mod && mod.__esModule) ? mod : { "default": mod };
26
+ };
27
+ Object.defineProperty(exports, "__esModule", { value: true });
28
+ exports.VirtualScroll = void 0;
29
+ /*---------------------------------------------------------------------------------------------
30
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
31
+ * See LICENSE.md in the project root for license terms and full copyright notice.
32
+ *--------------------------------------------------------------------------------------------*/
33
+ var react_1 = __importDefault(require("react"));
34
+ var useResizeObserver_1 = require("../hooks/useResizeObserver");
35
+ var getScrollableParent = function (element, ownerDocument) {
36
+ if (ownerDocument === void 0) { ownerDocument = document; }
37
+ if (!element || element === ownerDocument.body) {
38
+ return ownerDocument.body;
39
+ }
40
+ return isElementScrollable(element)
41
+ ? element
42
+ : getScrollableParent(element.parentElement, ownerDocument);
43
+ };
44
+ var isElementScrollable = function (element) {
45
+ return /(auto|scroll|overlay)/.test(getElementStyle(element, 'overflow') +
46
+ getElementStyle(element, 'overflow-y'));
47
+ };
48
+ var getElementStyle = function (element, prop) {
49
+ return getComputedStyle(element, null).getPropertyValue(prop);
50
+ };
51
+ var getElementHeight = function (element) {
52
+ var _a;
53
+ return (_a = element === null || element === void 0 ? void 0 : element.getBoundingClientRect().height) !== null && _a !== void 0 ? _a : 0;
54
+ };
55
+ var getNumberOfNodesInHeight = function (childHeight, totalHeight) {
56
+ if (!childHeight) {
57
+ return 0;
58
+ }
59
+ return Math.floor(totalHeight / childHeight);
60
+ };
61
+ var getTranslateValue = function (childHeight, startIndex) {
62
+ return childHeight * startIndex;
63
+ };
64
+ var getVisibleNodeCount = function (childHeight, startIndex, childrenLength, scrollContainer) {
65
+ return Math.min(childrenLength - startIndex, getNumberOfNodesInHeight(childHeight, getElementHeight(scrollContainer)));
66
+ };
67
+ /**
68
+ * `VirtualScroll` component is used to render a huge amount of items in the DOM. It renders only the ones which are visible
69
+ * and the amount provided through `bufferSize` prop at the start and the end. Can be used inside other components like `Table`.
70
+ *
71
+ * It has two wrapper elements, so DOM will be changed. One is used for setting full expected height in the scrollable container
72
+ * and other is for transformation (translateY) to show the correct part of the list.
73
+ *
74
+ * Currently it works only with the direct vertically scrollable parent element. It does not work with body scroll.
75
+ * It supports only static (same) height rows virtualization. Expect some issues, if list consists of different height elements.
76
+ * @example
77
+ * const itemRenderer = React.useCallback(() => (
78
+ * <div key={index}>
79
+ * This is my item #{index}
80
+ * </div>
81
+ * ), [])
82
+ * <VirtualScroll
83
+ * itemsLength={1000}
84
+ * itemRenderer={itemRenderer}
85
+ * />
86
+ * @private
87
+ */
88
+ exports.VirtualScroll = react_1.default.forwardRef(function (_a, ref) {
89
+ var itemsLength = _a.itemsLength, itemRenderer = _a.itemRenderer, _b = _a.bufferSize, bufferSize = _b === void 0 ? 10 : _b, style = _a.style, rest = __rest(_a, ["itemsLength", "itemRenderer", "bufferSize", "style"]);
90
+ var _c = react_1.default.useState(0), startNode = _c[0], setStartNode = _c[1];
91
+ var _d = react_1.default.useState(0), visibleNodeCount = _d[0], setVisibleNodeCount = _d[1];
92
+ var scrollContainer = react_1.default.useRef();
93
+ var parentRef = react_1.default.useRef(null);
94
+ var childHeight = react_1.default.useRef(0);
95
+ var onScrollRef = react_1.default.useRef();
96
+ // Used only to recalculate on resize
97
+ var _e = react_1.default.useState(0), scrollContainerHeight = _e[0], setScrollContainerHeight = _e[1];
98
+ var onResize = react_1.default.useCallback(function (_a) {
99
+ var height = _a.height;
100
+ setScrollContainerHeight(height);
101
+ }, []);
102
+ var resizeRef = (0, useResizeObserver_1.useResizeObserver)(onResize)[0];
103
+ // Find scrollable parent
104
+ // Needed only on init
105
+ react_1.default.useLayoutEffect(function () {
106
+ var _a;
107
+ var scrollableParent = getScrollableParent(parentRef.current, (_a = parentRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument);
108
+ scrollContainer.current = scrollableParent;
109
+ resizeRef(scrollableParent);
110
+ }, [resizeRef]);
111
+ var visibleChildren = react_1.default.useMemo(function () {
112
+ var arr = [];
113
+ var endIndex = Math.min(itemsLength, startNode + visibleNodeCount + bufferSize * 2);
114
+ for (var i = startNode; i < endIndex; i++) {
115
+ arr.push(itemRenderer(i));
116
+ }
117
+ return arr;
118
+ }, [itemsLength, itemRenderer, bufferSize, startNode, visibleNodeCount]);
119
+ // Get child height when children available
120
+ react_1.default.useLayoutEffect(function () {
121
+ if (!parentRef.current || !visibleChildren.length) {
122
+ return;
123
+ }
124
+ var firstChild = parentRef.current.children.item(0);
125
+ childHeight.current = Number(getElementHeight(firstChild).toFixed(2));
126
+ }, [visibleChildren.length]);
127
+ var updateVirtualScroll = react_1.default.useCallback(function () {
128
+ var _a, _b;
129
+ var scrollableContainer = (_a = scrollContainer.current) !== null && _a !== void 0 ? _a : (_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.scrollingElement;
130
+ if (!scrollableContainer) {
131
+ return;
132
+ }
133
+ var start = getNumberOfNodesInHeight(childHeight.current, scrollableContainer.scrollTop);
134
+ var startIndex = Math.max(0, start - bufferSize);
135
+ setStartNode(startIndex);
136
+ setVisibleNodeCount(getVisibleNodeCount(childHeight.current, start, itemsLength, scrollableContainer));
137
+ if (!parentRef.current) {
138
+ return;
139
+ }
140
+ parentRef.current.style.transform = "translateY(" + getTranslateValue(childHeight.current, startIndex) + "px)";
141
+ }, [bufferSize, itemsLength]);
142
+ var removeScrollListener = react_1.default.useCallback(function () {
143
+ var _a, _b;
144
+ if (!onScrollRef.current) {
145
+ return;
146
+ }
147
+ !scrollContainer.current ||
148
+ scrollContainer.current === ((_a = parentRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument.body)
149
+ ? (_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.removeEventListener('scroll', onScrollRef.current)
150
+ : scrollContainer.current.removeEventListener('scroll', onScrollRef.current);
151
+ }, []);
152
+ // Add event listener to the scrollable container.
153
+ react_1.default.useLayoutEffect(function () {
154
+ var _a, _b;
155
+ removeScrollListener();
156
+ onScrollRef.current = updateVirtualScroll;
157
+ if (!scrollContainer.current ||
158
+ scrollContainer.current === ((_a = parentRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument.body)) {
159
+ (_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.addEventListener('scroll', updateVirtualScroll);
160
+ }
161
+ else {
162
+ scrollContainer.current.addEventListener('scroll', updateVirtualScroll);
163
+ }
164
+ return removeScrollListener;
165
+ }, [updateVirtualScroll, removeScrollListener]);
166
+ react_1.default.useLayoutEffect(function () {
167
+ updateVirtualScroll();
168
+ }, [scrollContainerHeight, itemsLength, updateVirtualScroll]);
169
+ return (react_1.default.createElement("div", __assign({ style: __assign({ overflow: 'hidden', minHeight: itemsLength * childHeight.current, width: '100%' }, style), ref: ref }, rest),
170
+ react_1.default.createElement("div", { style: {
171
+ willChange: 'transform',
172
+ }, ref: parentRef }, visibleChildren)));
173
+ });
174
+ exports.default = exports.VirtualScroll;
@@ -4,3 +4,4 @@ export * from './InputContainer';
4
4
  export * from './icons';
5
5
  export * from './WithCSSTransition';
6
6
  export * from './MiddleTextTruncation';
7
+ export * from './VirtualScroll';
@@ -20,3 +20,4 @@ __exportStar(require("./InputContainer"), exports);
20
20
  __exportStar(require("./icons"), exports);
21
21
  __exportStar(require("./WithCSSTransition"), exports);
22
22
  __exportStar(require("./MiddleTextTruncation"), exports);
23
+ __exportStar(require("./VirtualScroll"), exports);
@@ -6,13 +6,20 @@ export declare type ButtonGroupProps = {
6
6
  */
7
7
  children: React.ReactNode;
8
8
  /**
9
- * If specified, this prop will be used to show a custom button as the last button
10
- * when overflow happens, i.e. when there is not enough space to fit all the buttons.
9
+ * If specified, this prop will be used to show a custom button when overflow happens,
10
+ * i.e. when there is not enough space to fit all the buttons.
11
11
  *
12
12
  * Expects a function that takes the index of the first button that is overflowing (i.e. hidden)
13
13
  * and returns the `ReactNode` to render.
14
+ *
15
+ * The placement of this button can be controlled using the `overflowPlacement` prop.
14
16
  */
15
17
  overflowButton?: (firstOverflowingIndex: number) => React.ReactNode;
18
+ /**
19
+ * If `overflowButton` is specified, should it placed at the start or the end?
20
+ * @default 'end'
21
+ */
22
+ overflowPlacement?: 'start' | 'end';
16
23
  } & React.ComponentPropsWithRef<'div'>;
17
24
  /**
18
25
  * Group buttons together for common actions.
@@ -42,5 +49,5 @@ export declare type ButtonGroupProps = {
42
49
  * {buttons}
43
50
  * </ButtonGroup>
44
51
  */
45
- export declare const ButtonGroup: React.ForwardRefExoticComponent<Pick<ButtonGroupProps, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "overflowButton"> & React.RefAttributes<HTMLDivElement>>;
52
+ export declare const ButtonGroup: React.ForwardRefExoticComponent<Pick<ButtonGroupProps, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "overflowButton" | "overflowPlacement"> & React.RefAttributes<HTMLDivElement>>;
46
53
  export default ButtonGroup;
@@ -57,13 +57,14 @@ import '@itwin/itwinui-css/css/button.css';
57
57
  * </ButtonGroup>
58
58
  */
59
59
  export var ButtonGroup = React.forwardRef(function (props, ref) {
60
- var children = props.children, className = props.className, style = props.style, overflowButton = props.overflowButton, rest = __rest(props, ["children", "className", "style", "overflowButton"]);
60
+ var children = props.children, className = props.className, style = props.style, overflowButton = props.overflowButton, _a = props.overflowPlacement, overflowPlacement = _a === void 0 ? 'end' : _a, rest = __rest(props, ["children", "className", "style", "overflowButton", "overflowPlacement"]);
61
61
  var items = React.useMemo(function () { var _a; return (_a = React.Children.map(children, function (child) { return React.createElement("div", null, child); })) !== null && _a !== void 0 ? _a : []; }, [children]);
62
62
  useTheme();
63
- var _a = useOverflow(items, !overflowButton), overflowRef = _a[0], visibleCount = _a[1];
63
+ var _b = useOverflow(items, !overflowButton), overflowRef = _b[0], visibleCount = _b[1];
64
64
  var refs = useMergedRefs(overflowRef, ref);
65
65
  return (React.createElement("div", __assign({ className: cx('iui-button-group', className), style: __assign(__assign({}, (!!overflowButton && { width: '100%' })), style), ref: refs }, rest), !!overflowButton && visibleCount < items.length ? (React.createElement(React.Fragment, null,
66
+ overflowButton && overflowPlacement === 'start' && (React.createElement("div", null, overflowButton(visibleCount))),
66
67
  items.slice(0, visibleCount - 1),
67
- overflowButton(visibleCount))) : (items)));
68
+ overflowButton && overflowPlacement === 'end' && (React.createElement("div", null, overflowButton(visibleCount))))) : (items)));
68
69
  });
69
70
  export default ButtonGroup;
@@ -132,22 +132,31 @@ export var ColorBuilder = React.forwardRef(function (props, ref) {
132
132
  }
133
133
  }, [colorDotActive, updateColorDot]);
134
134
  var handleSquarePointerUp = React.useCallback(function (event) {
135
+ if (!colorDotActive) {
136
+ return;
137
+ }
135
138
  updateSquareValue(event, 'onChange');
136
139
  setColorDotActive(false);
137
140
  event.preventDefault();
138
141
  event.stopPropagation();
139
- }, [updateSquareValue]);
142
+ }, [colorDotActive, updateSquareValue]);
140
143
  useEventListener('pointerup', handleSquarePointerUp, (_k = builderRef.current) === null || _k === void 0 ? void 0 : _k.ownerDocument);
141
144
  var handleSquarePointerMove = React.useCallback(function (event) {
145
+ if (!colorDotActive) {
146
+ return;
147
+ }
142
148
  event.preventDefault();
143
149
  event.stopPropagation();
144
150
  updateSquareValue(event, 'onUpdate');
145
- }, [updateSquareValue]);
151
+ }, [colorDotActive, updateSquareValue]);
146
152
  useEventListener('pointermove', handleSquarePointerMove, (_l = builderRef.current) === null || _l === void 0 ? void 0 : _l.ownerDocument);
147
153
  var handleSquarePointerLeave = React.useCallback(function (event) {
154
+ if (!colorDotActive) {
155
+ return;
156
+ }
148
157
  updateSquareValue(event, 'onChange');
149
158
  setColorDotActive(false);
150
- }, [updateSquareValue]);
159
+ }, [colorDotActive, updateSquareValue]);
151
160
  useEventListener('pointerleave', handleSquarePointerLeave, (_m = builderRef.current) === null || _m === void 0 ? void 0 : _m.ownerDocument);
152
161
  var keysPressed = React.useRef({}); // keep track of which keys are currently pressed
153
162
  // Arrow key navigation for color dot
@@ -158,10 +158,13 @@ export var Slider = React.forwardRef(function (props, ref) {
158
158
  onChange,
159
159
  ]);
160
160
  var handlePointerMove = React.useCallback(function (event) {
161
+ if (activeThumbIndex === undefined) {
162
+ return;
163
+ }
161
164
  event.preventDefault();
162
165
  event.stopPropagation();
163
166
  updateThumbValue(event, 'onUpdate');
164
- }, [updateThumbValue]);
167
+ }, [activeThumbIndex, updateThumbValue]);
165
168
  // function called by Thumb keyboard processing
166
169
  var onThumbValueChanged = React.useCallback(function (index, value) {
167
170
  if (currentValues[index] === value) {
@@ -176,11 +179,14 @@ export var Slider = React.forwardRef(function (props, ref) {
176
179
  setActiveThumbIndex(index);
177
180
  }, []);
178
181
  var handlePointerUp = React.useCallback(function (event) {
182
+ if (activeThumbIndex === undefined) {
183
+ return;
184
+ }
179
185
  updateThumbValue(event, 'onChange');
180
186
  setActiveThumbIndex(undefined);
181
187
  event.preventDefault();
182
188
  event.stopPropagation();
183
- }, [updateThumbValue]);
189
+ }, [activeThumbIndex, updateThumbValue]);
184
190
  var handlePointerDownOnSlider = React.useCallback(function (event) {
185
191
  if (containerRef.current) {
186
192
  var percent = getPercentageOfRectangle(containerRef.current.getBoundingClientRect(), event.clientX);
@@ -56,6 +56,7 @@ export declare type TableProps<T extends Record<string, unknown> = Record<string
56
56
  isSelectable?: boolean;
57
57
  /**
58
58
  * Handler for rows selection. Must be memoized.
59
+ * This is triggered only by user initiated actions (i.e. data change will not call it).
59
60
  */
60
61
  onSelect?: (selectedData: T[] | undefined, tableState?: TableState<T>) => void;
61
62
  /**
@@ -160,6 +161,15 @@ export declare type TableProps<T extends Record<string, unknown> = Record<string
160
161
  * @default 'default'
161
162
  */
162
163
  styleType?: 'default' | 'zebra-rows';
164
+ /**
165
+ * Virtualization is used for the scrollable table body.
166
+ * Height on the table is required for virtualization to work.
167
+ * @example
168
+ * <Table enableVirtualization style={{height: 400}} {...} />
169
+ * @default false
170
+ * @beta
171
+ */
172
+ enableVirtualization?: boolean;
163
173
  } & Omit<CommonProps, 'title'>;
164
174
  /**
165
175
  * Table based on [react-table](https://react-table.tanstack.com/docs/api/overview).
@@ -40,6 +40,7 @@ import { useExpanderCell, useSelectionCell, useSubRowFiltering, useSubRowSelecti
40
40
  import { onExpandHandler, onFilterHandler, onSelectHandler, } from './actionHandlers';
41
41
  import { onSingleSelectHandler } from './actionHandlers/selectHandler';
42
42
  import { onTableResizeEnd, onTableResizeStart, } from './actionHandlers/resizeHandler';
43
+ import VirtualScroll from '../utils/components/VirtualScroll';
43
44
  var singleRowSelectedAction = 'singleRowSelected';
44
45
  var tableResizeStartAction = 'tableResizeStart';
45
46
  var tableResizeEndAction = 'tableResizeEnd';
@@ -87,9 +88,9 @@ var tableResizeEndAction = 'tableResizeEnd';
87
88
  */
88
89
  export var Table = function (props) {
89
90
  var _a;
90
- var data = props.data, columns = props.columns, _b = props.isLoading, isLoading = _b === void 0 ? false : _b, emptyTableContent = props.emptyTableContent, className = props.className, style = props.style, id = props.id, _c = props.isSelectable, isSelectable = _c === void 0 ? false : _c, onSelect = props.onSelect, onRowClick = props.onRowClick, _d = props.isSortable, isSortable = _d === void 0 ? false : _d, onSort = props.onSort, stateReducer = props.stateReducer, onBottomReached = props.onBottomReached, onRowInViewport = props.onRowInViewport, _e = props.intersectionMargin, intersectionMargin = _e === void 0 ? 300 : _e, subComponent = props.subComponent, onExpand = props.onExpand, onFilter = props.onFilter, emptyFilteredTableContent = props.emptyFilteredTableContent, filterFunctions = props.filterTypes, expanderCell = props.expanderCell, isRowDisabled = props.isRowDisabled, rowProps = props.rowProps, _f = props.density, density = _f === void 0 ? 'default' : _f, _g = props.selectSubRows, selectSubRows = _g === void 0 ? true : _g, getSubRows = props.getSubRows, _h = props.selectRowOnClick, selectRowOnClick = _h === void 0 ? true : _h, paginatorRenderer = props.paginatorRenderer, _j = props.pageSize, pageSize = _j === void 0 ? 25 : _j, _k = props.isResizable, isResizable = _k === void 0 ? false : _k, _l = props.styleType, styleType = _l === void 0 ? 'default' : _l, rest = __rest(props, ["data", "columns", "isLoading", "emptyTableContent", "className", "style", "id", "isSelectable", "onSelect", "onRowClick", "isSortable", "onSort", "stateReducer", "onBottomReached", "onRowInViewport", "intersectionMargin", "subComponent", "onExpand", "onFilter", "emptyFilteredTableContent", "filterTypes", "expanderCell", "isRowDisabled", "rowProps", "density", "selectSubRows", "getSubRows", "selectRowOnClick", "paginatorRenderer", "pageSize", "isResizable", "styleType"]);
91
+ var data = props.data, columns = props.columns, _b = props.isLoading, isLoading = _b === void 0 ? false : _b, emptyTableContent = props.emptyTableContent, className = props.className, style = props.style, id = props.id, _c = props.isSelectable, isSelectable = _c === void 0 ? false : _c, onSelect = props.onSelect, onRowClick = props.onRowClick, _d = props.isSortable, isSortable = _d === void 0 ? false : _d, onSort = props.onSort, stateReducer = props.stateReducer, onBottomReached = props.onBottomReached, onRowInViewport = props.onRowInViewport, _e = props.intersectionMargin, intersectionMargin = _e === void 0 ? 300 : _e, subComponent = props.subComponent, onExpand = props.onExpand, onFilter = props.onFilter, emptyFilteredTableContent = props.emptyFilteredTableContent, filterFunctions = props.filterTypes, expanderCell = props.expanderCell, isRowDisabled = props.isRowDisabled, rowProps = props.rowProps, _f = props.density, density = _f === void 0 ? 'default' : _f, _g = props.selectSubRows, selectSubRows = _g === void 0 ? true : _g, getSubRows = props.getSubRows, _h = props.selectRowOnClick, selectRowOnClick = _h === void 0 ? true : _h, paginatorRenderer = props.paginatorRenderer, _j = props.pageSize, pageSize = _j === void 0 ? 25 : _j, _k = props.isResizable, isResizable = _k === void 0 ? false : _k, _l = props.styleType, styleType = _l === void 0 ? 'default' : _l, _m = props.enableVirtualization, enableVirtualization = _m === void 0 ? false : _m, rest = __rest(props, ["data", "columns", "isLoading", "emptyTableContent", "className", "style", "id", "isSelectable", "onSelect", "onRowClick", "isSortable", "onSort", "stateReducer", "onBottomReached", "onRowInViewport", "intersectionMargin", "subComponent", "onExpand", "onFilter", "emptyFilteredTableContent", "filterTypes", "expanderCell", "isRowDisabled", "rowProps", "density", "selectSubRows", "getSubRows", "selectRowOnClick", "paginatorRenderer", "pageSize", "isResizable", "styleType", "enableVirtualization"]);
91
92
  useTheme();
92
- var _m = React.useState(), ownerDocument = _m[0], setOwnerDocument = _m[1];
93
+ var _o = React.useState(), ownerDocument = _o[0], setOwnerDocument = _o[1];
93
94
  var defaultColumn = React.useMemo(function () { return ({
94
95
  maxWidth: 0,
95
96
  minWidth: 0,
@@ -227,6 +228,23 @@ export var Table = function (props) {
227
228
  });
228
229
  var headerRef = React.useRef(null);
229
230
  var bodyRef = React.useRef(null);
231
+ var getPreparedRow = React.useCallback(function (row) {
232
+ prepareRow(row);
233
+ return (React.createElement(TableRowMemoized, { row: row, rowProps: rowProps, isLast: row.index === data.length - 1, onRowInViewport: onRowInViewportRef, onBottomReached: onBottomReachedRef, intersectionMargin: intersectionMargin, state: state, key: row.getRowProps().key, onClick: onRowClickHandler, subComponent: subComponent, isDisabled: !!(isRowDisabled === null || isRowDisabled === void 0 ? void 0 : isRowDisabled(row.original)), tableHasSubRows: hasAnySubRows, tableInstance: instance, expanderCell: expanderCell }));
234
+ }, [
235
+ data.length,
236
+ expanderCell,
237
+ hasAnySubRows,
238
+ instance,
239
+ intersectionMargin,
240
+ isRowDisabled,
241
+ onRowClickHandler,
242
+ prepareRow,
243
+ rowProps,
244
+ state,
245
+ subComponent,
246
+ ]);
247
+ var virtualizedItemRenderer = React.useCallback(function (index) { return getPreparedRow(page[index]); }, [getPreparedRow, page]);
230
248
  return (React.createElement(React.Fragment, null,
231
249
  React.createElement("div", __assign({ ref: function (element) {
232
250
  setOwnerDocument(element === null || element === void 0 ? void 0 : element.ownerDocument);
@@ -263,16 +281,13 @@ export var Table = function (props) {
263
281
  className: cx('iui-table-body', {
264
282
  'iui-zebra-striping': styleType === 'zebra-rows',
265
283
  }),
284
+ style: { outline: 0 },
266
285
  }), { ref: bodyRef, onScroll: function () {
267
286
  if (headerRef.current && bodyRef.current) {
268
287
  headerRef.current.scrollLeft = bodyRef.current.scrollLeft;
269
288
  }
270
- } }),
271
- data.length !== 0 &&
272
- page.map(function (row) {
273
- prepareRow(row);
274
- return (React.createElement(TableRowMemoized, { row: row, rowProps: rowProps, isLast: row.index === data.length - 1, onRowInViewport: onRowInViewportRef, onBottomReached: onBottomReachedRef, intersectionMargin: intersectionMargin, state: state, key: row.getRowProps().key, onClick: onRowClickHandler, subComponent: subComponent, isDisabled: !!(isRowDisabled === null || isRowDisabled === void 0 ? void 0 : isRowDisabled(row.original)), tableHasSubRows: hasAnySubRows, tableInstance: instance, expanderCell: expanderCell }));
275
- }),
289
+ }, tabIndex: -1 }),
290
+ data.length !== 0 && (React.createElement(React.Fragment, null, enableVirtualization ? (React.createElement(VirtualScroll, { itemsLength: page.length, itemRenderer: virtualizedItemRenderer })) : (page.map(function (row) { return getPreparedRow(row); })))),
276
291
  isLoading && data.length === 0 && (React.createElement("div", { className: 'iui-table-empty' },
277
292
  React.createElement(ProgressRadial, { indeterminate: true }))),
278
293
  isLoading && data.length !== 0 && (React.createElement("div", { className: 'iui-row' },
@@ -57,28 +57,28 @@ export var Tile = function (props) {
57
57
  var showMenu = React.useCallback(function () { return setIsMenuVisible(true); }, []);
58
58
  var hideMenu = React.useCallback(function () { return setIsMenuVisible(false); }, []);
59
59
  return (React.createElement("div", __assign({ className: cx('iui-tile', { 'iui-folder': variant === 'folder' }, { 'iui-new': isNew }, { 'iui-selected': isSelected }, className) }, rest),
60
- thumbnail && (React.createElement("div", { className: 'iui-thumbnail' },
61
- typeof thumbnail === 'string' ? (React.createElement("div", { className: 'iui-picture', style: { backgroundImage: "url(" + thumbnail + ")" } })) : thumbnail && thumbnail.type === 'img' ? (React.cloneElement(thumbnail, {
62
- className: 'iui-picture',
60
+ thumbnail && (React.createElement("div", { className: 'iui-tile-thumbnail' },
61
+ typeof thumbnail === 'string' ? (React.createElement("div", { className: 'iui-tile-thumbnail-picture', style: { backgroundImage: "url(" + thumbnail + ")" } })) : thumbnail && thumbnail.type === 'img' ? (React.cloneElement(thumbnail, {
62
+ className: 'iui-tile-thumbnail-picture',
63
63
  })) : React.isValidElement(thumbnail) ? (React.cloneElement(thumbnail, {
64
64
  className: cx('iui-thumbnail-icon', thumbnail.props.className),
65
65
  })) : (thumbnail),
66
66
  leftIcon &&
67
67
  React.cloneElement(leftIcon, {
68
- className: 'iui-small iui-type-indicator',
68
+ className: 'iui-small iui-tile-thumbnail-type-indicator',
69
69
  }),
70
70
  rightIcon &&
71
71
  React.cloneElement(rightIcon, {
72
- className: 'iui-small iui-quick-action',
72
+ className: 'iui-small iui-tile-thumbnail-quick-action',
73
73
  }),
74
- badge && React.createElement("div", { className: 'iui-badge-container' }, badge))),
75
- React.createElement("div", { className: 'iui-content' },
76
- React.createElement("div", { className: 'iui-name' },
74
+ badge && (React.createElement("div", { className: 'iui-tile-thumbnail-badge-container' }, badge)))),
75
+ React.createElement("div", { className: 'iui-tile-content' },
76
+ React.createElement("div", { className: 'iui-tile-name' },
77
77
  isSelected && (React.createElement(SvgCheckmark, { className: cx('iui-tile-status-icon', 'iui-informational'), "aria-hidden": true })),
78
78
  isNew && (React.createElement(SvgNew, { className: cx('iui-tile-status-icon', 'iui-positive'), "aria-hidden": true })),
79
- React.createElement("span", { className: 'iui-name-label' }, name)),
80
- description != undefined && (React.createElement("div", { className: 'iui-description' }, description)),
81
- metadata != undefined && (React.createElement("div", { className: 'iui-metadata' }, metadata)),
79
+ React.createElement("span", { className: 'iui-tile-name-label' }, name)),
80
+ description != undefined && (React.createElement("div", { className: 'iui-tile-description' }, description)),
81
+ metadata != undefined && (React.createElement("div", { className: 'iui-tile-metadata' }, metadata)),
82
82
  moreOptions && (React.createElement(DropdownMenu, { onShow: showMenu, onHide: hideMenu, menuItems: function (close) {
83
83
  return moreOptions.map(function (option) {
84
84
  return React.cloneElement(option, {
@@ -91,15 +91,11 @@ export var Tile = function (props) {
91
91
  });
92
92
  });
93
93
  } },
94
- React.createElement(IconButton, { styleType: 'borderless', size: 'small', className: cx('iui-more-options', {
94
+ React.createElement(IconButton, { styleType: 'borderless', size: 'small', className: cx('iui-tile-more-options', {
95
95
  'iui-visible': isMenuVisible,
96
96
  }), "aria-label": 'More options' },
97
97
  React.createElement(SvgMore, null)))),
98
98
  children),
99
- buttons && (React.createElement("div", { className: 'iui-tile-buttons' }, buttons.map(function (button) {
100
- return React.cloneElement(button, {
101
- className: cx('iui-tile-button', button.props.className),
102
- });
103
- })))));
99
+ buttons && React.createElement("div", { className: 'iui-tile-buttons' }, buttons)));
104
100
  };
105
101
  export default Tile;
@@ -1,7 +1,8 @@
1
1
  import React from 'react';
2
2
  import { TippyProps } from '@tippyjs/react';
3
- import { Placement, Instance } from 'tippy.js';
3
+ import type { Placement, Instance } from 'tippy.js';
4
4
  export declare type PopoverInstance = Instance;
5
+ import '@itwin/itwinui-css/css/popover.css';
5
6
  export declare type PopoverProps = {
6
7
  /**
7
8
  * Controlled flag for whether the popover is visible.
@@ -23,15 +23,18 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
23
23
  return to.concat(ar || Array.prototype.slice.call(from));
24
24
  };
25
25
  import React from 'react';
26
+ import cx from 'classnames';
26
27
  import Tippy from '@tippyjs/react';
27
28
  import { useMergedRefs } from '../hooks/useMergedRefs';
29
+ import '@itwin/itwinui-css/css/popover.css';
28
30
  /**
29
31
  * Wrapper around [tippy.js](https://atomiks.github.io/tippyjs)
30
32
  * with pre-configured props and plugins (e.g. lazy mounting, focus, etc).
31
33
  * @private
32
34
  */
33
35
  export var Popover = React.forwardRef(function (props, ref) {
34
- var _a = React.useState(false), mounted = _a[0], setMounted = _a[1];
36
+ var _a;
37
+ var _b = React.useState(false), mounted = _b[0], setMounted = _b[1];
35
38
  var tippyRef = React.useRef(null);
36
39
  var refs = useMergedRefs(tippyRef, ref);
37
40
  // Plugin to allow lazy mounting. See https://github.com/atomiks/tippyjs-react#lazy-mounting
@@ -50,14 +53,14 @@ export var Popover = React.forwardRef(function (props, ref) {
50
53
  },
51
54
  }); },
52
55
  };
53
- var computedProps = __assign(__assign({ allowHTML: true, animation: false, appendTo: 'parent', arrow: false, duration: 0, interactive: true, popperOptions: {
54
- strategy: 'fixed',
55
- modifiers: [{ name: 'flip' }],
56
- }, role: undefined, offset: [0, 0], maxWidth: '' }, props), { plugins: __spreadArray([
56
+ var computedProps = __assign(__assign({ allowHTML: true, animation: false, appendTo: 'parent', arrow: false, duration: 0, interactive: true, role: undefined, offset: [0, 0], maxWidth: '' }, props), { className: cx('iui-popover', props.className), plugins: __spreadArray([
57
57
  lazyLoad,
58
58
  removeTabIndex,
59
59
  hideOnEscOrTab
60
- ], (props.plugins || []), true) });
60
+ ], (props.plugins || []), true), popperOptions: __assign(__assign({ strategy: 'fixed' }, props.popperOptions), { modifiers: __spreadArray([
61
+ { name: 'flip' },
62
+ { name: 'preventOverflow', options: { padding: 0 } }
63
+ ], (((_a = props.popperOptions) === null || _a === void 0 ? void 0 : _a.modifiers) || []), true) }) });
61
64
  if (props.render) {
62
65
  var render_1 = props.render;
63
66
  computedProps.render = function () {
@@ -0,0 +1,42 @@
1
+ import React from 'react';
2
+ export declare type VirtualScrollProps = {
3
+ /**
4
+ * Length of the items to virtualize.
5
+ */
6
+ itemsLength: number;
7
+ /**
8
+ * Single item render function, which gives index of the item (0 based) in the data array
9
+ * and expects to get the JSX of that element to render.
10
+ * Recommended to memoize the reference of the function.
11
+ */
12
+ itemRenderer: (index: number) => JSX.Element;
13
+ /**
14
+ * Number of items to be rendered at the start and the end.
15
+ * Not recommended to go lower than the visible items in viewport.
16
+ * @default 10
17
+ */
18
+ bufferSize?: number;
19
+ } & React.ComponentPropsWithRef<'div'>;
20
+ /**
21
+ * `VirtualScroll` component is used to render a huge amount of items in the DOM. It renders only the ones which are visible
22
+ * and the amount provided through `bufferSize` prop at the start and the end. Can be used inside other components like `Table`.
23
+ *
24
+ * It has two wrapper elements, so DOM will be changed. One is used for setting full expected height in the scrollable container
25
+ * and other is for transformation (translateY) to show the correct part of the list.
26
+ *
27
+ * Currently it works only with the direct vertically scrollable parent element. It does not work with body scroll.
28
+ * It supports only static (same) height rows virtualization. Expect some issues, if list consists of different height elements.
29
+ * @example
30
+ * const itemRenderer = React.useCallback(() => (
31
+ * <div key={index}>
32
+ * This is my item #{index}
33
+ * </div>
34
+ * ), [])
35
+ * <VirtualScroll
36
+ * itemsLength={1000}
37
+ * itemRenderer={itemRenderer}
38
+ * />
39
+ * @private
40
+ */
41
+ export declare const VirtualScroll: React.ForwardRefExoticComponent<Pick<VirtualScrollProps, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "itemsLength" | "itemRenderer" | "bufferSize"> & React.RefAttributes<HTMLDivElement>>;
42
+ export default VirtualScroll;
@@ -0,0 +1,168 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
12
+ var __rest = (this && this.__rest) || function (s, e) {
13
+ var t = {};
14
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
15
+ t[p] = s[p];
16
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
17
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
18
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
19
+ t[p[i]] = s[p[i]];
20
+ }
21
+ return t;
22
+ };
23
+ /*---------------------------------------------------------------------------------------------
24
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
25
+ * See LICENSE.md in the project root for license terms and full copyright notice.
26
+ *--------------------------------------------------------------------------------------------*/
27
+ import React from 'react';
28
+ import { useResizeObserver } from '../hooks/useResizeObserver';
29
+ var getScrollableParent = function (element, ownerDocument) {
30
+ if (ownerDocument === void 0) { ownerDocument = document; }
31
+ if (!element || element === ownerDocument.body) {
32
+ return ownerDocument.body;
33
+ }
34
+ return isElementScrollable(element)
35
+ ? element
36
+ : getScrollableParent(element.parentElement, ownerDocument);
37
+ };
38
+ var isElementScrollable = function (element) {
39
+ return /(auto|scroll|overlay)/.test(getElementStyle(element, 'overflow') +
40
+ getElementStyle(element, 'overflow-y'));
41
+ };
42
+ var getElementStyle = function (element, prop) {
43
+ return getComputedStyle(element, null).getPropertyValue(prop);
44
+ };
45
+ var getElementHeight = function (element) {
46
+ var _a;
47
+ return (_a = element === null || element === void 0 ? void 0 : element.getBoundingClientRect().height) !== null && _a !== void 0 ? _a : 0;
48
+ };
49
+ var getNumberOfNodesInHeight = function (childHeight, totalHeight) {
50
+ if (!childHeight) {
51
+ return 0;
52
+ }
53
+ return Math.floor(totalHeight / childHeight);
54
+ };
55
+ var getTranslateValue = function (childHeight, startIndex) {
56
+ return childHeight * startIndex;
57
+ };
58
+ var getVisibleNodeCount = function (childHeight, startIndex, childrenLength, scrollContainer) {
59
+ return Math.min(childrenLength - startIndex, getNumberOfNodesInHeight(childHeight, getElementHeight(scrollContainer)));
60
+ };
61
+ /**
62
+ * `VirtualScroll` component is used to render a huge amount of items in the DOM. It renders only the ones which are visible
63
+ * and the amount provided through `bufferSize` prop at the start and the end. Can be used inside other components like `Table`.
64
+ *
65
+ * It has two wrapper elements, so DOM will be changed. One is used for setting full expected height in the scrollable container
66
+ * and other is for transformation (translateY) to show the correct part of the list.
67
+ *
68
+ * Currently it works only with the direct vertically scrollable parent element. It does not work with body scroll.
69
+ * It supports only static (same) height rows virtualization. Expect some issues, if list consists of different height elements.
70
+ * @example
71
+ * const itemRenderer = React.useCallback(() => (
72
+ * <div key={index}>
73
+ * This is my item #{index}
74
+ * </div>
75
+ * ), [])
76
+ * <VirtualScroll
77
+ * itemsLength={1000}
78
+ * itemRenderer={itemRenderer}
79
+ * />
80
+ * @private
81
+ */
82
+ export var VirtualScroll = React.forwardRef(function (_a, ref) {
83
+ var itemsLength = _a.itemsLength, itemRenderer = _a.itemRenderer, _b = _a.bufferSize, bufferSize = _b === void 0 ? 10 : _b, style = _a.style, rest = __rest(_a, ["itemsLength", "itemRenderer", "bufferSize", "style"]);
84
+ var _c = React.useState(0), startNode = _c[0], setStartNode = _c[1];
85
+ var _d = React.useState(0), visibleNodeCount = _d[0], setVisibleNodeCount = _d[1];
86
+ var scrollContainer = React.useRef();
87
+ var parentRef = React.useRef(null);
88
+ var childHeight = React.useRef(0);
89
+ var onScrollRef = React.useRef();
90
+ // Used only to recalculate on resize
91
+ var _e = React.useState(0), scrollContainerHeight = _e[0], setScrollContainerHeight = _e[1];
92
+ var onResize = React.useCallback(function (_a) {
93
+ var height = _a.height;
94
+ setScrollContainerHeight(height);
95
+ }, []);
96
+ var resizeRef = useResizeObserver(onResize)[0];
97
+ // Find scrollable parent
98
+ // Needed only on init
99
+ React.useLayoutEffect(function () {
100
+ var _a;
101
+ var scrollableParent = getScrollableParent(parentRef.current, (_a = parentRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument);
102
+ scrollContainer.current = scrollableParent;
103
+ resizeRef(scrollableParent);
104
+ }, [resizeRef]);
105
+ var visibleChildren = React.useMemo(function () {
106
+ var arr = [];
107
+ var endIndex = Math.min(itemsLength, startNode + visibleNodeCount + bufferSize * 2);
108
+ for (var i = startNode; i < endIndex; i++) {
109
+ arr.push(itemRenderer(i));
110
+ }
111
+ return arr;
112
+ }, [itemsLength, itemRenderer, bufferSize, startNode, visibleNodeCount]);
113
+ // Get child height when children available
114
+ React.useLayoutEffect(function () {
115
+ if (!parentRef.current || !visibleChildren.length) {
116
+ return;
117
+ }
118
+ var firstChild = parentRef.current.children.item(0);
119
+ childHeight.current = Number(getElementHeight(firstChild).toFixed(2));
120
+ }, [visibleChildren.length]);
121
+ var updateVirtualScroll = React.useCallback(function () {
122
+ var _a, _b;
123
+ var scrollableContainer = (_a = scrollContainer.current) !== null && _a !== void 0 ? _a : (_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.scrollingElement;
124
+ if (!scrollableContainer) {
125
+ return;
126
+ }
127
+ var start = getNumberOfNodesInHeight(childHeight.current, scrollableContainer.scrollTop);
128
+ var startIndex = Math.max(0, start - bufferSize);
129
+ setStartNode(startIndex);
130
+ setVisibleNodeCount(getVisibleNodeCount(childHeight.current, start, itemsLength, scrollableContainer));
131
+ if (!parentRef.current) {
132
+ return;
133
+ }
134
+ parentRef.current.style.transform = "translateY(" + getTranslateValue(childHeight.current, startIndex) + "px)";
135
+ }, [bufferSize, itemsLength]);
136
+ var removeScrollListener = React.useCallback(function () {
137
+ var _a, _b;
138
+ if (!onScrollRef.current) {
139
+ return;
140
+ }
141
+ !scrollContainer.current ||
142
+ scrollContainer.current === ((_a = parentRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument.body)
143
+ ? (_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.removeEventListener('scroll', onScrollRef.current)
144
+ : scrollContainer.current.removeEventListener('scroll', onScrollRef.current);
145
+ }, []);
146
+ // Add event listener to the scrollable container.
147
+ React.useLayoutEffect(function () {
148
+ var _a, _b;
149
+ removeScrollListener();
150
+ onScrollRef.current = updateVirtualScroll;
151
+ if (!scrollContainer.current ||
152
+ scrollContainer.current === ((_a = parentRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument.body)) {
153
+ (_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.addEventListener('scroll', updateVirtualScroll);
154
+ }
155
+ else {
156
+ scrollContainer.current.addEventListener('scroll', updateVirtualScroll);
157
+ }
158
+ return removeScrollListener;
159
+ }, [updateVirtualScroll, removeScrollListener]);
160
+ React.useLayoutEffect(function () {
161
+ updateVirtualScroll();
162
+ }, [scrollContainerHeight, itemsLength, updateVirtualScroll]);
163
+ return (React.createElement("div", __assign({ style: __assign({ overflow: 'hidden', minHeight: itemsLength * childHeight.current, width: '100%' }, style), ref: ref }, rest),
164
+ React.createElement("div", { style: {
165
+ willChange: 'transform',
166
+ }, ref: parentRef }, visibleChildren)));
167
+ });
168
+ export default VirtualScroll;
@@ -4,3 +4,4 @@ export * from './InputContainer';
4
4
  export * from './icons';
5
5
  export * from './WithCSSTransition';
6
6
  export * from './MiddleTextTruncation';
7
+ export * from './VirtualScroll';
@@ -8,3 +8,4 @@ export * from './InputContainer';
8
8
  export * from './icons';
9
9
  export * from './WithCSSTransition';
10
10
  export * from './MiddleTextTruncation';
11
+ export * from './VirtualScroll';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itwin/itwinui-react",
3
- "version": "1.29.3",
3
+ "version": "1.30.0",
4
4
  "author": "Bentley Systems",
5
5
  "license": "MIT",
6
6
  "main": "cjs/index.js",
@@ -40,7 +40,7 @@
40
40
  "build-storybook": "build-storybook"
41
41
  },
42
42
  "dependencies": {
43
- "@itwin/itwinui-css": "^0.44.1",
43
+ "@itwin/itwinui-css": "^0.45.0",
44
44
  "@itwin/itwinui-icons-react": "^1.5.0",
45
45
  "@itwin/itwinui-illustrations-react": "^1.0.1",
46
46
  "@tippyjs/react": "^4.2.5",