first commit
This commit is contained in:
270
frontend/node_modules/rc-menu/es/hooks/useAccessibility.js
generated
vendored
Normal file
270
frontend/node_modules/rc-menu/es/hooks/useAccessibility.js
generated
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
|
||||
import { getFocusNodeList } from "rc-util/es/Dom/focus";
|
||||
import KeyCode from "rc-util/es/KeyCode";
|
||||
import raf from "rc-util/es/raf";
|
||||
import * as React from 'react';
|
||||
import { getMenuId } from "../context/IdContext";
|
||||
// destruct to reduce minify size
|
||||
var LEFT = KeyCode.LEFT,
|
||||
RIGHT = KeyCode.RIGHT,
|
||||
UP = KeyCode.UP,
|
||||
DOWN = KeyCode.DOWN,
|
||||
ENTER = KeyCode.ENTER,
|
||||
ESC = KeyCode.ESC,
|
||||
HOME = KeyCode.HOME,
|
||||
END = KeyCode.END;
|
||||
var ArrowKeys = [UP, DOWN, LEFT, RIGHT];
|
||||
function getOffset(mode, isRootLevel, isRtl, which) {
|
||||
var _offsets;
|
||||
var prev = 'prev';
|
||||
var next = 'next';
|
||||
var children = 'children';
|
||||
var parent = 'parent';
|
||||
|
||||
// Inline enter is special that we use unique operation
|
||||
if (mode === 'inline' && which === ENTER) {
|
||||
return {
|
||||
inlineTrigger: true
|
||||
};
|
||||
}
|
||||
var inline = _defineProperty(_defineProperty({}, UP, prev), DOWN, next);
|
||||
var horizontal = _defineProperty(_defineProperty(_defineProperty(_defineProperty({}, LEFT, isRtl ? next : prev), RIGHT, isRtl ? prev : next), DOWN, children), ENTER, children);
|
||||
var vertical = _defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty(_defineProperty({}, UP, prev), DOWN, next), ENTER, children), ESC, parent), LEFT, isRtl ? children : parent), RIGHT, isRtl ? parent : children);
|
||||
var offsets = {
|
||||
inline: inline,
|
||||
horizontal: horizontal,
|
||||
vertical: vertical,
|
||||
inlineSub: inline,
|
||||
horizontalSub: vertical,
|
||||
verticalSub: vertical
|
||||
};
|
||||
var type = (_offsets = offsets["".concat(mode).concat(isRootLevel ? '' : 'Sub')]) === null || _offsets === void 0 ? void 0 : _offsets[which];
|
||||
switch (type) {
|
||||
case prev:
|
||||
return {
|
||||
offset: -1,
|
||||
sibling: true
|
||||
};
|
||||
case next:
|
||||
return {
|
||||
offset: 1,
|
||||
sibling: true
|
||||
};
|
||||
case parent:
|
||||
return {
|
||||
offset: -1,
|
||||
sibling: false
|
||||
};
|
||||
case children:
|
||||
return {
|
||||
offset: 1,
|
||||
sibling: false
|
||||
};
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
function findContainerUL(element) {
|
||||
var current = element;
|
||||
while (current) {
|
||||
if (current.getAttribute('data-menu-list')) {
|
||||
return current;
|
||||
}
|
||||
current = current.parentElement;
|
||||
}
|
||||
|
||||
// Normally should not reach this line
|
||||
/* istanbul ignore next */
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find focused element within element set provided
|
||||
*/
|
||||
function getFocusElement(activeElement, elements) {
|
||||
var current = activeElement || document.activeElement;
|
||||
while (current) {
|
||||
if (elements.has(current)) {
|
||||
return current;
|
||||
}
|
||||
current = current.parentElement;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get focusable elements from the element set under provided container
|
||||
*/
|
||||
export function getFocusableElements(container, elements) {
|
||||
var list = getFocusNodeList(container, true);
|
||||
return list.filter(function (ele) {
|
||||
return elements.has(ele);
|
||||
});
|
||||
}
|
||||
function getNextFocusElement(parentQueryContainer, elements, focusMenuElement) {
|
||||
var offset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1;
|
||||
// Key on the menu item will not get validate parent container
|
||||
if (!parentQueryContainer) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// List current level menu item elements
|
||||
var sameLevelFocusableMenuElementList = getFocusableElements(parentQueryContainer, elements);
|
||||
|
||||
// Find next focus index
|
||||
var count = sameLevelFocusableMenuElementList.length;
|
||||
var focusIndex = sameLevelFocusableMenuElementList.findIndex(function (ele) {
|
||||
return focusMenuElement === ele;
|
||||
});
|
||||
if (offset < 0) {
|
||||
if (focusIndex === -1) {
|
||||
focusIndex = count - 1;
|
||||
} else {
|
||||
focusIndex -= 1;
|
||||
}
|
||||
} else if (offset > 0) {
|
||||
focusIndex += 1;
|
||||
}
|
||||
focusIndex = (focusIndex + count) % count;
|
||||
|
||||
// Focus menu item
|
||||
return sameLevelFocusableMenuElementList[focusIndex];
|
||||
}
|
||||
export var refreshElements = function refreshElements(keys, id) {
|
||||
var elements = new Set();
|
||||
var key2element = new Map();
|
||||
var element2key = new Map();
|
||||
keys.forEach(function (key) {
|
||||
var element = document.querySelector("[data-menu-id='".concat(getMenuId(id, key), "']"));
|
||||
if (element) {
|
||||
elements.add(element);
|
||||
element2key.set(element, key);
|
||||
key2element.set(key, element);
|
||||
}
|
||||
});
|
||||
return {
|
||||
elements: elements,
|
||||
key2element: key2element,
|
||||
element2key: element2key
|
||||
};
|
||||
};
|
||||
export function useAccessibility(mode, activeKey, isRtl, id, containerRef, getKeys, getKeyPath, triggerActiveKey, triggerAccessibilityOpen, originOnKeyDown) {
|
||||
var rafRef = React.useRef();
|
||||
var activeRef = React.useRef();
|
||||
activeRef.current = activeKey;
|
||||
var cleanRaf = function cleanRaf() {
|
||||
raf.cancel(rafRef.current);
|
||||
};
|
||||
React.useEffect(function () {
|
||||
return function () {
|
||||
cleanRaf();
|
||||
};
|
||||
}, []);
|
||||
return function (e) {
|
||||
var which = e.which;
|
||||
if ([].concat(ArrowKeys, [ENTER, ESC, HOME, END]).includes(which)) {
|
||||
var keys = getKeys();
|
||||
var refreshedElements = refreshElements(keys, id);
|
||||
var _refreshedElements = refreshedElements,
|
||||
elements = _refreshedElements.elements,
|
||||
key2element = _refreshedElements.key2element,
|
||||
element2key = _refreshedElements.element2key;
|
||||
|
||||
// First we should find current focused MenuItem/SubMenu element
|
||||
var activeElement = key2element.get(activeKey);
|
||||
var focusMenuElement = getFocusElement(activeElement, elements);
|
||||
var focusMenuKey = element2key.get(focusMenuElement);
|
||||
var offsetObj = getOffset(mode, getKeyPath(focusMenuKey, true).length === 1, isRtl, which);
|
||||
|
||||
// Some mode do not have fully arrow operation like inline
|
||||
if (!offsetObj && which !== HOME && which !== END) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Arrow prevent default to avoid page scroll
|
||||
if (ArrowKeys.includes(which) || [HOME, END].includes(which)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
var tryFocus = function tryFocus(menuElement) {
|
||||
if (menuElement) {
|
||||
var focusTargetElement = menuElement;
|
||||
|
||||
// Focus to link instead of menu item if possible
|
||||
var link = menuElement.querySelector('a');
|
||||
if (link !== null && link !== void 0 && link.getAttribute('href')) {
|
||||
focusTargetElement = link;
|
||||
}
|
||||
var targetKey = element2key.get(menuElement);
|
||||
triggerActiveKey(targetKey);
|
||||
|
||||
/**
|
||||
* Do not `useEffect` here since `tryFocus` may trigger async
|
||||
* which makes React sync update the `activeKey`
|
||||
* that force render before `useRef` set the next activeKey
|
||||
*/
|
||||
cleanRaf();
|
||||
rafRef.current = raf(function () {
|
||||
if (activeRef.current === targetKey) {
|
||||
focusTargetElement.focus();
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
if ([HOME, END].includes(which) || offsetObj.sibling || !focusMenuElement) {
|
||||
// ========================== Sibling ==========================
|
||||
// Find walkable focus menu element container
|
||||
var parentQueryContainer;
|
||||
if (!focusMenuElement || mode === 'inline') {
|
||||
parentQueryContainer = containerRef.current;
|
||||
} else {
|
||||
parentQueryContainer = findContainerUL(focusMenuElement);
|
||||
}
|
||||
|
||||
// Get next focus element
|
||||
var targetElement;
|
||||
var focusableElements = getFocusableElements(parentQueryContainer, elements);
|
||||
if (which === HOME) {
|
||||
targetElement = focusableElements[0];
|
||||
} else if (which === END) {
|
||||
targetElement = focusableElements[focusableElements.length - 1];
|
||||
} else {
|
||||
targetElement = getNextFocusElement(parentQueryContainer, elements, focusMenuElement, offsetObj.offset);
|
||||
}
|
||||
// Focus menu item
|
||||
tryFocus(targetElement);
|
||||
|
||||
// ======================= InlineTrigger =======================
|
||||
} else if (offsetObj.inlineTrigger) {
|
||||
// Inline trigger no need switch to sub menu item
|
||||
triggerAccessibilityOpen(focusMenuKey);
|
||||
// =========================== Level ===========================
|
||||
} else if (offsetObj.offset > 0) {
|
||||
triggerAccessibilityOpen(focusMenuKey, true);
|
||||
cleanRaf();
|
||||
rafRef.current = raf(function () {
|
||||
// Async should resync elements
|
||||
refreshedElements = refreshElements(keys, id);
|
||||
var controlId = focusMenuElement.getAttribute('aria-controls');
|
||||
var subQueryContainer = document.getElementById(controlId);
|
||||
|
||||
// Get sub focusable menu item
|
||||
var targetElement = getNextFocusElement(subQueryContainer, refreshedElements.elements);
|
||||
|
||||
// Focus menu item
|
||||
tryFocus(targetElement);
|
||||
}, 5);
|
||||
} else if (offsetObj.offset < 0) {
|
||||
var keyPath = getKeyPath(focusMenuKey, true);
|
||||
var parentKey = keyPath[keyPath.length - 2];
|
||||
var parentMenuElement = key2element.get(parentKey);
|
||||
|
||||
// Focus menu item
|
||||
triggerAccessibilityOpen(parentKey, false);
|
||||
tryFocus(parentMenuElement);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass origin key down event
|
||||
originOnKeyDown === null || originOnKeyDown === void 0 || originOnKeyDown(e);
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user