UNPKG

@wordpress/block-editor

Version:
325 lines (324 loc) 10.1 kB
// packages/block-editor/src/components/list-view/index.js import clsx from "clsx"; import { useInstanceId, useMergeRefs, __experimentalUseFixedWindowList as useFixedWindowList } from "@wordpress/compose"; import { __experimentalTreeGrid as TreeGrid, VisuallyHidden } from "@wordpress/components"; import { AsyncModeProvider, useSelect } from "@wordpress/data"; import deprecated from "@wordpress/deprecated"; import { useCallback, useEffect, useMemo, useRef, useReducer, forwardRef, useState } from "@wordpress/element"; import { __ } from "@wordpress/i18n"; import ListViewBranch from "./branch"; import { ListViewContext } from "./context"; import ListViewDropIndicatorPreview from "./drop-indicator"; import useBlockSelection from "./use-block-selection"; import useListViewBlockIndexes from "./use-list-view-block-indexes"; import useListViewClientIds from "./use-list-view-client-ids"; import useListViewCollapseItems from "./use-list-view-collapse-items"; import useListViewDropZone from "./use-list-view-drop-zone"; import useListViewExpandSelectedItem from "./use-list-view-expand-selected-item"; import { store as blockEditorStore } from "../../store"; import { BlockSettingsDropdown } from "../block-settings-menu/block-settings-dropdown"; import { focusListItem } from "./utils"; import useClipboardHandler from "./use-clipboard-handler"; import { jsx, jsxs } from "react/jsx-runtime"; var expanded = (state, action) => { if (action.type === "clear") { return {}; } if (Array.isArray(action.clientIds)) { return { ...state, ...action.clientIds.reduce( (newState, id) => ({ ...newState, [id]: action.type === "expand" }), {} ) }; } return state; }; var BLOCK_LIST_ITEM_HEIGHT = 32; function ListViewComponent({ id, blocks, dropZoneElement, showBlockMovers = false, isExpanded = false, showAppender = false, blockSettingsMenu: BlockSettingsMenu = BlockSettingsDropdown, rootClientId, description, onSelect, additionalBlockContent: AdditionalBlockContent }, ref) { if (blocks) { deprecated( "`blocks` property in `wp.blockEditor.__experimentalListView`", { since: "6.3", alternative: "`rootClientId` property" } ); } const instanceId = useInstanceId(ListViewComponent); const { clientIdsTree, draggedClientIds, selectedClientIds } = useListViewClientIds({ blocks, rootClientId }); const blockIndexes = useListViewBlockIndexes(clientIdsTree); const { getBlock } = useSelect(blockEditorStore); const { visibleBlockCount } = useSelect( (select) => { const { getGlobalBlockCount, getClientIdsOfDescendants } = select(blockEditorStore); const draggedBlockCount = draggedClientIds?.length > 0 ? getClientIdsOfDescendants(draggedClientIds).length + 1 : 0; return { visibleBlockCount: getGlobalBlockCount() - draggedBlockCount }; }, [draggedClientIds] ); const { updateBlockSelection } = useBlockSelection(); const [expandedState, setExpandedState] = useReducer(expanded, {}); const [insertedBlock, setInsertedBlock] = useState(null); const { setSelectedTreeId } = useListViewExpandSelectedItem({ firstSelectedBlockClientId: selectedClientIds[0], setExpandedState }); const selectEditorBlock = 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] ); const { ref: dropZoneRef, target: blockDropTarget } = useListViewDropZone({ dropZoneElement, expandedState, setExpandedState }); const elementRef = useRef(); const clipBoardRef = useClipboardHandler({ selectBlock: selectEditorBlock }); const treeGridRef = useMergeRefs([ clipBoardRef, elementRef, dropZoneRef, ref ]); useEffect(() => { if (selectedClientIds?.length) { focusListItem(selectedClientIds[0], elementRef?.current); } }, []); const expand = useCallback( (clientId) => { if (!clientId) { return; } const clientIds = Array.isArray(clientId) ? clientId : [clientId]; setExpandedState({ type: "expand", clientIds }); }, [setExpandedState] ); const collapse = useCallback( (clientId) => { if (!clientId) { return; } setExpandedState({ type: "collapse", clientIds: [clientId] }); }, [setExpandedState] ); const collapseAll = useCallback(() => { setExpandedState({ type: "clear" }); }, [setExpandedState]); const expandRow = useCallback( (row) => { expand(row?.dataset?.block); }, [expand] ); const collapseRow = useCallback( (row) => { collapse(row?.dataset?.block); }, [collapse] ); const focusRow = useCallback( (event, startRow, endRow) => { if (event.shiftKey) { updateBlockSelection( event, startRow?.dataset?.block, endRow?.dataset?.block ); } }, [updateBlockSelection] ); useListViewCollapseItems({ collapseAll, expand }); const firstDraggedBlockClientId = draggedClientIds?.[0]; const { blockDropTargetIndex, blockDropPosition, firstDraggedBlockIndex } = useMemo(() => { let _blockDropTargetIndex, _firstDraggedBlockIndex; if (blockDropTarget?.clientId) { const foundBlockIndex = blockIndexes[blockDropTarget.clientId]; _blockDropTargetIndex = foundBlockIndex === void 0 || blockDropTarget?.dropPosition === "top" ? foundBlockIndex : foundBlockIndex + 1; } else if (blockDropTarget === null) { _blockDropTargetIndex = null; } if (firstDraggedBlockClientId) { const foundBlockIndex = blockIndexes[firstDraggedBlockClientId]; _firstDraggedBlockIndex = foundBlockIndex === void 0 || blockDropTarget?.dropPosition === "top" ? foundBlockIndex : foundBlockIndex + 1; } return { blockDropTargetIndex: _blockDropTargetIndex, blockDropPosition: blockDropTarget?.dropPosition, firstDraggedBlockIndex: _firstDraggedBlockIndex }; }, [blockDropTarget, blockIndexes, firstDraggedBlockClientId]); const contextValue = useMemo( () => ({ blockDropPosition, blockDropTargetIndex, blockIndexes, draggedClientIds, expandedState, expand, firstDraggedBlockIndex, collapse, collapseAll, BlockSettingsMenu, listViewInstanceId: instanceId, AdditionalBlockContent, insertedBlock, setInsertedBlock, treeGridElementRef: elementRef, rootClientId }), [ blockDropPosition, blockDropTargetIndex, blockIndexes, draggedClientIds, expandedState, expand, firstDraggedBlockIndex, collapse, collapseAll, BlockSettingsMenu, instanceId, AdditionalBlockContent, insertedBlock, setInsertedBlock, rootClientId ] ); const [fixedListWindow] = useFixedWindowList( elementRef, BLOCK_LIST_ITEM_HEIGHT, visibleBlockCount, { // Ensure that the windowing logic is recalculated when the expanded state changes. // This is necessary because expanding a collapsed block in a short list view can // switch the list view to a tall list view with a scrollbar, and vice versa. // When this happens, the windowing logic needs to be recalculated to ensure that // the correct number of blocks are rendered, by rechecking for a scroll container. expandedState, useWindowing: true, windowOverscan: 40 } ); if (!clientIdsTree.length && !showAppender) { return null; } const describedById = description && `block-editor-list-view-description-${instanceId}`; return /* @__PURE__ */ jsxs(AsyncModeProvider, { value: true, children: [ /* @__PURE__ */ jsx( ListViewDropIndicatorPreview, { draggedBlockClientId: firstDraggedBlockClientId, listViewRef: elementRef, blockDropTarget } ), description && /* @__PURE__ */ jsx(VisuallyHidden, { id: describedById, children: description }), /* @__PURE__ */ jsx( TreeGrid, { id, className: clsx("block-editor-list-view-tree", { "is-dragging": draggedClientIds?.length > 0 && blockDropTargetIndex !== void 0 }), "aria-label": __("Block navigation structure"), ref: treeGridRef, onCollapseRow: collapseRow, onExpandRow: expandRow, onFocusRow: focusRow, applicationAriaLabel: __("Block navigation structure"), "aria-describedby": describedById, style: { "--wp-admin--list-view-dragged-items-height": draggedClientIds?.length ? `${BLOCK_LIST_ITEM_HEIGHT * (draggedClientIds.length - 1)}px` : null }, children: /* @__PURE__ */ jsx(ListViewContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsx( ListViewBranch, { blocks: clientIdsTree, parentId: rootClientId, selectBlock: selectEditorBlock, showBlockMovers, fixedListWindow, selectedClientIds, isExpanded, showAppender } ) }) } ) ] }); } var PrivateListView = forwardRef(ListViewComponent); var list_view_default = forwardRef((props, ref) => { return /* @__PURE__ */ jsx( PrivateListView, { ref, ...props, showAppender: false, rootClientId: null, onSelect: null, additionalBlockContent: null, blockSettingsMenu: void 0 } ); }); export { BLOCK_LIST_ITEM_HEIGHT, PrivateListView, list_view_default as default }; //# sourceMappingURL=index.js.map