UNPKG

@wordpress/block-editor

Version:
278 lines (240 loc) 10 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.PrivateListView = exports.BLOCK_LIST_ITEM_HEIGHT = void 0; var _element = require("@wordpress/element"); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _compose = require("@wordpress/compose"); var _components = require("@wordpress/components"); var _data = require("@wordpress/data"); var _deprecated = _interopRequireDefault(require("@wordpress/deprecated")); var _i18n = require("@wordpress/i18n"); var _branch = _interopRequireDefault(require("./branch")); var _context = require("./context"); var _dropIndicator = _interopRequireDefault(require("./drop-indicator")); var _useBlockSelection = _interopRequireDefault(require("./use-block-selection")); var _useListViewClientIds = _interopRequireDefault(require("./use-list-view-client-ids")); var _useListViewDropZone = _interopRequireDefault(require("./use-list-view-drop-zone")); var _useListViewExpandSelectedItem = _interopRequireDefault(require("./use-list-view-expand-selected-item")); var _store = require("../../store"); var _blockSettingsDropdown = require("../block-settings-menu/block-settings-dropdown"); /** * WordPress dependencies */ /** * Internal dependencies */ const expanded = (state, action) => { if (Array.isArray(action.clientIds)) { return { ...state, ...action.clientIds.reduce((newState, id) => ({ ...newState, [id]: action.type === 'expand' }), {}) }; } return state; }; const BLOCK_LIST_ITEM_HEIGHT = 36; /** @typedef {import('react').ComponentType} ComponentType */ /** @typedef {import('react').Ref<HTMLElement>} Ref */ /** * Show a hierarchical list of blocks. * * @param {Object} props Components props. * @param {string} props.id An HTML element id for the root element of ListView. * @param {Array} props.blocks _deprecated_ Custom subset of block client IDs to be used instead of the default hierarchy. * @param {?HTMLElement} props.dropZoneElement Optional element to be used as the drop zone. * @param {?boolean} props.showBlockMovers Flag to enable block movers. Defaults to `false`. * @param {?boolean} props.isExpanded Flag to determine whether nested levels are expanded by default. Defaults to `false`. * @param {?boolean} props.showAppender Flag to show or hide the block appender. Defaults to `false`. * @param {?ComponentType} props.blockSettingsMenu Optional more menu substitution. Defaults to the standard `BlockSettingsDropdown` component. * @param {string} props.rootClientId The client id of the root block from which we determine the blocks to show in the list. * @param {string} props.description Optional accessible description for the tree grid component. * @param {?Function} props.onSelect Optional callback to be invoked when a block is selected. Receives the block object that was selected. * @param {?ComponentType} props.additionalBlockContent Component that renders additional block content UI. * @param {Ref} ref Forwarded ref */ exports.BLOCK_LIST_ITEM_HEIGHT = BLOCK_LIST_ITEM_HEIGHT; function ListViewComponent({ id, blocks, dropZoneElement, showBlockMovers = false, isExpanded = false, showAppender = false, blockSettingsMenu: BlockSettingsMenu = _blockSettingsDropdown.BlockSettingsDropdown, rootClientId, description, onSelect, additionalBlockContent: AdditionalBlockContent }, ref) { // This can be removed once we no longer need to support the blocks prop. if (blocks) { (0, _deprecated.default)('`blocks` property in `wp.blockEditor.__experimentalListView`', { since: '6.3', alternative: '`rootClientId` property' }); } const instanceId = (0, _compose.useInstanceId)(ListViewComponent); const { clientIdsTree, draggedClientIds, selectedClientIds } = (0, _useListViewClientIds.default)({ blocks, rootClientId }); const { getBlock } = (0, _data.useSelect)(_store.store); const { visibleBlockCount, shouldShowInnerBlocks } = (0, _data.useSelect)(select => { const { getGlobalBlockCount, getClientIdsOfDescendants, __unstableGetEditorMode } = select(_store.store); const draggedBlockCount = draggedClientIds?.length > 0 ? getClientIdsOfDescendants(draggedClientIds).length + 1 : 0; return { visibleBlockCount: getGlobalBlockCount() - draggedBlockCount, shouldShowInnerBlocks: __unstableGetEditorMode() !== 'zoom-out' }; }, [draggedClientIds]); const { updateBlockSelection } = (0, _useBlockSelection.default)(); const [expandedState, setExpandedState] = (0, _element.useReducer)(expanded, {}); const { ref: dropZoneRef, target: blockDropTarget } = (0, _useListViewDropZone.default)({ dropZoneElement }); const elementRef = (0, _element.useRef)(); const treeGridRef = (0, _compose.useMergeRefs)([elementRef, dropZoneRef, ref]); const isMounted = (0, _element.useRef)(false); const [insertedBlock, setInsertedBlock] = (0, _element.useState)(null); const { setSelectedTreeId } = (0, _useListViewExpandSelectedItem.default)({ firstSelectedBlockClientId: selectedClientIds[0], setExpandedState }); const selectEditorBlock = (0, _element.useCallback)( /** * @param {MouseEvent | KeyboardEvent | undefined} event * @param {string} blockClientId * @param {null | undefined | -1 | 1} focusPosition */ (event, blockClientId, focusPosition) => { updateBlockSelection(event, blockClientId, null, focusPosition); setSelectedTreeId(blockClientId); if (onSelect) { onSelect(getBlock(blockClientId)); } }, [setSelectedTreeId, updateBlockSelection, onSelect, getBlock]); (0, _element.useEffect)(() => { isMounted.current = true; }, []); // List View renders a fixed number of items and relies on each having a fixed item height of 36px. // If this value changes, we should also change the itemHeight value set in useFixedWindowList. // See: https://github.com/WordPress/gutenberg/pull/35230 for additional context. const [fixedListWindow] = (0, _compose.__experimentalUseFixedWindowList)(elementRef, BLOCK_LIST_ITEM_HEIGHT, visibleBlockCount, { useWindowing: true, windowOverscan: 40 }); const expand = (0, _element.useCallback)(clientId => { if (!clientId) { return; } setExpandedState({ type: 'expand', clientIds: [clientId] }); }, [setExpandedState]); const collapse = (0, _element.useCallback)(clientId => { if (!clientId) { return; } setExpandedState({ type: 'collapse', clientIds: [clientId] }); }, [setExpandedState]); const expandRow = (0, _element.useCallback)(row => { expand(row?.dataset?.block); }, [expand]); const collapseRow = (0, _element.useCallback)(row => { collapse(row?.dataset?.block); }, [collapse]); const focusRow = (0, _element.useCallback)((event, startRow, endRow) => { if (event.shiftKey) { updateBlockSelection(event, startRow?.dataset?.block, endRow?.dataset?.block); } }, [updateBlockSelection]); const contextValue = (0, _element.useMemo)(() => ({ isTreeGridMounted: isMounted.current, draggedClientIds, expandedState, expand, collapse, BlockSettingsMenu, listViewInstanceId: instanceId, AdditionalBlockContent, insertedBlock, setInsertedBlock, treeGridElementRef: elementRef }), [draggedClientIds, expandedState, expand, collapse, BlockSettingsMenu, instanceId, AdditionalBlockContent, insertedBlock, setInsertedBlock]); // If there are no blocks to show and we're not showing the appender, do not render the list view. if (!clientIdsTree.length && !showAppender) { return null; } return (0, _element.createElement)(_data.AsyncModeProvider, { value: true }, (0, _element.createElement)(_dropIndicator.default, { listViewRef: elementRef, blockDropTarget: blockDropTarget }), (0, _element.createElement)(_components.__experimentalTreeGrid, { id: id, className: "block-editor-list-view-tree", "aria-label": (0, _i18n.__)('Block navigation structure'), ref: treeGridRef, onCollapseRow: collapseRow, onExpandRow: expandRow, onFocusRow: focusRow, applicationAriaLabel: (0, _i18n.__)('Block navigation structure') // eslint-disable-next-line jsx-a11y/aria-props , "aria-description": description }, (0, _element.createElement)(_context.ListViewContext.Provider, { value: contextValue }, (0, _element.createElement)(_branch.default, { blocks: clientIdsTree, parentId: rootClientId, selectBlock: selectEditorBlock, showBlockMovers: showBlockMovers, fixedListWindow: fixedListWindow, selectedClientIds: selectedClientIds, isExpanded: isExpanded, shouldShowInnerBlocks: shouldShowInnerBlocks, showAppender: showAppender })))); } // This is the private API for the ListView component. // It allows access to all props, not just the public ones. const PrivateListView = (0, _element.forwardRef)(ListViewComponent); // This is the public API for the ListView component. // We wrap the PrivateListView component to hide some props from the public API. exports.PrivateListView = PrivateListView; var _default = (0, _element.forwardRef)((props, ref) => { return (0, _element.createElement)(PrivateListView, (0, _extends2.default)({ ref: ref }, props, { showAppender: false, rootClientId: null, onSelect: null, additionalBlockContent: null, blockSettingsMenu: undefined })); }); exports.default = _default; //# sourceMappingURL=index.js.map