@itwin/itwinui-react 1.41.0 → 1.42.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 +13 -0
- package/cjs/core/ComboBox/ComboBox.js +15 -14
- package/cjs/core/Table/Table.d.ts +23 -0
- package/cjs/core/Table/Table.js +6 -3
- package/cjs/core/Table/TableRowMemoized.d.ts +2 -0
- package/cjs/core/Table/TableRowMemoized.js +2 -2
- package/cjs/core/Table/hooks/index.d.ts +1 -0
- package/cjs/core/Table/hooks/index.js +3 -1
- package/cjs/core/Table/hooks/useScrollToRow.d.ts +11 -0
- package/cjs/core/Table/hooks/useScrollToRow.js +49 -0
- package/cjs/core/Tree/Tree.d.ts +9 -0
- package/cjs/core/Tree/Tree.js +67 -19
- package/cjs/core/Tree/TreeContext.d.ts +4 -0
- package/cjs/core/Tree/TreeNode.js +8 -9
- package/cjs/core/Typography/Small/Small.js +1 -1
- package/cjs/core/utils/hooks/index.d.ts +1 -0
- package/cjs/core/utils/hooks/index.js +1 -0
- package/cjs/core/utils/hooks/useLatestRef.d.ts +9 -0
- package/cjs/core/utils/hooks/useLatestRef.js +26 -0
- package/esm/core/ComboBox/ComboBox.js +16 -15
- package/esm/core/Table/Table.d.ts +23 -0
- package/esm/core/Table/Table.js +7 -4
- package/esm/core/Table/TableRowMemoized.d.ts +2 -0
- package/esm/core/Table/TableRowMemoized.js +2 -2
- package/esm/core/Table/hooks/index.d.ts +1 -0
- package/esm/core/Table/hooks/index.js +1 -0
- package/esm/core/Table/hooks/useScrollToRow.d.ts +11 -0
- package/esm/core/Table/hooks/useScrollToRow.js +42 -0
- package/esm/core/Tree/Tree.d.ts +9 -0
- package/esm/core/Tree/Tree.js +68 -20
- package/esm/core/Tree/TreeContext.d.ts +4 -0
- package/esm/core/Tree/TreeNode.js +8 -9
- package/esm/core/Typography/Small/Small.js +1 -1
- package/esm/core/utils/hooks/index.d.ts +1 -0
- package/esm/core/utils/hooks/index.js +1 -0
- package/esm/core/utils/hooks/useLatestRef.d.ts +9 -0
- package/esm/core/utils/hooks/useLatestRef.js +19 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.42.0](https://www.github.com/iTwin/iTwinUI-react/compare/v1.41.0...v1.42.0) (2022-07-26)
|
|
4
|
+
|
|
5
|
+
### Fixes
|
|
6
|
+
|
|
7
|
+
* **ComboBox:** Prevent infinite loop when `options` change ([#738](https://www.github.com/iTwin/iTwinUI-react/issues/738)) ([7788f45](https://www.github.com/iTwin/iTwinUI-react/commit/7788f451fcbf9dd7959d3fa727c0cdee3485bbcd))
|
|
8
|
+
* **Small:** Use `small` element instead of `p` ([#735](https://www.github.com/iTwin/iTwinUI-react/issues/735)) ([c59f213](https://www.github.com/iTwin/iTwinUI-react/commit/c59f21326e049bce50e24c729a46482b74ef63c3))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### What's new
|
|
12
|
+
|
|
13
|
+
* **Table:** Scroll table to selected item ([#689](https://www.github.com/iTwin/iTwinUI-react/issues/689)) ([afa947b](https://www.github.com/iTwin/iTwinUI-react/commit/afa947b15411305c1bfc57b239d916fc599acfbe))
|
|
14
|
+
* **Tree:** Virtualization ([#713](https://www.github.com/iTwin/iTwinUI-react/issues/713)) ([d4d3575](https://www.github.com/iTwin/iTwinUI-react/commit/d4d35758fa0c8f00811f387cc8453b82e9773c85))
|
|
15
|
+
|
|
3
16
|
## [1.41.0](https://www.github.com/iTwin/iTwinUI-react/compare/v1.40.1...v1.41.0) (2022-07-13)
|
|
4
17
|
|
|
5
18
|
### What's new
|
|
@@ -75,11 +75,9 @@ var ComboBox = function (props) {
|
|
|
75
75
|
var menuRef = react_1.default.useRef(null);
|
|
76
76
|
var toggleButtonRef = react_1.default.useRef(null);
|
|
77
77
|
var mounted = react_1.default.useRef(false);
|
|
78
|
-
|
|
79
|
-
var onChangeProp =
|
|
80
|
-
|
|
81
|
-
onChangeProp.current = onChange;
|
|
82
|
-
}, [onChange]);
|
|
78
|
+
var valuePropRef = (0, utils_1.useLatestRef)(valueProp);
|
|
79
|
+
var onChangeProp = (0, utils_1.useLatestRef)(onChange);
|
|
80
|
+
var optionsRef = (0, utils_1.useLatestRef)(options);
|
|
83
81
|
// Record to store all extra information (e.g. original indexes), where the key is the id of the option
|
|
84
82
|
var optionsExtraInfoRef = react_1.default.useRef({});
|
|
85
83
|
// Clear the extra info when the options change so that it can be reinitialized below
|
|
@@ -99,7 +97,7 @@ var ComboBox = function (props) {
|
|
|
99
97
|
var _e = react_1.default.useReducer(helpers_1.comboBoxReducer, {
|
|
100
98
|
isOpen: false,
|
|
101
99
|
selectedIndex: valueProp
|
|
102
|
-
?
|
|
100
|
+
? optionsRef.current.findIndex(function (option) { return option.value === valueProp; })
|
|
103
101
|
: -1,
|
|
104
102
|
focusedIndex: -1,
|
|
105
103
|
}), _f = _e[0], isOpen = _f.isOpen, selectedIndex = _f.selectedIndex, focusedIndex = _f.focusedIndex, dispatch = _e[1];
|
|
@@ -108,7 +106,7 @@ var ComboBox = function (props) {
|
|
|
108
106
|
// When the dropdown opens
|
|
109
107
|
if (isOpen) {
|
|
110
108
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); // Focus the input
|
|
111
|
-
setFilteredOptions(
|
|
109
|
+
setFilteredOptions(optionsRef.current); // Reset the filtered list
|
|
112
110
|
dispatch(['focus']);
|
|
113
111
|
}
|
|
114
112
|
// When the dropdown closes
|
|
@@ -117,10 +115,10 @@ var ComboBox = function (props) {
|
|
|
117
115
|
dispatch(['focus']);
|
|
118
116
|
// Reset the input value
|
|
119
117
|
setInputValue(selectedIndex != undefined && selectedIndex >= 0
|
|
120
|
-
? (_b =
|
|
118
|
+
? (_b = optionsRef.current[selectedIndex]) === null || _b === void 0 ? void 0 : _b.label
|
|
121
119
|
: '');
|
|
122
120
|
}
|
|
123
|
-
}, [isOpen,
|
|
121
|
+
}, [isOpen, optionsRef, selectedIndex]);
|
|
124
122
|
// Set min-width of menu to be same as input
|
|
125
123
|
var _g = react_1.default.useState(0), minWidth = _g[0], setMinWidth = _g[1];
|
|
126
124
|
react_1.default.useEffect(function () {
|
|
@@ -151,14 +149,14 @@ var ComboBox = function (props) {
|
|
|
151
149
|
var value = event.currentTarget.value;
|
|
152
150
|
setInputValue(value);
|
|
153
151
|
dispatch(['open']); // reopen when typing
|
|
154
|
-
setFilteredOptions((_a = filterFunction === null || filterFunction === void 0 ? void 0 : filterFunction(
|
|
152
|
+
setFilteredOptions((_a = filterFunction === null || filterFunction === void 0 ? void 0 : filterFunction(optionsRef.current, value)) !== null && _a !== void 0 ? _a : optionsRef.current.filter(function (option) {
|
|
155
153
|
return option.label.toLowerCase().includes(value.toLowerCase());
|
|
156
154
|
}));
|
|
157
155
|
if (focusedIndex != -1) {
|
|
158
156
|
dispatch(['focus', -1]);
|
|
159
157
|
}
|
|
160
158
|
(_b = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onChange) === null || _b === void 0 ? void 0 : _b.call(inputProps, event);
|
|
161
|
-
}, [filterFunction, focusedIndex, inputProps,
|
|
159
|
+
}, [filterFunction, focusedIndex, inputProps, optionsRef]);
|
|
162
160
|
// When the value prop changes, update the selectedIndex
|
|
163
161
|
react_1.default.useEffect(function () {
|
|
164
162
|
dispatch([
|
|
@@ -174,9 +172,12 @@ var ComboBox = function (props) {
|
|
|
174
172
|
mounted.current = true;
|
|
175
173
|
return;
|
|
176
174
|
}
|
|
177
|
-
var
|
|
178
|
-
(
|
|
179
|
-
|
|
175
|
+
var currentValue = (_a = optionsRef.current[selectedIndex]) === null || _a === void 0 ? void 0 : _a.value;
|
|
176
|
+
if (currentValue === valuePropRef.current || selectedIndex === -1) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
(_b = onChangeProp.current) === null || _b === void 0 ? void 0 : _b.call(onChangeProp, currentValue);
|
|
180
|
+
}, [onChangeProp, optionsRef, selectedIndex, valuePropRef]);
|
|
180
181
|
var getMenuItem = react_1.default.useCallback(function (option, filteredIndex) {
|
|
181
182
|
var optionId = getOptionId(option, id);
|
|
182
183
|
var __originalIndex = optionsExtraInfoRef.current[optionId].__originalIndex;
|
|
@@ -182,6 +182,29 @@ export declare type TableProps<T extends Record<string, unknown> = Record<string
|
|
|
182
182
|
* @default false
|
|
183
183
|
*/
|
|
184
184
|
enableColumnReordering?: boolean;
|
|
185
|
+
/**
|
|
186
|
+
* Function that returns index of the row that you want to scroll to.
|
|
187
|
+
*
|
|
188
|
+
* It doesn't work with paginated tables and with lazy-loading.
|
|
189
|
+
* @beta
|
|
190
|
+
* @example
|
|
191
|
+
* <Table
|
|
192
|
+
* scrollToRow={React.useCallback(
|
|
193
|
+
* (rows, data) => rows.findIndex((row) => row.original === data[250]),
|
|
194
|
+
* []
|
|
195
|
+
* )}
|
|
196
|
+
* {...restProps}
|
|
197
|
+
* />
|
|
198
|
+
* @example
|
|
199
|
+
* <Table
|
|
200
|
+
* scrollToRow={React.useCallback(
|
|
201
|
+
* (rows, data) => rows.findIndex((row) => row.original.id === data[250].id),
|
|
202
|
+
* []
|
|
203
|
+
* )}
|
|
204
|
+
* {...restProps}
|
|
205
|
+
* />
|
|
206
|
+
*/
|
|
207
|
+
scrollToRow?: (rows: Row<T>[], data: T[]) => number;
|
|
185
208
|
} & Omit<CommonProps, 'title'>;
|
|
186
209
|
/**
|
|
187
210
|
* Table based on [react-table](https://react-table.tanstack.com/docs/api/overview).
|
package/cjs/core/Table/Table.js
CHANGED
|
@@ -240,6 +240,7 @@ var Table = function (props) {
|
|
|
240
240
|
state.pageIndex,
|
|
241
241
|
state.pageSize,
|
|
242
242
|
]);
|
|
243
|
+
var _r = (0, hooks_1.useScrollToRow)(__assign(__assign({}, props), { page: page })), scrollToIndex = _r.scrollToIndex, tableRowRef = _r.tableRowRef;
|
|
243
244
|
var columnRefs = react_1.default.useRef({});
|
|
244
245
|
var previousTableWidth = react_1.default.useRef(0);
|
|
245
246
|
var onTableResize = react_1.default.useCallback(function (_a) {
|
|
@@ -276,11 +277,11 @@ var Table = function (props) {
|
|
|
276
277
|
var headerRef = react_1.default.useRef(null);
|
|
277
278
|
var bodyRef = react_1.default.useRef(null);
|
|
278
279
|
// Using `useState` to rerender rows when table body ref is available
|
|
279
|
-
var
|
|
280
|
+
var _s = react_1.default.useState(null), bodyRefState = _s[0], setBodyRefState = _s[1];
|
|
280
281
|
var getPreparedRow = react_1.default.useCallback(function (index) {
|
|
281
282
|
var row = page[index];
|
|
282
283
|
prepareRow(row);
|
|
283
|
-
return (react_1.default.createElement(TableRowMemoized_1.TableRowMemoized, { row: row, rowProps: rowProps, isLast: index === page.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, bodyRef: bodyRefState }));
|
|
284
|
+
return (react_1.default.createElement(TableRowMemoized_1.TableRowMemoized, { row: row, rowProps: rowProps, isLast: index === page.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, bodyRef: bodyRefState, tableRowRef: enableVirtualization ? undefined : tableRowRef(row) }));
|
|
284
285
|
}, [
|
|
285
286
|
page,
|
|
286
287
|
prepareRow,
|
|
@@ -294,6 +295,8 @@ var Table = function (props) {
|
|
|
294
295
|
instance,
|
|
295
296
|
expanderCell,
|
|
296
297
|
bodyRefState,
|
|
298
|
+
enableVirtualization,
|
|
299
|
+
tableRowRef,
|
|
297
300
|
]);
|
|
298
301
|
var virtualizedItemRenderer = react_1.default.useCallback(function (index) { return getPreparedRow(index); }, [getPreparedRow]);
|
|
299
302
|
var updateStickyState = function () {
|
|
@@ -375,7 +378,7 @@ var Table = function (props) {
|
|
|
375
378
|
updateStickyState();
|
|
376
379
|
}
|
|
377
380
|
}, tabIndex: -1 }),
|
|
378
|
-
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 (_, index) { return getPreparedRow(index); })))),
|
|
381
|
+
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, scrollToIndex: scrollToIndex })) : (page.map(function (_, index) { return getPreparedRow(index); })))),
|
|
379
382
|
isLoading && data.length === 0 && (react_1.default.createElement("div", { className: 'iui-table-empty' },
|
|
380
383
|
react_1.default.createElement(ProgressIndicators_1.ProgressRadial, { indeterminate: true }))),
|
|
381
384
|
isLoading && data.length !== 0 && (react_1.default.createElement("div", { className: 'iui-row' },
|
|
@@ -21,6 +21,7 @@ export declare const TableRow: <T extends Record<string, unknown>>(props: {
|
|
|
21
21
|
tableInstance: TableInstance<T>;
|
|
22
22
|
expanderCell?: ((cellProps: CellProps<T, any>) => React.ReactNode) | undefined;
|
|
23
23
|
bodyRef: HTMLDivElement | null;
|
|
24
|
+
tableRowRef?: React.Ref<HTMLDivElement> | undefined;
|
|
24
25
|
}) => JSX.Element;
|
|
25
26
|
export declare const TableRowMemoized: <T extends Record<string, unknown>>(props: {
|
|
26
27
|
row: Row<T>;
|
|
@@ -37,4 +38,5 @@ export declare const TableRowMemoized: <T extends Record<string, unknown>>(props
|
|
|
37
38
|
tableInstance: TableInstance<T>;
|
|
38
39
|
expanderCell?: ((cellProps: CellProps<T, any>) => React.ReactNode) | undefined;
|
|
39
40
|
bodyRef: HTMLDivElement | null;
|
|
41
|
+
tableRowRef?: React.Ref<HTMLDivElement> | undefined;
|
|
40
42
|
}) => JSX.Element;
|
|
@@ -30,7 +30,7 @@ var TableCell_1 = require("./TableCell");
|
|
|
30
30
|
* When adding new features check whether it changes state that affects row. If it does then add equality check to `React.memo`.
|
|
31
31
|
*/
|
|
32
32
|
var TableRow = function (props) {
|
|
33
|
-
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;
|
|
33
|
+
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;
|
|
34
34
|
var onIntersect = react_1.default.useCallback(function () {
|
|
35
35
|
var _a, _b;
|
|
36
36
|
(_a = onRowInViewport.current) === null || _a === void 0 ? void 0 : _a.call(onRowInViewport, row.original);
|
|
@@ -58,7 +58,7 @@ var TableRow = function (props) {
|
|
|
58
58
|
'iui-disabled': isDisabled,
|
|
59
59
|
}, userRowProps === null || userRowProps === void 0 ? void 0 : userRowProps.className),
|
|
60
60
|
});
|
|
61
|
-
var refs = (0, utils_1.useMergedRefs)(intersectionRef, mergedProps.ref);
|
|
61
|
+
var refs = (0, utils_1.useMergedRefs)(intersectionRef, mergedProps.ref, tableRowRef);
|
|
62
62
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
63
63
|
react_1.default.createElement("div", __assign({}, mergedProps, { ref: refs, onClick: function (event) {
|
|
64
64
|
var _a;
|
|
@@ -4,4 +4,5 @@ export { useSubRowFiltering } from './useSubRowFiltering';
|
|
|
4
4
|
export { useSubRowSelection } from './useSubRowSelection';
|
|
5
5
|
export { useResizeColumns } from './useResizeColumns';
|
|
6
6
|
export { useColumnDragAndDrop } from './useColumnDragAndDrop';
|
|
7
|
+
export { useScrollToRow } from './useScrollToRow';
|
|
7
8
|
export { useStickyColumns } from './useStickyColumns';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useStickyColumns = exports.useColumnDragAndDrop = exports.useResizeColumns = exports.useSubRowSelection = exports.useSubRowFiltering = exports.useSelectionCell = exports.useExpanderCell = void 0;
|
|
3
|
+
exports.useStickyColumns = exports.useScrollToRow = exports.useColumnDragAndDrop = exports.useResizeColumns = exports.useSubRowSelection = exports.useSubRowFiltering = exports.useSelectionCell = exports.useExpanderCell = void 0;
|
|
4
4
|
/*---------------------------------------------------------------------------------------------
|
|
5
5
|
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
6
6
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
@@ -17,5 +17,7 @@ var useResizeColumns_1 = require("./useResizeColumns");
|
|
|
17
17
|
Object.defineProperty(exports, "useResizeColumns", { enumerable: true, get: function () { return useResizeColumns_1.useResizeColumns; } });
|
|
18
18
|
var useColumnDragAndDrop_1 = require("./useColumnDragAndDrop");
|
|
19
19
|
Object.defineProperty(exports, "useColumnDragAndDrop", { enumerable: true, get: function () { return useColumnDragAndDrop_1.useColumnDragAndDrop; } });
|
|
20
|
+
var useScrollToRow_1 = require("./useScrollToRow");
|
|
21
|
+
Object.defineProperty(exports, "useScrollToRow", { enumerable: true, get: function () { return useScrollToRow_1.useScrollToRow; } });
|
|
20
22
|
var useStickyColumns_1 = require("./useStickyColumns");
|
|
21
23
|
Object.defineProperty(exports, "useStickyColumns", { enumerable: true, get: function () { return useStickyColumns_1.useStickyColumns; } });
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Row } from 'react-table';
|
|
2
|
+
import { TableProps } from '../Table';
|
|
3
|
+
declare type ScrollToRow<T extends Record<string, unknown>> = {
|
|
4
|
+
scrollToIndex: number | undefined;
|
|
5
|
+
tableRowRef: (row: Row<T>) => (element: HTMLDivElement) => void;
|
|
6
|
+
};
|
|
7
|
+
declare type ScrollToRowProps<T extends Record<string, unknown>> = TableProps<T> & {
|
|
8
|
+
page: Row<T>[];
|
|
9
|
+
};
|
|
10
|
+
export declare function useScrollToRow<T extends Record<string, unknown>>({ data, enableVirtualization, page, paginatorRenderer, scrollToRow, onBottomReached, }: ScrollToRowProps<T>): ScrollToRow<T>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.useScrollToRow = void 0;
|
|
7
|
+
/*---------------------------------------------------------------------------------------------
|
|
8
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
9
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
10
|
+
*--------------------------------------------------------------------------------------------*/
|
|
11
|
+
var react_1 = __importDefault(require("react"));
|
|
12
|
+
function useScrollToRow(_a) {
|
|
13
|
+
var data = _a.data, enableVirtualization = _a.enableVirtualization, page = _a.page, paginatorRenderer = _a.paginatorRenderer, scrollToRow = _a.scrollToRow, onBottomReached = _a.onBottomReached;
|
|
14
|
+
var rowRefs = react_1.default.useRef({});
|
|
15
|
+
// Refs prevents from having `page` and `data` as dependencies
|
|
16
|
+
// therefore we avoid unnecessary scroll to row.
|
|
17
|
+
var pageRef = react_1.default.useRef(page);
|
|
18
|
+
pageRef.current = page;
|
|
19
|
+
var dataRef = react_1.default.useRef(data);
|
|
20
|
+
dataRef.current = data;
|
|
21
|
+
// For virtualized tables, all we need to do is pass the index of the item
|
|
22
|
+
// to the VirtualScroll component
|
|
23
|
+
var scrollToIndex = react_1.default.useMemo(function () {
|
|
24
|
+
if (!scrollToRow || paginatorRenderer || onBottomReached) {
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
var index = scrollToRow(pageRef.current, dataRef.current);
|
|
28
|
+
return index < 0 ? undefined : index;
|
|
29
|
+
}, [onBottomReached, paginatorRenderer, scrollToRow]);
|
|
30
|
+
// For non-virtualized tables, we need to add a ref to each row
|
|
31
|
+
// and scroll to the element
|
|
32
|
+
react_1.default.useEffect(function () {
|
|
33
|
+
var _a;
|
|
34
|
+
if (enableVirtualization ||
|
|
35
|
+
scrollToIndex === undefined ||
|
|
36
|
+
scrollToIndex === null ||
|
|
37
|
+
scrollToIndex < 0) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
(_a = rowRefs.current[pageRef.current[scrollToIndex].id]) === null || _a === void 0 ? void 0 : _a.scrollIntoView();
|
|
41
|
+
}, [enableVirtualization, scrollToIndex]);
|
|
42
|
+
var tableRowRef = react_1.default.useCallback(function (row) {
|
|
43
|
+
return function (element) {
|
|
44
|
+
rowRefs.current[row.id] = element;
|
|
45
|
+
};
|
|
46
|
+
}, []);
|
|
47
|
+
return { scrollToIndex: scrollToIndex, tableRowRef: tableRowRef };
|
|
48
|
+
}
|
|
49
|
+
exports.useScrollToRow = useScrollToRow;
|
package/cjs/core/Tree/Tree.d.ts
CHANGED
|
@@ -68,6 +68,15 @@ export declare type TreeProps<T> = {
|
|
|
68
68
|
* }, [expandedNodes]);
|
|
69
69
|
*/
|
|
70
70
|
getNode: (node: T) => NodeData<T>;
|
|
71
|
+
/**
|
|
72
|
+
* Virtualization is used to have a better performance with a lot of nodes.
|
|
73
|
+
*
|
|
74
|
+
* When enabled, Tree DOM structure will change - it will have a wrapper div
|
|
75
|
+
* to which `className` and `style` will be applied.
|
|
76
|
+
* @default false
|
|
77
|
+
* @beta
|
|
78
|
+
*/
|
|
79
|
+
enableVirtualization?: boolean;
|
|
71
80
|
} & Omit<CommonProps, 'title'>;
|
|
72
81
|
/**
|
|
73
82
|
* Tree component used to display a hierarchical structure of `TreeNodes`.
|
package/cjs/core/Tree/Tree.js
CHANGED
|
@@ -86,7 +86,7 @@ var TreeContext_1 = require("./TreeContext");
|
|
|
86
86
|
/>
|
|
87
87
|
*/
|
|
88
88
|
var Tree = function (props) {
|
|
89
|
-
var data = props.data, className = props.className, nodeRenderer = props.nodeRenderer, getNode = props.getNode, rest = __rest(props, ["data", "className", "nodeRenderer", "getNode"]);
|
|
89
|
+
var data = props.data, className = props.className, nodeRenderer = props.nodeRenderer, getNode = props.getNode, _a = props.enableVirtualization, enableVirtualization = _a === void 0 ? false : _a, style = props.style, rest = __rest(props, ["data", "className", "nodeRenderer", "getNode", "enableVirtualization", "style"]);
|
|
90
90
|
(0, utils_1.useTheme)();
|
|
91
91
|
var treeRef = react_1.default.useRef(null);
|
|
92
92
|
var focusedIndex = react_1.default.useRef(0);
|
|
@@ -124,7 +124,7 @@ var Tree = function (props) {
|
|
|
124
124
|
break;
|
|
125
125
|
}
|
|
126
126
|
};
|
|
127
|
-
var
|
|
127
|
+
var _b = react_1.default.useMemo(function () {
|
|
128
128
|
var flatList = [];
|
|
129
129
|
var firstLevelNodes = [];
|
|
130
130
|
var flattenNodes = function (nodes, depth, parentNode) {
|
|
@@ -153,25 +153,73 @@ var Tree = function (props) {
|
|
|
153
153
|
};
|
|
154
154
|
flattenNodes(data);
|
|
155
155
|
return [flatList, firstLevelNodes];
|
|
156
|
-
}, [data, getNode]), flatNodesList =
|
|
157
|
-
|
|
158
|
-
var _a;
|
|
159
|
-
var items = getFocusableNodes();
|
|
160
|
-
if (items.length > 0) {
|
|
161
|
-
(_a = items[focusedIndex.current]) === null || _a === void 0 ? void 0 : _a.focus();
|
|
162
|
-
}
|
|
163
|
-
} }, rest), flatNodesList.map(function (flatNode) {
|
|
156
|
+
}, [data, getNode]), flatNodesList = _b[0], firstLevelNodesList = _b[1];
|
|
157
|
+
var itemRenderer = react_1.default.useCallback(function (index) {
|
|
164
158
|
var _a, _b, _c, _d;
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
159
|
+
var node = flatNodesList[index];
|
|
160
|
+
return (react_1.default.createElement(TreeContext_1.TreeContext.Provider, { key: node.nodeProps.nodeId, value: {
|
|
161
|
+
nodeDepth: node.depth,
|
|
162
|
+
subNodeIds: node.subNodeIds,
|
|
163
|
+
groupSize: node.depth === 0
|
|
169
164
|
? firstLevelNodesList.length
|
|
170
|
-
: (_c = (_b = (_a =
|
|
171
|
-
indexInGroup:
|
|
172
|
-
parentNodeId: (_d =
|
|
173
|
-
|
|
174
|
-
|
|
165
|
+
: (_c = (_b = (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.subNodeIds) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0,
|
|
166
|
+
indexInGroup: node.indexInGroup,
|
|
167
|
+
parentNodeId: (_d = node.parentNode) === null || _d === void 0 ? void 0 : _d.nodeProps.nodeId,
|
|
168
|
+
scrollToParent: node.parentNode
|
|
169
|
+
? function () {
|
|
170
|
+
var _a;
|
|
171
|
+
var parentNodeId = (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.nodeProps.nodeId;
|
|
172
|
+
var parentNodeIndex = flatNodesList.findIndex(function (n) { return n.nodeProps.nodeId === parentNodeId; });
|
|
173
|
+
setScrollToIndex(parentNodeIndex);
|
|
174
|
+
}
|
|
175
|
+
: undefined,
|
|
176
|
+
} }, nodeRenderer(node.nodeProps)));
|
|
177
|
+
}, [firstLevelNodesList.length, flatNodesList, nodeRenderer]);
|
|
178
|
+
var _c = react_1.default.useState(), scrollToIndex = _c[0], setScrollToIndex = _c[1];
|
|
179
|
+
var flatNodesListRef = react_1.default.useRef(flatNodesList);
|
|
180
|
+
react_1.default.useEffect(function () {
|
|
181
|
+
flatNodesListRef.current = flatNodesList;
|
|
182
|
+
}, [flatNodesList]);
|
|
183
|
+
react_1.default.useEffect(function () {
|
|
184
|
+
setTimeout(function () {
|
|
185
|
+
var _a;
|
|
186
|
+
if (scrollToIndex !== undefined) {
|
|
187
|
+
var nodeId = flatNodesListRef.current[scrollToIndex].nodeProps.nodeId;
|
|
188
|
+
var nodeElement = (_a = treeRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument.querySelector("#".concat(nodeId));
|
|
189
|
+
nodeElement === null || nodeElement === void 0 ? void 0 : nodeElement.focus();
|
|
190
|
+
// Need to reset that if navigating with mouse and keyboard,
|
|
191
|
+
// e.g. pressing arrow left to go to parent node and then with mouse
|
|
192
|
+
// clicking some other child node and then pressing arrow left
|
|
193
|
+
setScrollToIndex(undefined);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
}, [scrollToIndex]);
|
|
197
|
+
var handleFocus = function (event) {
|
|
198
|
+
var _a, _b;
|
|
199
|
+
if ((_a = treeRef.current) === null || _a === void 0 ? void 0 : _a.contains(event.relatedTarget)) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
var items = getFocusableNodes();
|
|
203
|
+
if (items.length > 0) {
|
|
204
|
+
(_b = items[focusedIndex.current]) === null || _b === void 0 ? void 0 : _b.focus();
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
return (react_1.default.createElement(react_1.default.Fragment, null, enableVirtualization ? (react_1.default.createElement(VirtualizedTree, __assign({ flatNodesList: flatNodesList, itemRenderer: itemRenderer, scrollToIndex: scrollToIndex, onFocus: handleFocus, onKeyDown: handleKeyDown, ref: treeRef, className: className, style: style }, rest))) : (react_1.default.createElement(TreeElement, __assign({ onKeyDown: handleKeyDown, onFocus: handleFocus, className: className, style: style, ref: treeRef }, rest), flatNodesList.map(function (_, i) { return itemRenderer(i); })))));
|
|
175
208
|
};
|
|
176
209
|
exports.Tree = Tree;
|
|
210
|
+
var TreeElement = react_1.default.forwardRef(function (_a, ref) {
|
|
211
|
+
var children = _a.children, className = _a.className, rest = __rest(_a, ["children", "className"]);
|
|
212
|
+
return (react_1.default.createElement("ul", __assign({ className: (0, classnames_1.default)('iui-tree', className), role: 'tree', ref: ref, tabIndex: 0 }, rest), children));
|
|
213
|
+
});
|
|
214
|
+
// Having virtualized tree separately prevents from running all virtualization logic
|
|
215
|
+
var VirtualizedTree = react_1.default.forwardRef(function (_a, ref) {
|
|
216
|
+
var flatNodesList = _a.flatNodesList, itemRenderer = _a.itemRenderer, scrollToIndex = _a.scrollToIndex, className = _a.className, style = _a.style, rest = __rest(_a, ["flatNodesList", "itemRenderer", "scrollToIndex", "className", "style"]);
|
|
217
|
+
var _b = (0, utils_1.useVirtualization)({
|
|
218
|
+
itemsLength: flatNodesList.length,
|
|
219
|
+
itemRenderer: itemRenderer,
|
|
220
|
+
scrollToIndex: scrollToIndex,
|
|
221
|
+
}), outerProps = _b.outerProps, innerProps = _b.innerProps, visibleChildren = _b.visibleChildren;
|
|
222
|
+
return (react_1.default.createElement("div", __assign({}, __assign(__assign({}, outerProps), { className: (0, classnames_1.default)(className, outerProps.className), style: __assign(__assign({}, style), outerProps.style) })),
|
|
223
|
+
react_1.default.createElement(TreeElement, __assign({}, innerProps, rest, { ref: (0, utils_1.mergeRefs)(ref, innerProps.ref) }), visibleChildren)));
|
|
224
|
+
});
|
|
177
225
|
exports.default = exports.Tree;
|
|
@@ -20,6 +20,10 @@ export declare type TreeContextProps = {
|
|
|
20
20
|
* Node index in the list of nodes under the same parent node or in the root. Used for an accessibility attribute.
|
|
21
21
|
*/
|
|
22
22
|
indexInGroup: number;
|
|
23
|
+
/**
|
|
24
|
+
* Function that scrolls to the node's parent node.
|
|
25
|
+
*/
|
|
26
|
+
scrollToParent?: () => void;
|
|
23
27
|
};
|
|
24
28
|
export declare const TreeContext: React.Context<TreeContextProps | undefined>;
|
|
25
29
|
export declare const useTreeContext: () => TreeContextProps;
|
|
@@ -58,7 +58,7 @@ var TreeContext_1 = require("./TreeContext");
|
|
|
58
58
|
var TreeNode = function (props) {
|
|
59
59
|
var nodeId = props.nodeId, label = props.label, sublabel = props.sublabel, children = props.children, className = props.className, icon = props.icon, _a = props.hasSubNodes, hasSubNodes = _a === void 0 ? false : _a, _b = props.isDisabled, isDisabled = _b === void 0 ? false : _b, _c = props.isExpanded, isExpanded = _c === void 0 ? false : _c, _d = props.isSelected, isSelected = _d === void 0 ? false : _d, onSelected = props.onSelected, onExpanded = props.onExpanded, checkbox = props.checkbox, expander = props.expander, rest = __rest(props, ["nodeId", "label", "sublabel", "children", "className", "icon", "hasSubNodes", "isDisabled", "isExpanded", "isSelected", "onSelected", "onExpanded", "checkbox", "expander"]);
|
|
60
60
|
(0, utils_1.useTheme)();
|
|
61
|
-
var _e = (0, TreeContext_1.useTreeContext)(), nodeDepth = _e.nodeDepth, _f = _e.subNodeIds, subNodeIds = _f === void 0 ? [] : _f, parentNodeId = _e.parentNodeId, groupSize = _e.groupSize, indexInGroup = _e.indexInGroup;
|
|
61
|
+
var _e = (0, TreeContext_1.useTreeContext)(), nodeDepth = _e.nodeDepth, _f = _e.subNodeIds, subNodeIds = _f === void 0 ? [] : _f, parentNodeId = _e.parentNodeId, scrollToParent = _e.scrollToParent, groupSize = _e.groupSize, indexInGroup = _e.indexInGroup;
|
|
62
62
|
var _g = react_1.default.useState(false), isFocused = _g[0], setIsFocused = _g[1];
|
|
63
63
|
var nodeRef = react_1.default.useRef(null);
|
|
64
64
|
var styleDepth = react_1.default.useMemo(function () {
|
|
@@ -68,7 +68,7 @@ var TreeNode = function (props) {
|
|
|
68
68
|
: { marginLeft: nodeDepth ? nodeDepth * 28 : 0 };
|
|
69
69
|
}, [nodeDepth]);
|
|
70
70
|
var onKeyDown = function (event) {
|
|
71
|
-
var _a, _b, _c, _d, _e, _f
|
|
71
|
+
var _a, _b, _c, _d, _e, _f;
|
|
72
72
|
var isNodeFocused = nodeRef.current === ((_a = nodeRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument.activeElement);
|
|
73
73
|
switch (event.key) {
|
|
74
74
|
case 'ArrowLeft': {
|
|
@@ -79,20 +79,19 @@ var TreeNode = function (props) {
|
|
|
79
79
|
break;
|
|
80
80
|
}
|
|
81
81
|
if (parentNodeId) {
|
|
82
|
-
|
|
83
|
-
parentNode === null || parentNode === void 0 ? void 0 : parentNode.focus();
|
|
82
|
+
scrollToParent === null || scrollToParent === void 0 ? void 0 : scrollToParent();
|
|
84
83
|
break;
|
|
85
84
|
}
|
|
86
85
|
// If it is top level node (doesn't have parent node), then do nothing.
|
|
87
86
|
break;
|
|
88
87
|
}
|
|
89
88
|
var focusableElements = (0, utils_1.getFocusableElements)(nodeRef.current);
|
|
90
|
-
var currentIndex = focusableElements.indexOf((
|
|
89
|
+
var currentIndex = focusableElements.indexOf((_b = nodeRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.activeElement);
|
|
91
90
|
if (currentIndex === 0) {
|
|
92
|
-
(
|
|
91
|
+
(_c = nodeRef.current) === null || _c === void 0 ? void 0 : _c.focus();
|
|
93
92
|
}
|
|
94
93
|
else {
|
|
95
|
-
(
|
|
94
|
+
(_d = focusableElements[currentIndex - 1]) === null || _d === void 0 ? void 0 : _d.focus();
|
|
96
95
|
}
|
|
97
96
|
break;
|
|
98
97
|
}
|
|
@@ -104,10 +103,10 @@ var TreeNode = function (props) {
|
|
|
104
103
|
onExpanded(nodeId, true);
|
|
105
104
|
break;
|
|
106
105
|
}
|
|
107
|
-
(
|
|
106
|
+
(_e = focusableElements[0]) === null || _e === void 0 ? void 0 : _e.focus();
|
|
108
107
|
break;
|
|
109
108
|
}
|
|
110
|
-
var currentIndex = focusableElements.indexOf((
|
|
109
|
+
var currentIndex = focusableElements.indexOf((_f = nodeRef.current) === null || _f === void 0 ? void 0 : _f.ownerDocument.activeElement);
|
|
111
110
|
if (currentIndex < focusableElements.length - 1) {
|
|
112
111
|
focusableElements[currentIndex + 1].focus();
|
|
113
112
|
break;
|
|
@@ -43,6 +43,6 @@ require("@itwin/itwinui-css/css/text.css");
|
|
|
43
43
|
exports.Small = react_1.default.forwardRef(function (props, ref) {
|
|
44
44
|
var className = props.className, _a = props.isMuted, isMuted = _a === void 0 ? false : _a, rest = __rest(props, ["className", "isMuted"]);
|
|
45
45
|
(0, utils_1.useTheme)();
|
|
46
|
-
return (react_1.default.createElement("
|
|
46
|
+
return (react_1.default.createElement("small", __assign({ ref: ref, className: (0, classnames_1.default)('iui-text-small', { 'iui-text-muted': isMuted }, className) }, rest)));
|
|
47
47
|
});
|
|
48
48
|
exports.default = exports.Small;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Hook that keeps track of the latest value in a ref.
|
|
4
|
+
* @private
|
|
5
|
+
* @example
|
|
6
|
+
* const { value } = props;
|
|
7
|
+
* const valueRef = useLatestRef(value);
|
|
8
|
+
*/
|
|
9
|
+
export declare const useLatestRef: <T>(value: T) => React.MutableRefObject<T>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.useLatestRef = void 0;
|
|
7
|
+
/*---------------------------------------------------------------------------------------------
|
|
8
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
9
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
10
|
+
*--------------------------------------------------------------------------------------------*/
|
|
11
|
+
var react_1 = __importDefault(require("react"));
|
|
12
|
+
/**
|
|
13
|
+
* Hook that keeps track of the latest value in a ref.
|
|
14
|
+
* @private
|
|
15
|
+
* @example
|
|
16
|
+
* const { value } = props;
|
|
17
|
+
* const valueRef = useLatestRef(value);
|
|
18
|
+
*/
|
|
19
|
+
var useLatestRef = function (value) {
|
|
20
|
+
var valueRef = react_1.default.useRef(value);
|
|
21
|
+
react_1.default.useEffect(function () {
|
|
22
|
+
valueRef.current = value;
|
|
23
|
+
}, [value]);
|
|
24
|
+
return valueRef;
|
|
25
|
+
};
|
|
26
|
+
exports.useLatestRef = useLatestRef;
|
|
@@ -28,7 +28,7 @@ import React from 'react';
|
|
|
28
28
|
import cx from 'classnames';
|
|
29
29
|
import { MenuExtraContent } from '../Menu';
|
|
30
30
|
import { Text } from '../Typography';
|
|
31
|
-
import { useTheme, getRandomValue, mergeRefs, } from '../utils';
|
|
31
|
+
import { useTheme, getRandomValue, mergeRefs, useLatestRef, } from '../utils';
|
|
32
32
|
import 'tippy.js/animations/shift-away.css';
|
|
33
33
|
import { ComboBoxActionContext, comboBoxReducer, ComboBoxRefsContext, ComboBoxStateContext, } from './helpers';
|
|
34
34
|
import { ComboBoxDropdown } from './ComboBoxDropdown';
|
|
@@ -69,11 +69,9 @@ export var ComboBox = function (props) {
|
|
|
69
69
|
var menuRef = React.useRef(null);
|
|
70
70
|
var toggleButtonRef = React.useRef(null);
|
|
71
71
|
var mounted = React.useRef(false);
|
|
72
|
-
|
|
73
|
-
var onChangeProp =
|
|
74
|
-
|
|
75
|
-
onChangeProp.current = onChange;
|
|
76
|
-
}, [onChange]);
|
|
72
|
+
var valuePropRef = useLatestRef(valueProp);
|
|
73
|
+
var onChangeProp = useLatestRef(onChange);
|
|
74
|
+
var optionsRef = useLatestRef(options);
|
|
77
75
|
// Record to store all extra information (e.g. original indexes), where the key is the id of the option
|
|
78
76
|
var optionsExtraInfoRef = React.useRef({});
|
|
79
77
|
// Clear the extra info when the options change so that it can be reinitialized below
|
|
@@ -93,7 +91,7 @@ export var ComboBox = function (props) {
|
|
|
93
91
|
var _e = React.useReducer(comboBoxReducer, {
|
|
94
92
|
isOpen: false,
|
|
95
93
|
selectedIndex: valueProp
|
|
96
|
-
?
|
|
94
|
+
? optionsRef.current.findIndex(function (option) { return option.value === valueProp; })
|
|
97
95
|
: -1,
|
|
98
96
|
focusedIndex: -1,
|
|
99
97
|
}), _f = _e[0], isOpen = _f.isOpen, selectedIndex = _f.selectedIndex, focusedIndex = _f.focusedIndex, dispatch = _e[1];
|
|
@@ -102,7 +100,7 @@ export var ComboBox = function (props) {
|
|
|
102
100
|
// When the dropdown opens
|
|
103
101
|
if (isOpen) {
|
|
104
102
|
(_a = inputRef.current) === null || _a === void 0 ? void 0 : _a.focus(); // Focus the input
|
|
105
|
-
setFilteredOptions(
|
|
103
|
+
setFilteredOptions(optionsRef.current); // Reset the filtered list
|
|
106
104
|
dispatch(['focus']);
|
|
107
105
|
}
|
|
108
106
|
// When the dropdown closes
|
|
@@ -111,10 +109,10 @@ export var ComboBox = function (props) {
|
|
|
111
109
|
dispatch(['focus']);
|
|
112
110
|
// Reset the input value
|
|
113
111
|
setInputValue(selectedIndex != undefined && selectedIndex >= 0
|
|
114
|
-
? (_b =
|
|
112
|
+
? (_b = optionsRef.current[selectedIndex]) === null || _b === void 0 ? void 0 : _b.label
|
|
115
113
|
: '');
|
|
116
114
|
}
|
|
117
|
-
}, [isOpen,
|
|
115
|
+
}, [isOpen, optionsRef, selectedIndex]);
|
|
118
116
|
// Set min-width of menu to be same as input
|
|
119
117
|
var _g = React.useState(0), minWidth = _g[0], setMinWidth = _g[1];
|
|
120
118
|
React.useEffect(function () {
|
|
@@ -145,14 +143,14 @@ export var ComboBox = function (props) {
|
|
|
145
143
|
var value = event.currentTarget.value;
|
|
146
144
|
setInputValue(value);
|
|
147
145
|
dispatch(['open']); // reopen when typing
|
|
148
|
-
setFilteredOptions((_a = filterFunction === null || filterFunction === void 0 ? void 0 : filterFunction(
|
|
146
|
+
setFilteredOptions((_a = filterFunction === null || filterFunction === void 0 ? void 0 : filterFunction(optionsRef.current, value)) !== null && _a !== void 0 ? _a : optionsRef.current.filter(function (option) {
|
|
149
147
|
return option.label.toLowerCase().includes(value.toLowerCase());
|
|
150
148
|
}));
|
|
151
149
|
if (focusedIndex != -1) {
|
|
152
150
|
dispatch(['focus', -1]);
|
|
153
151
|
}
|
|
154
152
|
(_b = inputProps === null || inputProps === void 0 ? void 0 : inputProps.onChange) === null || _b === void 0 ? void 0 : _b.call(inputProps, event);
|
|
155
|
-
}, [filterFunction, focusedIndex, inputProps,
|
|
153
|
+
}, [filterFunction, focusedIndex, inputProps, optionsRef]);
|
|
156
154
|
// When the value prop changes, update the selectedIndex
|
|
157
155
|
React.useEffect(function () {
|
|
158
156
|
dispatch([
|
|
@@ -168,9 +166,12 @@ export var ComboBox = function (props) {
|
|
|
168
166
|
mounted.current = true;
|
|
169
167
|
return;
|
|
170
168
|
}
|
|
171
|
-
var
|
|
172
|
-
(
|
|
173
|
-
|
|
169
|
+
var currentValue = (_a = optionsRef.current[selectedIndex]) === null || _a === void 0 ? void 0 : _a.value;
|
|
170
|
+
if (currentValue === valuePropRef.current || selectedIndex === -1) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
(_b = onChangeProp.current) === null || _b === void 0 ? void 0 : _b.call(onChangeProp, currentValue);
|
|
174
|
+
}, [onChangeProp, optionsRef, selectedIndex, valuePropRef]);
|
|
174
175
|
var getMenuItem = React.useCallback(function (option, filteredIndex) {
|
|
175
176
|
var optionId = getOptionId(option, id);
|
|
176
177
|
var __originalIndex = optionsExtraInfoRef.current[optionId].__originalIndex;
|
|
@@ -182,6 +182,29 @@ export declare type TableProps<T extends Record<string, unknown> = Record<string
|
|
|
182
182
|
* @default false
|
|
183
183
|
*/
|
|
184
184
|
enableColumnReordering?: boolean;
|
|
185
|
+
/**
|
|
186
|
+
* Function that returns index of the row that you want to scroll to.
|
|
187
|
+
*
|
|
188
|
+
* It doesn't work with paginated tables and with lazy-loading.
|
|
189
|
+
* @beta
|
|
190
|
+
* @example
|
|
191
|
+
* <Table
|
|
192
|
+
* scrollToRow={React.useCallback(
|
|
193
|
+
* (rows, data) => rows.findIndex((row) => row.original === data[250]),
|
|
194
|
+
* []
|
|
195
|
+
* )}
|
|
196
|
+
* {...restProps}
|
|
197
|
+
* />
|
|
198
|
+
* @example
|
|
199
|
+
* <Table
|
|
200
|
+
* scrollToRow={React.useCallback(
|
|
201
|
+
* (rows, data) => rows.findIndex((row) => row.original.id === data[250].id),
|
|
202
|
+
* []
|
|
203
|
+
* )}
|
|
204
|
+
* {...restProps}
|
|
205
|
+
* />
|
|
206
|
+
*/
|
|
207
|
+
scrollToRow?: (rows: Row<T>[], data: T[]) => number;
|
|
185
208
|
} & Omit<CommonProps, 'title'>;
|
|
186
209
|
/**
|
|
187
210
|
* Table based on [react-table](https://react-table.tanstack.com/docs/api/overview).
|
package/esm/core/Table/Table.js
CHANGED
|
@@ -36,7 +36,7 @@ import { getCellStyle, getStickyStyle } from './utils';
|
|
|
36
36
|
import { TableRowMemoized } from './TableRowMemoized';
|
|
37
37
|
import { FilterToggle } from './filters';
|
|
38
38
|
import { customFilterFunctions } from './filters/customFilterFunctions';
|
|
39
|
-
import { useExpanderCell, useSelectionCell, useSubRowFiltering, useSubRowSelection, useResizeColumns, useColumnDragAndDrop, useStickyColumns, } from './hooks';
|
|
39
|
+
import { useExpanderCell, useSelectionCell, useSubRowFiltering, useSubRowSelection, useResizeColumns, useColumnDragAndDrop, useScrollToRow, useStickyColumns, } from './hooks';
|
|
40
40
|
import { onExpandHandler, onFilterHandler, onSelectHandler, onSingleSelectHandler, onTableResizeEnd, onTableResizeStart, } from './actionHandlers';
|
|
41
41
|
import VirtualScroll from '../utils/components/VirtualScroll';
|
|
42
42
|
import { SELECTION_CELL_ID } from './columns';
|
|
@@ -234,6 +234,7 @@ export var Table = function (props) {
|
|
|
234
234
|
state.pageIndex,
|
|
235
235
|
state.pageSize,
|
|
236
236
|
]);
|
|
237
|
+
var _r = useScrollToRow(__assign(__assign({}, props), { page: page })), scrollToIndex = _r.scrollToIndex, tableRowRef = _r.tableRowRef;
|
|
237
238
|
var columnRefs = React.useRef({});
|
|
238
239
|
var previousTableWidth = React.useRef(0);
|
|
239
240
|
var onTableResize = React.useCallback(function (_a) {
|
|
@@ -270,11 +271,11 @@ export var Table = function (props) {
|
|
|
270
271
|
var headerRef = React.useRef(null);
|
|
271
272
|
var bodyRef = React.useRef(null);
|
|
272
273
|
// Using `useState` to rerender rows when table body ref is available
|
|
273
|
-
var
|
|
274
|
+
var _s = React.useState(null), bodyRefState = _s[0], setBodyRefState = _s[1];
|
|
274
275
|
var getPreparedRow = React.useCallback(function (index) {
|
|
275
276
|
var row = page[index];
|
|
276
277
|
prepareRow(row);
|
|
277
|
-
return (React.createElement(TableRowMemoized, { row: row, rowProps: rowProps, isLast: index === page.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, bodyRef: bodyRefState }));
|
|
278
|
+
return (React.createElement(TableRowMemoized, { row: row, rowProps: rowProps, isLast: index === page.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, bodyRef: bodyRefState, tableRowRef: enableVirtualization ? undefined : tableRowRef(row) }));
|
|
278
279
|
}, [
|
|
279
280
|
page,
|
|
280
281
|
prepareRow,
|
|
@@ -288,6 +289,8 @@ export var Table = function (props) {
|
|
|
288
289
|
instance,
|
|
289
290
|
expanderCell,
|
|
290
291
|
bodyRefState,
|
|
292
|
+
enableVirtualization,
|
|
293
|
+
tableRowRef,
|
|
291
294
|
]);
|
|
292
295
|
var virtualizedItemRenderer = React.useCallback(function (index) { return getPreparedRow(index); }, [getPreparedRow]);
|
|
293
296
|
var updateStickyState = function () {
|
|
@@ -369,7 +372,7 @@ export var Table = function (props) {
|
|
|
369
372
|
updateStickyState();
|
|
370
373
|
}
|
|
371
374
|
}, tabIndex: -1 }),
|
|
372
|
-
data.length !== 0 && (React.createElement(React.Fragment, null, enableVirtualization ? (React.createElement(VirtualScroll, { itemsLength: page.length, itemRenderer: virtualizedItemRenderer })) : (page.map(function (_, index) { return getPreparedRow(index); })))),
|
|
375
|
+
data.length !== 0 && (React.createElement(React.Fragment, null, enableVirtualization ? (React.createElement(VirtualScroll, { itemsLength: page.length, itemRenderer: virtualizedItemRenderer, scrollToIndex: scrollToIndex })) : (page.map(function (_, index) { return getPreparedRow(index); })))),
|
|
373
376
|
isLoading && data.length === 0 && (React.createElement("div", { className: 'iui-table-empty' },
|
|
374
377
|
React.createElement(ProgressRadial, { indeterminate: true }))),
|
|
375
378
|
isLoading && data.length !== 0 && (React.createElement("div", { className: 'iui-row' },
|
|
@@ -21,6 +21,7 @@ export declare const TableRow: <T extends Record<string, unknown>>(props: {
|
|
|
21
21
|
tableInstance: TableInstance<T>;
|
|
22
22
|
expanderCell?: ((cellProps: CellProps<T, any>) => React.ReactNode) | undefined;
|
|
23
23
|
bodyRef: HTMLDivElement | null;
|
|
24
|
+
tableRowRef?: React.Ref<HTMLDivElement> | undefined;
|
|
24
25
|
}) => JSX.Element;
|
|
25
26
|
export declare const TableRowMemoized: <T extends Record<string, unknown>>(props: {
|
|
26
27
|
row: Row<T>;
|
|
@@ -37,4 +38,5 @@ export declare const TableRowMemoized: <T extends Record<string, unknown>>(props
|
|
|
37
38
|
tableInstance: TableInstance<T>;
|
|
38
39
|
expanderCell?: ((cellProps: CellProps<T, any>) => React.ReactNode) | undefined;
|
|
39
40
|
bodyRef: HTMLDivElement | null;
|
|
41
|
+
tableRowRef?: React.Ref<HTMLDivElement> | undefined;
|
|
40
42
|
}) => JSX.Element;
|
|
@@ -24,7 +24,7 @@ import { TableCell } from './TableCell';
|
|
|
24
24
|
* When adding new features check whether it changes state that affects row. If it does then add equality check to `React.memo`.
|
|
25
25
|
*/
|
|
26
26
|
export var TableRow = function (props) {
|
|
27
|
-
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;
|
|
27
|
+
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
28
|
var onIntersect = React.useCallback(function () {
|
|
29
29
|
var _a, _b;
|
|
30
30
|
(_a = onRowInViewport.current) === null || _a === void 0 ? void 0 : _a.call(onRowInViewport, row.original);
|
|
@@ -52,7 +52,7 @@ export var TableRow = function (props) {
|
|
|
52
52
|
'iui-disabled': isDisabled,
|
|
53
53
|
}, userRowProps === null || userRowProps === void 0 ? void 0 : userRowProps.className),
|
|
54
54
|
});
|
|
55
|
-
var refs = useMergedRefs(intersectionRef, mergedProps.ref);
|
|
55
|
+
var refs = useMergedRefs(intersectionRef, mergedProps.ref, tableRowRef);
|
|
56
56
|
return (React.createElement(React.Fragment, null,
|
|
57
57
|
React.createElement("div", __assign({}, mergedProps, { ref: refs, onClick: function (event) {
|
|
58
58
|
var _a;
|
|
@@ -4,4 +4,5 @@ export { useSubRowFiltering } from './useSubRowFiltering';
|
|
|
4
4
|
export { useSubRowSelection } from './useSubRowSelection';
|
|
5
5
|
export { useResizeColumns } from './useResizeColumns';
|
|
6
6
|
export { useColumnDragAndDrop } from './useColumnDragAndDrop';
|
|
7
|
+
export { useScrollToRow } from './useScrollToRow';
|
|
7
8
|
export { useStickyColumns } from './useStickyColumns';
|
|
@@ -8,4 +8,5 @@ export { useSubRowFiltering } from './useSubRowFiltering';
|
|
|
8
8
|
export { useSubRowSelection } from './useSubRowSelection';
|
|
9
9
|
export { useResizeColumns } from './useResizeColumns';
|
|
10
10
|
export { useColumnDragAndDrop } from './useColumnDragAndDrop';
|
|
11
|
+
export { useScrollToRow } from './useScrollToRow';
|
|
11
12
|
export { useStickyColumns } from './useStickyColumns';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Row } from 'react-table';
|
|
2
|
+
import { TableProps } from '../Table';
|
|
3
|
+
declare type ScrollToRow<T extends Record<string, unknown>> = {
|
|
4
|
+
scrollToIndex: number | undefined;
|
|
5
|
+
tableRowRef: (row: Row<T>) => (element: HTMLDivElement) => void;
|
|
6
|
+
};
|
|
7
|
+
declare type ScrollToRowProps<T extends Record<string, unknown>> = TableProps<T> & {
|
|
8
|
+
page: Row<T>[];
|
|
9
|
+
};
|
|
10
|
+
export declare function useScrollToRow<T extends Record<string, unknown>>({ data, enableVirtualization, page, paginatorRenderer, scrollToRow, onBottomReached, }: ScrollToRowProps<T>): ScrollToRow<T>;
|
|
11
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
3
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
export function useScrollToRow(_a) {
|
|
7
|
+
var data = _a.data, enableVirtualization = _a.enableVirtualization, page = _a.page, paginatorRenderer = _a.paginatorRenderer, scrollToRow = _a.scrollToRow, onBottomReached = _a.onBottomReached;
|
|
8
|
+
var rowRefs = React.useRef({});
|
|
9
|
+
// Refs prevents from having `page` and `data` as dependencies
|
|
10
|
+
// therefore we avoid unnecessary scroll to row.
|
|
11
|
+
var pageRef = React.useRef(page);
|
|
12
|
+
pageRef.current = page;
|
|
13
|
+
var dataRef = React.useRef(data);
|
|
14
|
+
dataRef.current = data;
|
|
15
|
+
// For virtualized tables, all we need to do is pass the index of the item
|
|
16
|
+
// to the VirtualScroll component
|
|
17
|
+
var scrollToIndex = React.useMemo(function () {
|
|
18
|
+
if (!scrollToRow || paginatorRenderer || onBottomReached) {
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
var index = scrollToRow(pageRef.current, dataRef.current);
|
|
22
|
+
return index < 0 ? undefined : index;
|
|
23
|
+
}, [onBottomReached, paginatorRenderer, scrollToRow]);
|
|
24
|
+
// For non-virtualized tables, we need to add a ref to each row
|
|
25
|
+
// and scroll to the element
|
|
26
|
+
React.useEffect(function () {
|
|
27
|
+
var _a;
|
|
28
|
+
if (enableVirtualization ||
|
|
29
|
+
scrollToIndex === undefined ||
|
|
30
|
+
scrollToIndex === null ||
|
|
31
|
+
scrollToIndex < 0) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
(_a = rowRefs.current[pageRef.current[scrollToIndex].id]) === null || _a === void 0 ? void 0 : _a.scrollIntoView();
|
|
35
|
+
}, [enableVirtualization, scrollToIndex]);
|
|
36
|
+
var tableRowRef = React.useCallback(function (row) {
|
|
37
|
+
return function (element) {
|
|
38
|
+
rowRefs.current[row.id] = element;
|
|
39
|
+
};
|
|
40
|
+
}, []);
|
|
41
|
+
return { scrollToIndex: scrollToIndex, tableRowRef: tableRowRef };
|
|
42
|
+
}
|
package/esm/core/Tree/Tree.d.ts
CHANGED
|
@@ -68,6 +68,15 @@ export declare type TreeProps<T> = {
|
|
|
68
68
|
* }, [expandedNodes]);
|
|
69
69
|
*/
|
|
70
70
|
getNode: (node: T) => NodeData<T>;
|
|
71
|
+
/**
|
|
72
|
+
* Virtualization is used to have a better performance with a lot of nodes.
|
|
73
|
+
*
|
|
74
|
+
* When enabled, Tree DOM structure will change - it will have a wrapper div
|
|
75
|
+
* to which `className` and `style` will be applied.
|
|
76
|
+
* @default false
|
|
77
|
+
* @beta
|
|
78
|
+
*/
|
|
79
|
+
enableVirtualization?: boolean;
|
|
71
80
|
} & Omit<CommonProps, 'title'>;
|
|
72
81
|
/**
|
|
73
82
|
* Tree component used to display a hierarchical structure of `TreeNodes`.
|
package/esm/core/Tree/Tree.js
CHANGED
|
@@ -25,7 +25,7 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
25
25
|
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
26
26
|
*--------------------------------------------------------------------------------------------*/
|
|
27
27
|
import React from 'react';
|
|
28
|
-
import { useTheme, getFocusableElements } from '../utils';
|
|
28
|
+
import { useTheme, getFocusableElements, useVirtualization, mergeRefs, } from '../utils';
|
|
29
29
|
import '@itwin/itwinui-css/css/tree.css';
|
|
30
30
|
import cx from 'classnames';
|
|
31
31
|
import { TreeContext } from './TreeContext';
|
|
@@ -80,7 +80,7 @@ import { TreeContext } from './TreeContext';
|
|
|
80
80
|
/>
|
|
81
81
|
*/
|
|
82
82
|
export var Tree = function (props) {
|
|
83
|
-
var data = props.data, className = props.className, nodeRenderer = props.nodeRenderer, getNode = props.getNode, rest = __rest(props, ["data", "className", "nodeRenderer", "getNode"]);
|
|
83
|
+
var data = props.data, className = props.className, nodeRenderer = props.nodeRenderer, getNode = props.getNode, _a = props.enableVirtualization, enableVirtualization = _a === void 0 ? false : _a, style = props.style, rest = __rest(props, ["data", "className", "nodeRenderer", "getNode", "enableVirtualization", "style"]);
|
|
84
84
|
useTheme();
|
|
85
85
|
var treeRef = React.useRef(null);
|
|
86
86
|
var focusedIndex = React.useRef(0);
|
|
@@ -118,7 +118,7 @@ export var Tree = function (props) {
|
|
|
118
118
|
break;
|
|
119
119
|
}
|
|
120
120
|
};
|
|
121
|
-
var
|
|
121
|
+
var _b = React.useMemo(function () {
|
|
122
122
|
var flatList = [];
|
|
123
123
|
var firstLevelNodes = [];
|
|
124
124
|
var flattenNodes = function (nodes, depth, parentNode) {
|
|
@@ -147,24 +147,72 @@ export var Tree = function (props) {
|
|
|
147
147
|
};
|
|
148
148
|
flattenNodes(data);
|
|
149
149
|
return [flatList, firstLevelNodes];
|
|
150
|
-
}, [data, getNode]), flatNodesList =
|
|
151
|
-
|
|
152
|
-
var _a;
|
|
153
|
-
var items = getFocusableNodes();
|
|
154
|
-
if (items.length > 0) {
|
|
155
|
-
(_a = items[focusedIndex.current]) === null || _a === void 0 ? void 0 : _a.focus();
|
|
156
|
-
}
|
|
157
|
-
} }, rest), flatNodesList.map(function (flatNode) {
|
|
150
|
+
}, [data, getNode]), flatNodesList = _b[0], firstLevelNodesList = _b[1];
|
|
151
|
+
var itemRenderer = React.useCallback(function (index) {
|
|
158
152
|
var _a, _b, _c, _d;
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
153
|
+
var node = flatNodesList[index];
|
|
154
|
+
return (React.createElement(TreeContext.Provider, { key: node.nodeProps.nodeId, value: {
|
|
155
|
+
nodeDepth: node.depth,
|
|
156
|
+
subNodeIds: node.subNodeIds,
|
|
157
|
+
groupSize: node.depth === 0
|
|
163
158
|
? firstLevelNodesList.length
|
|
164
|
-
: (_c = (_b = (_a =
|
|
165
|
-
indexInGroup:
|
|
166
|
-
parentNodeId: (_d =
|
|
167
|
-
|
|
168
|
-
|
|
159
|
+
: (_c = (_b = (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.subNodeIds) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0,
|
|
160
|
+
indexInGroup: node.indexInGroup,
|
|
161
|
+
parentNodeId: (_d = node.parentNode) === null || _d === void 0 ? void 0 : _d.nodeProps.nodeId,
|
|
162
|
+
scrollToParent: node.parentNode
|
|
163
|
+
? function () {
|
|
164
|
+
var _a;
|
|
165
|
+
var parentNodeId = (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.nodeProps.nodeId;
|
|
166
|
+
var parentNodeIndex = flatNodesList.findIndex(function (n) { return n.nodeProps.nodeId === parentNodeId; });
|
|
167
|
+
setScrollToIndex(parentNodeIndex);
|
|
168
|
+
}
|
|
169
|
+
: undefined,
|
|
170
|
+
} }, nodeRenderer(node.nodeProps)));
|
|
171
|
+
}, [firstLevelNodesList.length, flatNodesList, nodeRenderer]);
|
|
172
|
+
var _c = React.useState(), scrollToIndex = _c[0], setScrollToIndex = _c[1];
|
|
173
|
+
var flatNodesListRef = React.useRef(flatNodesList);
|
|
174
|
+
React.useEffect(function () {
|
|
175
|
+
flatNodesListRef.current = flatNodesList;
|
|
176
|
+
}, [flatNodesList]);
|
|
177
|
+
React.useEffect(function () {
|
|
178
|
+
setTimeout(function () {
|
|
179
|
+
var _a;
|
|
180
|
+
if (scrollToIndex !== undefined) {
|
|
181
|
+
var nodeId = flatNodesListRef.current[scrollToIndex].nodeProps.nodeId;
|
|
182
|
+
var nodeElement = (_a = treeRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument.querySelector("#".concat(nodeId));
|
|
183
|
+
nodeElement === null || nodeElement === void 0 ? void 0 : nodeElement.focus();
|
|
184
|
+
// Need to reset that if navigating with mouse and keyboard,
|
|
185
|
+
// e.g. pressing arrow left to go to parent node and then with mouse
|
|
186
|
+
// clicking some other child node and then pressing arrow left
|
|
187
|
+
setScrollToIndex(undefined);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}, [scrollToIndex]);
|
|
191
|
+
var handleFocus = function (event) {
|
|
192
|
+
var _a, _b;
|
|
193
|
+
if ((_a = treeRef.current) === null || _a === void 0 ? void 0 : _a.contains(event.relatedTarget)) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
var items = getFocusableNodes();
|
|
197
|
+
if (items.length > 0) {
|
|
198
|
+
(_b = items[focusedIndex.current]) === null || _b === void 0 ? void 0 : _b.focus();
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
return (React.createElement(React.Fragment, null, enableVirtualization ? (React.createElement(VirtualizedTree, __assign({ flatNodesList: flatNodesList, itemRenderer: itemRenderer, scrollToIndex: scrollToIndex, onFocus: handleFocus, onKeyDown: handleKeyDown, ref: treeRef, className: className, style: style }, rest))) : (React.createElement(TreeElement, __assign({ onKeyDown: handleKeyDown, onFocus: handleFocus, className: className, style: style, ref: treeRef }, rest), flatNodesList.map(function (_, i) { return itemRenderer(i); })))));
|
|
169
202
|
};
|
|
203
|
+
var TreeElement = React.forwardRef(function (_a, ref) {
|
|
204
|
+
var children = _a.children, className = _a.className, rest = __rest(_a, ["children", "className"]);
|
|
205
|
+
return (React.createElement("ul", __assign({ className: cx('iui-tree', className), role: 'tree', ref: ref, tabIndex: 0 }, rest), children));
|
|
206
|
+
});
|
|
207
|
+
// Having virtualized tree separately prevents from running all virtualization logic
|
|
208
|
+
var VirtualizedTree = React.forwardRef(function (_a, ref) {
|
|
209
|
+
var flatNodesList = _a.flatNodesList, itemRenderer = _a.itemRenderer, scrollToIndex = _a.scrollToIndex, className = _a.className, style = _a.style, rest = __rest(_a, ["flatNodesList", "itemRenderer", "scrollToIndex", "className", "style"]);
|
|
210
|
+
var _b = useVirtualization({
|
|
211
|
+
itemsLength: flatNodesList.length,
|
|
212
|
+
itemRenderer: itemRenderer,
|
|
213
|
+
scrollToIndex: scrollToIndex,
|
|
214
|
+
}), outerProps = _b.outerProps, innerProps = _b.innerProps, visibleChildren = _b.visibleChildren;
|
|
215
|
+
return (React.createElement("div", __assign({}, __assign(__assign({}, outerProps), { className: cx(className, outerProps.className), style: __assign(__assign({}, style), outerProps.style) })),
|
|
216
|
+
React.createElement(TreeElement, __assign({}, innerProps, rest, { ref: mergeRefs(ref, innerProps.ref) }), visibleChildren)));
|
|
217
|
+
});
|
|
170
218
|
export default Tree;
|
|
@@ -20,6 +20,10 @@ export declare type TreeContextProps = {
|
|
|
20
20
|
* Node index in the list of nodes under the same parent node or in the root. Used for an accessibility attribute.
|
|
21
21
|
*/
|
|
22
22
|
indexInGroup: number;
|
|
23
|
+
/**
|
|
24
|
+
* Function that scrolls to the node's parent node.
|
|
25
|
+
*/
|
|
26
|
+
scrollToParent?: () => void;
|
|
23
27
|
};
|
|
24
28
|
export declare const TreeContext: React.Context<TreeContextProps | undefined>;
|
|
25
29
|
export declare const useTreeContext: () => TreeContextProps;
|
|
@@ -52,7 +52,7 @@ import { useTreeContext } from './TreeContext';
|
|
|
52
52
|
export var TreeNode = function (props) {
|
|
53
53
|
var nodeId = props.nodeId, label = props.label, sublabel = props.sublabel, children = props.children, className = props.className, icon = props.icon, _a = props.hasSubNodes, hasSubNodes = _a === void 0 ? false : _a, _b = props.isDisabled, isDisabled = _b === void 0 ? false : _b, _c = props.isExpanded, isExpanded = _c === void 0 ? false : _c, _d = props.isSelected, isSelected = _d === void 0 ? false : _d, onSelected = props.onSelected, onExpanded = props.onExpanded, checkbox = props.checkbox, expander = props.expander, rest = __rest(props, ["nodeId", "label", "sublabel", "children", "className", "icon", "hasSubNodes", "isDisabled", "isExpanded", "isSelected", "onSelected", "onExpanded", "checkbox", "expander"]);
|
|
54
54
|
useTheme();
|
|
55
|
-
var _e = useTreeContext(), nodeDepth = _e.nodeDepth, _f = _e.subNodeIds, subNodeIds = _f === void 0 ? [] : _f, parentNodeId = _e.parentNodeId, groupSize = _e.groupSize, indexInGroup = _e.indexInGroup;
|
|
55
|
+
var _e = useTreeContext(), nodeDepth = _e.nodeDepth, _f = _e.subNodeIds, subNodeIds = _f === void 0 ? [] : _f, parentNodeId = _e.parentNodeId, scrollToParent = _e.scrollToParent, groupSize = _e.groupSize, indexInGroup = _e.indexInGroup;
|
|
56
56
|
var _g = React.useState(false), isFocused = _g[0], setIsFocused = _g[1];
|
|
57
57
|
var nodeRef = React.useRef(null);
|
|
58
58
|
var styleDepth = React.useMemo(function () {
|
|
@@ -62,7 +62,7 @@ export var TreeNode = function (props) {
|
|
|
62
62
|
: { marginLeft: nodeDepth ? nodeDepth * 28 : 0 };
|
|
63
63
|
}, [nodeDepth]);
|
|
64
64
|
var onKeyDown = function (event) {
|
|
65
|
-
var _a, _b, _c, _d, _e, _f
|
|
65
|
+
var _a, _b, _c, _d, _e, _f;
|
|
66
66
|
var isNodeFocused = nodeRef.current === ((_a = nodeRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument.activeElement);
|
|
67
67
|
switch (event.key) {
|
|
68
68
|
case 'ArrowLeft': {
|
|
@@ -73,20 +73,19 @@ export var TreeNode = function (props) {
|
|
|
73
73
|
break;
|
|
74
74
|
}
|
|
75
75
|
if (parentNodeId) {
|
|
76
|
-
|
|
77
|
-
parentNode === null || parentNode === void 0 ? void 0 : parentNode.focus();
|
|
76
|
+
scrollToParent === null || scrollToParent === void 0 ? void 0 : scrollToParent();
|
|
78
77
|
break;
|
|
79
78
|
}
|
|
80
79
|
// If it is top level node (doesn't have parent node), then do nothing.
|
|
81
80
|
break;
|
|
82
81
|
}
|
|
83
82
|
var focusableElements = getFocusableElements(nodeRef.current);
|
|
84
|
-
var currentIndex = focusableElements.indexOf((
|
|
83
|
+
var currentIndex = focusableElements.indexOf((_b = nodeRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument.activeElement);
|
|
85
84
|
if (currentIndex === 0) {
|
|
86
|
-
(
|
|
85
|
+
(_c = nodeRef.current) === null || _c === void 0 ? void 0 : _c.focus();
|
|
87
86
|
}
|
|
88
87
|
else {
|
|
89
|
-
(
|
|
88
|
+
(_d = focusableElements[currentIndex - 1]) === null || _d === void 0 ? void 0 : _d.focus();
|
|
90
89
|
}
|
|
91
90
|
break;
|
|
92
91
|
}
|
|
@@ -98,10 +97,10 @@ export var TreeNode = function (props) {
|
|
|
98
97
|
onExpanded(nodeId, true);
|
|
99
98
|
break;
|
|
100
99
|
}
|
|
101
|
-
(
|
|
100
|
+
(_e = focusableElements[0]) === null || _e === void 0 ? void 0 : _e.focus();
|
|
102
101
|
break;
|
|
103
102
|
}
|
|
104
|
-
var currentIndex = focusableElements.indexOf((
|
|
103
|
+
var currentIndex = focusableElements.indexOf((_f = nodeRef.current) === null || _f === void 0 ? void 0 : _f.ownerDocument.activeElement);
|
|
105
104
|
if (currentIndex < focusableElements.length - 1) {
|
|
106
105
|
focusableElements[currentIndex + 1].focus();
|
|
107
106
|
break;
|
|
@@ -37,6 +37,6 @@ import '@itwin/itwinui-css/css/text.css';
|
|
|
37
37
|
export var Small = React.forwardRef(function (props, ref) {
|
|
38
38
|
var className = props.className, _a = props.isMuted, isMuted = _a === void 0 ? false : _a, rest = __rest(props, ["className", "isMuted"]);
|
|
39
39
|
useTheme();
|
|
40
|
-
return (React.createElement("
|
|
40
|
+
return (React.createElement("small", __assign({ ref: ref, className: cx('iui-text-small', { 'iui-text-muted': isMuted }, className) }, rest)));
|
|
41
41
|
});
|
|
42
42
|
export default Small;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Hook that keeps track of the latest value in a ref.
|
|
4
|
+
* @private
|
|
5
|
+
* @example
|
|
6
|
+
* const { value } = props;
|
|
7
|
+
* const valueRef = useLatestRef(value);
|
|
8
|
+
*/
|
|
9
|
+
export declare const useLatestRef: <T>(value: T) => React.MutableRefObject<T>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/*---------------------------------------------------------------------------------------------
|
|
2
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
3
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
4
|
+
*--------------------------------------------------------------------------------------------*/
|
|
5
|
+
import React from 'react';
|
|
6
|
+
/**
|
|
7
|
+
* Hook that keeps track of the latest value in a ref.
|
|
8
|
+
* @private
|
|
9
|
+
* @example
|
|
10
|
+
* const { value } = props;
|
|
11
|
+
* const valueRef = useLatestRef(value);
|
|
12
|
+
*/
|
|
13
|
+
export var useLatestRef = function (value) {
|
|
14
|
+
var valueRef = React.useRef(value);
|
|
15
|
+
React.useEffect(function () {
|
|
16
|
+
valueRef.current = value;
|
|
17
|
+
}, [value]);
|
|
18
|
+
return valueRef;
|
|
19
|
+
};
|