@itwin/itwinui-react 1.32.0-dev.0 → 1.33.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +33 -0
- package/cjs/core/ButtonGroup/ButtonGroup.d.ts +6 -1
- package/cjs/core/ButtonGroup/ButtonGroup.js +6 -3
- package/cjs/core/Checkbox/Checkbox.d.ts +13 -0
- package/cjs/core/Checkbox/Checkbox.js +15 -22
- package/cjs/core/ColorPicker/ColorSwatch.d.ts +1 -1
- package/cjs/core/ComboBox/ComboBox.d.ts +17 -1
- package/cjs/core/ComboBox/ComboBox.js +55 -20
- package/cjs/core/ExpandableBlock/ExpandableBlock.d.ts +6 -0
- package/cjs/core/ExpandableBlock/ExpandableBlock.js +3 -2
- package/cjs/core/Footer/Footer.d.ts +14 -2
- package/cjs/core/Footer/Footer.js +40 -17
- package/cjs/core/InputGroup/InputGroup.js +12 -2
- package/cjs/core/LabeledSelect/LabeledSelect.js +10 -4
- package/cjs/core/Menu/Menu.js +3 -3
- package/cjs/core/Radio/Radio.d.ts +13 -0
- package/cjs/core/Radio/Radio.js +7 -8
- package/cjs/core/Slider/Slider.js +10 -1
- package/cjs/core/StatusMessage/StatusMessage.d.ts +24 -0
- package/cjs/core/StatusMessage/StatusMessage.js +39 -0
- package/cjs/core/StatusMessage/index.d.ts +4 -0
- package/cjs/core/StatusMessage/index.js +10 -0
- package/cjs/core/Table/hooks/useSelectionCell.js +1 -2
- package/cjs/core/Tree/Tree.d.ts +123 -0
- package/cjs/core/Tree/Tree.js +177 -0
- package/cjs/core/Tree/TreeContext.d.ts +25 -0
- package/cjs/core/Tree/TreeContext.js +20 -0
- package/cjs/core/Tree/TreeNode.d.ts +87 -0
- package/cjs/core/Tree/TreeNode.js +169 -0
- package/cjs/core/Tree/TreeNodeExpander.d.ts +8 -0
- package/cjs/core/Tree/TreeNodeExpander.js +46 -0
- package/cjs/core/Tree/index.d.ts +6 -0
- package/cjs/core/Tree/index.js +13 -0
- package/cjs/core/index.d.ts +4 -0
- package/cjs/core/index.js +7 -1
- package/cjs/core/utils/components/InputContainer.d.ts +1 -0
- package/cjs/core/utils/components/InputContainer.js +8 -7
- package/cjs/core/utils/functions/focusable.js +6 -2
- package/cjs/core/utils/hooks/useIntersection.d.ts +4 -3
- package/cjs/core/utils/hooks/useIntersection.js +10 -5
- package/cjs/core/utils/hooks/useOverflow.d.ts +3 -2
- package/cjs/core/utils/hooks/useOverflow.js +24 -21
- package/esm/core/ButtonGroup/ButtonGroup.d.ts +6 -1
- package/esm/core/ButtonGroup/ButtonGroup.js +6 -3
- package/esm/core/Checkbox/Checkbox.d.ts +13 -0
- package/esm/core/Checkbox/Checkbox.js +15 -22
- package/esm/core/ColorPicker/ColorSwatch.d.ts +1 -1
- package/esm/core/ComboBox/ComboBox.d.ts +17 -1
- package/esm/core/ComboBox/ComboBox.js +56 -21
- package/esm/core/ExpandableBlock/ExpandableBlock.d.ts +6 -0
- package/esm/core/ExpandableBlock/ExpandableBlock.js +3 -2
- package/esm/core/Footer/Footer.d.ts +14 -2
- package/esm/core/Footer/Footer.js +40 -17
- package/esm/core/InputGroup/InputGroup.js +12 -2
- package/esm/core/LabeledSelect/LabeledSelect.js +10 -4
- package/esm/core/Menu/Menu.js +3 -3
- package/esm/core/Radio/Radio.d.ts +13 -0
- package/esm/core/Radio/Radio.js +7 -8
- package/esm/core/Slider/Slider.js +10 -1
- package/esm/core/StatusMessage/StatusMessage.d.ts +24 -0
- package/esm/core/StatusMessage/StatusMessage.js +32 -0
- package/esm/core/StatusMessage/index.d.ts +4 -0
- package/esm/core/StatusMessage/index.js +6 -0
- package/esm/core/Table/hooks/useSelectionCell.js +1 -2
- package/esm/core/Tree/Tree.d.ts +123 -0
- package/esm/core/Tree/Tree.js +170 -0
- package/esm/core/Tree/TreeContext.d.ts +25 -0
- package/esm/core/Tree/TreeContext.js +13 -0
- package/esm/core/Tree/TreeNode.d.ts +87 -0
- package/esm/core/Tree/TreeNode.js +162 -0
- package/esm/core/Tree/TreeNodeExpander.d.ts +8 -0
- package/esm/core/Tree/TreeNodeExpander.js +39 -0
- package/esm/core/Tree/index.d.ts +6 -0
- package/esm/core/Tree/index.js +7 -0
- package/esm/core/index.d.ts +4 -0
- package/esm/core/index.js +2 -0
- package/esm/core/utils/components/InputContainer.d.ts +1 -0
- package/esm/core/utils/components/InputContainer.js +8 -7
- package/esm/core/utils/functions/focusable.js +6 -2
- package/esm/core/utils/hooks/useIntersection.d.ts +4 -3
- package/esm/core/utils/hooks/useIntersection.js +10 -5
- package/esm/core/utils/hooks/useOverflow.d.ts +3 -2
- package/esm/core/utils/hooks/useOverflow.js +24 -21
- package/package.json +15 -14
package/cjs/core/Menu/Menu.js
CHANGED
|
@@ -43,6 +43,9 @@ exports.Menu = react_1.default.forwardRef(function (props, ref) {
|
|
|
43
43
|
var _c = react_1.default.useState(), focusedIndex = _c[0], setFocusedIndex = _c[1];
|
|
44
44
|
var menuRef = react_1.default.useRef(null);
|
|
45
45
|
var refs = (0, utils_1.useMergedRefs)(menuRef, ref);
|
|
46
|
+
react_1.default.useEffect(function () {
|
|
47
|
+
setFocusedIndex(null);
|
|
48
|
+
}, [children]);
|
|
46
49
|
react_1.default.useEffect(function () {
|
|
47
50
|
var _a;
|
|
48
51
|
var items = (0, utils_1.getFocusableElements)(menuRef.current);
|
|
@@ -55,9 +58,6 @@ exports.Menu = react_1.default.forwardRef(function (props, ref) {
|
|
|
55
58
|
setFocusedIndex(selectedIndex > -1 ? selectedIndex : 0);
|
|
56
59
|
}
|
|
57
60
|
}, [setFocus, focusedIndex]);
|
|
58
|
-
react_1.default.useEffect(function () {
|
|
59
|
-
setFocusedIndex(null);
|
|
60
|
-
}, [children]);
|
|
61
61
|
var onKeyDown = function (event) {
|
|
62
62
|
var items = (0, utils_1.getFocusableElements)(menuRef.current);
|
|
63
63
|
if (!(items === null || items === void 0 ? void 0 : items.length)) {
|
|
@@ -11,10 +11,16 @@ export declare type RadioProps = {
|
|
|
11
11
|
status?: 'positive' | 'warning' | 'negative';
|
|
12
12
|
/**
|
|
13
13
|
* Custom CSS class name for the checkmark element.
|
|
14
|
+
*
|
|
15
|
+
* @deprecated As of 1.32.0, this is applied on the actual radio `<input>` element.
|
|
16
|
+
* The checkmark has been moved into a pseudo-element.
|
|
14
17
|
*/
|
|
15
18
|
checkmarkClassName?: string;
|
|
16
19
|
/**
|
|
17
20
|
* Custom CSS Style for the checkmark element.
|
|
21
|
+
*
|
|
22
|
+
* @deprecated As of 1.32.0, this is applied on the actual radio `<input>` element.
|
|
23
|
+
* The checkmark has been moved into a pseudo-element.
|
|
18
24
|
*/
|
|
19
25
|
checkmarkStyle?: React.CSSProperties;
|
|
20
26
|
/**
|
|
@@ -26,6 +32,7 @@ export declare type RadioProps = {
|
|
|
26
32
|
/**
|
|
27
33
|
* Basic radio input component
|
|
28
34
|
* @example
|
|
35
|
+
* <Radio />
|
|
29
36
|
* <Radio label='Radio' />
|
|
30
37
|
* <Radio disabled={true} label='Radio' />
|
|
31
38
|
* <Radio status='positive' label='Positive' />
|
|
@@ -43,10 +50,16 @@ export declare const Radio: React.ForwardRefExoticComponent<{
|
|
|
43
50
|
status?: "positive" | "warning" | "negative" | undefined;
|
|
44
51
|
/**
|
|
45
52
|
* Custom CSS class name for the checkmark element.
|
|
53
|
+
*
|
|
54
|
+
* @deprecated As of 1.32.0, this is applied on the actual radio `<input>` element.
|
|
55
|
+
* The checkmark has been moved into a pseudo-element.
|
|
46
56
|
*/
|
|
47
57
|
checkmarkClassName?: string | undefined;
|
|
48
58
|
/**
|
|
49
59
|
* Custom CSS Style for the checkmark element.
|
|
60
|
+
*
|
|
61
|
+
* @deprecated As of 1.32.0, this is applied on the actual radio `<input>` element.
|
|
62
|
+
* The checkmark has been moved into a pseudo-element.
|
|
50
63
|
*/
|
|
51
64
|
checkmarkStyle?: React.CSSProperties | undefined;
|
|
52
65
|
/**
|
package/cjs/core/Radio/Radio.js
CHANGED
|
@@ -37,6 +37,7 @@ require("@itwin/itwinui-css/css/inputs.css");
|
|
|
37
37
|
/**
|
|
38
38
|
* Basic radio input component
|
|
39
39
|
* @example
|
|
40
|
+
* <Radio />
|
|
40
41
|
* <Radio label='Radio' />
|
|
41
42
|
* <Radio disabled={true} label='Radio' />
|
|
42
43
|
* <Radio status='positive' label='Positive' />
|
|
@@ -44,8 +45,8 @@ require("@itwin/itwinui-css/css/inputs.css");
|
|
|
44
45
|
* <Radio status='negative' label='Negative' />
|
|
45
46
|
*/
|
|
46
47
|
exports.Radio = react_1.default.forwardRef(function (props, ref) {
|
|
47
|
-
var _a;
|
|
48
|
-
var className = props.className,
|
|
48
|
+
var _a, _b;
|
|
49
|
+
var className = props.className, _c = props.disabled, disabled = _c === void 0 ? false : _c, label = props.label, status = props.status, style = props.style, checkmarkClassName = props.checkmarkClassName, checkmarkStyle = props.checkmarkStyle, _d = props.setFocus, setFocus = _d === void 0 ? false : _d, rest = __rest(props, ["className", "disabled", "label", "status", "style", "checkmarkClassName", "checkmarkStyle", "setFocus"]);
|
|
49
50
|
(0, utils_1.useTheme)();
|
|
50
51
|
var inputElementRef = react_1.default.useRef(null);
|
|
51
52
|
var refs = (0, utils_1.useMergedRefs)(inputElementRef, ref);
|
|
@@ -54,11 +55,9 @@ exports.Radio = react_1.default.forwardRef(function (props, ref) {
|
|
|
54
55
|
inputElementRef.current.focus();
|
|
55
56
|
}
|
|
56
57
|
}, [setFocus]);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
react_1.default.createElement("circle", { cx: '8', cy: '8', r: '4' }))),
|
|
62
|
-
label && react_1.default.createElement("span", { className: 'iui-label' }, label)));
|
|
58
|
+
var radio = (react_1.default.createElement("input", __assign({ className: (0, classnames_1.default)('iui-radio', className && (_a = {}, _a[className] = !label, _a), checkmarkClassName), style: __assign(__assign({}, (!label && style)), checkmarkStyle), disabled: disabled, type: 'radio', ref: refs }, rest)));
|
|
59
|
+
return !label ? (radio) : (react_1.default.createElement("label", { className: (0, classnames_1.default)('iui-radio-wrapper', (_b = { 'iui-disabled': disabled }, _b["iui-" + status] = !!status, _b), className), style: style },
|
|
60
|
+
radio,
|
|
61
|
+
label && react_1.default.createElement("span", { className: 'iui-radio-label' }, label)));
|
|
63
62
|
});
|
|
64
63
|
exports.default = exports.Radio;
|
|
@@ -208,11 +208,20 @@ exports.Slider = react_1.default.forwardRef(function (props, ref) {
|
|
|
208
208
|
newValues[closestValueIndex] = pointerValue;
|
|
209
209
|
setCurrentValues(newValues);
|
|
210
210
|
onChange === null || onChange === void 0 ? void 0 : onChange(newValues);
|
|
211
|
+
onUpdate === null || onUpdate === void 0 ? void 0 : onUpdate(newValues);
|
|
211
212
|
focusThumb(containerRef.current, closestValueIndex);
|
|
212
213
|
event.preventDefault();
|
|
213
214
|
event.stopPropagation();
|
|
214
215
|
}
|
|
215
|
-
}, [
|
|
216
|
+
}, [
|
|
217
|
+
min,
|
|
218
|
+
max,
|
|
219
|
+
step,
|
|
220
|
+
currentValues,
|
|
221
|
+
getAllowableThumbRange,
|
|
222
|
+
onChange,
|
|
223
|
+
onUpdate,
|
|
224
|
+
]);
|
|
216
225
|
(0, utils_1.useEventListener)('pointermove', handlePointerMove, (_a = containerRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument);
|
|
217
226
|
(0, utils_1.useEventListener)('pointerup', handlePointerUp, (_b = containerRef.current) === null || _b === void 0 ? void 0 : _b.ownerDocument);
|
|
218
227
|
var tickMarkArea = react_1.default.useMemo(function () {
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export declare type StatusMessageProps = {
|
|
3
|
+
/**
|
|
4
|
+
* Custom icon to be displayed at the beginning.
|
|
5
|
+
* It will default to the `status` icon, if it's set.
|
|
6
|
+
*/
|
|
7
|
+
startIcon?: JSX.Element;
|
|
8
|
+
/**
|
|
9
|
+
* Message content.
|
|
10
|
+
*/
|
|
11
|
+
children: React.ReactNode;
|
|
12
|
+
/**
|
|
13
|
+
* Status of the message.
|
|
14
|
+
*/
|
|
15
|
+
status?: 'positive' | 'warning' | 'negative';
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Component to display icon and text below the `Combobox` component.
|
|
19
|
+
* @example
|
|
20
|
+
* <StatusMessage>This is the text</StatusMessage>
|
|
21
|
+
* <StatusMessage startIcon={<SvgStar />}>This is the text</StatusMessage>
|
|
22
|
+
*/
|
|
23
|
+
export declare const StatusMessage: ({ startIcon: userStartIcon, children, status, }: StatusMessageProps) => JSX.Element;
|
|
24
|
+
export default StatusMessage;
|
|
@@ -0,0 +1,39 @@
|
|
|
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.StatusMessage = 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
|
+
var utils_1 = require("../utils");
|
|
13
|
+
var classnames_1 = __importDefault(require("classnames"));
|
|
14
|
+
/**
|
|
15
|
+
* Component to display icon and text below the `Combobox` component.
|
|
16
|
+
* @example
|
|
17
|
+
* <StatusMessage>This is the text</StatusMessage>
|
|
18
|
+
* <StatusMessage startIcon={<SvgStar />}>This is the text</StatusMessage>
|
|
19
|
+
*/
|
|
20
|
+
var StatusMessage = function (_a) {
|
|
21
|
+
var userStartIcon = _a.startIcon, children = _a.children, status = _a.status;
|
|
22
|
+
(0, utils_1.useTheme)();
|
|
23
|
+
var StartIcon = function () {
|
|
24
|
+
var _a;
|
|
25
|
+
var icon = userStartIcon !== null && userStartIcon !== void 0 ? userStartIcon : (status && utils_1.StatusIconMap[status]());
|
|
26
|
+
if (!icon) {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
return react_1.default.cloneElement(icon, {
|
|
30
|
+
className: (0, classnames_1.default)('iui-input-icon', (_a = icon.props) === null || _a === void 0 ? void 0 : _a.className),
|
|
31
|
+
'aria-hidden': true,
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
35
|
+
react_1.default.createElement(StartIcon, null),
|
|
36
|
+
react_1.default.createElement("div", { className: 'iui-message' }, children)));
|
|
37
|
+
};
|
|
38
|
+
exports.StatusMessage = StatusMessage;
|
|
39
|
+
exports.default = exports.StatusMessage;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StatusMessage = void 0;
|
|
4
|
+
/*---------------------------------------------------------------------------------------------
|
|
5
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
6
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
7
|
+
*--------------------------------------------------------------------------------------------*/
|
|
8
|
+
var StatusMessage_1 = require("./StatusMessage");
|
|
9
|
+
Object.defineProperty(exports, "StatusMessage", { enumerable: true, get: function () { return StatusMessage_1.StatusMessage; } });
|
|
10
|
+
exports.default = './StatusMessage';
|
|
@@ -55,8 +55,7 @@ var useSelectionCell = function (isSelectable, isRowDisabled) { return function
|
|
|
55
55
|
},
|
|
56
56
|
Cell: function (_a) {
|
|
57
57
|
var row = _a.row;
|
|
58
|
-
return (react_1.default.createElement(
|
|
59
|
-
react_1.default.createElement(Checkbox_1.Checkbox, __assign({}, row.getToggleRowSelectedProps(), { disabled: isRowDisabled === null || isRowDisabled === void 0 ? void 0 : isRowDisabled(row.original) }))));
|
|
58
|
+
return (react_1.default.createElement(Checkbox_1.Checkbox, __assign({}, row.getToggleRowSelectedProps(), { disabled: isRowDisabled === null || isRowDisabled === void 0 ? void 0 : isRowDisabled(row.original), onClick: function (e) { return e.stopPropagation(); } })));
|
|
60
59
|
},
|
|
61
60
|
}
|
|
62
61
|
], columns, true); });
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { CommonProps } from '../utils';
|
|
3
|
+
import '@itwin/itwinui-css/css/tree.css';
|
|
4
|
+
export declare type NodeData<T> = {
|
|
5
|
+
/**
|
|
6
|
+
* Array of the child nodes contained in the node.
|
|
7
|
+
*/
|
|
8
|
+
subNodes?: Array<T>;
|
|
9
|
+
/**
|
|
10
|
+
* Unique id of the node.
|
|
11
|
+
*/
|
|
12
|
+
nodeId: string;
|
|
13
|
+
/**
|
|
14
|
+
* Custom type used to map type `T` to `NodeData`
|
|
15
|
+
*/
|
|
16
|
+
node: T;
|
|
17
|
+
/**
|
|
18
|
+
* Flag whether the node is expanded.
|
|
19
|
+
*/
|
|
20
|
+
isExpanded?: boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Flag whether the node is disabled.
|
|
23
|
+
*/
|
|
24
|
+
isDisabled?: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Flag whether the node is selected.
|
|
27
|
+
*/
|
|
28
|
+
isSelected?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Flag whether the node has sub-nodes.
|
|
31
|
+
* Used to determine if node should be expandable.
|
|
32
|
+
*/
|
|
33
|
+
hasSubNodes: boolean;
|
|
34
|
+
};
|
|
35
|
+
export declare type NodeRenderProps<T> = Omit<NodeData<T>, 'subNodes'>;
|
|
36
|
+
export declare type TreeProps<T> = {
|
|
37
|
+
/**
|
|
38
|
+
* Render function that should return the node element.
|
|
39
|
+
* Recommended to use `TreeNode` component.
|
|
40
|
+
* Must be memoized.
|
|
41
|
+
* @example
|
|
42
|
+
* const nodeRenderer = React.useCallback(({ node, ...rest }: NodeRenderProps<DataType>) => (
|
|
43
|
+
* <TreeNode
|
|
44
|
+
* label={node.label}
|
|
45
|
+
* onNodeExpanded={onNodeExpanded}
|
|
46
|
+
* {...rest}
|
|
47
|
+
* />
|
|
48
|
+
* ), [onNodeExpanded])
|
|
49
|
+
*/
|
|
50
|
+
nodeRenderer: (props: NodeRenderProps<T>) => JSX.Element;
|
|
51
|
+
/**
|
|
52
|
+
* Array of custom data used for `TreeNodes` inside `Tree`.
|
|
53
|
+
*/
|
|
54
|
+
data: T[];
|
|
55
|
+
/**
|
|
56
|
+
* Function that maps your `data` entry to `NodeData` that has all info about the node state.
|
|
57
|
+
* It will be used to render a tree node in `nodeRenderer`.
|
|
58
|
+
* Must be memoized.
|
|
59
|
+
* @example
|
|
60
|
+
* const getNode = React.useCallback((node: DemoData): NodeData<DemoData> => {
|
|
61
|
+
* return {
|
|
62
|
+
* subNodes: node.subItems,
|
|
63
|
+
* nodeId: node.id,
|
|
64
|
+
* node,
|
|
65
|
+
* isExpanded: expandedNodes[node.id],
|
|
66
|
+
* hasSubNodes: node.subItems.length > 0,
|
|
67
|
+
* };
|
|
68
|
+
* }, [expandedNodes]);
|
|
69
|
+
*/
|
|
70
|
+
getNode: (node: T) => NodeData<T>;
|
|
71
|
+
} & Omit<CommonProps, 'title'>;
|
|
72
|
+
/**
|
|
73
|
+
* Tree component used to display a hierarchical structure of `TreeNodes`.
|
|
74
|
+
* User should control state of expanded, selected and disabled nodes using `getNode` prop.
|
|
75
|
+
* @example
|
|
76
|
+
type DemoData = {
|
|
77
|
+
id: string;
|
|
78
|
+
label: string;
|
|
79
|
+
subItems: DemoData[];
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const data: Array<DemoData> = [
|
|
83
|
+
{
|
|
84
|
+
id: 'Node-1',
|
|
85
|
+
label: 'Facility 1',
|
|
86
|
+
subItems: [{ id: 'Node-1-1', label: 'Unit 1', subItems: [] }],
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'Node-2',
|
|
90
|
+
label: 'Facility 2',
|
|
91
|
+
subItems: [{ id: 'Node-2-1', label: 'Unit 2', subItems: [] }],
|
|
92
|
+
},
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
const [expandedNodes, setExpandedNodes] = React.useState<Record<string, boolean>>({});
|
|
96
|
+
const onNodeExpanded = React.useCallback((nodeId: string, isExpanded: boolean) => {
|
|
97
|
+
setExpandedNodes((oldExpanded) => ({ ...oldExpanded, [nodeId]: isExpanded }));
|
|
98
|
+
}, []);
|
|
99
|
+
|
|
100
|
+
const getNode = React.useCallback((node: DemoData): NodeData<DemoData> => {
|
|
101
|
+
return {
|
|
102
|
+
subNodes: node.subItems,
|
|
103
|
+
nodeId: node.id,
|
|
104
|
+
node,
|
|
105
|
+
isExpanded: expandedNodes[node.id],
|
|
106
|
+
hasSubNodes: node.subItems.length > 0,
|
|
107
|
+
};
|
|
108
|
+
}, [expandedNodes]);
|
|
109
|
+
|
|
110
|
+
<Tree<DemoData>
|
|
111
|
+
data={data}
|
|
112
|
+
getNode={getNode}
|
|
113
|
+
nodeRenderer={React.useCallback(({ node, ...rest }) => (
|
|
114
|
+
<TreeNode
|
|
115
|
+
label={node.label}
|
|
116
|
+
onNodeExpanded={onNodeExpanded}
|
|
117
|
+
{...rest}
|
|
118
|
+
/>
|
|
119
|
+
), [onNodeExpanded])}
|
|
120
|
+
/>
|
|
121
|
+
*/
|
|
122
|
+
export declare const Tree: <T>(props: TreeProps<T>) => JSX.Element;
|
|
123
|
+
export default Tree;
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
13
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
14
|
+
var t = {};
|
|
15
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
16
|
+
t[p] = s[p];
|
|
17
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
18
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
19
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
20
|
+
t[p[i]] = s[p[i]];
|
|
21
|
+
}
|
|
22
|
+
return t;
|
|
23
|
+
};
|
|
24
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
25
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
26
|
+
};
|
|
27
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
28
|
+
exports.Tree = void 0;
|
|
29
|
+
/*---------------------------------------------------------------------------------------------
|
|
30
|
+
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
|
|
31
|
+
* See LICENSE.md in the project root for license terms and full copyright notice.
|
|
32
|
+
*--------------------------------------------------------------------------------------------*/
|
|
33
|
+
var react_1 = __importDefault(require("react"));
|
|
34
|
+
var utils_1 = require("../utils");
|
|
35
|
+
require("@itwin/itwinui-css/css/tree.css");
|
|
36
|
+
var classnames_1 = __importDefault(require("classnames"));
|
|
37
|
+
var TreeContext_1 = require("./TreeContext");
|
|
38
|
+
/**
|
|
39
|
+
* Tree component used to display a hierarchical structure of `TreeNodes`.
|
|
40
|
+
* User should control state of expanded, selected and disabled nodes using `getNode` prop.
|
|
41
|
+
* @example
|
|
42
|
+
type DemoData = {
|
|
43
|
+
id: string;
|
|
44
|
+
label: string;
|
|
45
|
+
subItems: DemoData[];
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const data: Array<DemoData> = [
|
|
49
|
+
{
|
|
50
|
+
id: 'Node-1',
|
|
51
|
+
label: 'Facility 1',
|
|
52
|
+
subItems: [{ id: 'Node-1-1', label: 'Unit 1', subItems: [] }],
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'Node-2',
|
|
56
|
+
label: 'Facility 2',
|
|
57
|
+
subItems: [{ id: 'Node-2-1', label: 'Unit 2', subItems: [] }],
|
|
58
|
+
},
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
const [expandedNodes, setExpandedNodes] = React.useState<Record<string, boolean>>({});
|
|
62
|
+
const onNodeExpanded = React.useCallback((nodeId: string, isExpanded: boolean) => {
|
|
63
|
+
setExpandedNodes((oldExpanded) => ({ ...oldExpanded, [nodeId]: isExpanded }));
|
|
64
|
+
}, []);
|
|
65
|
+
|
|
66
|
+
const getNode = React.useCallback((node: DemoData): NodeData<DemoData> => {
|
|
67
|
+
return {
|
|
68
|
+
subNodes: node.subItems,
|
|
69
|
+
nodeId: node.id,
|
|
70
|
+
node,
|
|
71
|
+
isExpanded: expandedNodes[node.id],
|
|
72
|
+
hasSubNodes: node.subItems.length > 0,
|
|
73
|
+
};
|
|
74
|
+
}, [expandedNodes]);
|
|
75
|
+
|
|
76
|
+
<Tree<DemoData>
|
|
77
|
+
data={data}
|
|
78
|
+
getNode={getNode}
|
|
79
|
+
nodeRenderer={React.useCallback(({ node, ...rest }) => (
|
|
80
|
+
<TreeNode
|
|
81
|
+
label={node.label}
|
|
82
|
+
onNodeExpanded={onNodeExpanded}
|
|
83
|
+
{...rest}
|
|
84
|
+
/>
|
|
85
|
+
), [onNodeExpanded])}
|
|
86
|
+
/>
|
|
87
|
+
*/
|
|
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"]);
|
|
90
|
+
(0, utils_1.useTheme)();
|
|
91
|
+
var treeRef = react_1.default.useRef(null);
|
|
92
|
+
var focusedIndex = react_1.default.useRef(0);
|
|
93
|
+
react_1.default.useEffect(function () {
|
|
94
|
+
focusedIndex.current = 0;
|
|
95
|
+
}, [data]);
|
|
96
|
+
var getFocusableNodes = react_1.default.useCallback(function () {
|
|
97
|
+
var focusableItems = (0, utils_1.getFocusableElements)(treeRef.current);
|
|
98
|
+
// Filter out focusable elements that are inside each node, e.g. checkbox
|
|
99
|
+
return focusableItems.filter(function (i) { return !focusableItems.some(function (p) { return p.contains(i.parentElement); }); });
|
|
100
|
+
}, []);
|
|
101
|
+
var handleKeyDown = function (event) {
|
|
102
|
+
var items = getFocusableNodes();
|
|
103
|
+
if (!(items === null || items === void 0 ? void 0 : items.length)) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
var activeIndex = items.findIndex(function (el) { var _a; return el.contains((_a = treeRef.current) === null || _a === void 0 ? void 0 : _a.ownerDocument.activeElement); });
|
|
107
|
+
var currentIndex = activeIndex > -1 ? activeIndex : 0;
|
|
108
|
+
switch (event.key) {
|
|
109
|
+
case 'ArrowUp': {
|
|
110
|
+
event.preventDefault();
|
|
111
|
+
var newIndex = Math.max(0, currentIndex - 1);
|
|
112
|
+
items[newIndex].focus();
|
|
113
|
+
focusedIndex.current = newIndex;
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
case 'ArrowDown': {
|
|
117
|
+
event.preventDefault();
|
|
118
|
+
var newIndex = Math.min(items.length - 1, currentIndex + 1);
|
|
119
|
+
items[newIndex].focus();
|
|
120
|
+
focusedIndex.current = newIndex;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
default:
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
var _a = react_1.default.useMemo(function () {
|
|
128
|
+
var flatList = [];
|
|
129
|
+
var firstLevelNodes = [];
|
|
130
|
+
var flattenNodes = function (nodes, depth, parentNode) {
|
|
131
|
+
if (nodes === void 0) { nodes = []; }
|
|
132
|
+
if (depth === void 0) { depth = 0; }
|
|
133
|
+
var nodeIdList = Array();
|
|
134
|
+
nodes.forEach(function (element, index) {
|
|
135
|
+
var _a = getNode(element), subNodes = _a.subNodes, nodeProps = __rest(_a, ["subNodes"]);
|
|
136
|
+
var flatNode = {
|
|
137
|
+
nodeProps: nodeProps,
|
|
138
|
+
depth: depth,
|
|
139
|
+
parentNode: parentNode,
|
|
140
|
+
indexInGroup: index,
|
|
141
|
+
};
|
|
142
|
+
nodeIdList.push(flatNode.nodeProps.nodeId);
|
|
143
|
+
flatList.push(flatNode);
|
|
144
|
+
if (depth === 0) {
|
|
145
|
+
firstLevelNodes.push(flatNode);
|
|
146
|
+
}
|
|
147
|
+
if (flatNode.nodeProps.isExpanded) {
|
|
148
|
+
var subNodeIds = flattenNodes(subNodes, depth + 1, flatNode);
|
|
149
|
+
flatNode.subNodeIds = subNodeIds;
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
return nodeIdList;
|
|
153
|
+
};
|
|
154
|
+
flattenNodes(data);
|
|
155
|
+
return [flatList, firstLevelNodes];
|
|
156
|
+
}, [data, getNode]), flatNodesList = _a[0], firstLevelNodesList = _a[1];
|
|
157
|
+
return (react_1.default.createElement("ul", __assign({ className: (0, classnames_1.default)('iui-tree', className), role: 'tree', onKeyDown: handleKeyDown, ref: treeRef, tabIndex: 0, onFocus: function () {
|
|
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) {
|
|
164
|
+
var _a, _b, _c, _d;
|
|
165
|
+
return (react_1.default.createElement(TreeContext_1.TreeContext.Provider, { key: flatNode.nodeProps.nodeId, value: {
|
|
166
|
+
nodeDepth: flatNode.depth,
|
|
167
|
+
subNodeIds: flatNode.subNodeIds,
|
|
168
|
+
groupSize: flatNode.depth === 0
|
|
169
|
+
? firstLevelNodesList.length
|
|
170
|
+
: (_c = (_b = (_a = flatNode.parentNode) === null || _a === void 0 ? void 0 : _a.subNodeIds) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : 0,
|
|
171
|
+
indexInGroup: flatNode.indexInGroup,
|
|
172
|
+
parentNodeId: (_d = flatNode.parentNode) === null || _d === void 0 ? void 0 : _d.nodeProps.nodeId,
|
|
173
|
+
} }, nodeRenderer(flatNode.nodeProps)));
|
|
174
|
+
})));
|
|
175
|
+
};
|
|
176
|
+
exports.Tree = Tree;
|
|
177
|
+
exports.default = exports.Tree;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export declare type TreeContextProps = {
|
|
3
|
+
/**
|
|
4
|
+
* Depth of the node.
|
|
5
|
+
*/
|
|
6
|
+
nodeDepth: number;
|
|
7
|
+
/**
|
|
8
|
+
* List of sub-node IDs. Used for an accessibility attribute and keyboard navigation.
|
|
9
|
+
*/
|
|
10
|
+
subNodeIds?: string[];
|
|
11
|
+
/**
|
|
12
|
+
* ID of the parent node. Used for keyboard navigation.
|
|
13
|
+
*/
|
|
14
|
+
parentNodeId?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Number of nodes that are under the same parent node or in the root. Used for an accessibility attribute.
|
|
17
|
+
*/
|
|
18
|
+
groupSize: number;
|
|
19
|
+
/**
|
|
20
|
+
* Node index in the list of nodes under the same parent node or in the root. Used for an accessibility attribute.
|
|
21
|
+
*/
|
|
22
|
+
indexInGroup: number;
|
|
23
|
+
};
|
|
24
|
+
export declare const TreeContext: React.Context<TreeContextProps | undefined>;
|
|
25
|
+
export declare const useTreeContext: () => TreeContextProps;
|
|
@@ -0,0 +1,20 @@
|
|
|
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.useTreeContext = exports.TreeContext = 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
|
+
exports.TreeContext = react_1.default.createContext(undefined);
|
|
13
|
+
var useTreeContext = function () {
|
|
14
|
+
var context = react_1.default.useContext(exports.TreeContext);
|
|
15
|
+
if (context == undefined) {
|
|
16
|
+
throw new Error('TreeContext must be used within a TreeContext.Provider');
|
|
17
|
+
}
|
|
18
|
+
return context;
|
|
19
|
+
};
|
|
20
|
+
exports.useTreeContext = useTreeContext;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { CommonProps } from '../utils';
|
|
3
|
+
import '@itwin/itwinui-css/css/tree.css';
|
|
4
|
+
export declare type TreeNodeProps = {
|
|
5
|
+
/**
|
|
6
|
+
* Unique id of the node.
|
|
7
|
+
* It has to be compatible with HTML id attribute.
|
|
8
|
+
*/
|
|
9
|
+
nodeId: string;
|
|
10
|
+
/**
|
|
11
|
+
* The main text displayed on the node.
|
|
12
|
+
*/
|
|
13
|
+
label: React.ReactNode;
|
|
14
|
+
/**
|
|
15
|
+
* Small note displayed below main label.
|
|
16
|
+
*/
|
|
17
|
+
sublabel?: React.ReactNode;
|
|
18
|
+
/**
|
|
19
|
+
* Icon shown before label and sublabel content.
|
|
20
|
+
*/
|
|
21
|
+
icon?: JSX.Element;
|
|
22
|
+
/**
|
|
23
|
+
* Flag whether the node has child sub-nodes. It is used to show expander icon.
|
|
24
|
+
* @default false
|
|
25
|
+
*/
|
|
26
|
+
hasSubNodes?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Flag whether the node is disabled.
|
|
29
|
+
* @default false
|
|
30
|
+
*/
|
|
31
|
+
isDisabled?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Flag whether the node is expanded.
|
|
34
|
+
* @default false
|
|
35
|
+
*/
|
|
36
|
+
isExpanded?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Flag whether the node is selected.
|
|
39
|
+
* @default false
|
|
40
|
+
*/
|
|
41
|
+
isSelected?: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Callback fired when expanding or closing a TreeNode.
|
|
44
|
+
* Gives nodeId and new isExpanded value of specified node.
|
|
45
|
+
*/
|
|
46
|
+
onExpanded: (nodeId: string, isExpanded: boolean) => void;
|
|
47
|
+
/**
|
|
48
|
+
* Callback fired when selecting a TreeNode.
|
|
49
|
+
* Gives nodeId and new isSelected value of specified node.
|
|
50
|
+
*/
|
|
51
|
+
onSelected?: (nodeId: string, isSelected: boolean) => void;
|
|
52
|
+
/**
|
|
53
|
+
* Checkbox to be shown at the very beginning of the node.
|
|
54
|
+
* If undefined, checkbox will not be shown.
|
|
55
|
+
* Recommended to use `Checkbox` component.
|
|
56
|
+
*/
|
|
57
|
+
checkbox?: React.ReactNode;
|
|
58
|
+
/**
|
|
59
|
+
* Custom expander element. If `hasSubNodes` is false, it won't be shown.
|
|
60
|
+
*/
|
|
61
|
+
expander?: React.ReactNode;
|
|
62
|
+
/**
|
|
63
|
+
* Content shown after `TreeNode`.
|
|
64
|
+
*/
|
|
65
|
+
children?: React.ReactNode;
|
|
66
|
+
} & Omit<CommonProps, 'id'>;
|
|
67
|
+
/**
|
|
68
|
+
* `TreeNode` component to display node content within a `Tree`.
|
|
69
|
+
* Must be used inside `Tree` component to correctly set node `depth` and `subNodes`.
|
|
70
|
+
* @example
|
|
71
|
+
<TreeNode
|
|
72
|
+
nodeId={props.nodeId}
|
|
73
|
+
label={props.node.label}
|
|
74
|
+
sublabel={props.node.sublabel}
|
|
75
|
+
onExpanded={onExpanded}
|
|
76
|
+
onSelected={onSelectedNodeChange}
|
|
77
|
+
isDisabled={props.isDisabled}
|
|
78
|
+
isExpanded={props.isExpanded}
|
|
79
|
+
isSelected={props.isSelected}
|
|
80
|
+
checkbox={
|
|
81
|
+
<Checkbox variant='eyeball' disabled={props.isDisabled} />
|
|
82
|
+
}
|
|
83
|
+
icon={<SvgPlaceholder />}
|
|
84
|
+
/>
|
|
85
|
+
*/
|
|
86
|
+
export declare const TreeNode: (props: TreeNodeProps) => JSX.Element;
|
|
87
|
+
export default TreeNode;
|