Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(TreeView): support additional navigation and selection behavior #11328

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
43 changes: 42 additions & 1 deletion packages/react/src/components/TreeView/TreeView.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export default function TreeView({
}
);
}

function handleTreeSelect(event, node = {}) {
const { id: nodeId } = node;
if (multiselect && (event.metaKey || event.ctrlKey)) {
Expand All @@ -56,6 +57,7 @@ export default function TreeView({
}
onSelect?.(event, node);
}

function handleFocusEvent(event) {
if (event.type === 'blur') {
const {
Expand All @@ -78,6 +80,7 @@ export default function TreeView({
currentFocusedNode.tabIndex = 0;
}
}

let focusTarget = false;
const nodesWithProps = React.Children.map(children, (node) => {
const sharedNodeProps = {
Expand All @@ -99,17 +102,55 @@ export default function TreeView({

function handleKeyDown(event) {
event.stopPropagation();
if (matches(event, [keys.ArrowUp, keys.ArrowDown])) {
if (
matches(event, [
keys.ArrowUp,
keys.ArrowDown,
keys.Home,
keys.End,
{ code: 'KeyA' },
])
) {
event.preventDefault();
}

treeWalker.current.currentNode = event.target;
let nextFocusNode;

if (match(event, keys.ArrowUp)) {
nextFocusNode = treeWalker.current.previousNode();
}
if (match(event, keys.ArrowDown)) {
nextFocusNode = treeWalker.current.nextNode();
}
if (matches(event, [keys.Home, keys.End, { code: 'KeyA' }])) {
const nodeIds = [];

if (matches(event, [keys.Home, keys.End])) {
if (multiselect && event.shiftKey && event.ctrlKey) {
nodeIds.push(treeWalker.current.currentNode?.id);
}
while (
match(event, keys.Home)
? treeWalker.current.previousNode()
: treeWalker.current.nextNode()
) {
nextFocusNode = treeWalker.current.currentNode;

if (multiselect && event.shiftKey && event.ctrlKey) {
nodeIds.push(nextFocusNode?.id);
}
}
}
if (match(event, { code: 'KeyA' }) && event.ctrlKey) {
treeWalker.current.currentNode = treeWalker.current.root;

while (treeWalker.current.nextNode()) {
nodeIds.push(treeWalker.current.currentNode?.id);
}
}
setSelected(selected.concat(nodeIds));
}
if (nextFocusNode && nextFocusNode !== event.target) {
resetNodeTabIndices();
nextFocusNode.tabIndex = 0;
Expand Down
13 changes: 13 additions & 0 deletions packages/react/src/internal/keyboard/keys.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ export const Tab = {
key: 'Tab',
which: 9,
keyCode: 9,
code: 'Tab',
};

export const Enter = {
key: 'Enter',
which: 13,
keyCode: 13,
code: 'Enter',
};

export const Escape = {
Expand All @@ -25,64 +27,75 @@ export const Escape = {
],
which: 27,
keyCode: 27,
code: 'Esc',
};

export const Space = {
key: ' ',
which: 32,
keyCode: 32,
code: 'Space',
};

export const PageUp = {
key: 'PageUp',
which: 33,
keyCode: 33,
code: 'Numpad9',
};

export const PageDown = {
key: 'PageDown',
which: 34,
keyCode: 34,
code: 'Numpad3',
};

export const End = {
key: 'End',
which: 35,
keyCode: 35,
code: 'Numpad1',
};

export const Home = {
key: 'Home',
which: 36,
keyCode: 36,
code: 'Numpad7',
};

export const ArrowLeft = {
key: 'ArrowLeft',
which: 37,
keyCode: 37,
code: 'ArrowLeft',
};

export const ArrowUp = {
key: 'ArrowUp',
which: 38,
keyCode: 38,
code: 'ArrowUp',
};

export const ArrowRight = {
key: 'ArrowRight',
which: 39,
keyCode: 39,
code: 'ArrowRight',
};

export const ArrowDown = {
key: 'ArrowDown',
which: 40,
keyCode: 40,
code: 'ArrowDown',
};

export const Delete = {
key: 'Delete',
which: 8 || 46,
keyCode: 8 || 46,
code: 'ArrowDecimal',
};
5 changes: 3 additions & 2 deletions packages/react/src/internal/keyboard/match.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export function matches(event, keysToMatch) {
* @param {Key} key
* @returns {boolean}
*/
export function match(eventOrCode, { key, which, keyCode } = {}) {
export function match(eventOrCode, { key, which, keyCode, code } = {}) {
if (typeof eventOrCode === 'string') {
return eventOrCode === key;
}
Expand All @@ -73,7 +73,8 @@ export function match(eventOrCode, { key, which, keyCode } = {}) {
return (
eventOrCode.key === key ||
eventOrCode.which === which ||
eventOrCode.keyCode === keyCode
eventOrCode.keyCode === keyCode ||
eventOrCode.code === code
);
}

Expand Down