@itwin/itwinui-react 1.37.2 → 1.38.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/cjs/core/Breadcrumbs/Breadcrumbs.js +3 -5
  3. package/cjs/core/ColorPicker/ColorSwatch.d.ts +1 -1
  4. package/cjs/core/ComboBox/ComboBox.d.ts +11 -2
  5. package/cjs/core/ComboBox/ComboBox.js +135 -245
  6. package/cjs/core/ComboBox/ComboBoxDropdown.d.ts +8 -0
  7. package/cjs/core/ComboBox/ComboBoxDropdown.js +55 -0
  8. package/cjs/core/ComboBox/ComboBoxEndIcon.d.ts +5 -0
  9. package/cjs/core/ComboBox/ComboBoxEndIcon.js +54 -0
  10. package/cjs/core/ComboBox/ComboBoxInput.d.ts +5 -0
  11. package/cjs/core/ComboBox/ComboBoxInput.js +134 -0
  12. package/cjs/core/ComboBox/ComboBoxInputContainer.d.ts +8 -0
  13. package/cjs/core/ComboBox/ComboBoxInputContainer.js +45 -0
  14. package/cjs/core/ComboBox/ComboBoxMenu.d.ts +3 -0
  15. package/cjs/core/ComboBox/ComboBoxMenu.js +81 -0
  16. package/cjs/core/ComboBox/ComboBoxMenuItem.d.ts +21 -0
  17. package/cjs/core/ComboBox/ComboBoxMenuItem.js +64 -0
  18. package/cjs/core/ComboBox/helpers.d.ts +32 -0
  19. package/cjs/core/ComboBox/helpers.js +50 -0
  20. package/cjs/core/Modal/Modal.d.ts +1 -1
  21. package/cjs/core/Modal/Modal.js +6 -6
  22. package/cjs/core/Modal/ModalButtonBar.d.ts +1 -1
  23. package/cjs/core/Modal/ModalButtonBar.js +2 -2
  24. package/cjs/core/Modal/ModalContent.d.ts +1 -1
  25. package/cjs/core/Modal/ModalContent.js +2 -2
  26. package/cjs/core/RadioTiles/RadioTile.d.ts +1 -1
  27. package/cjs/core/RadioTiles/RadioTile.js +7 -9
  28. package/cjs/core/Select/Select.js +1 -1
  29. package/cjs/core/Slider/Thumb.js +15 -1
  30. package/cjs/core/Slider/Track.js +23 -12
  31. package/cjs/core/Table/Table.js +2 -2
  32. package/cjs/core/Table/filters/FilterToggle.js +3 -2
  33. package/cjs/core/Toast/ToastWrapper.d.ts +7 -5
  34. package/cjs/core/Toast/ToastWrapper.js +8 -4
  35. package/cjs/core/Toast/Toaster.d.ts +3 -0
  36. package/cjs/core/Toast/Toaster.js +30 -5
  37. package/cjs/core/utils/components/Popover.d.ts +1 -1
  38. package/cjs/core/utils/components/VirtualScroll.d.ts +35 -1
  39. package/cjs/core/utils/components/VirtualScroll.js +159 -26
  40. package/cjs/core/utils/hooks/index.d.ts +1 -0
  41. package/cjs/core/utils/hooks/index.js +1 -0
  42. package/cjs/core/utils/hooks/useSafeContext.d.ts +6 -0
  43. package/cjs/core/utils/hooks/useSafeContext.js +23 -0
  44. package/esm/core/Breadcrumbs/Breadcrumbs.js +3 -5
  45. package/esm/core/ColorPicker/ColorSwatch.d.ts +1 -1
  46. package/esm/core/ComboBox/ComboBox.d.ts +11 -2
  47. package/esm/core/ComboBox/ComboBox.js +137 -247
  48. package/esm/core/ComboBox/ComboBoxDropdown.d.ts +8 -0
  49. package/esm/core/ComboBox/ComboBoxDropdown.js +49 -0
  50. package/esm/core/ComboBox/ComboBoxEndIcon.d.ts +5 -0
  51. package/esm/core/ComboBox/ComboBoxEndIcon.js +48 -0
  52. package/esm/core/ComboBox/ComboBoxInput.d.ts +5 -0
  53. package/esm/core/ComboBox/ComboBoxInput.js +128 -0
  54. package/esm/core/ComboBox/ComboBoxInputContainer.d.ts +8 -0
  55. package/esm/core/ComboBox/ComboBoxInputContainer.js +38 -0
  56. package/esm/core/ComboBox/ComboBoxMenu.d.ts +3 -0
  57. package/esm/core/ComboBox/ComboBoxMenu.js +75 -0
  58. package/esm/core/ComboBox/ComboBoxMenuItem.d.ts +21 -0
  59. package/esm/core/ComboBox/ComboBoxMenuItem.js +58 -0
  60. package/esm/core/ComboBox/helpers.d.ts +32 -0
  61. package/esm/core/ComboBox/helpers.js +43 -0
  62. package/esm/core/Modal/Modal.d.ts +1 -1
  63. package/esm/core/Modal/Modal.js +6 -6
  64. package/esm/core/Modal/ModalButtonBar.d.ts +1 -1
  65. package/esm/core/Modal/ModalButtonBar.js +2 -2
  66. package/esm/core/Modal/ModalContent.d.ts +1 -1
  67. package/esm/core/Modal/ModalContent.js +2 -2
  68. package/esm/core/RadioTiles/RadioTile.d.ts +1 -1
  69. package/esm/core/RadioTiles/RadioTile.js +7 -9
  70. package/esm/core/Select/Select.js +1 -1
  71. package/esm/core/Slider/Thumb.js +15 -1
  72. package/esm/core/Slider/Track.js +23 -12
  73. package/esm/core/Table/Table.js +2 -2
  74. package/esm/core/Table/filters/FilterToggle.js +3 -2
  75. package/esm/core/Toast/ToastWrapper.d.ts +7 -5
  76. package/esm/core/Toast/ToastWrapper.js +8 -3
  77. package/esm/core/Toast/Toaster.d.ts +3 -0
  78. package/esm/core/Toast/Toaster.js +30 -5
  79. package/esm/core/utils/components/Popover.d.ts +1 -1
  80. package/esm/core/utils/components/VirtualScroll.d.ts +35 -1
  81. package/esm/core/utils/components/VirtualScroll.js +157 -25
  82. package/esm/core/utils/hooks/index.d.ts +1 -0
  83. package/esm/core/utils/hooks/index.js +1 -0
  84. package/esm/core/utils/hooks/useSafeContext.d.ts +6 -0
  85. package/esm/core/utils/hooks/useSafeContext.js +16 -0
  86. package/package.json +5 -33
