UNPKG

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