mirror of
https://github.com/idanoo/GoScrobble.git
synced 2024-11-24 09:25:15 +00:00
294 lines
9.4 KiB
JavaScript
294 lines
9.4 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard");
|
||
|
|
||
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
||
|
|
||
|
exports.__esModule = true;
|
||
|
exports["default"] = void 0;
|
||
|
|
||
|
var _matches = _interopRequireDefault(require("dom-helpers/matches"));
|
||
|
|
||
|
var _querySelectorAll = _interopRequireDefault(require("dom-helpers/querySelectorAll"));
|
||
|
|
||
|
var _addEventListener = _interopRequireDefault(require("dom-helpers/addEventListener"));
|
||
|
|
||
|
var _react = _interopRequireWildcard(require("react"));
|
||
|
|
||
|
var _propTypes = _interopRequireDefault(require("prop-types"));
|
||
|
|
||
|
var _uncontrollable = require("uncontrollable");
|
||
|
|
||
|
var _usePrevious = _interopRequireDefault(require("@restart/hooks/usePrevious"));
|
||
|
|
||
|
var _useForceUpdate = _interopRequireDefault(require("@restart/hooks/useForceUpdate"));
|
||
|
|
||
|
var _useGlobalListener = _interopRequireDefault(require("@restart/hooks/useGlobalListener"));
|
||
|
|
||
|
var _useEventCallback = _interopRequireDefault(require("@restart/hooks/useEventCallback"));
|
||
|
|
||
|
var _DropdownContext = _interopRequireDefault(require("./DropdownContext"));
|
||
|
|
||
|
var _DropdownMenu = _interopRequireDefault(require("./DropdownMenu"));
|
||
|
|
||
|
var _DropdownToggle = _interopRequireDefault(require("./DropdownToggle"));
|
||
|
|
||
|
var propTypes = {
|
||
|
/**
|
||
|
* A render prop that returns the root dropdown element. The `props`
|
||
|
* argument should spread through to an element containing _both_ the
|
||
|
* menu and toggle in order to handle keyboard events for focus management.
|
||
|
*
|
||
|
* @type {Function ({
|
||
|
* props: {
|
||
|
* onKeyDown: (SyntheticEvent) => void,
|
||
|
* },
|
||
|
* }) => React.Element}
|
||
|
*/
|
||
|
children: _propTypes["default"].node,
|
||
|
|
||
|
/**
|
||
|
* Determines the direction and location of the Menu in relation to it's Toggle.
|
||
|
*/
|
||
|
drop: _propTypes["default"].oneOf(['up', 'left', 'right', 'down']),
|
||
|
|
||
|
/**
|
||
|
* Controls the focus behavior for when the Dropdown is opened. Set to
|
||
|
* `true` to always focus the first menu item, `keyboard` to focus only when
|
||
|
* navigating via the keyboard, or `false` to disable completely
|
||
|
*
|
||
|
* The Default behavior is `false` **unless** the Menu has a `role="menu"`
|
||
|
* where it will default to `keyboard` to match the recommended [ARIA Authoring practices](https://www.w3.org/TR/wai-aria-practices-1.1/#menubutton).
|
||
|
*/
|
||
|
focusFirstItemOnShow: _propTypes["default"].oneOf([false, true, 'keyboard']),
|
||
|
|
||
|
/**
|
||
|
* A css slector string that will return __focusable__ menu items.
|
||
|
* Selectors should be relative to the menu component:
|
||
|
* e.g. ` > li:not('.disabled')`
|
||
|
*/
|
||
|
itemSelector: _propTypes["default"].string,
|
||
|
|
||
|
/**
|
||
|
* Align the menu to the 'end' side of the placement side of the Dropdown toggle. The default placement is `top-start` or `bottom-start`.
|
||
|
*/
|
||
|
alignEnd: _propTypes["default"].bool,
|
||
|
|
||
|
/**
|
||
|
* Whether or not the Dropdown is visible.
|
||
|
*
|
||
|
* @controllable onToggle
|
||
|
*/
|
||
|
show: _propTypes["default"].bool,
|
||
|
|
||
|
/**
|
||
|
* Sets the initial show position of the Dropdown.
|
||
|
*/
|
||
|
defaultShow: _propTypes["default"].bool,
|
||
|
|
||
|
/**
|
||
|
* A callback fired when the Dropdown wishes to change visibility. Called with the requested
|
||
|
* `show` value, the DOM event, and the source that fired it: `'click'`,`'keydown'`,`'rootClose'`, or `'select'`.
|
||
|
*
|
||
|
* ```ts static
|
||
|
* function(
|
||
|
* isOpen: boolean,
|
||
|
* event: SyntheticEvent,
|
||
|
* ): void
|
||
|
* ```
|
||
|
*
|
||
|
* @controllable show
|
||
|
*/
|
||
|
onToggle: _propTypes["default"].func
|
||
|
};
|
||
|
|
||
|
function useRefWithUpdate() {
|
||
|
var forceUpdate = (0, _useForceUpdate["default"])();
|
||
|
var ref = (0, _react.useRef)(null);
|
||
|
var attachRef = (0, _react.useCallback)(function (element) {
|
||
|
ref.current = element; // ensure that a menu set triggers an update for consumers
|
||
|
|
||
|
forceUpdate();
|
||
|
}, [forceUpdate]);
|
||
|
return [ref, attachRef];
|
||
|
}
|
||
|
/**
|
||
|
* @displayName Dropdown
|
||
|
* @public
|
||
|
*/
|
||
|
|
||
|
|
||
|
function Dropdown(_ref) {
|
||
|
var drop = _ref.drop,
|
||
|
alignEnd = _ref.alignEnd,
|
||
|
defaultShow = _ref.defaultShow,
|
||
|
rawShow = _ref.show,
|
||
|
rawOnToggle = _ref.onToggle,
|
||
|
_ref$itemSelector = _ref.itemSelector,
|
||
|
itemSelector = _ref$itemSelector === void 0 ? '* > *' : _ref$itemSelector,
|
||
|
focusFirstItemOnShow = _ref.focusFirstItemOnShow,
|
||
|
children = _ref.children;
|
||
|
|
||
|
var _useUncontrolledProp = (0, _uncontrollable.useUncontrolledProp)(rawShow, defaultShow, rawOnToggle),
|
||
|
show = _useUncontrolledProp[0],
|
||
|
onToggle = _useUncontrolledProp[1]; // We use normal refs instead of useCallbackRef in order to populate the
|
||
|
// the value as quickly as possible, otherwise the effect to focus the element
|
||
|
// may run before the state value is set
|
||
|
|
||
|
|
||
|
var _useRefWithUpdate = useRefWithUpdate(),
|
||
|
menuRef = _useRefWithUpdate[0],
|
||
|
setMenu = _useRefWithUpdate[1];
|
||
|
|
||
|
var menuElement = menuRef.current;
|
||
|
|
||
|
var _useRefWithUpdate2 = useRefWithUpdate(),
|
||
|
toggleRef = _useRefWithUpdate2[0],
|
||
|
setToggle = _useRefWithUpdate2[1];
|
||
|
|
||
|
var toggleElement = toggleRef.current;
|
||
|
var lastShow = (0, _usePrevious["default"])(show);
|
||
|
var lastSourceEvent = (0, _react.useRef)(null);
|
||
|
var focusInDropdown = (0, _react.useRef)(false);
|
||
|
var toggle = (0, _react.useCallback)(function (nextShow, event) {
|
||
|
onToggle(nextShow, event);
|
||
|
}, [onToggle]);
|
||
|
var context = (0, _react.useMemo)(function () {
|
||
|
return {
|
||
|
toggle: toggle,
|
||
|
drop: drop,
|
||
|
show: show,
|
||
|
alignEnd: alignEnd,
|
||
|
menuElement: menuElement,
|
||
|
toggleElement: toggleElement,
|
||
|
setMenu: setMenu,
|
||
|
setToggle: setToggle
|
||
|
};
|
||
|
}, [toggle, drop, show, alignEnd, menuElement, toggleElement, setMenu, setToggle]);
|
||
|
|
||
|
if (menuElement && lastShow && !show) {
|
||
|
focusInDropdown.current = menuElement.contains(document.activeElement);
|
||
|
}
|
||
|
|
||
|
var focusToggle = (0, _useEventCallback["default"])(function () {
|
||
|
if (toggleElement && toggleElement.focus) {
|
||
|
toggleElement.focus();
|
||
|
}
|
||
|
});
|
||
|
var maybeFocusFirst = (0, _useEventCallback["default"])(function () {
|
||
|
var type = lastSourceEvent.current;
|
||
|
var focusType = focusFirstItemOnShow;
|
||
|
|
||
|
if (focusType == null) {
|
||
|
focusType = menuRef.current && (0, _matches["default"])(menuRef.current, '[role=menu]') ? 'keyboard' : false;
|
||
|
}
|
||
|
|
||
|
if (focusType === false || focusType === 'keyboard' && !/^key.+$/.test(type)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var first = (0, _querySelectorAll["default"])(menuRef.current, itemSelector)[0];
|
||
|
if (first && first.focus) first.focus();
|
||
|
});
|
||
|
(0, _react.useEffect)(function () {
|
||
|
if (show) maybeFocusFirst();else if (focusInDropdown.current) {
|
||
|
focusInDropdown.current = false;
|
||
|
focusToggle();
|
||
|
} // only `show` should be changing
|
||
|
}, [show, focusInDropdown, focusToggle, maybeFocusFirst]);
|
||
|
(0, _react.useEffect)(function () {
|
||
|
lastSourceEvent.current = null;
|
||
|
});
|
||
|
|
||
|
var getNextFocusedChild = function getNextFocusedChild(current, offset) {
|
||
|
if (!menuRef.current) return null;
|
||
|
var items = (0, _querySelectorAll["default"])(menuRef.current, itemSelector);
|
||
|
var index = items.indexOf(current) + offset;
|
||
|
index = Math.max(0, Math.min(index, items.length));
|
||
|
return items[index];
|
||
|
};
|
||
|
|
||
|
(0, _useGlobalListener["default"])('keydown', function (event) {
|
||
|
var _menuRef$current, _toggleRef$current;
|
||
|
|
||
|
var key = event.key;
|
||
|
var target = event.target;
|
||
|
var fromMenu = (_menuRef$current = menuRef.current) == null ? void 0 : _menuRef$current.contains(target);
|
||
|
var fromToggle = (_toggleRef$current = toggleRef.current) == null ? void 0 : _toggleRef$current.contains(target); // Second only to https://github.com/twbs/bootstrap/blob/8cfbf6933b8a0146ac3fbc369f19e520bd1ebdac/js/src/dropdown.js#L400
|
||
|
// in inscrutability
|
||
|
|
||
|
var isInput = /input|textarea/i.test(target.tagName);
|
||
|
|
||
|
if (isInput && (key === ' ' || key !== 'Escape' && fromMenu)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!fromMenu && !fromToggle) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!menuRef.current && key === 'Tab') {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
lastSourceEvent.current = event.type;
|
||
|
|
||
|
switch (key) {
|
||
|
case 'ArrowUp':
|
||
|
{
|
||
|
var next = getNextFocusedChild(target, -1);
|
||
|
if (next && next.focus) next.focus();
|
||
|
event.preventDefault();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
case 'ArrowDown':
|
||
|
event.preventDefault();
|
||
|
|
||
|
if (!show) {
|
||
|
onToggle(true, event);
|
||
|
} else {
|
||
|
var _next = getNextFocusedChild(target, 1);
|
||
|
|
||
|
if (_next && _next.focus) _next.focus();
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
|
||
|
case 'Tab':
|
||
|
// on keydown the target is the element being tabbed FROM, we need that
|
||
|
// to know if this event is relevant to this dropdown (e.g. in this menu).
|
||
|
// On `keyup` the target is the element being tagged TO which we use to check
|
||
|
// if focus has left the menu
|
||
|
(0, _addEventListener["default"])(document, 'keyup', function (e) {
|
||
|
var _menuRef$current2;
|
||
|
|
||
|
if (e.key === 'Tab' && !e.target || !((_menuRef$current2 = menuRef.current) != null && _menuRef$current2.contains(e.target))) {
|
||
|
onToggle(false, event);
|
||
|
}
|
||
|
}, {
|
||
|
once: true
|
||
|
});
|
||
|
break;
|
||
|
|
||
|
case 'Escape':
|
||
|
event.preventDefault();
|
||
|
event.stopPropagation();
|
||
|
onToggle(false, event);
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
}
|
||
|
});
|
||
|
return /*#__PURE__*/_react["default"].createElement(_DropdownContext["default"].Provider, {
|
||
|
value: context
|
||
|
}, children);
|
||
|
}
|
||
|
|
||
|
Dropdown.displayName = 'ReactOverlaysDropdown';
|
||
|
Dropdown.propTypes = propTypes;
|
||
|
Dropdown.Menu = _DropdownMenu["default"];
|
||
|
Dropdown.Toggle = _DropdownToggle["default"];
|
||
|
var _default = Dropdown;
|
||
|
exports["default"] = _default;
|
||
|
module.exports = exports.default;
|