@@ -30,11 +30,10 @@ exports.RadioTile = void 0;
30
30
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
31
31
  * See LICENSE.md in the project root for license terms and full copyright notice.
32
32
  *--------------------------------------------------------------------------------------------*/
33
- var Checkmark_1 = __importDefault(require("@itwin/itwinui-icons-react/cjs/icons/Checkmark"));
34
33
  var classnames_1 = __importDefault(require("classnames"));
35
34
  var react_1 = __importDefault(require("react"));
36
35
  var utils_1 = require("../utils");
37
- require("@itwin/itwinui-css/css/inputs.css");
36
+ require("@itwin/itwinui-css/css/radio-tile.css");
38
37
  /**
39
38
  * RadioTile component to be used in RadioTileGroup component
40
39
  * @example
@@ -50,15 +49,14 @@ exports.RadioTile = react_1.default.forwardRef(function (props, ref) {
50
49
  inputElementRef.current.focus();
51
50
  }
52
51
  }, [setFocus]);
53
- return (react_1.default.createElement("label", { className: className, style: style },
54
- react_1.default.createElement("input", __assign({ type: 'radio', ref: refs }, rest)),
55
- react_1.default.createElement("div", { className: 'iui-radio-tile' },
56
- react_1.default.createElement(Checkmark_1.default, { className: 'iui-checkmark', "aria-hidden": true }),
52
+ return (react_1.default.createElement("label", { className: (0, classnames_1.default)('iui-radio-tile', className), style: style },
53
+ react_1.default.createElement("input", __assign({ className: 'iui-radio-tile-input', type: 'radio', ref: refs }, rest)),
54
+ react_1.default.createElement("div", { className: 'iui-radio-tile-content' },
57
55
  icon &&
58
56
  react_1.default.cloneElement(icon, {
59
- className: (0, classnames_1.default)('iui-icon', icon.props.className),
57
+ className: (0, classnames_1.default)('iui-radio-tile-icon', icon.props.className),
60
58
  }),
61
- label && react_1.default.createElement("div", { className: 'iui-label' }, label),
62
- description && react_1.default.createElement("div", { className: 'iui-description' }, description))));
59
+ label && react_1.default.createElement("div", { className: 'iui-radio-tile-label' }, label),
60
+ description && (react_1.default.createElement("div", { className: 'iui-radio-tile-sublabel' }, description)))));
63
61
  });
64
62
  exports.default = exports.RadioTile;
@@ -146,7 +146,7 @@ var Select = function (props) {
146
146
  return options.find(function (option) { return option.value === value; });
147
147
  }, [options, value]);
148
148
  return (react_1.default.createElement("div", __assign({ className: (0, classnames_1.default)('iui-input-with-icon', className), "aria-expanded": isOpen, "aria-haspopup": 'listbox', style: style }, rest),
149
- react_1.default.createElement(DropdownMenu_1.DropdownMenu, __assign({ menuItems: menuItems, placement: 'bottom-start', className: (0, classnames_1.default)('iui-scroll', menuClassName), style: __assign({ minWidth: minWidth, maxWidth: "min(".concat(minWidth * 2, "px, 90vw)"), maxHeight: "300px" }, menuStyle), role: 'listbox', onShow: onShowHandler, onHide: onHideHandler, disabled: disabled }, popoverProps, { visible: isOpen, onClickOutside: function (_, _a) {
149
+ react_1.default.createElement(DropdownMenu_1.DropdownMenu, __assign({ menuItems: menuItems, placement: 'bottom-start', className: (0, classnames_1.default)('iui-scroll', menuClassName), style: __assign({ minWidth: minWidth, maxWidth: "min(".concat(minWidth * 2, "px, 90vw)"), maxHeight: 315 }, menuStyle), role: 'listbox', onShow: onShowHandler, onHide: onHideHandler, disabled: disabled }, popoverProps, { visible: isOpen, onClickOutside: function (_, _a) {
150
150
  var _b;
151
151
  var target = _a.target;
152
152
  if (!((_b = toggleButtonRef.current) === null || _b === void 0 ? void 0 : _b.contains(target))) {
@@ -70,7 +70,21 @@ var Thumb = function (props) {
70
70
  }, [disabled, index, onThumbActivated]);
71
71
  var _a = react_1.default.useState(false), hasFocus = _a[0], setHasFocus = _a[1];
72
72
  var _b = react_1.default.useState(false), isHovered = _b[0], setIsHovered = _b[1];
73
- var leftPercent = (100.0 * (value - sliderMin)) / (sliderMax - sliderMin);
73
+ var adjustedValue = react_1.default.useMemo(function () {
74
+ if (value < sliderMin) {
75
+ return sliderMin;
76
+ }
77
+ if (value > sliderMax) {
78
+ return sliderMax;
79
+ }
80
+ return value;
81
+ }, [sliderMax, sliderMin, value]);
82
+ var leftPercent = react_1.default.useMemo(function () {
83
+ if (sliderMax === sliderMin) {
84
+ return 0;
85
+ }
86
+ return (100.0 * (adjustedValue - sliderMin)) / (sliderMax - sliderMin);
87
+ }, [adjustedValue, sliderMax, sliderMin]);
74
88
  var _c = thumbProps || {}, style = _c.style, className = _c.className, rest = __rest(_c, ["style", "className"]);
75
89
  return (react_1.default.createElement(Tooltip_1.Tooltip, __assign({ visible: isActive || hasFocus || isHovered, placement: 'top' }, tooltipProps),
76
90
  react_1.default.createElement("div", __assign({}, rest, { "data-index": index, ref: thumbRef, style: __assign(__assign({}, style), { left: "".concat(leftPercent, "%") }), className: (0, classnames_1.default)('iui-slider-thumb', { 'iui-active': isActive }, className), role: 'slider', tabIndex: disabled ? undefined : 0, "aria-valuemin": minVal, "aria-valuenow": value, "aria-valuemax": maxVal, "aria-disabled": disabled, onPointerDown: handlePointerDownOnThumb, onKeyDown: handleOnKeyDown, onFocus: function () { return setHasFocus(true); }, onBlur: function () { return setHasFocus(false); }, onMouseEnter: function () { return setIsHovered(true); }, onMouseLeave: function () { return setIsHovered(false); } }))));
@@ -29,10 +29,18 @@ function shouldDisplaySegment(segmentIndex, mode) {
29
29
  }
30
30
  function generateSegments(values, min, max) {
31
31
  var segments = [];
32
+ var newValues = __spreadArray([], values, true);
33
+ newValues.sort(function (a, b) { return a - b; });
34
+ if (0 === newValues.length ||
35
+ newValues[0] < min ||
36
+ newValues[newValues.length - 1] > max ||
37
+ min === max) {
38
+ return [];
39
+ }
32
40
  var lastValue = min;
33
- for (var i = 0; i < values.length; i++) {
34
- segments.push({ left: lastValue, right: values[i] });
35
- lastValue = values[i];
41
+ for (var i = 0; i < newValues.length; i++) {
42
+ segments.push({ left: lastValue, right: newValues[i] });
43
+ lastValue = newValues[i];
36
44
  }
37
45
  segments.push({ left: lastValue, right: max });
38
46
  return segments;
@@ -43,18 +51,21 @@ function generateSegments(values, min, max) {
43
51
  */
