UNPKG

@wordpress/block-editor

Version:
249 lines (222 loc) 8.45 kB
import _extends from "@babel/runtime/helpers/esm/extends"; import { createElement } from "@wordpress/element"; /** * External dependencies */ import classnames from 'classnames'; /** * WordPress dependencies */ import { dragHandle } from '@wordpress/icons'; import { Button, Flex, FlexItem } from '@wordpress/components'; import { useSelect, useDispatch } from '@wordpress/data'; import { useEffect, useRef } from '@wordpress/element'; import { BACKSPACE, DELETE, UP, DOWN, LEFT, RIGHT, TAB, ESCAPE, ENTER, SPACE } from '@wordpress/keycodes'; import { getBlockType, __experimentalGetAccessibleBlockLabel as getAccessibleBlockLabel } from '@wordpress/blocks'; import { speak } from '@wordpress/a11y'; import { focus } from '@wordpress/dom'; import { __ } from '@wordpress/i18n'; /** * Internal dependencies */ import BlockTitle from '../block-title'; import BlockIcon from '../block-icon'; import { store as blockEditorStore } from '../../store'; import BlockDraggable from '../block-draggable'; import useBlockDisplayInformation from '../use-block-display-information'; import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs'; import BlockMover from '../block-mover'; /** * Block selection button component, displaying the label of the block. If the block * descends from a root block, a button is displayed enabling the user to select * the root block. * * @param {string} props Component props. * @param {string} props.clientId Client ID of block. * * @return {WPComponent} The component to be rendered. */ function BlockSelectionButton({ clientId, rootClientId }) { const blockInformation = useBlockDisplayInformation(clientId); const selected = useSelect(select => { const { getBlock, getBlockIndex, hasBlockMovingClientId, getBlockListSettings, __unstableGetEditorMode } = select(blockEditorStore); const index = getBlockIndex(clientId); const { name, attributes } = getBlock(clientId); const blockMovingMode = hasBlockMovingClientId(); return { index, name, attributes, blockMovingMode, orientation: getBlockListSettings(rootClientId)?.orientation, editorMode: __unstableGetEditorMode() }; }, [clientId, rootClientId]); const { index, name, attributes, blockMovingMode, orientation, editorMode } = selected; const { setNavigationMode, removeBlock } = useDispatch(blockEditorStore); const ref = useRef(); const blockType = getBlockType(name); const label = getAccessibleBlockLabel(blockType, attributes, index + 1, orientation); // Focus the breadcrumb in navigation mode. useEffect(() => { ref.current.focus(); speak(label); }, [label]); const blockElement = useBlockElement(clientId); const { hasBlockMovingClientId, getBlockIndex, getBlockRootClientId, getClientIdsOfDescendants, getSelectedBlockClientId, getMultiSelectedBlocksEndClientId, getPreviousBlockClientId, getNextBlockClientId } = useSelect(blockEditorStore); const { selectBlock, clearSelectedBlock, setBlockMovingClientId, moveBlockToPosition } = useDispatch(blockEditorStore); function onKeyDown(event) { const { keyCode } = event; const isUp = keyCode === UP; const isDown = keyCode === DOWN; const isLeft = keyCode === LEFT; const isRight = keyCode === RIGHT; const isTab = keyCode === TAB; const isEscape = keyCode === ESCAPE; const isEnter = keyCode === ENTER; const isSpace = keyCode === SPACE; const isShift = event.shiftKey; if (keyCode === BACKSPACE || keyCode === DELETE) { removeBlock(clientId); event.preventDefault(); return; } const selectedBlockClientId = getSelectedBlockClientId(); const selectionEndClientId = getMultiSelectedBlocksEndClientId(); const selectionBeforeEndClientId = getPreviousBlockClientId(selectionEndClientId || selectedBlockClientId); const selectionAfterEndClientId = getNextBlockClientId(selectionEndClientId || selectedBlockClientId); const navigateUp = isTab && isShift || isUp; const navigateDown = isTab && !isShift || isDown; // Move out of current nesting level (no effect if at root level). const navigateOut = isLeft; // Move into next nesting level (no effect if the current block has no innerBlocks). const navigateIn = isRight; let focusedBlockUid; if (navigateUp) { focusedBlockUid = selectionBeforeEndClientId; } else if (navigateDown) { focusedBlockUid = selectionAfterEndClientId; } else if (navigateOut) { var _getBlockRootClientId; focusedBlockUid = (_getBlockRootClientId = getBlockRootClientId(selectedBlockClientId)) !== null && _getBlockRootClientId !== void 0 ? _getBlockRootClientId : selectedBlockClientId; } else if (navigateIn) { var _getClientIdsOfDescen; focusedBlockUid = (_getClientIdsOfDescen = getClientIdsOfDescendants([selectedBlockClientId])[0]) !== null && _getClientIdsOfDescen !== void 0 ? _getClientIdsOfDescen : selectedBlockClientId; } const startingBlockClientId = hasBlockMovingClientId(); if (isEscape && startingBlockClientId && !event.defaultPrevented) { setBlockMovingClientId(null); event.preventDefault(); } if ((isEnter || isSpace) && startingBlockClientId) { const sourceRoot = getBlockRootClientId(startingBlockClientId); const destRoot = getBlockRootClientId(selectedBlockClientId); const sourceBlockIndex = getBlockIndex(startingBlockClientId); let destinationBlockIndex = getBlockIndex(selectedBlockClientId); if (sourceBlockIndex < destinationBlockIndex && sourceRoot === destRoot) { destinationBlockIndex -= 1; } moveBlockToPosition(startingBlockClientId, sourceRoot, destRoot, destinationBlockIndex); selectBlock(startingBlockClientId); setBlockMovingClientId(null); } if (navigateDown || navigateUp || navigateOut || navigateIn) { if (focusedBlockUid) { event.preventDefault(); selectBlock(focusedBlockUid); } else if (isTab && selectedBlockClientId) { let nextTabbable; if (navigateDown) { nextTabbable = blockElement; do { nextTabbable = focus.tabbable.findNext(nextTabbable); } while (nextTabbable && blockElement.contains(nextTabbable)); if (!nextTabbable) { nextTabbable = blockElement.ownerDocument.defaultView.frameElement; nextTabbable = focus.tabbable.findNext(nextTabbable); } } else { nextTabbable = focus.tabbable.findPrevious(blockElement); } if (nextTabbable) { event.preventDefault(); nextTabbable.focus(); clearSelectedBlock(); } } } } const classNames = classnames('block-editor-block-list__block-selection-button', { 'is-block-moving-mode': !!blockMovingMode }); const dragHandleLabel = __('Drag'); return createElement("div", { className: classNames }, createElement(Flex, { justify: "center", className: "block-editor-block-list__block-selection-button__content" }, createElement(FlexItem, null, createElement(BlockIcon, { icon: blockInformation?.icon, showColors: true })), createElement(FlexItem, null, editorMode === 'zoom-out' && createElement(BlockMover, { clientIds: [clientId], hideDragHandle: true }), editorMode === 'navigation' && createElement(BlockDraggable, { clientIds: [clientId] }, draggableProps => createElement(Button, _extends({ icon: dragHandle, className: "block-selection-button_drag-handle", "aria-hidden": "true", label: dragHandleLabel // Should not be able to tab to drag handle as this // button can only be used with a pointer device. , tabIndex: "-1" }, draggableProps)))), createElement(FlexItem, null, createElement(Button, { ref: ref, onClick: editorMode === 'navigation' ? () => setNavigationMode(false) : undefined, onKeyDown: onKeyDown, label: label, showTooltip: false, className: "block-selection-button_select-button" }, createElement(BlockTitle, { clientId: clientId, maximumLength: 35 }))))); } export default BlockSelectionButton; //# sourceMappingURL=block-selection-button.js.map