@mui/x-tree-view 7.3.0 → 7.3.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 +79 -2
- package/TreeItem/TreeItem.js +1 -2
- package/TreeItem/TreeItemContent.js +1 -2
- package/TreeItem/useTreeItemState.js +1 -3
- package/TreeItem2/TreeItem2.js +1 -2
- package/hooks/useTreeItem2Utils/useTreeItem2Utils.js +1 -3
- package/index.js +1 -1
- package/internals/TreeViewProvider/useTreeViewContext.d.ts +1 -1
- package/internals/hooks/useInstanceEventHandler.d.ts +2 -2
- package/internals/models/treeView.d.ts +0 -6
- package/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +1 -4
- package/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +31 -63
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +82 -94
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.types.d.ts +32 -5
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.d.ts +9 -0
- package/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +21 -0
- package/internals/useTreeView/useTreeView.types.d.ts +1 -1
- package/internals/useTreeView/useTreeViewModels.d.ts +1 -1
- package/internals/utils/extractPluginParamsFromProps.d.ts +1 -1
- package/internals/utils/publishTreeViewEvent.d.ts +1 -1
- package/internals/utils/tree.d.ts +17 -1
- package/internals/utils/tree.js +34 -4
- package/modern/TreeItem/TreeItem.js +1 -2
- package/modern/TreeItem/TreeItemContent.js +1 -2
- package/modern/TreeItem/useTreeItemState.js +1 -3
- package/modern/TreeItem2/TreeItem2.js +1 -2
- package/modern/hooks/useTreeItem2Utils/useTreeItem2Utils.js +1 -3
- package/modern/index.js +1 -1
- package/modern/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +1 -4
- package/modern/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +31 -63
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +82 -94
- package/modern/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +21 -0
- package/modern/internals/utils/tree.js +34 -4
- package/node/RichTreeView/RichTreeView.js +1 -1
- package/node/SimpleTreeView/SimpleTreeView.js +1 -1
- package/node/TreeItem/TreeItem.js +1 -1
- package/node/TreeItem/TreeItemContent.js +1 -1
- package/node/TreeItem/useTreeItemState.js +1 -3
- package/node/TreeItem2/TreeItem2.js +1 -1
- package/node/TreeItem2Icon/TreeItem2Icon.js +1 -1
- package/node/TreeView/TreeView.js +1 -1
- package/node/hooks/useTreeItem2Utils/useTreeItem2Utils.js +1 -3
- package/node/hooks/useTreeViewApiRef.js +1 -1
- package/node/icons/icons.js +1 -1
- package/node/index.js +1 -1
- package/node/internals/TreeViewProvider/TreeViewChildrenItemProvider.js +1 -1
- package/node/internals/TreeViewProvider/TreeViewContext.js +1 -1
- package/node/internals/TreeViewProvider/TreeViewProvider.js +1 -1
- package/node/internals/TreeViewProvider/useTreeViewContext.js +1 -1
- package/node/internals/corePlugins/useTreeViewInstanceEvents/useTreeViewInstanceEvents.js +1 -1
- package/node/internals/hooks/useInstanceEventHandler.js +1 -1
- package/node/internals/plugins/useTreeViewExpansion/useTreeViewExpansion.js +1 -1
- package/node/internals/plugins/useTreeViewFocus/useTreeViewFocus.js +2 -5
- package/node/internals/plugins/useTreeViewId/useTreeViewId.js +1 -1
- package/node/internals/plugins/useTreeViewItems/useTreeViewItems.js +1 -1
- package/node/internals/plugins/useTreeViewJSXItems/useTreeViewJSXItems.js +1 -1
- package/node/internals/plugins/useTreeViewKeyboardNavigation/useTreeViewKeyboardNavigation.js +32 -64
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.js +82 -94
- package/node/internals/plugins/useTreeViewSelection/useTreeViewSelection.utils.js +29 -0
- package/node/internals/useTreeView/useTreeView.js +1 -1
- package/node/internals/useTreeView/useTreeViewModels.js +1 -1
- package/node/internals/utils/tree.js +37 -5
- package/package.json +3 -5
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,83 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## 7.3.1
|
|
7
|
+
|
|
8
|
+
_Apr 26, 2024_
|
|
9
|
+
|
|
10
|
+
We'd like to offer a big thanks to the 13 contributors who made this release possible. Here are some highlights ✨:
|
|
11
|
+
|
|
12
|
+
- 🎁 Scatter Charts get a [z-axis to allow coloring data points independently from their coordinates](https://mui.com/x/react-charts/scatter/#color-scale)
|
|
13
|
+
- 🌍 Improve Catalan (ca-ES) and Spanish (es-ES) locales on the Date and Time Pickers
|
|
14
|
+
- 🐞 Bugfixes
|
|
15
|
+
- 📚 Documentation improvements
|
|
16
|
+
|
|
17
|
+
### Data Grid
|
|
18
|
+
|
|
19
|
+
#### `@mui/x-data-grid@7.3.1`
|
|
20
|
+
|
|
21
|
+
- [DataGrid] Fix date filtering for negative timezone offsets (#12836) @cherniavskii
|
|
22
|
+
- [DataGrid] Fix flex column width when used with pinned columns (#12849) @romgrk
|
|
23
|
+
- [DataGrid] Fix group header resize (#12863) @arminmeh
|
|
24
|
+
- [DataGrid] Pass slot props to `columnHeaders` slot (#12768) @cherniavskii
|
|
25
|
+
|
|
26
|
+
#### `@mui/x-data-grid-pro@7.3.1` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
27
|
+
|
|
28
|
+
Same changes as in `@mui/x-data-grid@7.3.1`.
|
|
29
|
+
|
|
30
|
+
#### `@mui/x-data-grid-premium@7.3.1` [](https://mui.com/r/x-premium-svg-link 'Premium plan')
|
|
31
|
+
|
|
32
|
+
Same changes as in `@mui/x-data-grid-pro@7.3.1`.
|
|
33
|
+
|
|
34
|
+
### Date and Time Pickers
|
|
35
|
+
|
|
36
|
+
#### `@mui/x-date-pickers@7.3.1`
|
|
37
|
+
|
|
38
|
+
- [l10n] Improve Catalan (ca-ES) locale (#12856) @soler1212
|
|
39
|
+
- [l10n] Improve Spanish (es-ES) locale (#12858) @soler1212
|
|
40
|
+
|
|
41
|
+
#### `@mui/x-date-pickers-pro@7.3.1` [](https://mui.com/r/x-pro-svg-link 'Pro plan')
|
|
42
|
+
|
|
43
|
+
Same changes as in `@mui/x-date-pickers@7.3.1`.
|
|
44
|
+
|
|
45
|
+
### Charts
|
|
46
|
+
|
|
47
|
+
#### `@mui/x-charts@7.3.1`
|
|
48
|
+
|
|
49
|
+
- [charts] Add documentation on border radius alternative for `BarCharts` (#12859) @JCQuintas
|
|
50
|
+
- [charts] Add z-axis to colorize scatter charts (#12738) @alexfauquette
|
|
51
|
+
- [charts] Fix left/bottomAxis not picking up default axis id (#12894) @JCQuintas
|
|
52
|
+
- [charts] Improve default tooltip content (#12257) @oliviertassinari
|
|
53
|
+
- [charts] Round y values for bar chart (#12846) @alexfauquette
|
|
54
|
+
|
|
55
|
+
### Tree View
|
|
56
|
+
|
|
57
|
+
#### `@mui/x-tree-view@7.3.1`
|
|
58
|
+
|
|
59
|
+
- [TreeView] Remove un-needed `aria-activedescendant` attribute (#12867) @flaviendelangle
|
|
60
|
+
- [TreeView] Rework the selection internals (#12703) @flaviendelangle
|
|
61
|
+
- [TreeView] Use the order in which the items are displayed for `type-ahead` (#12827) @flaviendelangle
|
|
62
|
+
|
|
63
|
+
### Docs
|
|
64
|
+
|
|
65
|
+
- [docs] Add demo for styling charts with `sx` props (#12791) @derek-0000
|
|
66
|
+
- [docs] Cover webpack 4 support in migration guide (#12710) @cherniavskii
|
|
67
|
+
- [docs] Document interfaces for charts (#12656) @alexfauquette
|
|
68
|
+
- [docs] Fix Vale regression (#12862) @oliviertassinari
|
|
69
|
+
- [docs] Improve Data Grid migration guide (#12879) @MBilalShafi
|
|
70
|
+
- [docs] Update Column features availability (#12865) @DanailH
|
|
71
|
+
|
|
72
|
+
### Core
|
|
73
|
+
|
|
74
|
+
- [core] Fix `l10n` GH workflow (#12895) @LukasTy
|
|
75
|
+
- [core] Match Base UI and Toolpad @oliviertassinari
|
|
76
|
+
- [core] Remove redundant `setupFiles` entries in `package.json` (#12899) @LukasTy
|
|
77
|
+
- [core] Use `describeTreeView` for focus tests (#12698) @flaviendelangle
|
|
78
|
+
- [core] Use `describeTreeView` for type-ahead tests (#12811) @flaviendelangle
|
|
79
|
+
- [code-infra] Change package manager to `pnpm` (#11875) @LukasTy
|
|
80
|
+
- [code-infra] Closer sync with eslint config of codebase (#12864) @oliviertassinari
|
|
81
|
+
- [support-infra] Add release announcement to GitHub workflows (#11867) (#12843) @michelengelen
|
|
82
|
+
|
|
6
83
|
## 7.3.0
|
|
7
84
|
|
|
8
85
|
_Apr 18, 2024_
|
|
@@ -243,7 +320,7 @@ Same changes as in `@mui/x-date-pickers@7.1.1`, plus:
|
|
|
243
320
|
- [docs] Fix type arguments in Custom Field page (#12619) @Juneezee
|
|
244
321
|
- [docs] Fix typo in `getItemId` prop description (#12637) @flaviendelangle
|
|
245
322
|
- [docs] Make the Charts `margin` usage more visible (#12591) @alexfauquette
|
|
246
|
-
- [docs] Match IE 11 spacing with Material
|
|
323
|
+
- [docs] Match IE 11 spacing with Material UI @oliviertassinari
|
|
247
324
|
- [docs] Move data grid interfaces to standard API page layout (#12016) @alexfauquette
|
|
248
325
|
- [docs] Remove ` around @default values (#12158) @alexfauquette
|
|
249
326
|
- [docs] Remove `day` from the default `dayOfWeekFormatter` function params (#12644) @LukasTy
|
|
@@ -506,7 +583,7 @@ Same changes as in `@mui/x-date-pickers@7.0.0`, plus:
|
|
|
506
583
|
- [docs] Update links to v6 (#12496) @cherniavskii
|
|
507
584
|
- [docs] Update links to v7 docs (#12500) @noraleonte
|
|
508
585
|
- [docs] Update supported versions (#12508) @joserodolfofreitas
|
|
509
|
-
- [docs] Update "What's new in MUI
|
|
586
|
+
- [docs] Update "What's new in MUI X" page #12527 @cherniavskii
|
|
510
587
|
|
|
511
588
|
### Core
|
|
512
589
|
|
package/TreeItem/TreeItem.js
CHANGED
|
@@ -19,8 +19,7 @@ import { treeItemClasses, getTreeItemUtilityClass } from './treeItemClasses';
|
|
|
19
19
|
import { useTreeViewContext } from '../internals/TreeViewProvider/useTreeViewContext';
|
|
20
20
|
import { TreeViewCollapseIcon, TreeViewExpandIcon } from '../icons';
|
|
21
21
|
import { TreeItem2Provider } from '../TreeItem2Provider';
|
|
22
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
23
|
-
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
22
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
24
23
|
const useUtilityClasses = ownerState => {
|
|
25
24
|
const {
|
|
26
25
|
classes
|
|
@@ -5,8 +5,7 @@ import * as React from 'react';
|
|
|
5
5
|
import PropTypes from 'prop-types';
|
|
6
6
|
import clsx from 'clsx';
|
|
7
7
|
import { useTreeItemState } from './useTreeItemState';
|
|
8
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
-
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
9
|
/**
|
|
11
10
|
* @ignore - internal component.
|
|
12
11
|
*/
|
|
@@ -32,9 +32,7 @@ export function useTreeItemState(itemId) {
|
|
|
32
32
|
const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey);
|
|
33
33
|
if (multiple) {
|
|
34
34
|
if (event.shiftKey) {
|
|
35
|
-
instance.
|
|
36
|
-
end: itemId
|
|
37
|
-
});
|
|
35
|
+
instance.expandSelectionRange(event, itemId);
|
|
38
36
|
} else {
|
|
39
37
|
instance.selectItem(event, itemId, true);
|
|
40
38
|
}
|
package/TreeItem2/TreeItem2.js
CHANGED
|
@@ -14,8 +14,7 @@ import { unstable_useTreeItem2 as useTreeItem2 } from '../useTreeItem2';
|
|
|
14
14
|
import { getTreeItemUtilityClass, treeItemClasses } from '../TreeItem';
|
|
15
15
|
import { TreeItem2Icon } from '../TreeItem2Icon';
|
|
16
16
|
import { TreeItem2Provider } from '../TreeItem2Provider';
|
|
17
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
18
|
-
import { jsxs as _jsxs } from "react/jsx-runtime";
|
|
17
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
19
18
|
export const TreeItem2Root = styled('li', {
|
|
20
19
|
name: 'MuiTreeItem2',
|
|
21
20
|
slot: 'Root',
|
|
@@ -40,9 +40,7 @@ export const useTreeItem2Utils = ({
|
|
|
40
40
|
const multiple = multiSelect && (event.shiftKey || event.ctrlKey || event.metaKey);
|
|
41
41
|
if (multiple) {
|
|
42
42
|
if (event.shiftKey) {
|
|
43
|
-
instance.
|
|
44
|
-
end: itemId
|
|
45
|
-
});
|
|
43
|
+
instance.expandSelectionRange(event, itemId);
|
|
46
44
|
} else {
|
|
47
45
|
instance.selectItem(event, itemId, true);
|
|
48
46
|
}
|
package/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { TreeViewAnyPluginSignature } from '../models';
|
|
2
2
|
import { TreeViewContextValue } from './TreeViewProvider.types';
|
|
3
|
-
export declare const useTreeViewContext: <TPlugins extends readonly TreeViewAnyPluginSignature[]>() => TreeViewContextValue<TPlugins
|
|
3
|
+
export declare const useTreeViewContext: <TPlugins extends readonly TreeViewAnyPluginSignature[]>() => NonNullable<TreeViewContextValue<TPlugins>>;
|
|
@@ -7,9 +7,9 @@ interface RegistryContainer {
|
|
|
7
7
|
}
|
|
8
8
|
export declare function createUseInstanceEventHandler(registryContainer: RegistryContainer): <Instance extends UseTreeViewInstanceEventsInstance & {
|
|
9
9
|
$$signature: TreeViewAnyPluginSignature;
|
|
10
|
-
}, E extends keyof Instance["$$signature"]["events"] | keyof import("../models").MergePluginsProperty<[import("../corePlugins").TreeViewCorePluginsSignature, ...Instance["$$signature"]["dependantPlugins"]], "events">>(instance: Instance, eventName: E, handler: TreeViewEventListener<TreeViewUsedEvents<Instance[
|
|
10
|
+
}, E extends keyof Instance["$$signature"]["events"] | keyof import("../models").MergePluginsProperty<[import("../corePlugins").TreeViewCorePluginsSignature, ...Instance["$$signature"]["dependantPlugins"]], "events">>(instance: Instance, eventName: E, handler: TreeViewEventListener<TreeViewUsedEvents<Instance['$$signature']>[E]>) => void;
|
|
11
11
|
export declare const unstable_resetCleanupTracking: () => void;
|
|
12
12
|
export declare const useInstanceEventHandler: <Instance extends UseTreeViewInstanceEventsInstance & {
|
|
13
13
|
$$signature: TreeViewAnyPluginSignature;
|
|
14
|
-
}, E extends keyof Instance["$$signature"]["events"] | keyof import("../models").MergePluginsProperty<[import("../corePlugins").TreeViewCorePluginsSignature, ...Instance["$$signature"]["dependantPlugins"]], "events">>(instance: Instance, eventName: E, handler: TreeViewEventListener<TreeViewUsedEvents<Instance[
|
|
14
|
+
}, E extends keyof Instance["$$signature"]["events"] | keyof import("../models").MergePluginsProperty<[import("../corePlugins").TreeViewCorePluginsSignature, ...Instance["$$signature"]["dependantPlugins"]], "events">>(instance: Instance, eventName: E, handler: TreeViewEventListener<TreeViewUsedEvents<Instance['$$signature']>[E]>) => void;
|
|
15
15
|
export {};
|
|
@@ -11,12 +11,6 @@ export interface TreeViewItemMeta {
|
|
|
11
11
|
*/
|
|
12
12
|
label?: string;
|
|
13
13
|
}
|
|
14
|
-
export interface TreeViewItemRange {
|
|
15
|
-
start?: string | null;
|
|
16
|
-
end?: string | null;
|
|
17
|
-
next?: string | null;
|
|
18
|
-
current?: string;
|
|
19
|
-
}
|
|
20
14
|
export interface TreeViewModel<TValue> {
|
|
21
15
|
name: string;
|
|
22
16
|
value: TValue;
|
|
@@ -105,12 +105,9 @@ export const useTreeViewFocus = ({
|
|
|
105
105
|
instance.focusDefaultItem(event);
|
|
106
106
|
}
|
|
107
107
|
};
|
|
108
|
-
const focusedItem = instance.getItemMeta(state.focusedItemId);
|
|
109
|
-
const activeDescendant = focusedItem ? instance.getTreeItemIdAttribute(focusedItem.id, focusedItem.idAttribute) : null;
|
|
110
108
|
return {
|
|
111
109
|
getRootProps: otherHandlers => ({
|
|
112
|
-
onFocus: createRootHandleFocus(otherHandlers)
|
|
113
|
-
'aria-activedescendant': activeDescendant ?? undefined
|
|
110
|
+
onFocus: createRootHandleFocus(otherHandlers)
|
|
114
111
|
}),
|
|
115
112
|
publicAPI: {
|
|
116
113
|
focusItem
|
|
@@ -5,14 +5,6 @@ import { getFirstNavigableItem, getLastNavigableItem, getNextNavigableItem, getP
|
|
|
5
5
|
function isPrintableCharacter(string) {
|
|
6
6
|
return !!string && string.length === 1 && !!string.match(/\S/);
|
|
7
7
|
}
|
|
8
|
-
function findNextFirstChar(firstChars, startIndex, char) {
|
|
9
|
-
for (let i = startIndex; i < firstChars.length; i += 1) {
|
|
10
|
-
if (char === firstChars[i]) {
|
|
11
|
-
return i;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
return -1;
|
|
15
|
-
}
|
|
16
8
|
export const useTreeViewKeyboardNavigation = ({
|
|
17
9
|
instance,
|
|
18
10
|
params,
|
|
@@ -35,42 +27,29 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
35
27
|
Object.values(state.items.itemMetaMap).forEach(processItem);
|
|
36
28
|
firstCharMap.current = newFirstCharMap;
|
|
37
29
|
}, [state.items.itemMetaMap, params.getItemId, instance]);
|
|
38
|
-
const getFirstMatchingItem = (itemId,
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
30
|
+
const getFirstMatchingItem = (itemId, query) => {
|
|
31
|
+
const cleanQuery = query.toLowerCase();
|
|
32
|
+
const getNextItem = itemIdToCheck => {
|
|
33
|
+
const nextItemId = getNextNavigableItem(instance, itemIdToCheck);
|
|
34
|
+
// We reached the end of the tree, check from the beginning
|
|
35
|
+
if (nextItemId === null) {
|
|
36
|
+
return getFirstNavigableItem(instance);
|
|
37
|
+
}
|
|
38
|
+
return nextItemId;
|
|
39
|
+
};
|
|
40
|
+
let matchingItemId = null;
|
|
41
|
+
let currentItemId = getNextItem(itemId);
|
|
42
|
+
const checkedItems = {};
|
|
43
|
+
// The "!checkedItems[currentItemId]" condition avoids an infinite loop when there is no matching item.
|
|
44
|
+
while (matchingItemId == null && !checkedItems[currentItemId]) {
|
|
45
|
+
if (firstCharMap.current[currentItemId] === cleanQuery) {
|
|
46
|
+
matchingItemId = currentItemId;
|
|
47
|
+
} else {
|
|
48
|
+
checkedItems[currentItemId] = true;
|
|
49
|
+
currentItemId = getNextItem(currentItemId);
|
|
52
50
|
}
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
// Get start index for search based on position of currentItem
|
|
56
|
-
start = firstCharIds.indexOf(itemId) + 1;
|
|
57
|
-
if (start >= firstCharIds.length) {
|
|
58
|
-
start = 0;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Check remaining slots in the menu
|
|
62
|
-
index = findNextFirstChar(firstChars, start, lowercaseChar);
|
|
63
|
-
|
|
64
|
-
// If not found in remaining slots, check from beginning
|
|
65
|
-
if (index === -1) {
|
|
66
|
-
index = findNextFirstChar(firstChars, 0, lowercaseChar);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// If a match was found...
|
|
70
|
-
if (index > -1) {
|
|
71
|
-
return firstCharIds[index];
|
|
72
51
|
}
|
|
73
|
-
return
|
|
52
|
+
return matchingItemId;
|
|
74
53
|
};
|
|
75
54
|
const canToggleItemSelection = itemId => !params.disableSelection && !instance.isItemDisabled(itemId);
|
|
76
55
|
const canToggleItemExpansion = itemId => {
|
|
@@ -95,9 +74,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
95
74
|
{
|
|
96
75
|
event.preventDefault();
|
|
97
76
|
if (params.multiSelect && event.shiftKey) {
|
|
98
|
-
instance.
|
|
99
|
-
end: itemId
|
|
100
|
-
});
|
|
77
|
+
instance.expandSelectionRange(event, itemId);
|
|
101
78
|
} else if (params.multiSelect) {
|
|
102
79
|
instance.selectItem(event, itemId, true);
|
|
103
80
|
} else {
|
|
@@ -136,10 +113,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
136
113
|
// Multi select behavior when pressing Shift + ArrowDown
|
|
137
114
|
// Toggles the selection state of the next item
|
|
138
115
|
if (params.multiSelect && event.shiftKey && canToggleItemSelection(nextItem)) {
|
|
139
|
-
instance.
|
|
140
|
-
end: nextItem,
|
|
141
|
-
current: itemId
|
|
142
|
-
}, true);
|
|
116
|
+
instance.selectItemFromArrowNavigation(event, itemId, nextItem);
|
|
143
117
|
}
|
|
144
118
|
}
|
|
145
119
|
break;
|
|
@@ -156,10 +130,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
156
130
|
// Multi select behavior when pressing Shift + ArrowUp
|
|
157
131
|
// Toggles the selection state of the previous item
|
|
158
132
|
if (params.multiSelect && event.shiftKey && canToggleItemSelection(previousItem)) {
|
|
159
|
-
instance.
|
|
160
|
-
end: previousItem,
|
|
161
|
-
current: itemId
|
|
162
|
-
}, true);
|
|
133
|
+
instance.selectItemFromArrowNavigation(event, itemId, previousItem);
|
|
163
134
|
}
|
|
164
135
|
}
|
|
165
136
|
break;
|
|
@@ -202,12 +173,12 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
202
173
|
// Focuses the first item in the tree
|
|
203
174
|
case key === 'Home':
|
|
204
175
|
{
|
|
205
|
-
instance.focusItem(event, getFirstNavigableItem(instance));
|
|
206
|
-
|
|
207
176
|
// Multi select behavior when pressing Ctrl + Shift + Home
|
|
208
177
|
// Selects the focused item and all items up to the first item.
|
|
209
178
|
if (canToggleItemSelection(itemId) && params.multiSelect && ctrlPressed && event.shiftKey) {
|
|
210
|
-
instance.
|
|
179
|
+
instance.selectRangeFromStartToItem(event, itemId);
|
|
180
|
+
} else {
|
|
181
|
+
instance.focusItem(event, getFirstNavigableItem(instance));
|
|
211
182
|
}
|
|
212
183
|
event.preventDefault();
|
|
213
184
|
break;
|
|
@@ -216,12 +187,12 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
216
187
|
// Focuses the last item in the tree
|
|
217
188
|
case key === 'End':
|
|
218
189
|
{
|
|
219
|
-
instance.focusItem(event, getLastNavigableItem(instance));
|
|
220
|
-
|
|
221
190
|
// Multi select behavior when pressing Ctrl + Shirt + End
|
|
222
191
|
// Selects the focused item and all the items down to the last item.
|
|
223
192
|
if (canToggleItemSelection(itemId) && params.multiSelect && ctrlPressed && event.shiftKey) {
|
|
224
|
-
instance.
|
|
193
|
+
instance.selectRangeFromItemToEnd(event, itemId);
|
|
194
|
+
} else {
|
|
195
|
+
instance.focusItem(event, getLastNavigableItem(instance));
|
|
225
196
|
}
|
|
226
197
|
event.preventDefault();
|
|
227
198
|
break;
|
|
@@ -239,10 +210,7 @@ export const useTreeViewKeyboardNavigation = ({
|
|
|
239
210
|
// Selects all the items
|
|
240
211
|
case key === 'a' && ctrlPressed && params.multiSelect && !params.disableSelection:
|
|
241
212
|
{
|
|
242
|
-
instance.
|
|
243
|
-
start: getFirstNavigableItem(instance),
|
|
244
|
-
end: getLastNavigableItem(instance)
|
|
245
|
-
});
|
|
213
|
+
instance.selectAllNavigableItems(event);
|
|
246
214
|
event.preventDefault();
|
|
247
215
|
break;
|
|
248
216
|
}
|
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
2
2
|
import * as React from 'react';
|
|
3
|
-
import { getFirstNavigableItem, getLastNavigableItem,
|
|
3
|
+
import { findOrderInTremauxTree, getAllNavigableItems, getFirstNavigableItem, getLastNavigableItem, getNonDisabledItemsInRange } from '../../utils/tree';
|
|
4
|
+
import { convertSelectedItemsToArray, getLookupFromArray } from './useTreeViewSelection.utils';
|
|
4
5
|
export const useTreeViewSelection = ({
|
|
5
6
|
instance,
|
|
6
7
|
params,
|
|
7
8
|
models
|
|
8
9
|
}) => {
|
|
9
10
|
const lastSelectedItem = React.useRef(null);
|
|
10
|
-
const
|
|
11
|
-
const
|
|
11
|
+
const lastSelectedRange = React.useRef({});
|
|
12
|
+
const selectedItemsMap = React.useMemo(() => {
|
|
13
|
+
const temp = new Map();
|
|
14
|
+
if (Array.isArray(models.selectedItems.value)) {
|
|
15
|
+
models.selectedItems.value.forEach(id => {
|
|
16
|
+
temp.set(id, true);
|
|
17
|
+
});
|
|
18
|
+
} else if (models.selectedItems.value != null) {
|
|
19
|
+
temp.set(models.selectedItems.value, true);
|
|
20
|
+
}
|
|
21
|
+
return temp;
|
|
22
|
+
}, [models.selectedItems.value]);
|
|
12
23
|
const setSelectedItems = (event, newSelectedItems) => {
|
|
13
24
|
if (params.onItemSelectionToggle) {
|
|
14
25
|
if (params.multiSelect) {
|
|
@@ -34,115 +45,90 @@ export const useTreeViewSelection = ({
|
|
|
34
45
|
}
|
|
35
46
|
models.selectedItems.setControlledValue(newSelectedItems);
|
|
36
47
|
};
|
|
37
|
-
const isItemSelected = itemId =>
|
|
48
|
+
const isItemSelected = itemId => selectedItemsMap.has(itemId);
|
|
38
49
|
const selectItem = (event, itemId, multiple = false) => {
|
|
39
50
|
if (params.disableSelection) {
|
|
40
51
|
return;
|
|
41
52
|
}
|
|
53
|
+
let newSelected;
|
|
42
54
|
if (multiple) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
newSelected = [itemId].concat(models.selectedItems.value);
|
|
49
|
-
}
|
|
50
|
-
setSelectedItems(event, newSelected);
|
|
55
|
+
const cleanSelectedItems = convertSelectedItemsToArray(models.selectedItems.value);
|
|
56
|
+
if (instance.isItemSelected(itemId)) {
|
|
57
|
+
newSelected = cleanSelectedItems.filter(id => id !== itemId);
|
|
58
|
+
} else {
|
|
59
|
+
newSelected = [itemId].concat(cleanSelectedItems);
|
|
51
60
|
}
|
|
52
61
|
} else {
|
|
53
|
-
|
|
54
|
-
setSelectedItems(event, newSelected);
|
|
62
|
+
newSelected = params.multiSelect ? [itemId] : itemId;
|
|
55
63
|
}
|
|
64
|
+
setSelectedItems(event, newSelected);
|
|
56
65
|
lastSelectedItem.current = itemId;
|
|
57
|
-
|
|
58
|
-
currentRangeSelection.current = [];
|
|
66
|
+
lastSelectedRange.current = {};
|
|
59
67
|
};
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
const {
|
|
63
|
-
start,
|
|
64
|
-
next,
|
|
65
|
-
current
|
|
66
|
-
} = items;
|
|
67
|
-
if (!next || !current) {
|
|
68
|
+
const selectRange = (event, [start, end]) => {
|
|
69
|
+
if (params.disableSelection || !params.multiSelect) {
|
|
68
70
|
return;
|
|
69
71
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
+
let newSelectedItems = convertSelectedItemsToArray(models.selectedItems.value).slice();
|
|
73
|
+
|
|
74
|
+
// If the last selection was a range selection,
|
|
75
|
+
// remove the items that were part of the last range from the model
|
|
76
|
+
if (Object.keys(lastSelectedRange.current).length > 0) {
|
|
77
|
+
newSelectedItems = newSelectedItems.filter(id => !lastSelectedRange.current[id]);
|
|
72
78
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
} else {
|
|
82
|
-
base.push(next);
|
|
83
|
-
currentRangeSelection.current.push(current, next);
|
|
84
|
-
}
|
|
85
|
-
setSelectedItems(event, base);
|
|
79
|
+
|
|
80
|
+
// Add to the model the items that are part of the new range and not already part of the model.
|
|
81
|
+
const selectedItemsLookup = getLookupFromArray(newSelectedItems);
|
|
82
|
+
const range = getNonDisabledItemsInRange(instance, start, end);
|
|
83
|
+
const itemsToAddToModel = range.filter(id => !selectedItemsLookup[id]);
|
|
84
|
+
newSelectedItems = newSelectedItems.concat(itemsToAddToModel);
|
|
85
|
+
setSelectedItems(event, newSelectedItems);
|
|
86
|
+
lastSelectedRange.current = getLookupFromArray(range);
|
|
86
87
|
};
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
start,
|
|
91
|
-
end
|
|
92
|
-
} = items;
|
|
93
|
-
// If last selection was a range selection ignore items that were selected.
|
|
94
|
-
if (lastSelectionWasRange.current) {
|
|
95
|
-
base = base.filter(id => currentRangeSelection.current.indexOf(id) === -1);
|
|
88
|
+
const expandSelectionRange = (event, itemId) => {
|
|
89
|
+
if (lastSelectedItem.current != null) {
|
|
90
|
+
const [start, end] = findOrderInTremauxTree(instance, itemId, lastSelectedItem.current);
|
|
91
|
+
selectRange(event, [start, end]);
|
|
96
92
|
}
|
|
97
|
-
let range = getNavigableItemsInRange(instance, start, end);
|
|
98
|
-
range = range.filter(item => !instance.isItemDisabled(item));
|
|
99
|
-
currentRangeSelection.current = range;
|
|
100
|
-
let newSelected = base.concat(range);
|
|
101
|
-
newSelected = newSelected.filter((id, i) => newSelected.indexOf(id) === i);
|
|
102
|
-
setSelectedItems(event, newSelected);
|
|
103
93
|
};
|
|
104
|
-
const
|
|
105
|
-
|
|
94
|
+
const selectRangeFromStartToItem = (event, itemId) => {
|
|
95
|
+
selectRange(event, [getFirstNavigableItem(instance), itemId]);
|
|
96
|
+
};
|
|
97
|
+
const selectRangeFromItemToEnd = (event, itemId) => {
|
|
98
|
+
selectRange(event, [itemId, getLastNavigableItem(instance)]);
|
|
99
|
+
};
|
|
100
|
+
const selectAllNavigableItems = event => {
|
|
101
|
+
if (params.disableSelection || !params.multiSelect) {
|
|
106
102
|
return;
|
|
107
103
|
}
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
current
|
|
112
|
-
} = items;
|
|
113
|
-
if (stacked) {
|
|
114
|
-
handleRangeArrowSelect(event, {
|
|
115
|
-
start,
|
|
116
|
-
next: end,
|
|
117
|
-
current
|
|
118
|
-
});
|
|
119
|
-
} else if (start != null && end != null) {
|
|
120
|
-
handleRangeSelect(event, {
|
|
121
|
-
start,
|
|
122
|
-
end
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
lastSelectionWasRange.current = true;
|
|
104
|
+
const navigableItems = getAllNavigableItems(instance);
|
|
105
|
+
setSelectedItems(event, navigableItems);
|
|
106
|
+
lastSelectedRange.current = getLookupFromArray(navigableItems);
|
|
126
107
|
};
|
|
127
|
-
const
|
|
128
|
-
if (!
|
|
129
|
-
|
|
108
|
+
const selectItemFromArrowNavigation = (event, currentItem, nextItem) => {
|
|
109
|
+
if (params.disableSelection || !params.multiSelect) {
|
|
110
|
+
return;
|
|
130
111
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
112
|
+
let newSelectedItems = convertSelectedItemsToArray(models.selectedItems.value).slice();
|
|
113
|
+
if (Object.keys(lastSelectedRange.current).length === 0) {
|
|
114
|
+
newSelectedItems.push(nextItem);
|
|
115
|
+
lastSelectedRange.current = {
|
|
116
|
+
[currentItem]: true,
|
|
117
|
+
[nextItem]: true
|
|
118
|
+
};
|
|
119
|
+
} else {
|
|
120
|
+
if (!lastSelectedRange.current[currentItem]) {
|
|
121
|
+
lastSelectedRange.current = {};
|
|
122
|
+
}
|
|
123
|
+
if (lastSelectedRange.current[nextItem]) {
|
|
124
|
+
newSelectedItems = newSelectedItems.filter(id => id !== currentItem);
|
|
125
|
+
delete lastSelectedRange.current[currentItem];
|
|
126
|
+
} else {
|
|
127
|
+
newSelectedItems.push(nextItem);
|
|
128
|
+
lastSelectedRange.current[nextItem] = true;
|
|
129
|
+
}
|
|
140
130
|
}
|
|
141
|
-
|
|
142
|
-
instance.selectRange(event, {
|
|
143
|
-
start,
|
|
144
|
-
end: getLastNavigableItem(instance)
|
|
145
|
-
});
|
|
131
|
+
setSelectedItems(event, newSelectedItems);
|
|
146
132
|
};
|
|
147
133
|
return {
|
|
148
134
|
getRootProps: () => ({
|
|
@@ -151,9 +137,11 @@ export const useTreeViewSelection = ({
|
|
|
151
137
|
instance: {
|
|
152
138
|
isItemSelected,
|
|
153
139
|
selectItem,
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
140
|
+
selectAllNavigableItems,
|
|
141
|
+
expandSelectionRange,
|
|
142
|
+
selectRangeFromStartToItem,
|
|
143
|
+
selectRangeFromItemToEnd,
|
|
144
|
+
selectItemFromArrowNavigation
|
|
157
145
|
},
|
|
158
146
|
contextValue: {
|
|
159
147
|
selection: {
|
|
@@ -1,13 +1,40 @@
|
|
|
1
1
|
import * as React from 'react';
|
|
2
|
-
import type { DefaultizedProps,
|
|
2
|
+
import type { DefaultizedProps, TreeViewPluginSignature } from '../../models';
|
|
3
3
|
import { UseTreeViewItemsSignature } from '../useTreeViewItems';
|
|
4
4
|
import { UseTreeViewExpansionSignature } from '../useTreeViewExpansion';
|
|
5
5
|
export interface UseTreeViewSelectionInstance {
|
|
6
6
|
isItemSelected: (itemId: string) => boolean;
|
|
7
|
-
selectItem: (event: React.SyntheticEvent, itemId: string,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
selectItem: (event: React.SyntheticEvent, itemId: string, keepExistingSelection?: boolean) => void;
|
|
8
|
+
/**
|
|
9
|
+
* Select all the navigable items in the tree.
|
|
10
|
+
* @param {React.SyntheticEvent} event The event source of the callback.
|
|
11
|
+
*/
|
|
12
|
+
selectAllNavigableItems: (event: React.SyntheticEvent) => void;
|
|
13
|
+
/**
|
|
14
|
+
* Expand the current selection range up to the given item.
|
|
15
|
+
* @param {React.SyntheticEvent} event The event source of the callback.
|
|
16
|
+
* @param {string} itemId The id of the item to expand the selection to.
|
|
17
|
+
*/
|
|
18
|
+
expandSelectionRange: (event: React.SyntheticEvent, itemId: string) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Expand the current selection range from the first navigable item to the given item.
|
|
21
|
+
* @param {React.SyntheticEvent} event The event source of the callback.
|
|
22
|
+
* @param {string} itemId The id of the item up to which the selection range should be expanded.
|
|
23
|
+
*/
|
|
24
|
+
selectRangeFromStartToItem: (event: React.SyntheticEvent, itemId: string) => void;
|
|
25
|
+
/**
|
|
26
|
+
* Expand the current selection range from the given item to the last navigable item.
|
|
27
|
+
* @param {React.SyntheticEvent} event The event source of the callback.
|
|
28
|
+
* @param {string} itemId The id of the item from which the selection range should be expanded.
|
|
29
|
+
*/
|
|
30
|
+
selectRangeFromItemToEnd: (event: React.SyntheticEvent, itemId: string) => void;
|
|
31
|
+
/**
|
|
32
|
+
* Update the selection when navigating with ArrowUp / ArrowDown keys.
|
|
33
|
+
* @param {React.SyntheticEvent} event The event source of the callback.
|
|
34
|
+
* @param {string} currentItemId The id of the active item before the keyboard navigation.
|
|
35
|
+
* @param {string} nextItemId The id of the active item after the keyboard navigation.
|
|
36
|
+
*/
|
|
37
|
+
selectItemFromArrowNavigation: (event: React.SyntheticEvent, currentItemId: string, nextItemId: string) => void;
|
|
11
38
|
}
|
|
12
39
|
type TreeViewSelectionValue<Multiple extends boolean | undefined> = Multiple extends true ? string[] : string | null;
|
|
13
40
|
export interface UseTreeViewSelectionParameters<Multiple extends boolean | undefined> {
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transform the `selectedItems` model to be an array if it was a string or null.
|
|
3
|
+
* @param {string[] | string | null} model The raw model.
|
|
4
|
+
* @returns {string[]} The converted model.
|
|
5
|
+
*/
|
|
6
|
+
export declare const convertSelectedItemsToArray: (model: string[] | string | null) => string[];
|
|
7
|
+
export declare const getLookupFromArray: (array: string[]) => {
|
|
8
|
+
[itemId: string]: boolean;
|
|
9
|
+
};
|