44
52
  var Track = function (props) {
45
53
  var trackDisplayMode = props.trackDisplayMode, sliderMin = props.sliderMin, sliderMax = props.sliderMax, values = props.values;
46
- var _a = react_1.default.useState(__spreadArray([], values, true).sort(function (a, b) { return a - b; })), currentValues = _a[0], setCurrentValues = _a[1];
54
+ var _a = react_1.default.useState(function () {
55
+ return generateSegments(values, sliderMin, sliderMax);
56
+ }), segments = _a[0], setSegments = _a[1];
47
57
  react_1.default.useEffect(function () {
48
- var newValues = __spreadArray([], values, true);
49
- newValues.sort(function (a, b) { return a - b; });
50
- setCurrentValues(newValues);
51
- }, [values]);
52
- var segments = react_1.default.useMemo(function () { return generateSegments(currentValues, sliderMin, sliderMax); }, [currentValues, sliderMin, sliderMax]);
58
+ setSegments(generateSegments(values, sliderMin, sliderMax));
59
+ }, [values, sliderMin, sliderMax]);
53
60
  return (react_1.default.createElement(react_1.default.Fragment, null, 'none' !== trackDisplayMode &&
54
61
  segments.map(function (segment, index) {
55
- var leftPercent = (100.0 * (segment.left - sliderMin)) / (sliderMax - sliderMin);
56
- var rightPercent = (100.0 * (segment.right - sliderMin)) / (sliderMax - sliderMin);
57
- rightPercent = 100.0 - rightPercent;
62
+ var leftPercent = segment.left >= sliderMin && sliderMax !== sliderMin
63
+ ? (100.0 * (segment.left - sliderMin)) / (sliderMax - sliderMin)
64
+ : 0;
65
+ var rightPercent = segment.right >= sliderMin && sliderMax !== sliderMin
66
+ ? 100.0 -
67
+ (100.0 * (segment.right - sliderMin)) / (sliderMax - sliderMin)
68
+ : 100;
58
69
  return (react_1.default.createElement(react_1.default.Fragment, { key: index }, shouldDisplaySegment(index, trackDisplayMode) ? (react_1.default.createElement("div", { className: 'iui-slider-track', style: { left: "".concat(leftPercent, "%"), right: "".concat(rightPercent, "%") } })) : null));
59
70
  })));
60
71
  };
@@ -186,7 +186,7 @@ var Table = function (props) {
186
186
  }
187
187
  return result;
188
188
  }, {});
