@itwin/itwinui-react 1.43.0 → 1.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +2 -2
  3. package/cjs/core/Dialog/DialogTitleBar.js +2 -2
  4. package/cjs/core/Dialog/DialogTitleBarTitle.js +2 -2
  5. package/cjs/core/Menu/Menu.js +0 -3
  6. package/cjs/core/Select/Select.d.ts +23 -8
  7. package/cjs/core/Select/Select.js +82 -25
  8. package/cjs/core/Select/SelectTag.d.ts +15 -0
  9. package/cjs/core/Select/SelectTag.js +48 -0
  10. package/cjs/core/Select/index.d.ts +1 -1
  11. package/cjs/core/Table/Table.d.ts +3 -1
  12. package/cjs/core/Table/Table.js +2 -1
  13. package/cjs/core/Table/TableRowMemoized.d.ts +10 -2
  14. package/cjs/core/Table/TableRowMemoized.js +23 -7
  15. package/cjs/core/Table/cells/DefaultCell.d.ts +14 -1
  16. package/cjs/core/Table/cells/DefaultCell.js +10 -4
  17. package/cjs/core/index.d.ts +1 -1
  18. package/cjs/core/utils/components/MiddleTextTruncation.d.ts +15 -1
  19. package/cjs/core/utils/components/MiddleTextTruncation.js +14 -3
  20. package/cjs/core/utils/hooks/useOverflow.js +4 -1
  21. package/esm/core/Dialog/DialogTitleBar.js +1 -1
  22. package/esm/core/Dialog/DialogTitleBarTitle.js +1 -1
  23. package/esm/core/Menu/Menu.js +0 -3
  24. package/esm/core/Select/Select.d.ts +23 -8
  25. package/esm/core/Select/Select.js +83 -26
  26. package/esm/core/Select/SelectTag.d.ts +15 -0
  27. package/esm/core/Select/SelectTag.js +41 -0
  28. package/esm/core/Select/index.d.ts +1 -1
  29. package/esm/core/Table/Table.d.ts +3 -1
  30. package/esm/core/Table/Table.js +2 -1
  31. package/esm/core/Table/TableRowMemoized.d.ts +10 -2
  32. package/esm/core/Table/TableRowMemoized.js +23 -7
  33. package/esm/core/Table/cells/DefaultCell.d.ts +14 -1
  34. package/esm/core/Table/cells/DefaultCell.js +10 -4
  35. package/esm/core/index.d.ts +1 -1
  36. package/esm/core/utils/components/MiddleTextTruncation.d.ts +15 -1
  37. package/esm/core/utils/components/MiddleTextTruncation.js +14 -3
  38. package/esm/core/utils/hooks/useOverflow.js +4 -1
  39. package/package.json +1 -1
@@ -76,7 +76,10 @@ var useOverflow = function (items, disabled, orientation) {
76
76
  }
