@wordpress/block-editor
Version:
123 lines (93 loc) • 3.37 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.useFocusFirstElement = useFocusFirstElement;
var _element = require("@wordpress/element");
var _dom = require("@wordpress/dom");
var _data = require("@wordpress/data");
var _dom2 = require("../../../utils/dom");
var _store = require("../../../store");
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
/** @typedef {import('@wordpress/element').RefObject} RefObject */
/**
* Returns the initial position if the block needs to be focussed, `undefined`
* otherwise. The initial position is either 0 (start) or -1 (end).
*
* @param {string} clientId Block client ID.
*
* @return {number} The initial position, either 0 (start) or -1 (end).
*/
function useInitialPosition(clientId) {
return (0, _data.useSelect)(select => {
const {
getSelectedBlocksInitialCaretPosition,
__unstableGetEditorMode,
isBlockSelected
} = select(_store.store);
if (!isBlockSelected(clientId)) {
return;
}
if (__unstableGetEditorMode() !== 'edit') {
return;
} // If there's no initial position, return 0 to focus the start.
return getSelectedBlocksInitialCaretPosition();
}, [clientId]);
}
/**
* Transitions focus to the block or inner tabbable when the block becomes
* selected and an initial position is set.
*
* @param {string} clientId Block client ID.
*
* @return {RefObject} React ref with the block element.
*/
function useFocusFirstElement(clientId) {
const ref = (0, _element.useRef)();
const initialPosition = useInitialPosition(clientId);
const {
isBlockSelected,
isMultiSelecting
} = (0, _data.useSelect)(_store.store);
(0, _element.useEffect)(() => {
// Check if the block is still selected at the time this effect runs.
if (!isBlockSelected(clientId) || isMultiSelecting()) {
return;
}
if (initialPosition === undefined || initialPosition === null) {
return;
}
if (!ref.current) {
return;
}
const {
ownerDocument
} = ref.current; // Do not focus the block if it already contains the active element.
if ((0, _dom2.isInsideRootBlock)(ref.current, ownerDocument.activeElement)) {
return;
} // Find all tabbables within node.
const textInputs = _dom.focus.tabbable.find(ref.current).filter(node => (0, _dom.isTextField)(node)); // If reversed (e.g. merge via backspace), use the last in the set of
// tabbables.
const isReverse = -1 === initialPosition;
const target = textInputs[isReverse ? textInputs.length - 1 : 0] || ref.current;
if (!(0, _dom2.isInsideRootBlock)(ref.current, target)) {
ref.current.focus();
return;
} // Check to see if element is focussable before a generic caret insert.
if (!ref.current.getAttribute('contenteditable')) {
const focusElement = _dom.focus.tabbable.findNext(ref.current); // Make sure focusElement is valid, contained in the same block, and a form field.
if (focusElement && (0, _dom2.isInsideRootBlock)(ref.current, focusElement) && (0, _dom.isFormElement)(focusElement)) {
focusElement.focus();
return;
}
}
(0, _dom.placeCaretAtHorizontalEdge)(target, isReverse);
}, [initialPosition, clientId]);
return ref;
}
//# sourceMappingURL=use-focus-first-element.js.map