@wordpress/block-editor
Version:
278 lines (240 loc) • 10 kB
JavaScript
;
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