189
- var areFiltersSet = allColumns.some(function (column) { return !!column.filterValue; });
189
+ var areFiltersSet = allColumns.some(function (column) { return column.filterValue != null && column.filterValue !== ''; });
190
190
  var onRowClickHandler = react_1.default.useCallback(function (event, row) {
191
191
  var isDisabled = isRowDisabled === null || isRowDisabled === void 0 ? void 0 : isRowDisabled(row.original);
192
192
  if (!isDisabled) {
@@ -302,7 +302,7 @@ var Table = function (props) {
302
302
  className: 'iui-row',
303
303
  });
304
304
  return (react_1.default.createElement("div", __assign({}, headerGroupProps, { key: headerGroupProps.key }), headerGroup.headers.map(function (column, index) {
305
- var columnProps = column.getHeaderProps(__assign(__assign({}, column.getSortByToggleProps()), { className: (0, classnames_1.default)('iui-cell', { 'iui-actionable': column.canSort }, { 'iui-sorted': column.isSorted }, column.columnClassName), style: __assign({}, (0, utils_2.getCellStyle)(column, !!state.isTableResizing)) }));
305
+ var columnProps = column.getHeaderProps(__assign(__assign({}, column.getSortByToggleProps()), { className: (0, classnames_1.default)('iui-cell', { 'iui-actionable': column.canSort }, { 'iui-sorted': column.isSorted }, column.columnClassName), style: __assign(__assign({}, (0, utils_2.getCellStyle)(column, !!state.isTableResizing)), { flexWrap: 'unset' }) }));
306
306
  return (react_1.default.createElement("div", __assign({}, columnProps, column.getDragAndDropProps(), { key: columnProps.key, title: undefined, ref: function (el) {
307
307
  if (el && isResizable) {
308
308
  columnRefs.current[column.id] = el;
@@ -53,11 +53,12 @@ var FilterToggle = function (props) {
53
53
  column.setFilter(undefined);
54
54
  close();
55
55
  }, [close, column]);
56
+ var isColumnFiltered = column.filterValue != null && column.filterValue !== '';
56
57
  return (react_1.default.createElement(react_1.default.Fragment, null, column.canFilter && column.Filter && (react_1.default.createElement(utils_1.Popover, { content: column.render('Filter', { close: close, setFilter: setFilter, clearFilter: clearFilter }), placement: 'bottom-start', visible: isVisible, onClickOutside: close, appendTo: ownerDocument === null || ownerDocument === void 0 ? void 0 : ownerDocument.body },
57
- react_1.default.createElement(Buttons_1.IconButton, __assign({ styleType: 'borderless', isActive: isVisible || column.filterValue, className: (0, classnames_1.default)('iui-filter-button', className), onClick: function (e) {
58
+ react_1.default.createElement(Buttons_1.IconButton, __assign({ styleType: 'borderless', isActive: isVisible || isColumnFiltered, className: (0, classnames_1.default)('iui-filter-button', className), onClick: function (e) {
58
59
  setIsVisible(function (v) { return !v; });
59
60
  // Prevents from triggering sort
60
61
  e.stopPropagation();
61
- } }, rest), column.filterValue ? react_1.default.createElement(Filter_1.default, null) : react_1.default.createElement(FilterHollow_1.default, null))))));
62
+ } }, rest), isColumnFiltered ? react_1.default.createElement(Filter_1.default, null) : react_1.default.createElement(FilterHollow_1.default, null))))));
62
63
  };
63
64
  exports.FilterToggle = FilterToggle;
@@ -1,9 +1,11 @@
1
- /// <reference types="react" />
2
1
  import '@itwin/itwinui-css/css/toast-notification.css';
2
+ import React from 'react';
3
3
  import { ToastProps } from './Toast';
4
4
  import { ToasterSettings } from './Toaster';
5
- declare type ToastWrapperProps = {
6
- toasts: ToastProps[];
7
- } & Pick<ToasterSettings, 'placement'>;
8
- export declare const ToastWrapper: (props: ToastWrapperProps) => JSX.Element;
5
+ declare type ToastPlacement = NonNullable<ToasterSettings['placement']>;
6
+ export declare type ToastWrapperHandle = {
7
+ setToasts: (toasts: ToastProps[]) => void;
8
+ setPlacement: (placement: ToastPlacement) => void;
9
+ };
10
+ export declare const ToastWrapper: React.ForwardRefExoticComponent<React.RefAttributes<ToastWrapperHandle>>;
9
11
  export {};
@@ -23,11 +23,15 @@ require("@itwin/itwinui-css/css/toast-notification.css");
23
23
  var react_1 = __importDefault(require("react"));
24
24
  var classnames_1 = __importDefault(require("classnames"));
25
25
  var Toast_1 = __importDefault(require("./Toast"));
26
- var ToastWrapper = function (props) {
27
- var toasts = props.toasts, _a = props.placement, placement = _a === void 0 ? 'top' : _a;
26
+ exports.ToastWrapper = react_1.default.forwardRef(function (_, ref) {
27
+ var _a = react_1.default.useState([]), toasts = _a[0], setToasts = _a[1];
28
+ var _b = react_1.default.useState('top'), placement = _b[0], setPlacement = _b[1];
28
29
  var placementPosition = placement.startsWith('top') ? 'top' : 'bottom';
30
+ react_1.default.useImperativeHandle(ref, function () { return ({
31
+ setToasts: setToasts,
32
+ setPlacement: setPlacement,
33
+ }); }, []);
29
34
  return (react_1.default.createElement("span", { className: (0, classnames_1.default)("iui-toast-wrapper", "iui-placement-".concat(placement)) }, toasts.map(function (toastProps) {
30
35
  return (react_1.default.createElement(Toast_1.default, __assign({ key: toastProps.id, placementPosition: placementPosition }, toastProps)));
31
36
  })));
32
- };
33
- exports.ToastWrapper = ToastWrapper;
37
+ });
@@ -20,6 +20,9 @@ export default class Toaster {
20
20
  private toasts;
21
21
  private lastId;
22
22
  private settings;
23
+ private toastsRef;
24
+ private isInitialized;
25
+ private asyncInit;
23
26
  /**
24
27
  * Set global Toaster settings for toasts order and placement.
25
28
  * Settings will be applied to new toasts on the page.
@@ -34,24 +34,49 @@ var ToastWrapper_1 = require("./ToastWrapper");
34
34
  var TOASTS_CONTAINER_ID = 'iui-toasts-container';
35
35
  var Toaster = /** @class */ (function () {
36
36
  function Toaster() {
37
+ var _this = this;
37
38
  this.toasts = [];
38
39
  this.lastId = 0;
39
40
  this.settings = {
40
41
  order: 'descending',
41
42
  placement: 'top',
42
43
  };
44
+ this.toastsRef = react_1.default.createRef();
45
+ this.isInitialized = false;
46
+ // Create container on demand.
47
+ // Cannot do it in constructor, because SSG/SSR apps would fail.
48
+ this.asyncInit = new Promise(function (resolve) {
49
+ if (_this.isInitialized) {
50
+ resolve();
51
+ return;
52
+ }
53
+ var container = (0, utils_1.getContainer)(TOASTS_CONTAINER_ID);
54
+ if (!container) {
55
+ // should never happen
56
+ resolve();
57
+ return;
58
+ }
59
+ _this.isInitialized = true;
60
+ react_dom_1.default.render(react_1.default.createElement(ToastWrapper_1.ToastWrapper, { ref: _this.toastsRef }), container);
61
+ resolve();
62
+ });
43
63
  }
44
64
  /**
45
65
  * Set global Toaster settings for toasts order and placement.
46
66
  * Settings will be applied to new toasts on the page.
47
67
  */
48
68
  Toaster.prototype.setSettings = function (newSettings) {
69
+ var _this = this;
49
70
  var _a, _b, _c;
50
71
  (_a = newSettings.placement) !== null && _a !== void 0 ? _a : (newSettings.placement = this.settings.placement);
51
72
  (_b = newSettings.order) !== null && _b !== void 0 ? _b : (newSettings.order = ((_c = newSettings.placement) === null || _c === void 0 ? void 0 : _c.startsWith('bottom'))
52
73
  ? 'ascending'
53
74
  : 'descending');
54
75
  this.settings = newSettings;
76
+ this.asyncInit.then(function () {
77
+ var _a, _b;
78
+ (_a = _this.toastsRef.current) === null || _a === void 0 ? void 0 : _a.setPlacement((_b = _this.settings.placement) !== null && _b !== void 0 ? _b : 'top');
79
+ });
55
80
  };
56
81
  Toaster.prototype.positive = function (content, options) {
57
82
  return this.createToast(content, 'positive', options);
@@ -84,11 +109,11 @@ var Toaster = /** @class */ (function () {
84
109
  this.updateView();
85
110
  };
86
111
  Toaster.prototype.updateView = function () {
87
- var container = (0, utils_1.getContainer)(TOASTS_CONTAINER_ID);
88
- if (!container) {
89
- return;
90
- }
91
- react_dom_1.default.render(react_1.default.createElement(ToastWrapper_1.ToastWrapper, { toasts: this.toasts, placement: this.settings.placement }), container);
112
+ var _this = this;
113
+ this.asyncInit.then(function () {
114
+ var _a;
115
+ (_a = _this.toastsRef.current) === null || _a === void 0 ? void 0 : _a.setToasts(_this.toasts);
116
+ });
92
117
  };
93
118
  Toaster.prototype.closeToast = function (toastId) {
94
119
  this.toasts = this.toasts.map(function (toast) {
@@ -43,7 +43,7 @@ export declare const Popover: React.ForwardRefExoticComponent<Pick<{
43
43
  * @see [tippy.js placement prop](https://atomiks.github.io/tippyjs/v6/all-props/#placement).
44
44
  */
45
45
  placement?: import("@popperjs/core").Placement | undefined;
46
- } & Omit<TippyProps, "placement" | "trigger" | "visible">, "disabled" | "placement" | "trigger" | "visible" | "content" | "render" | "animateFill" | "appendTo" | "aria" | "delay" | "duration" | "followCursor" | "getReferenceClientRect" | "hideOnClick" | "ignoreAttributes" | "inlinePositioning" | "interactive" | "interactiveBorder" | "interactiveDebounce" | "moveTransition" | "offset" | "plugins" | "popperOptions" | "showOnCreate" | "sticky" | "touch" | "triggerTarget" | "onAfterUpdate" | "onBeforeUpdate" | "onCreate" | "onDestroy" | "onHidden" | "onHide" | "onMount" | "onShow" | "onShown" | "onTrigger" | "onUntrigger" | "onClickOutside" | "allowHTML" | "animation" | "arrow" | "inertia" | "maxWidth" | "role" | "theme" | "zIndex" | "children" | "className" | "singleton" | "reference"> & React.RefAttributes<unknown>>;
46
+ } & Omit<TippyProps, "placement" | "trigger" | "visible">, "disabled" | "children" | "placement" | "trigger" | "visible" | "content" | "render" | "animateFill" | "appendTo" | "aria" | "delay" | "duration" | "followCursor" | "getReferenceClientRect" | "hideOnClick" | "ignoreAttributes" | "inlinePositioning" | "interactive" | "interactiveBorder" | "interactiveDebounce" | "moveTransition" | "offset" | "plugins" | "popperOptions" | "showOnCreate" | "sticky" | "touch" | "triggerTarget" | "onAfterUpdate" | "onBeforeUpdate" | "onCreate" | "onDestroy" | "onHidden" | "onHide" | "onMount" | "onShow" | "onShown" | "onTrigger" | "onUntrigger" | "onClickOutside" | "allowHTML" | "animation" | "arrow" | "inertia" | "maxWidth" | "role" | "theme" | "zIndex" | "className" | "singleton" | "reference"> & React.RefAttributes<unknown>>;
47
47
  /**
48
48
  * Plugin to hide Popover when either Esc key is pressed,
49
49
  * or when the content inside is not tabbable and Tab key is pressed.
@@ -16,6 +16,10 @@ export declare type VirtualScrollProps = {
16
16
  * @default 10
17
17
  */
18
18
  bufferSize?: number;
19
+ /**
20
+ * Index of the first element on initial render.
21
+ */
22
+ scrollToIndex?: number;
19
23
  } & React.ComponentPropsWithRef<'div'>;
20
24
  /**
21
25
  * `VirtualScroll` component is used to render a huge amount of items in the DOM. It renders only the ones which are visible
@@ -38,5 +42,35 @@ export declare type VirtualScrollProps = {
38
42
  * />
39
43
  * @private
40
44
  */
41
- export declare const VirtualScroll: React.ForwardRefExoticComponent<Pick<VirtualScrollProps, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "itemsLength" | "itemRenderer" | "bufferSize"> & React.RefAttributes<HTMLDivElement>>;
45
+ export declare const VirtualScroll: React.ForwardRefExoticComponent<Pick<VirtualScrollProps, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "itemsLength" | "itemRenderer" | "bufferSize" | "scrollToIndex"> & React.RefAttributes<HTMLDivElement>>;
46
+ /**
47
+ * `useVirtualization` is used for efficiently rendering only the visible rows from a large list.
48
+ * It returns `outerProps` and `innerProps`, which need to be applied on 2 container elements and `visibleChildren` which is a list of virtualized items.
49
+ * @example
50
+ * const itemRenderer = React.useCallback((index: number) => (
51
+ * <li key={index}>
52
+ * This is my item #{index}
53
+ * </li>
54
+ * ), [])
55
+ *
56
+ * const { outerProps, innerProps, visibleChildren } = useVirtualization({itemsLength: 1000, itemRenderer: itemRenderer});
57
+ * return (
58
+ * <div {...outerProps}>
59
+ * <ul {...innerProps}>
60
+ * {visibleChildren}
61
+ * </ul>
62
+ * </div>
63
+ * );
64
+ * @private
65
+ */
66
+ export declare const useVirtualization: (props: VirtualScrollProps) => {
67
+ outerProps: React.HTMLAttributes<HTMLElement>;
68
+ innerProps: {
69
+ readonly style: {
70
+ readonly willChange: "transform";
71
+ };
72
+ readonly ref: (instance: HTMLElement | null) => void;
73
+ };
74
+ visibleChildren: JSX.Element[];
75
+ };
42
76
  export default VirtualScroll;
@@ -25,12 +25,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
25
25
  return (mod && mod.__esModule) ? mod : { "default": mod };
26
26
  };
27
27
  Object.defineProperty(exports, "__esModule", { value: true });
28
- exports.VirtualScroll = void 0;
28
+ exports.useVirtualization = exports.VirtualScroll = void 0;
29
29
  /*---------------------------------------------------------------------------------------------
30
30
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
31
31
  * See LICENSE.md in the project root for license terms and full copyright notice.
32
32
  *--------------------------------------------------------------------------------------------*/
33
33
  var react_1 = __importDefault(require("react"));
34
+ var hooks_1 = require("../hooks");
34
35
  var useResizeObserver_1 = require("../hooks/useResizeObserver");
35
36
  var getScrollableParent = function (element, ownerDocument) {
36
37
  if (ownerDocument === void 0) { ownerDocument = document; }
@@ -52,6 +53,14 @@ var getElementHeight = function (element) {
52
53
  var _a;
53
54
  return (_a = element === null || element === void 0 ? void 0 : element.getBoundingClientRect().height) !== null && _a !== void 0 ? _a : 0;
54
55
  };
56
+ var getElementHeightWithMargins = function (element) {
57
+ if (!element) {
58
+ return undefined;
59
+ }
60
+ var margin = parseFloat(getElementStyle(element, 'margin-top')) +
61
+ parseFloat(getElementStyle(element, 'margin-bottom'));
62
+ return getElementHeight(element) + (isNaN(margin) ? 0 : margin);
63
+ };
55
64
  var getNumberOfNodesInHeight = function (childHeight, totalHeight) {
56
65
  if (!childHeight) {
57
66
  return 0;
@@ -59,7 +68,10 @@ var getNumberOfNodesInHeight = function (childHeight, totalHeight) {
59
68
  return Math.floor(totalHeight / childHeight);
60
69
  };
61
70
  var getTranslateValue = function (childHeight, startIndex) {
62
- return childHeight * startIndex;
71
+ if (startIndex > 0) {
72
+ return childHeight * startIndex;
73
+ }
74
+ return 0;
63
75
  };
64
76
  var getVisibleNodeCount = function (childHeight, startIndex, childrenLength, scrollContainer) {
65
77
  return Math.min(childrenLength - startIndex, getNumberOfNodesInHeight(childHeight, getElementHeight(scrollContainer)));
@@ -85,21 +97,55 @@ var getVisibleNodeCount = function (childHeight, startIndex, childrenLength, scr
85
97
  * />
86
98
  * @private
87
99
  */
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];
100
+ exports.VirtualScroll = react_1.default.forwardRef(function (props, ref) {
101
+ var _a = (0, exports.useVirtualization)(props), innerProps = _a.innerProps, outerProps = _a.outerProps, visibleChildren = _a.visibleChildren;
102
+ return (react_1.default.createElement("div", __assign({}, outerProps, { ref: ref }),
103
+ react_1.default.createElement("div", __assign({}, innerProps), visibleChildren)));
104
+ });
105
+ /**
106
+ * `useVirtualization` is used for efficiently rendering only the visible rows from a large list.
107
+ * It returns `outerProps` and `innerProps`, which need to be applied on 2 container elements and `visibleChildren` which is a list of virtualized items.
108
+ * @example
109
+ * const itemRenderer = React.useCallback((index: number) => (
110
+ * <li key={index}>
111
+ * This is my item #{index}
112
+ * </li>
113
+ * ), [])
114
+ *
115
+ * const { outerProps, innerProps, visibleChildren } = useVirtualization({itemsLength: 1000, itemRenderer: itemRenderer});
116
+ * return (
117
+ * <div {...outerProps}>
118
+ * <ul {...innerProps}>
119
+ * {visibleChildren}
120
+ * </ul>
121
+ * </div>
122
+ * );
123
+ * @private
124
+ */
125
+ var useVirtualization = function (props) {
126
+ var itemsLength = props.itemsLength, itemRenderer = props.itemRenderer, _a = props.bufferSize, bufferSize = _a === void 0 ? 10 : _a, scrollToIndex = props.scrollToIndex, style = props.style, rest = __rest(props, ["itemsLength", "itemRenderer", "bufferSize", "scrollToIndex", "style"]);
127
+ var _b = react_1.default.useState(0), startNode = _b[0], setStartNode = _b[1];
128
+ var _c = react_1.default.useState(0), visibleNodeCount = _c[0], setVisibleNodeCount = _c[1];
92
129
  var scrollContainer = react_1.default.useRef();
93
130
  var parentRef = react_1.default.useRef(null);
94
- var childHeight = react_1.default.useRef(0);
131
+ var childHeight = react_1.default.useRef({ first: 0, middle: 0, last: 0 });
95
132
  var onScrollRef = react_1.default.useRef();
96
133
  // Used only to recalculate on resize
97
- var _e = react_1.default.useState(0), scrollContainerHeight = _e[0], setScrollContainerHeight = _e[1];
134
+ var _d = react_1.default.useState(0), scrollContainerHeight = _d[0], setScrollContainerHeight = _d[1];
135
+ var visibleIndex = react_1.default.useRef({ start: 0, end: 0 });
136
+ // Used to mark when scroll container has height (updated by resize observer)
137
+ // because before that calculations are not right
138
+ var _e = react_1.default.useState(false), isMounted = _e[0], setIsMounted = _e[1];
98
139
  var onResize = react_1.default.useCallback(function (_a) {
99
140
  var height = _a.height;
141
+ // Initial value returned by resize observer is 0
142
+ // So wait for the next one
143
+ if (height > 0) {
144
+ setIsMounted(true);
145
+ }
100
146
  setScrollContainerHeight(height);
101
147
  }, []);
102
- var resizeRef = (0, useResizeObserver_1.useResizeObserver)(onResize)[0];
148
+ var _f = (0, useResizeObserver_1.useResizeObserver)(onResize), resizeRef = _f[0], resizeObserver = _f[1];
103
149
  // Find scrollable parent
104
150
  // Needed only on init
105
151
  react_1.default.useLayoutEffect(function () {
@@ -108,6 +154,14 @@ exports.VirtualScroll = react_1.default.forwardRef(function (_a, ref) {
108
154
  scrollContainer.current = scrollableParent;
109
155
  resizeRef(scrollableParent);
110
156
  }, [resizeRef]);
157
+ // Stop watching resize, when virtual scroll is unmounted
158
+ react_1.default.useLayoutEffect(function () {
159
+ return function () { return resizeObserver === null || resizeObserver === void 0 ? void 0 : resizeObserver.disconnect(); };
160
+ }, [resizeObserver]);
161
+ var getScrollableContainer = function () {
162
+ var _a, _b;
163
+ return (_a = scrollContainer.current) !== null && _a !== void 0 ? _a : (_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.scrollingElement;
164
+ };
111
165
  var visibleChildren = react_1.default.useMemo(function () {
112
166
  var arr = [];
113
167
  var endIndex = Math.min(itemsLength, startNode + visibleNodeCount + bufferSize * 2);
@@ -118,27 +172,42 @@ exports.VirtualScroll = react_1.default.forwardRef(function (_a, ref) {
118
172
  }, [itemsLength, itemRenderer, bufferSize, startNode, visibleNodeCount]);
119
173
  // Get child height when children available
120
174
  react_1.default.useLayoutEffect(function () {
175
+ var _a, _b, _c, _d, _e, _f;
121
176
  if (!parentRef.current || !visibleChildren.length) {
122
177
  return;
123
178
  }
124
179
  var firstChild = parentRef.current.children.item(0);
125
- childHeight.current = Number(getElementHeight(firstChild).toFixed(2));
180
+ var secondChild = parentRef.current.children.item(1);
181
+ var lastChild = parentRef.current.children.item(parentRef.current.children.length - 1);
182
+ var firstChildHeight = Number((_b = (_a = getElementHeightWithMargins(firstChild)) === null || _a === void 0 ? void 0 : _a.toFixed(2)) !== null && _b !== void 0 ? _b : 0);
183
+ childHeight.current = {
184
+ first: firstChildHeight,
185
+ middle: Number((_d = (_c = getElementHeightWithMargins(secondChild)) === null || _c === void 0 ? void 0 : _c.toFixed(2)) !== null && _d !== void 0 ? _d : firstChildHeight),
186
+ last: Number((_f = (_e = getElementHeightWithMargins(lastChild)) === null || _e === void 0 ? void 0 : _e.toFixed(2)) !== null && _f !== void 0 ? _f : firstChildHeight),
187
+ };
126
188
  }, [visibleChildren.length]);
127
189
  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;
190
+ var scrollableContainer = getScrollableContainer();
130
191
  if (!scrollableContainer) {
131
192
  return;
132
193
  }
133
- var start = getNumberOfNodesInHeight(childHeight.current, scrollableContainer.scrollTop);
134
- var startIndex = Math.max(0, start - bufferSize);
194
+ var start = getNumberOfNodesInHeight(childHeight.current.middle, Math.round(scrollableContainer.scrollTop));
195
+ var visibleNodes = getVisibleNodeCount(childHeight.current.middle, start, itemsLength, scrollableContainer);
196
+ // If there are less items at the end than buffer size
197
+ // show more items at the start.
198
+ // Have boundaries for edge cases, e.g. 1 item length
199
+ var startIndex = Math.min(Math.max(0, start - bufferSize), Math.max(0, itemsLength - bufferSize * 2 - visibleNodes));
200
+ visibleIndex.current = { start: start, end: start + visibleNodes };
135
201
  setStartNode(startIndex);
136
- setVisibleNodeCount(getVisibleNodeCount(childHeight.current, start, itemsLength, scrollableContainer));
202
+ setVisibleNodeCount(visibleNodes);
137
203
  if (!parentRef.current) {
138
204
  return;
139
205
  }
140
- parentRef.current.style.transform = "translateY(".concat(getTranslateValue(childHeight.current, startIndex), "px)");
206
+ parentRef.current.style.transform = "translateY(".concat(getTranslateValue(childHeight.current.middle, startIndex), "px)");
141
207
  }, [bufferSize, itemsLength]);
208
+ var onScroll = react_1.default.useCallback(function () {
209
+ updateVirtualScroll();
210
+ }, [updateVirtualScroll]);
142
211
  var removeScrollListener = react_1.default.useCallback(function () {
143
212
  var _a, _b;
144
213
  if (!onScrollRef.current) {
@@ -153,22 +222,86 @@ exports.VirtualScroll = react_1.default.forwardRef(function (_a, ref) {
153
222
  react_1.default.useLayoutEffect(function () {
154
223
  var _a, _b;
155
224
  removeScrollListener();
156
- onScrollRef.current = updateVirtualScroll;
225
+ onScrollRef.current = onScroll;
157
226
  if (!scrollContainer.current ||
158
227
  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);
228
+ (_b = parentRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.addEventListener('scroll', onScroll);
160
229
  }
161
230
  else {
162
- scrollContainer.current.addEventListener('scroll', updateVirtualScroll);
231
+ scrollContainer.current.addEventListener('scroll', onScroll);
163
232
  }
164
233
  return removeScrollListener;
165
- }, [updateVirtualScroll, removeScrollListener]);
234
+ }, [onScroll, removeScrollListener]);
235
+ react_1.default.useLayoutEffect(function () {
236
+ if (!isMounted) {
237
+ return;
238
+ }
239
+ var scrollableContainer = getScrollableContainer();
240
+ if (!scrollableContainer || scrollToIndex == null) {
241
+ return;
242
+ }
243
+ // if `scrollToIndex` is not visible, scroll to it
244
+ if (scrollToIndex > visibleIndex.current.end ||
245
+ scrollToIndex < visibleIndex.current.start) {
246
+ var indexDiff = scrollToIndex > visibleIndex.current.end
247
+ ? scrollToIndex - visibleIndex.current.end
248
+ : scrollToIndex - visibleIndex.current.start;
249
+ if (scrollToIndex === 0) {
250
+ scrollableContainer.scrollTo({ top: 0 });
251
+ return;
252
+ }
253
+ // If go down: add to the existing scrollTop needed height
254
+ // If go up: calculate the exact scroll top
255
+ scrollableContainer.scrollTo({
256
+ top: indexDiff > 0
257
+ ? Math.ceil(scrollableContainer.scrollTop) +
258
+ indexDiff * childHeight.current.middle
259
+ : scrollToIndex * childHeight.current.middle,
260
+ });
261
+ }
262
+ // if `scrollToIndex` is the first visible node
263
+ // ensure it is fully visible
264
+ if (scrollToIndex === visibleIndex.current.start) {
265
+ var roundedScrollTop = Math.round(scrollableContainer.scrollTop);
266
+ var diff = roundedScrollTop % childHeight.current.middle;
267
+ diff > 0 &&
268
+ scrollableContainer.scrollTo({
269
+ top: roundedScrollTop - diff,
270
+ });
271
+ return;
272
+ }
273
+ // if `scrollToIndex` is the last visible node
274
+ // ensure it is fully visible
275
+ if (scrollToIndex === visibleIndex.current.end) {
276
+ var diff = (scrollableContainer.offsetHeight - childHeight.current.first) %
277
+ childHeight.current.middle;
278
+ var roundedScrollTop = Math.ceil(scrollableContainer.scrollTop);
279
+ var scrollTopMod = roundedScrollTop % childHeight.current.middle;
280
+ if (diff > 0 && scrollTopMod === 0) {
281
+ scrollableContainer.scrollTo({
282
+ top: roundedScrollTop + childHeight.current.middle - diff,
283
+ });
284
+ }
285
+ }
286
+ }, [scrollToIndex, isMounted]);
166
287
  react_1.default.useLayoutEffect(function () {
288
+ if (!scrollContainerHeight) {
289
+ return;
290
+ }
167
291
  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
- });
292
+ }, [scrollContainerHeight, updateVirtualScroll]);
293
+ return {
294
+ outerProps: __assign({ style: __assign({ overflow: 'hidden', minHeight: itemsLength > 1
295
+ ? Math.max(itemsLength - 2, 0) * childHeight.current.middle +
296
+ childHeight.current.first +
297
+ childHeight.current.last
298
+ : childHeight.current.middle, width: '100%' }, style) }, rest),
299
+ innerProps: {
300
+ style: { willChange: 'transform' },
301
+ ref: (0, hooks_1.mergeRefs)(parentRef), // convert object ref to callback ref for better types
302
+ },
303
+ visibleChildren: visibleChildren,
304
+ };
305
+ };
306
+ exports.useVirtualization = useVirtualization;
174
307
  exports.default = exports.VirtualScroll;
@@ -6,3 +6,4 @@ export * from './useContainerWidth';
6
6
  export * from './useTheme';
7
7
  export * from './useIntersection';
8
8
  export * from './useMediaQuery';
9
+ export * from './useSafeContext';
@@ -26,3 +26,4 @@ __exportStar(require("./useContainerWidth"), exports);
26
26
  __exportStar(require("./useTheme"), exports);
27
27
  __exportStar(require("./useIntersection"), exports);
28
28
  __exportStar(require("./useMediaQuery"), exports);
29
+ __exportStar(require("./useSafeContext"), exports);
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ /**
3
+ * Wrapper hook around `useContext` that throws an error if the context is not provided.
4
+ * @param context Context to use. Must have a `displayName` for useful errors.
5
+ */
6
+ export declare const useSafeContext: <T>(context: React.Context<T>) => NonNullable<T>;