77
77
  else if (needsFullRerender.current) {
78
78
  var childrenSize = Array.from(containerRef.current.children).reduce(function (sum, child) { return sum + child["offset".concat(dimension)]; }, 0);
79
- var avgItemSize = childrenSize / visibleCount;
79
+ // Previous `useEffect` might have updated visible count, but we still have old one
80
+ // If it is 0, lets try to update it with items length.
81
+ var currentVisibleCount = visibleCount || Math.min(items.length, STARTING_MAX_ITEMS_COUNT);
82
+ var avgItemSize = childrenSize / currentVisibleCount;
80
83
  var visibleItems = Math.floor(availableSize / avgItemSize);
81
84
  if (!isNaN(visibleItems)) {
82
85
  // Doubling the visible items to overflow the container. Just to be safe.
@@ -27,7 +27,7 @@ var __rest = (this && this.__rest) || function (s, e) {
27
27
  import React from 'react';
28
28
  import cx from 'classnames';
29
29
  import SvgClose from '@itwin/itwinui-icons-react/cjs/icons/Close';
30
- import { useTheme } from '@storybook/theming';
30
+ import { useTheme } from '../utils';
31
31
  import { IconButton } from '../Buttons';
32
32
  import '@itwin/itwinui-css/css/dialog.css';
33
33
  import { useDialogContext } from './DialogContext';
@@ -26,7 +26,7 @@ var __rest = (this && this.__rest) || function (s, e) {
26
26
  *--------------------------------------------------------------------------------------------*/
27
27
  import React from 'react';
28
28
  import cx from 'classnames';
29
- import { useTheme } from '@storybook/theming';
29
+ import { useTheme } from '../utils';
30
30
  import '@itwin/itwinui-css/css/dialog.css';
31
31
  /**
32
32
  * Dialog title bar. Recommended to be used as a child of `Dialog`.
@@ -37,9 +37,6 @@ export var Menu = React.forwardRef(function (props, ref) {
37
37
  var _c = React.useState(), focusedIndex = _c[0], setFocusedIndex = _c[1];
38
38
  var menuRef = React.useRef(null);
39
39
  var refs = useMergedRefs(menuRef, ref);
40
- React.useEffect(function () {
41
- setFocusedIndex(null);
42
- }, [children]);
43
40
  var getFocusableNodes = React.useCallback(function () {
44
41
  var focusableItems = getFocusableElements(menuRef.current);
45
42
  // Filter out focusable elements that are inside each menu item, e.g. checkbox, anchor
@@ -44,19 +44,38 @@ export declare type SelectOption<T> = {
44
44
  */
45
45
  [key: string]: unknown;
46
46
  } & CommonProps;
47
- export declare type SelectProps<T> = {
47
+ export declare type SelectValueChangeEvent = 'added' | 'removed';
48
+ export declare type SelectMultipleTypeProps<T> = {
48
49
  /**
49
- * Array of options that populates the select menu.
50
+ * Enable multiple selection.
51
+ * @default false
50
52
  */
51
- options: SelectOption<T>[];
53
+ multiple?: false;
54
+ /**
55
+ * Custom renderer for the selected item in select.
56
+ * If `multiple` is enabled, it will give array of options to render.
57
+ */
58
+ selectedItemRenderer?: (option: SelectOption<T>) => JSX.Element;
52
59
  /**
53
60
  * Selected option value.
61
+ * If `multiple` is enabled, it is an array of values.
54
62
  */
55
63
  value?: T;
56
64
  /**
57
65
  * Callback function handling change event on select.
58
66
  */
59
67
  onChange?: (value: T) => void;
68
+ } | {
69
+ multiple: true;
70
+ selectedItemRenderer?: (options: SelectOption<T>[]) => JSX.Element;
71
+ value?: T[];
72
+ onChange?: (value: T, event: SelectValueChangeEvent) => void;
73
+ };
74
+ export declare type SelectProps<T> = {
75
+ /**
76
+ * Array of options that populates the select menu.
77
+ */
78
+ options: SelectOption<T>[];
60
79
  /**
61
80
  * Placeholder when no item is selected.
62
81
  */
@@ -79,10 +98,6 @@ export declare type SelectProps<T> = {
79
98
  * Custom renderer for an item in the dropdown list. `MenuItem` item props are going to be populated if not provided.
80
99
  */
81
100
  itemRenderer?: (option: SelectOption<T>, itemProps: ItemRendererProps) => JSX.Element;
82
- /**
83
- * Custom renderer for the selected item in select.
84
- */
85
- selectedItemRenderer?: (option: SelectOption<T>) => JSX.Element;
86
101
  /**
87
102
  * Custom class for menu.
88
103
  */
@@ -96,7 +111,7 @@ export declare type SelectProps<T> = {
96
111
  * @see [tippy.js props](https://atomiks.github.io/tippyjs/v6/all-props/)
97
112
  */
98
113
  popoverProps?: Omit<PopoverProps, 'onShow' | 'onHide' | 'disabled'>;
99
- } & Pick<PopoverProps, 'onShow' | 'onHide'> & Omit<React.ComponentPropsWithoutRef<'div'>, 'size' | 'disabled' | 'placeholder' | 'onChange'>;
114
+ } & SelectMultipleTypeProps<T> & Pick<PopoverProps, 'onShow' | 'onHide'> & Omit<React.ComponentPropsWithoutRef<'div'>, 'size' | 'disabled' | 'placeholder' | 'onChange'>;
100
115
  /**
101
116
  * Select component to select value from options.
102
117
  * Generic type is used for value. It prevents you from mistakenly using other types in `options`, `value` and `onChange`.
@@ -28,9 +28,17 @@ import React from 'react';
28
28
  import cx from 'classnames';
29
29
  import { DropdownMenu } from '../DropdownMenu';
30
30
  import { MenuItem } from '../Menu/MenuItem';
31
- import { useTheme } from '../utils';
31
+ import { useTheme, useOverflow, } from '../utils';
32
32
  import '@itwin/itwinui-css/css/inputs.css';
33
33
  import SvgCaretDownSmall from '@itwin/itwinui-icons-react/cjs/icons/CaretDownSmall';
34
+ import SelectTag from './SelectTag';
35
+ var isMultipleEnabled = function (variable, multiple) {
36
+ return multiple;
37
+ };
38
+ // Type guard for multiple did not work
39
+ var isSingleOnChange = function (onChange, multiple) {
40
+ return !multiple;
41
+ };
34
42
  /**
35
43
  * Select component to select value from options.
36
44
  * Generic type is used for value. It prevents you from mistakenly using other types in `options`, `value` and `onChange`.
@@ -83,13 +91,13 @@ import SvgCaretDownSmall from '@itwin/itwinui-icons-react/cjs/icons/CaretDownSma
83
91
  export var Select = function (props) {
84
92
  var _a;
85
93
  var _b;
86
- var options = props.options, value = props.value, onChange = props.onChange, placeholder = props.placeholder, _c = props.disabled, disabled = _c === void 0 ? false : _c, size = props.size, _d = props.setFocus, setFocus = _d === void 0 ? false : _d, itemRenderer = props.itemRenderer, selectedItemRenderer = props.selectedItemRenderer, className = props.className, style = props.style, menuClassName = props.menuClassName, menuStyle = props.menuStyle, onShow = props.onShow, onHide = props.onHide, popoverProps = props.popoverProps, rest = __rest(props, ["options", "value", "onChange", "placeholder", "disabled", "size", "setFocus", "itemRenderer", "selectedItemRenderer", "className", "style", "menuClassName", "menuStyle", "onShow", "onHide", "popoverProps"]);
94
+ var options = props.options, value = props.value, onChange = props.onChange, placeholder = props.placeholder, _c = props.disabled, disabled = _c === void 0 ? false : _c, size = props.size, _d = props.setFocus, setFocus = _d === void 0 ? false : _d, itemRenderer = props.itemRenderer, selectedItemRenderer = props.selectedItemRenderer, className = props.className, style = props.style, menuClassName = props.menuClassName, menuStyle = props.menuStyle, onShow = props.onShow, onHide = props.onHide, popoverProps = props.popoverProps, _e = props.multiple, multiple = _e === void 0 ? false : _e, rest = __rest(props, ["options", "value", "onChange", "placeholder", "disabled", "size", "setFocus", "itemRenderer", "selectedItemRenderer", "className", "style", "menuClassName", "menuStyle", "onShow", "onHide", "popoverProps", "multiple"]);
87
95
  useTheme();
88
- var _e = React.useState((_b = popoverProps === null || popoverProps === void 0 ? void 0 : popoverProps.visible) !== null && _b !== void 0 ? _b : false), isOpen = _e[0], setIsOpen = _e[1];
96
+ var _f = React.useState((_b = popoverProps === null || popoverProps === void 0 ? void 0 : popoverProps.visible) !== null && _b !== void 0 ? _b : false), isOpen = _f[0], setIsOpen = _f[1];
89
97
  React.useEffect(function () {
90
98
  setIsOpen(function (open) { var _a; return (_a = popoverProps === null || popoverProps === void 0 ? void 0 : popoverProps.visible) !== null && _a !== void 0 ? _a : open; });
91
99
  }, [popoverProps]);
92
- var _f = React.useState(0), minWidth = _f[0], setMinWidth = _f[1];
100
+ var _g = React.useState(0), minWidth = _g[0], setMinWidth = _g[1];
93
101
  var toggle = function () { return setIsOpen(function (open) { return !open; }); };
94
102
  var selectRef = React.useRef(null);
95
103
  var toggleButtonRef = React.useRef(null);
@@ -116,8 +124,10 @@ export var Select = function (props) {
116
124
  case 'Enter':
117
125
  case ' ':
118
126
  case 'Spacebar':
119
- toggle();
120
- event.preventDefault();
127
+ if (event.target === selectRef.current) {
128
+ toggle();
129
+ event.preventDefault();
130
+ }
121
131
  break;
122
132
  default:
123
133
  break;
@@ -125,20 +135,41 @@ export var Select = function (props) {
125
135
  };
126
136
  var menuItems = React.useCallback(function (close) {
127
137
  return options.map(function (option, index) {
128
- var isSelected = value === option.value;
138
+ var _a;
139
+ var isSelected = isMultipleEnabled(value, multiple)
140
+ ? (_a = value === null || value === void 0 ? void 0 : value.includes(option.value)) !== null && _a !== void 0 ? _a : false
141
+ : value === option.value;
129
142
  var menuItem = itemRenderer ? (itemRenderer(option, { close: close, isSelected: isSelected })) : (React.createElement(MenuItem, null, option.label));
130
- return React.cloneElement(menuItem, __assign(__assign({ key: "".concat(option.label, "-").concat(index), isSelected: isSelected, onClick: function () {
131
- !option.disabled && (onChange === null || onChange === void 0 ? void 0 : onChange(option.value));
132
- close();
133
- }, ref: function (el) { return isSelected && (el === null || el === void 0 ? void 0 : el.scrollIntoView()); }, role: 'option' }, option), menuItem.props));
143
+ var label = option.label, restOption = __rest(option, ["label"]);
144
+ return React.cloneElement(menuItem, __assign(__assign({ key: "".concat(label, "-").concat(index), isSelected: isSelected, onClick: function () {
145
+ if (option.disabled) {
146
+ return;
147
+ }
148
+ if (isSingleOnChange(onChange, multiple)) {
149
+ onChange === null || onChange === void 0 ? void 0 : onChange(option.value);
150
+ close();
151
+ }
152
+ else {
153
+ onChange === null || onChange === void 0 ? void 0 : onChange(option.value, isSelected ? 'removed' : 'added');
154
+ }
155
+ }, ref: function (el) {
156
+ if (isSelected && !multiple) {
157
+ el === null || el === void 0 ? void 0 : el.scrollIntoView();
158
+ }
159
+ }, role: 'option' }, restOption), menuItem.props));
134
160
  });
135
- }, [itemRenderer, onChange, options, value]);
136
- var selectedItem = React.useMemo(function () {
161
+ }, [itemRenderer, multiple, onChange, options, value]);
162
+ var selectedItems = React.useMemo(function () {
137
163
  if (value == null) {
138
164
  return undefined;
139
165
  }
140
- return options.find(function (option) { return option.value === value; });
141
- }, [options, value]);
166
+ return isMultipleEnabled(value, multiple)
167
+ ? options.filter(function (option) { return value.some(function (val) { return val === option.value; }); })
168
+ : options.find(function (option) { return option.value === value; });
169
+ }, [multiple, options, value]);
170
+ var tagRenderer = React.useCallback(function (item) {
171
+ return React.createElement(SelectTag, { key: item.label, label: item.label });
172
+ }, []);
142
173
  return (React.createElement("div", __assign({ className: cx('iui-input-with-icon', className), "aria-expanded": isOpen, "aria-haspopup": 'listbox', style: style }, rest),
143
174
  React.createElement(DropdownMenu, __assign({ menuItems: menuItems, placement: 'bottom-start', className: cx('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) {
144
175
  var _b;
@@ -148,21 +179,13 @@ export var Select = function (props) {
148
179
  }
149
180
  } }),
150
181
  React.createElement("div", { ref: selectRef, className: cx('iui-select-button', (_a = {
151
- 'iui-placeholder': !selectedItem && !!placeholder,
182
+ 'iui-placeholder': (!selectedItems || selectedItems.length === 0) && !!placeholder,
152
183
  'iui-disabled': disabled
153
184
  },
154
185
  _a["iui-".concat(size)] = !!size,
155
186
  _a)), onClick: function () { return !disabled && toggle(); }, onKeyDown: function (e) { return !disabled && onKeyDown(e, toggle); }, tabIndex: !disabled ? 0 : undefined },
156
- !selectedItem && React.createElement("span", { className: 'iui-content' }, placeholder),
157
- selectedItem &&
158
- selectedItemRenderer &&
159
- selectedItemRenderer(selectedItem),
160
- selectedItem && !selectedItemRenderer && (React.createElement(React.Fragment, null,
161
- (selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.icon) &&
162
- React.cloneElement(selectedItem.icon, {
163
- className: cx(selectedItem === null || selectedItem === void 0 ? void 0 : selectedItem.icon.props.className, 'iui-icon'),
164
- }),
165
- React.createElement("span", { className: 'iui-content' }, selectedItem.label))))),
187
+ (!selectedItems || selectedItems.length === 0) && (React.createElement("span", { className: 'iui-content' }, placeholder)),
188
+ isMultipleEnabled(selectedItems, multiple) ? (React.createElement(MultipleSelectButton, { selectedItems: selectedItems, selectedItemsRenderer: selectedItemRenderer, tagRenderer: tagRenderer })) : (React.createElement(SingleSelectButton, { selectedItem: selectedItems, selectedItemRenderer: selectedItemRenderer })))),
166
189
  React.createElement("span", { ref: toggleButtonRef, className: cx('iui-end-icon', {
167
190
  'iui-actionable': !disabled,
168
191
  'iui-disabled': disabled,
@@ -170,4 +193,38 @@ export var Select = function (props) {
170
193
  }), onClick: function () { return !disabled && toggle(); } },
171
194
  React.createElement(SvgCaretDownSmall, { "aria-hidden": true }))));
172
195
  };
196
+ var SingleSelectButton = function (_a) {
197
+ var selectedItem = _a.selectedItem, selectedItemRenderer = _a.selectedItemRenderer;
198
+ return (React.createElement(React.Fragment, null,
199
+ selectedItem &&
200
+ selectedItemRenderer &&
201
+ selectedItemRenderer(selectedItem),
202
+ selectedItem && !selectedItemRenderer && (React.createElement(React.Fragment, null,
203
+ selectedItem.icon &&
204
+ React.cloneElement(selectedItem.icon, {
205
+ className: cx(selectedItem.icon.props.className, 'iui-icon'),
206
+ }),
207
+ React.createElement("span", { className: 'iui-content' }, selectedItem.label)))));
208
+ };
209
+ var MultipleSelectButton = function (_a) {
210
+ var selectedItems = _a.selectedItems, selectedItemsRenderer = _a.selectedItemsRenderer, tagRenderer = _a.tagRenderer;
211
+ var selectedItemsElements = React.useMemo(function () {
212
+ if (!selectedItems) {
213
+ return [];
214
+ }
215
+ return selectedItems.map(function (item) { return tagRenderer(item); });
216
+ }, [selectedItems, tagRenderer]);
217
+ var _b = useOverflow(selectedItemsElements), containerRef = _b[0], visibleCount = _b[1];
218
+ return (React.createElement(React.Fragment, null,
219
+ selectedItems &&
220
+ selectedItemsRenderer &&
221
+ selectedItemsRenderer(selectedItems),
222
+ selectedItems && !selectedItemsRenderer && (React.createElement("span", { className: 'iui-content' },
223
+ React.createElement("div", { className: 'iui-select-tag-container', ref: containerRef },
224
+ React.createElement(React.Fragment, null,
225
+ visibleCount < selectedItemsElements.length
226
+ ? selectedItemsElements.slice(0, visibleCount - 1)
227
+ : selectedItemsElements,
228
+ visibleCount < selectedItemsElements.length && (React.createElement(SelectTag, { label: "+".concat(selectedItemsElements.length - visibleCount + 1, " item(s)") }))))))));
229
+ };
173
230
  export default Select;
@@ -0,0 +1,15 @@
1
+ /// <reference types="react" />
2
+ import { CommonProps } from '../utils';
3
+ import '@itwin/itwinui-css/css/inputs.css';
4
+ export declare type SelectTagProps = {
5
+ /**
6
+ * Text inside the tag.
7
+ */
8
+ label: string;
9
+ } & CommonProps;
10
+ /**
11
+ * Tag for showing selected value in `Select`.
12
+ * @private
13
+ */
14
+ export declare const SelectTag: (props: SelectTagProps) => JSX.Element;
15
+ export default SelectTag;
@@ -0,0 +1,41 @@
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 cx from 'classnames';
28
+ import React from 'react';
29
+ import { useTheme } from '../utils';
30
+ import '@itwin/itwinui-css/css/inputs.css';
31
+ /**
32
+ * Tag for showing selected value in `Select`.
33
+ * @private
34
+ */
35
+ export var SelectTag = function (props) {
36
+ var className = props.className, label = props.label, rest = __rest(props, ["className", "label"]);
37
+ useTheme();
38
+ return (React.createElement("span", __assign({ className: cx('iui-select-tag', className) }, rest),
39
+ React.createElement("span", { className: 'iui-select-tag-label' }, label)));
40
+ };
41
+ export default SelectTag;
@@ -1,4 +1,4 @@
1
1
  export { Select } from './Select';
2
- export type { SelectProps, SelectOption, ItemRendererProps } from './Select';
2
+ export type { SelectProps, SelectOption, ItemRendererProps, SelectValueChangeEvent, } from './Select';
3
3
  declare const _default: "./Select";
4
4
  export default _default;
@@ -129,7 +129,9 @@ export declare type TableProps<T extends Record<string, unknown> = Record<string
129
129
  * Function that should return custom props passed to the each row.
130
130
  * Must be memoized.
131
131
  */
132
- rowProps?: (row: Row<T>) => React.ComponentPropsWithRef<'div'>;
132
+ rowProps?: (row: Row<T>) => React.ComponentPropsWithRef<'div'> & {
133
+ status?: 'positive' | 'warning' | 'negative';
134
+ };
133
135
  /**
134
136
  * Modify the density of the table (adjusts the row height).
135
137
  * @default 'default'
@@ -353,7 +353,8 @@ export var Table = function (props) {
353
353
  (showFilterButton(column) ||
354
354
  showSortButton(column)) && (React.createElement("div", { className: 'iui-table-header-actions-container' },
355
355
  showFilterButton(column) && (React.createElement(FilterToggle, { column: column, ownerDocument: ownerDocument })),
356
- showSortButton(column) && (React.createElement("div", { className: 'iui-cell-end-icon' }, column.isSorted && column.isSortedDesc ? (React.createElement(SvgSortDown, { className: 'iui-icon iui-sort', "aria-hidden": true })) : (React.createElement(SvgSortUp, { className: 'iui-icon iui-sort', "aria-hidden": true })))))),
356
+ showSortButton(column) && (React.createElement("div", { className: 'iui-cell-end-icon' }, column.isSortedDesc ||
357
+ (!column.isSorted && column.sortDescFirst) ? (React.createElement(SvgSortDown, { className: 'iui-icon iui-sort', "aria-hidden": true })) : (React.createElement(SvgSortUp, { className: 'iui-icon iui-sort', "aria-hidden": true })))))),
357
358
  isResizable &&
358
359
  column.isResizerVisible &&
359
360
  index !== headerGroup.headers.length - 1 && (React.createElement("div", __assign({}, column.getResizerProps(), { className: 'iui-resizer' }),
@@ -8,7 +8,11 @@ import { CellProps, Row, TableInstance, TableState } from 'react-table';
8
8
  */
9
9
  export declare const TableRow: <T extends Record<string, unknown>>(props: {
10
10
  row: Row<T>;
11
- rowProps?: ((row: Row<T>) => React.ComponentPropsWithRef<'div'>) | undefined;
11
+ rowProps?: ((row: Row<T>) => Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
12
+ ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject<HTMLDivElement> | null | undefined;
13
+ } & {
14
+ status?: "positive" | "warning" | "negative" | undefined;
15
+ }) | undefined;
12
16
  isLast: boolean;
13
17
  onRowInViewport: React.MutableRefObject<((rowData: T) => void) | undefined>;
14
18
  onBottomReached: React.MutableRefObject<(() => void) | undefined>;
@@ -25,7 +29,11 @@ export declare const TableRow: <T extends Record<string, unknown>>(props: {
25
29
  }) => JSX.Element;
26
30
  export declare const TableRowMemoized: <T extends Record<string, unknown>>(props: {
27
31
  row: Row<T>;
28
- rowProps?: ((row: Row<T>) => React.ComponentPropsWithRef<'div'>) | undefined;
32
+ rowProps?: ((row: Row<T>) => Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
33
+ ref?: ((instance: HTMLDivElement | null) => void) | React.RefObject<HTMLDivElement> | null | undefined;
34
+ } & {
35
+ status?: "positive" | "warning" | "negative" | undefined;
36
+ }) | undefined;
29
37
  isLast: boolean;
30
38
  onRowInViewport: React.MutableRefObject<((rowData: T) => void) | undefined>;
31
39
  onBottomReached: React.MutableRefObject<(() => void) | undefined>;
@@ -9,6 +9,17 @@ var __assign = (this && this.__assign) || function () {
9
9
  };
10
10
  return __assign.apply(this, arguments);
11
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
+ };
12
23
  /*---------------------------------------------------------------------------------------------
13
24
  * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
14
25
  * See LICENSE.md in the project root for license terms and full copyright notice.
@@ -24,6 +35,8 @@ import { TableCell } from './TableCell';
24
35
  * When adding new features check whether it changes state that affects row. If it does then add equality check to `React.memo`.
25
36
  */
26
37
  export var TableRow = function (props) {
38
+ var _a;
39
+ var _b;
27
40
  var row = props.row, rowProps = props.rowProps, isLast = props.isLast, onRowInViewport = props.onRowInViewport, onBottomReached = props.onBottomReached, intersectionMargin = props.intersectionMargin, onClick = props.onClick, subComponent = props.subComponent, isDisabled = props.isDisabled, tableHasSubRows = props.tableHasSubRows, tableInstance = props.tableInstance, expanderCell = props.expanderCell, bodyRef = props.bodyRef, tableRowRef = props.tableRowRef;
28
41
  var onIntersect = React.useCallback(function () {
29
42
  var _a, _b;
@@ -44,13 +57,16 @@ export var TableRow = function (props) {
44
57
  rootMargin: "".concat(intersectionMargin, "px"),
45
58
  root: intersectionRoot,
46
59
  });
47
- var userRowProps = rowProps === null || rowProps === void 0 ? void 0 : rowProps(row);
48
- var mergedProps = __assign(__assign(__assign({}, row.getRowProps({ style: { flex: "0 0 auto", minWidth: '100%' } })), userRowProps), {
49
- className: cx('iui-row', {
50
- 'iui-selected': row.isSelected,
51
- 'iui-row-expanded': row.isExpanded && subComponent,
52
- 'iui-disabled': isDisabled,
53
- }, userRowProps === null || userRowProps === void 0 ? void 0 : userRowProps.className),
60
+ var userRowProps = (_b = rowProps === null || rowProps === void 0 ? void 0 : rowProps(row)) !== null && _b !== void 0 ? _b : {};
61
+ var status = userRowProps.status, restUserRowProps = __rest(userRowProps, ["status"]);
62
+ var mergedProps = __assign(__assign(__assign({}, row.getRowProps({ style: { flex: "0 0 auto", minWidth: '100%' } })), restUserRowProps), {
63
+ className: cx('iui-row', (_a = {
64
+ 'iui-selected': row.isSelected,
65
+ 'iui-row-expanded': row.isExpanded && subComponent,
66
+ 'iui-disabled': isDisabled
67
+ },
68
+ _a["iui-".concat(status)] = !!status,
69
+ _a), userRowProps === null || userRowProps === void 0 ? void 0 : userRowProps.className),
54
70
  });
55
71
  var refs = useMergedRefs(intersectionRef, mergedProps.ref, tableRowRef);
56
72
  return (React.createElement(React.Fragment, null,
@@ -1,6 +1,19 @@
1
1
  import React from 'react';
2
2
  import { CellRendererProps } from 'react-table';
3
- export declare type DefaultCellProps<T extends Record<string, unknown>> = CellRendererProps<T> & React.ComponentPropsWithoutRef<'div'>;
3
+ export declare type DefaultCellProps<T extends Record<string, unknown>> = {
4
+ /**
5
+ * Custom icon to be displayed at the beginning of the cell.
6
+ */
7
+ startIcon?: JSX.Element;
8
+ /**
9
+ * Custom icon to be displayed at the end of the cell.
10
+ */
11
+ endIcon?: JSX.Element;
12
+ /**
13
+ * Status of the cell.
14
+ */
15
+ status?: 'positive' | 'negative' | 'warning';
16
+ } & CellRendererProps<T> & React.ComponentPropsWithoutRef<'div'>;
4
17
  /**
5
18
  * Default cell.
6
19
  * It should be passed to `cellRenderer`.
@@ -38,11 +38,17 @@ import cx from 'classnames';
38
38
  * }
39
39
  */
40
40
  export var DefaultCell = function (props) {
41
+ var _a;
41
42
  // Omitting `cellProps`
42
43
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
43
- var _a = props.cellElementProps, cellElementClassName = _a.className, cellElementStyle = _a.style, cellElementProps = __rest(_a, ["className", "style"]), children = props.children, cellProps = props.cellProps, isDisabled = props.isDisabled, className = props.className, style = props.style, rest = __rest(props, ["cellElementProps", "children", "cellProps", "isDisabled", "className", "style"]);
44
- return (React.createElement("div", __assign({}, cellElementProps, rest, { className: cx(cellElementClassName, className, {
45
- 'iui-disabled': isDisabled === null || isDisabled === void 0 ? void 0 : isDisabled(cellProps.row.original),
46
- }), style: __assign(__assign({}, cellElementStyle), style) }), children));
44
+ var _b = props.cellElementProps, cellElementClassName = _b.className, cellElementStyle = _b.style, cellElementProps = __rest(_b, ["className", "style"]), children = props.children, startIcon = props.startIcon, endIcon = props.endIcon, cellProps = props.cellProps, isDisabled = props.isDisabled, className = props.className, style = props.style, status = props.status, rest = __rest(props, ["cellElementProps", "children", "startIcon", "endIcon", "cellProps", "isDisabled", "className", "style", "status"]);
45
+ return (React.createElement("div", __assign({}, cellElementProps, rest, { className: cx(cellElementClassName, className, (_a = {
46
+ 'iui-disabled': isDisabled === null || isDisabled === void 0 ? void 0 : isDisabled(cellProps.row.original)
47
+ },
48
+ _a["iui-".concat(status)] = !!status,
49
+ _a)), style: __assign(__assign({}, cellElementStyle), style) }),
50
+ startIcon && React.createElement("div", { className: 'iui-cell-start-icon' }, startIcon),
51
+ children,
52
+ endIcon && React.createElement("div", { className: 'iui-cell-end-icon' }, endIcon)));
47
53
  };
48
54
  export default DefaultCell;
@@ -61,7 +61,7 @@ export type { RadioProps } from './Radio';
61
61
  export { RadioTile, RadioTileGroup } from './RadioTiles';
62
62
  export type { RadioTileGroupProps, RadioTileProps } from './RadioTiles';
63
63
  export { Select } from './Select';
64
- export type { SelectProps, SelectOption, ItemRendererProps } from './Select';
64
+ export type { SelectProps, SelectOption, ItemRendererProps, SelectValueChangeEvent, } from './Select';
65
65
  export { SideNavigation, SidenavButton, SidenavSubmenu, SidenavSubmenuHeader, } from './SideNavigation';
66
66
  export type { SideNavigationProps, SidenavButtonProps, SidenavSubmenuProps, SidenavSubmenuHeaderProps, } from './SideNavigation';
67
67
  export { SkipToContentLink } from './SkipToContentLink';
@@ -1,4 +1,4 @@
1
- /// <reference types="react" />
1
+ import React from 'react';
2
2
  import { CommonProps } from '../props';
3
3
  export declare type MiddleTextTruncationProps = {
4
4
  /**
@@ -10,12 +10,26 @@ export declare type MiddleTextTruncationProps = {
10
10
  * @default 6
11
11
  */
12
12
  endCharsCount?: number;
13
+ /**
14
+ * Custom renderer for the truncated text.
15
+ */
16
+ textRenderer?: (truncatedText: string, originalText: string) => React.ReactNode;
13
17
  } & CommonProps;
14
18
  /**
15
19
  * Truncates text with the ellipsis in the middle,
16
20
  * leaving defined number of chars at the end.
17
21
  * @example
18
22
  * <MiddleTextTruncation text='ThisIsMyVeryLongFileName.dgn' />
23
+ * @example
24
+ * <MiddleTextTruncation text='ThisIsMyVeryLongFileName.dgn' endCharsCount={10} />
25
+ * @example
26
+ * <MiddleTextTruncation
27
+ * text='ThisIsMyVeryLongFileName.dgn'
28
+ * textRenderer={React.useCallback(
29
+ * (truncatedText) => <b>{truncatedText}</b>,
30
+ * []
31
+ * )}
32
+ * />
19
33
  */
20
34
  export declare const MiddleTextTruncation: (props: MiddleTextTruncationProps) => JSX.Element;
21
35
  export default MiddleTextTruncation;
@@ -32,10 +32,21 @@ var ELLIPSIS_CHAR = '…';
32
32
  * leaving defined number of chars at the end.
33
33
  * @example
34
34
  * <MiddleTextTruncation text='ThisIsMyVeryLongFileName.dgn' />
35
+ * @example
36
+ * <MiddleTextTruncation text='ThisIsMyVeryLongFileName.dgn' endCharsCount={10} />
37
+ * @example
38
+ * <MiddleTextTruncation
39
+ * text='ThisIsMyVeryLongFileName.dgn'
40
+ * textRenderer={React.useCallback(
41
+ * (truncatedText) => <b>{truncatedText}</b>,
42
+ * []
43
+ * )}
44
+ * />
35
45
  */
36
46
  export var MiddleTextTruncation = function (props) {
37
- var text = props.text, _a = props.endCharsCount, endCharsCount = _a === void 0 ? 6 : _a, style = props.style, rest = __rest(props, ["text", "endCharsCount", "style"]);
38
- var _b = useOverflow(text), ref = _b[0], visibleCount = _b[1];
47
+ var _a;
48
+ var text = props.text, _b = props.endCharsCount, endCharsCount = _b === void 0 ? 6 : _b, textRenderer = props.textRenderer, style = props.style, rest = __rest(props, ["text", "endCharsCount", "textRenderer", "style"]);
49
+ var _c = useOverflow(text), ref = _c[0], visibleCount = _c[1];
39
50
  var truncatedText = React.useMemo(function () {
40
51
  if (visibleCount < text.length) {
41
52
  return "".concat(text.substring(0, visibleCount - endCharsCount - ELLIPSIS_CHAR.length)).concat(ELLIPSIS_CHAR).concat(text.substring(text.length - endCharsCount));
@@ -44,6 +55,6 @@ export var MiddleTextTruncation = function (props) {
44
55
  return text;
45
56
  }
46
57
  }, [endCharsCount, text, visibleCount]);
47
- return (React.createElement("span", __assign({ style: __assign({ display: 'flex', minWidth: 0, flexGrow: 1, whiteSpace: 'nowrap' }, style), ref: ref }, rest), truncatedText));
58
+ return (React.createElement("span", __assign({ style: __assign({ display: 'flex', minWidth: 0, flexGrow: 1, whiteSpace: 'nowrap' }, style), ref: ref }, rest), (_a = textRenderer === null || textRenderer === void 0 ? void 0 : textRenderer(truncatedText, text)) !== null && _a !== void 0 ? _a : truncatedText));
48
59
  };
49
60
  export default MiddleTextTruncation;
@@ -70,7 +70,10 @@ export var useOverflow = function (items, disabled, orientation) {
70
70
  }
71
71
  else if (needsFullRerender.current) {
72
72
  var childrenSize = Array.from(containerRef.current.children).reduce(function (sum, child) { return sum + child["offset".concat(dimension)]; }, 0);
73
- var avgItemSize = childrenSize / visibleCount;
73
+ // Previous `useEffect` might have updated visible count, but we still have old one
74
+ // If it is 0, lets try to update it with items length.
75
+ var currentVisibleCount = visibleCount || Math.min(items.length, STARTING_MAX_ITEMS_COUNT);
76
+ var avgItemSize = childrenSize / currentVisibleCount;
74
77
  var visibleItems = Math.floor(availableSize / avgItemSize);
75
78
  if (!isNaN(visibleItems)) {
76
79
  // Doubling the visible items to overflow the container. Just to be safe.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itwin/itwinui-react",
3
- "version": "1.43.0",
3
+ "version": "1.45.0",
4
4
  "author": "Bentley Systems",
5
5
  "license": "MIT",
6
6
  "main": "cjs/index.js",