reakit
Version:
Toolkit for building accessible rich web apps with React
321 lines (276 loc) • 13.7 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var _rollupPluginBabelHelpers = require('../_rollupPluginBabelHelpers-8f9a8751.js');
var createComponent = require('reakit-system/createComponent');
var createHook = require('reakit-system/createHook');
require('reakit-utils/shallowEqual');
var React = require('react');
var useForkRef = require('reakit-utils/useForkRef');
require('reakit-utils/isButton');
var reakitWarning = require('reakit-warning');
var useLiveRef = require('reakit-utils/useLiveRef');
var isSelfTarget = require('reakit-utils/isSelfTarget');
require('reakit-utils/useIsomorphicEffect');
var hasFocusWithin = require('reakit-utils/hasFocusWithin');
var isPortalEvent = require('reakit-utils/isPortalEvent');
require('reakit-utils/dom');
require('reakit-utils/tabbable');
require('../Role/Role.js');
require('../Tabbable/Tabbable.js');
var Clickable_Clickable = require('../Clickable/Clickable.js');
var getDocument = require('reakit-utils/getDocument');
var getCurrentId = require('../getCurrentId-eade2850.js');
var __keys = require('../__keys-3b597476.js');
var userFocus = require('../userFocus-0afea51a.js');
var isTextField = require('reakit-utils/isTextField');
var ensureFocus = require('reakit-utils/ensureFocus');
require('../Id/IdProvider.js');
var Id_Id = require('../Id/Id.js');
require('reakit-utils/fireEvent');
var setTextFieldValue = require('../setTextFieldValue-b0584ae1.js');
function getWidget(itemElement) {
return itemElement.querySelector("[data-composite-item-widget]");
}
function useItem(options) {
return React.useMemo(function () {
var _options$items;
return (_options$items = options.items) === null || _options$items === void 0 ? void 0 : _options$items.find(function (item) {
return options.id && item.id === options.id;
});
}, [options.items, options.id]);
}
function targetIsAnotherItem(event, items) {
if (isSelfTarget.isSelfTarget(event)) return false;
for (var _iterator = _rollupPluginBabelHelpers._createForOfIteratorHelperLoose(items), _step; !(_step = _iterator()).done;) {
var item = _step.value;
if (item.ref.current === event.target) {
return true;
}
}
return false;
}
var useCompositeItem = createHook.createHook({
name: "CompositeItem",
compose: [Clickable_Clickable.useClickable, Id_Id.unstable_useId],
keys: __keys.COMPOSITE_ITEM_KEYS,
propsAreEqual: function propsAreEqual(prev, next) {
if (!next.id || prev.id !== next.id) {
return Clickable_Clickable.useClickable.unstable_propsAreEqual(prev, next);
}
var prevCurrentId = prev.currentId,
prevMoves = prev.unstable_moves,
prevProps = _rollupPluginBabelHelpers._objectWithoutPropertiesLoose(prev, ["currentId", "unstable_moves"]);
var nextCurrentId = next.currentId,
nextMoves = next.unstable_moves,
nextProps = _rollupPluginBabelHelpers._objectWithoutPropertiesLoose(next, ["currentId", "unstable_moves"]);
if (nextCurrentId !== prevCurrentId) {
if (next.id === nextCurrentId || next.id === prevCurrentId) {
return false;
}
} else if (prevMoves !== nextMoves) {
return false;
}
return Clickable_Clickable.useClickable.unstable_propsAreEqual(prevProps, nextProps);
},
useOptions: function useOptions(options) {
return _rollupPluginBabelHelpers._objectSpread2(_rollupPluginBabelHelpers._objectSpread2({}, options), {}, {
id: options.id,
currentId: getCurrentId.getCurrentId(options),
unstable_clickOnSpace: options.unstable_hasActiveWidget ? false : options.unstable_clickOnSpace
});
},
useProps: function useProps(options, _ref) {
var _options$items2;
var htmlRef = _ref.ref,
_ref$tabIndex = _ref.tabIndex,
htmlTabIndex = _ref$tabIndex === void 0 ? 0 : _ref$tabIndex,
htmlOnMouseDown = _ref.onMouseDown,
htmlOnFocus = _ref.onFocus,
htmlOnBlurCapture = _ref.onBlurCapture,
htmlOnKeyDown = _ref.onKeyDown,
htmlOnClick = _ref.onClick,
htmlProps = _rollupPluginBabelHelpers._objectWithoutPropertiesLoose(_ref, ["ref", "tabIndex", "onMouseDown", "onFocus", "onBlurCapture", "onKeyDown", "onClick"]);
var ref = React.useRef(null);
var id = options.id;
var trulyDisabled = options.disabled && !options.focusable;
var isCurrentItem = options.currentId === id;
var isCurrentItemRef = useLiveRef.useLiveRef(isCurrentItem);
var hasFocusedComposite = React.useRef(false);
var item = useItem(options);
var onMouseDownRef = useLiveRef.useLiveRef(htmlOnMouseDown);
var onFocusRef = useLiveRef.useLiveRef(htmlOnFocus);
var onBlurCaptureRef = useLiveRef.useLiveRef(htmlOnBlurCapture);
var onKeyDownRef = useLiveRef.useLiveRef(htmlOnKeyDown);
var onClickRef = useLiveRef.useLiveRef(htmlOnClick);
var shouldTabIndex = !options.unstable_virtual && !options.unstable_hasActiveWidget && isCurrentItem || // We don't want to set tabIndex="-1" when using CompositeItem as a
// standalone component, without state props.
!((_options$items2 = options.items) !== null && _options$items2 !== void 0 && _options$items2.length);
React.useEffect(function () {
var _options$registerItem;
if (!id) return undefined;
(_options$registerItem = options.registerItem) === null || _options$registerItem === void 0 ? void 0 : _options$registerItem.call(options, {
id: id,
ref: ref,
disabled: !!trulyDisabled
});
return function () {
var _options$unregisterIt;
(_options$unregisterIt = options.unregisterItem) === null || _options$unregisterIt === void 0 ? void 0 : _options$unregisterIt.call(options, id);
};
}, [id, trulyDisabled, options.registerItem, options.unregisterItem]);
React.useEffect(function () {
var element = ref.current;
if (!element) {
process.env.NODE_ENV !== "production" ? reakitWarning.warning(true, "Can't focus composite item component because `ref` wasn't passed to component.", "See https://reakit.io/docs/composite") : void 0;
return;
} // `moves` will be incremented whenever next, previous, up, down, first,
// last or move have been called. This means that the composite item will
// be focused whenever some of these functions are called. We're using
// isCurrentItemRef instead of isCurrentItem because we don't want to
// focus the item if isCurrentItem changes (and options.moves doesn't).
if (options.unstable_moves && isCurrentItemRef.current) {
userFocus.userFocus(element);
}
}, [options.unstable_moves]);
var onMouseDown = React.useCallback(function (event) {
var _onMouseDownRef$curre;
(_onMouseDownRef$curre = onMouseDownRef.current) === null || _onMouseDownRef$curre === void 0 ? void 0 : _onMouseDownRef$curre.call(onMouseDownRef, event);
userFocus.setUserFocus(event.currentTarget, true);
}, []);
var onFocus = React.useCallback(function (event) {
var _onFocusRef$current, _options$setCurrentId;
var shouldFocusComposite = userFocus.hasUserFocus(event.currentTarget);
userFocus.setUserFocus(event.currentTarget, false);
(_onFocusRef$current = onFocusRef.current) === null || _onFocusRef$current === void 0 ? void 0 : _onFocusRef$current.call(onFocusRef, event);
if (event.defaultPrevented) return;
if (isPortalEvent.isPortalEvent(event)) return;
if (!id) return;
if (targetIsAnotherItem(event, options.items)) return;
(_options$setCurrentId = options.setCurrentId) === null || _options$setCurrentId === void 0 ? void 0 : _options$setCurrentId.call(options, id); // When using aria-activedescendant, we want to make sure that the
// composite container receives focus, not the composite item.
// But we don't want to do this if the target is another focusable
// element inside the composite item, such as CompositeItemWidget.
if (shouldFocusComposite && options.unstable_virtual && options.baseId && isSelfTarget.isSelfTarget(event)) {
var target = event.target;
var composite = getDocument.getDocument(target).getElementById(options.baseId);
if (composite) {
hasFocusedComposite.current = true;
ensureFocus.ensureFocus(composite);
}
}
}, [id, options.items, options.setCurrentId, options.unstable_virtual, options.baseId]);
var onBlurCapture = React.useCallback(function (event) {
var _onBlurCaptureRef$cur;
(_onBlurCaptureRef$cur = onBlurCaptureRef.current) === null || _onBlurCaptureRef$cur === void 0 ? void 0 : _onBlurCaptureRef$cur.call(onBlurCaptureRef, event);
if (event.defaultPrevented) return;
if (options.unstable_virtual && hasFocusedComposite.current) {
// When hasFocusedComposite is true, composite has been focused right
// after focusing this item. This is an intermediate blur event, so
// we ignore it.
hasFocusedComposite.current = false;
event.preventDefault();
event.stopPropagation();
}
}, [options.unstable_virtual]);
var onKeyDown = React.useCallback(function (event) {
var _onKeyDownRef$current;
if (!isSelfTarget.isSelfTarget(event)) return;
var isVertical = options.orientation !== "horizontal";
var isHorizontal = options.orientation !== "vertical";
var isGrid = !!(item !== null && item !== void 0 && item.groupId);
var keyMap = {
ArrowUp: (isGrid || isVertical) && options.up,
ArrowRight: (isGrid || isHorizontal) && options.next,
ArrowDown: (isGrid || isVertical) && options.down,
ArrowLeft: (isGrid || isHorizontal) && options.previous,
Home: function Home() {
if (!isGrid || event.ctrlKey) {
var _options$first;
(_options$first = options.first) === null || _options$first === void 0 ? void 0 : _options$first.call(options);
} else {
var _options$previous;
(_options$previous = options.previous) === null || _options$previous === void 0 ? void 0 : _options$previous.call(options, true);
}
},
End: function End() {
if (!isGrid || event.ctrlKey) {
var _options$last;
(_options$last = options.last) === null || _options$last === void 0 ? void 0 : _options$last.call(options);
} else {
var _options$next;
(_options$next = options.next) === null || _options$next === void 0 ? void 0 : _options$next.call(options, true);
}
},
PageUp: function PageUp() {
if (isGrid) {
var _options$up;
(_options$up = options.up) === null || _options$up === void 0 ? void 0 : _options$up.call(options, true);
} else {
var _options$first2;
(_options$first2 = options.first) === null || _options$first2 === void 0 ? void 0 : _options$first2.call(options);
}
},
PageDown: function PageDown() {
if (isGrid) {
var _options$down;
(_options$down = options.down) === null || _options$down === void 0 ? void 0 : _options$down.call(options, true);
} else {
var _options$last2;
(_options$last2 = options.last) === null || _options$last2 === void 0 ? void 0 : _options$last2.call(options);
}
}
};
var action = keyMap[event.key];
if (action) {
event.preventDefault();
action();
return;
}
(_onKeyDownRef$current = onKeyDownRef.current) === null || _onKeyDownRef$current === void 0 ? void 0 : _onKeyDownRef$current.call(onKeyDownRef, event);
if (event.defaultPrevented) return;
if (event.key.length === 1 && event.key !== " ") {
var widget = getWidget(event.currentTarget);
if (widget && isTextField.isTextField(widget)) {
widget.focus();
setTextFieldValue.setTextFieldValue(widget, "");
}
} else if (event.key === "Delete" || event.key === "Backspace") {
var _widget = getWidget(event.currentTarget);
if (_widget && isTextField.isTextField(_widget)) {
event.preventDefault();
setTextFieldValue.setTextFieldValue(_widget, "");
}
}
}, [options.orientation, item, options.up, options.next, options.down, options.previous, options.first, options.last]);
var onClick = React.useCallback(function (event) {
var _onClickRef$current;
(_onClickRef$current = onClickRef.current) === null || _onClickRef$current === void 0 ? void 0 : _onClickRef$current.call(onClickRef, event);
if (event.defaultPrevented) return;
var element = event.currentTarget;
var widget = getWidget(element);
if (widget && !hasFocusWithin.hasFocusWithin(widget)) {
// If there's a widget inside the composite item, we make sure it's
// focused when pressing enter, space or clicking on the composite item.
widget.focus();
}
}, []);
return _rollupPluginBabelHelpers._objectSpread2({
ref: useForkRef.useForkRef(ref, htmlRef),
id: id,
tabIndex: shouldTabIndex ? htmlTabIndex : -1,
"aria-selected": options.unstable_virtual && isCurrentItem ? true : undefined,
onMouseDown: onMouseDown,
onFocus: onFocus,
onBlurCapture: onBlurCapture,
onKeyDown: onKeyDown,
onClick: onClick
}, htmlProps);
}
});
var CompositeItem = createComponent.createComponent({
as: "button",
memo: true,
useHook: useCompositeItem
});
exports.CompositeItem = CompositeItem;
exports.useCompositeItem = useCompositeItem;