226 lines
8.1 KiB
JavaScript
226 lines
8.1 KiB
JavaScript
import _extends from "@babel/runtime/helpers/esm/extends";
|
|
import * as React from 'react';
|
|
import { useTheme } from '@mui/material/styles';
|
|
import useEventCallback from '@mui/utils/useEventCallback';
|
|
import { getFirstNode, getLastNode, getNextNode, getPreviousNode, populateInstance } from '../../useTreeView/useTreeView.utils';
|
|
function isPrintableCharacter(string) {
|
|
return string && string.length === 1 && string.match(/\S/);
|
|
}
|
|
function findNextFirstChar(firstChars, startIndex, char) {
|
|
for (var i = startIndex; i < firstChars.length; i += 1) {
|
|
if (char === firstChars[i]) {
|
|
return i;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
export var useTreeViewKeyboardNavigation = function useTreeViewKeyboardNavigation(_ref) {
|
|
var instance = _ref.instance,
|
|
params = _ref.params,
|
|
state = _ref.state;
|
|
var theme = useTheme();
|
|
var isRtl = theme.direction === 'rtl';
|
|
var firstCharMap = React.useRef({});
|
|
var mapFirstChar = useEventCallback(function (nodeId, firstChar) {
|
|
firstCharMap.current[nodeId] = firstChar;
|
|
return function () {
|
|
var newMap = _extends({}, firstCharMap.current);
|
|
delete newMap[nodeId];
|
|
firstCharMap.current = newMap;
|
|
};
|
|
});
|
|
populateInstance(instance, {
|
|
mapFirstChar: mapFirstChar
|
|
});
|
|
var handleNextArrow = function handleNextArrow(event) {
|
|
if (state.focusedNodeId != null && instance.isNodeExpandable(state.focusedNodeId)) {
|
|
if (instance.isNodeExpanded(state.focusedNodeId)) {
|
|
instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
|
|
} else if (!instance.isNodeDisabled(state.focusedNodeId)) {
|
|
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
var handlePreviousArrow = function handlePreviousArrow(event) {
|
|
if (state.focusedNodeId == null) {
|
|
return false;
|
|
}
|
|
if (instance.isNodeExpanded(state.focusedNodeId) && !instance.isNodeDisabled(state.focusedNodeId)) {
|
|
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
return true;
|
|
}
|
|
var parent = instance.getNode(state.focusedNodeId).parentId;
|
|
if (parent) {
|
|
instance.focusNode(event, parent);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
var focusByFirstCharacter = function focusByFirstCharacter(event, nodeId, firstChar) {
|
|
var start;
|
|
var index;
|
|
var lowercaseChar = firstChar.toLowerCase();
|
|
var firstCharIds = [];
|
|
var firstChars = [];
|
|
// This really only works since the ids are strings
|
|
Object.keys(firstCharMap.current).forEach(function (mapNodeId) {
|
|
var map = instance.getNode(mapNodeId);
|
|
var visible = map.parentId ? instance.isNodeExpanded(map.parentId) : true;
|
|
var shouldBeSkipped = params.disabledItemsFocusable ? false : instance.isNodeDisabled(mapNodeId);
|
|
if (visible && !shouldBeSkipped) {
|
|
firstCharIds.push(mapNodeId);
|
|
firstChars.push(firstCharMap.current[mapNodeId]);
|
|
}
|
|
});
|
|
|
|
// Get start index for search based on position of currentItem
|
|
start = firstCharIds.indexOf(nodeId) + 1;
|
|
if (start >= firstCharIds.length) {
|
|
start = 0;
|
|
}
|
|
|
|
// Check remaining slots in the menu
|
|
index = findNextFirstChar(firstChars, start, lowercaseChar);
|
|
|
|
// If not found in remaining slots, check from beginning
|
|
if (index === -1) {
|
|
index = findNextFirstChar(firstChars, 0, lowercaseChar);
|
|
}
|
|
|
|
// If match was found...
|
|
if (index > -1) {
|
|
instance.focusNode(event, firstCharIds[index]);
|
|
}
|
|
};
|
|
var selectNextNode = function selectNextNode(event, id) {
|
|
if (!instance.isNodeDisabled(getNextNode(instance, id))) {
|
|
instance.selectRange(event, {
|
|
end: getNextNode(instance, id),
|
|
current: id
|
|
}, true);
|
|
}
|
|
};
|
|
var selectPreviousNode = function selectPreviousNode(event, nodeId) {
|
|
if (!instance.isNodeDisabled(getPreviousNode(instance, nodeId))) {
|
|
instance.selectRange(event, {
|
|
end: getPreviousNode(instance, nodeId),
|
|
current: nodeId
|
|
}, true);
|
|
}
|
|
};
|
|
var createHandleKeyDown = function createHandleKeyDown(otherHandlers) {
|
|
return function (event) {
|
|
var _otherHandlers$onKeyD;
|
|
(_otherHandlers$onKeyD = otherHandlers.onKeyDown) == null || _otherHandlers$onKeyD.call(otherHandlers, event);
|
|
var flag = false;
|
|
var key = event.key;
|
|
|
|
// If the tree is empty there will be no focused node
|
|
if (event.altKey || event.currentTarget !== event.target || state.focusedNodeId == null) {
|
|
return;
|
|
}
|
|
var ctrlPressed = event.ctrlKey || event.metaKey;
|
|
switch (key) {
|
|
case ' ':
|
|
if (!params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
|
|
flag = true;
|
|
if (params.multiSelect && event.shiftKey) {
|
|
instance.selectRange(event, {
|
|
end: state.focusedNodeId
|
|
});
|
|
} else if (params.multiSelect) {
|
|
instance.selectNode(event, state.focusedNodeId, true);
|
|
} else {
|
|
instance.selectNode(event, state.focusedNodeId);
|
|
}
|
|
}
|
|
event.stopPropagation();
|
|
break;
|
|
case 'Enter':
|
|
if (!instance.isNodeDisabled(state.focusedNodeId)) {
|
|
if (instance.isNodeExpandable(state.focusedNodeId)) {
|
|
instance.toggleNodeExpansion(event, state.focusedNodeId);
|
|
flag = true;
|
|
} else if (!params.disableSelection) {
|
|
flag = true;
|
|
if (params.multiSelect) {
|
|
instance.selectNode(event, state.focusedNodeId, true);
|
|
} else {
|
|
instance.selectNode(event, state.focusedNodeId);
|
|
}
|
|
}
|
|
}
|
|
event.stopPropagation();
|
|
break;
|
|
case 'ArrowDown':
|
|
if (params.multiSelect && event.shiftKey && !params.disableSelection) {
|
|
selectNextNode(event, state.focusedNodeId);
|
|
}
|
|
instance.focusNode(event, getNextNode(instance, state.focusedNodeId));
|
|
flag = true;
|
|
break;
|
|
case 'ArrowUp':
|
|
if (params.multiSelect && event.shiftKey && !params.disableSelection) {
|
|
selectPreviousNode(event, state.focusedNodeId);
|
|
}
|
|
instance.focusNode(event, getPreviousNode(instance, state.focusedNodeId));
|
|
flag = true;
|
|
break;
|
|
case 'ArrowRight':
|
|
if (isRtl) {
|
|
flag = handlePreviousArrow(event);
|
|
} else {
|
|
flag = handleNextArrow(event);
|
|
}
|
|
break;
|
|
case 'ArrowLeft':
|
|
if (isRtl) {
|
|
flag = handleNextArrow(event);
|
|
} else {
|
|
flag = handlePreviousArrow(event);
|
|
}
|
|
break;
|
|
case 'Home':
|
|
if (params.multiSelect && ctrlPressed && event.shiftKey && !params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
|
|
instance.rangeSelectToFirst(event, state.focusedNodeId);
|
|
}
|
|
instance.focusNode(event, getFirstNode(instance));
|
|
flag = true;
|
|
break;
|
|
case 'End':
|
|
if (params.multiSelect && ctrlPressed && event.shiftKey && !params.disableSelection && !instance.isNodeDisabled(state.focusedNodeId)) {
|
|
instance.rangeSelectToLast(event, state.focusedNodeId);
|
|
}
|
|
instance.focusNode(event, getLastNode(instance));
|
|
flag = true;
|
|
break;
|
|
default:
|
|
if (key === '*') {
|
|
instance.expandAllSiblings(event, state.focusedNodeId);
|
|
flag = true;
|
|
} else if (params.multiSelect && ctrlPressed && key.toLowerCase() === 'a' && !params.disableSelection) {
|
|
instance.selectRange(event, {
|
|
start: getFirstNode(instance),
|
|
end: getLastNode(instance)
|
|
});
|
|
flag = true;
|
|
} else if (!ctrlPressed && !event.shiftKey && isPrintableCharacter(key)) {
|
|
focusByFirstCharacter(event, state.focusedNodeId, key);
|
|
flag = true;
|
|
}
|
|
}
|
|
if (flag) {
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
}
|
|
};
|
|
};
|
|
return {
|
|
getRootProps: function getRootProps(otherHandlers) {
|
|
return {
|
|
onKeyDown: createHandleKeyDown(otherHandlers)
|
|
};
|
|
}
|
|
};
|
|
}; |