UNPKG

3.8 kBJavaScriptView Raw
1import _extends from "@babel/runtime/helpers/esm/extends";
2import _objectWithoutPropertiesLoose from "@babel/runtime/helpers/esm/objectWithoutPropertiesLoose";
3import qsa from 'dom-helpers/querySelectorAll';
4import React, { useContext, useEffect, useRef } from 'react';
5import useForceUpdate from '@restart/hooks/useForceUpdate';
6import useMergedRefs from '@restart/hooks/useMergedRefs';
7import NavContext from './NavContext';
8import SelectableContext, { makeEventKey } from './SelectableContext';
9import TabContext from './TabContext';
10
11// eslint-disable-next-line @typescript-eslint/no-empty-function
12var noop = function noop() {};
13
14var AbstractNav = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
15 var _ref$as = _ref.as,
16 Component = _ref$as === void 0 ? 'ul' : _ref$as,
17 onSelect = _ref.onSelect,
18 activeKey = _ref.activeKey,
19 role = _ref.role,
20 onKeyDown = _ref.onKeyDown,
21 props = _objectWithoutPropertiesLoose(_ref, ["as", "onSelect", "activeKey", "role", "onKeyDown"]);
22
23 // A ref and forceUpdate for refocus, b/c we only want to trigger when needed
24 // and don't want to reset the set in the effect
25 var forceUpdate = useForceUpdate();
26 var needsRefocusRef = useRef(false);
27 var parentOnSelect = useContext(SelectableContext);
28 var tabContext = useContext(TabContext);
29 var getControlledId, getControllerId;
30
31 if (tabContext) {
32 role = role || 'tablist';
33 activeKey = tabContext.activeKey;
34 getControlledId = tabContext.getControlledId;
35 getControllerId = tabContext.getControllerId;
36 }
37
38 var listNode = useRef(null);
39
40 var getNextActiveChild = function getNextActiveChild(offset) {
41 var currentListNode = listNode.current;
42 if (!currentListNode) return null;
43 var items = qsa(currentListNode, '[data-rb-event-key]:not(.disabled)');
44 var activeChild = currentListNode.querySelector('.active');
45 if (!activeChild) return null;
46 var index = items.indexOf(activeChild);
47 if (index === -1) return null;
48 var nextIndex = index + offset;
49 if (nextIndex >= items.length) nextIndex = 0;
50 if (nextIndex < 0) nextIndex = items.length - 1;
51 return items[nextIndex];
52 };
53
54 var handleSelect = function handleSelect(key, event) {
55 if (key == null) return;
56 if (onSelect) onSelect(key, event);
57 if (parentOnSelect) parentOnSelect(key, event);
58 };
59
60 var handleKeyDown = function handleKeyDown(event) {
61 if (onKeyDown) onKeyDown(event);
62 var nextActiveChild;
63
64 switch (event.key) {
65 case 'ArrowLeft':
66 case 'ArrowUp':
67 nextActiveChild = getNextActiveChild(-1);
68 break;
69
70 case 'ArrowRight':
71 case 'ArrowDown':
72 nextActiveChild = getNextActiveChild(1);
73 break;
74
75 default:
76 return;
77 }
78
79 if (!nextActiveChild) return;
80 event.preventDefault();
81 handleSelect(nextActiveChild.dataset.rbEventKey, event);
82 needsRefocusRef.current = true;
83 forceUpdate();
84 };
85
86 useEffect(function () {
87 if (listNode.current && needsRefocusRef.current) {
88 var activeChild = listNode.current.querySelector('[data-rb-event-key].active');
89 if (activeChild) activeChild.focus();
90 }
91
92 needsRefocusRef.current = false;
93 });
94 var mergedRef = useMergedRefs(ref, listNode);
95 return /*#__PURE__*/React.createElement(SelectableContext.Provider, {
96 value: handleSelect
97 }, /*#__PURE__*/React.createElement(NavContext.Provider, {
98 value: {
99 role: role,
100 // used by NavLink to determine it's role
101 activeKey: makeEventKey(activeKey),
102 getControlledId: getControlledId || noop,
103 getControllerId: getControllerId || noop
104 }
105 }, /*#__PURE__*/React.createElement(Component, _extends({}, props, {
106 onKeyDown: handleKeyDown,
107 ref: mergedRef,
108 role: role
109 }))));
110});
111export default AbstractNav;
\No newline at end of file