|
1 | 1 | /** |
2 | 2 | * WordPress dependencies |
3 | 3 | */ |
4 | | -import { |
5 | | - useInstanceId, |
6 | | - useMergeRefs, |
7 | | - __experimentalUseFixedWindowList as useFixedWindowList, |
8 | | -} from '@wordpress/compose'; |
9 | | -import { __experimentalTreeGrid as TreeGrid } from '@wordpress/components'; |
10 | | -import { AsyncModeProvider, useSelect } from '@wordpress/data'; |
11 | | -import deprecated from '@wordpress/deprecated'; |
12 | | -import { |
13 | | - useCallback, |
14 | | - useEffect, |
15 | | - useMemo, |
16 | | - useRef, |
17 | | - useReducer, |
18 | | - forwardRef, |
19 | | -} from '@wordpress/element'; |
20 | | -import { __ } from '@wordpress/i18n'; |
| 4 | +import { forwardRef } from '@wordpress/element'; |
21 | 5 |
|
22 | 6 | /** |
23 | 7 | * Internal dependencies |
24 | 8 | */ |
25 | | -import ListViewBranch from './branch'; |
26 | | -import { ListViewContext } from './context'; |
27 | | -import ListViewDropIndicator from './drop-indicator'; |
28 | | -import useBlockSelection from './use-block-selection'; |
29 | | -import useListViewClientIds from './use-list-view-client-ids'; |
30 | | -import useListViewDropZone from './use-list-view-drop-zone'; |
31 | | -import useListViewExpandSelectedItem from './use-list-view-expand-selected-item'; |
32 | | -import { store as blockEditorStore } from '../../store'; |
33 | | -import { BlockSettingsDropdown } from '../block-settings-menu/block-settings-dropdown'; |
| 9 | +import { unlock } from '../../lock-unlock'; |
| 10 | +import { privateApis as blockEditorPrivateApis } from '../../private-apis'; |
34 | 11 |
|
35 | | -const expanded = ( state, action ) => { |
36 | | - if ( Array.isArray( action.clientIds ) ) { |
37 | | - return { |
38 | | - ...state, |
39 | | - ...action.clientIds.reduce( |
40 | | - ( newState, id ) => ( { |
41 | | - ...newState, |
42 | | - [ id ]: action.type === 'expand', |
43 | | - } ), |
44 | | - {} |
45 | | - ), |
46 | | - }; |
47 | | - } |
48 | | - return state; |
49 | | -}; |
| 12 | +export { BLOCK_LIST_ITEM_HEIGHT } from './private-list-view'; |
50 | 13 |
|
51 | | -export const BLOCK_LIST_ITEM_HEIGHT = 36; |
52 | | - |
53 | | -/** @typedef {import('react').ComponentType} ComponentType */ |
54 | | -/** @typedef {import('react').Ref<HTMLElement>} Ref */ |
55 | | - |
56 | | -/** |
57 | | - * Show a hierarchical list of blocks. |
58 | | - * |
59 | | - * @param {Object} props Components props. |
60 | | - * @param {string} props.id An HTML element id for the root element of ListView. |
61 | | - * @param {Array} props.blocks _deprecated_ Custom subset of block client IDs to be used instead of the default hierarchy. |
62 | | - * @param {?boolean} props.showBlockMovers Flag to enable block movers. Defaults to `false`. |
63 | | - * @param {?boolean} props.isExpanded Flag to determine whether nested levels are expanded by default. Defaults to `false`. |
64 | | - * @param {?boolean} props.showAppender Flag to show or hide the block appender. Defaults to `false`. |
65 | | - * @param {?ComponentType} props.blockSettingsMenu Optional more menu substitution. Defaults to the standard `BlockSettingsDropdown` component. |
66 | | - * @param {string} props.rootClientId The client id of the root block from which we determine the blocks to show in the list. |
67 | | - * @param {string} props.description Optional accessible description for the tree grid component. |
68 | | - * @param {?Function} props.onSelect Optional callback to be invoked when a block is selected. Receives the block object that was selected. |
69 | | - * @param {Function} props.renderAdditionalBlockUI Function that renders additional block content UI. |
70 | | - * @param {Ref} ref Forwarded ref |
71 | | - */ |
72 | | -function ListViewComponent( |
73 | | - { |
74 | | - id, |
75 | | - blocks, |
76 | | - showBlockMovers = false, |
77 | | - isExpanded = false, |
78 | | - showAppender = false, |
79 | | - blockSettingsMenu: BlockSettingsMenu = BlockSettingsDropdown, |
80 | | - rootClientId, |
81 | | - description, |
82 | | - onSelect, |
83 | | - renderAdditionalBlockUI, |
84 | | - }, |
85 | | - ref |
86 | | -) { |
87 | | - // This can be removed once we no longer need to support the blocks prop. |
88 | | - if ( blocks ) { |
89 | | - deprecated( |
90 | | - '`blocks` property in `wp.blockEditor.__experimentalListView`', |
91 | | - { |
92 | | - since: '6.3', |
93 | | - alternative: '`rootClientId` property', |
94 | | - } |
95 | | - ); |
96 | | - } |
97 | | - |
98 | | - const instanceId = useInstanceId( ListViewComponent ); |
99 | | - const { clientIdsTree, draggedClientIds, selectedClientIds } = |
100 | | - useListViewClientIds( { blocks, rootClientId } ); |
101 | | - |
102 | | - const { getBlock } = useSelect( blockEditorStore ); |
103 | | - const { visibleBlockCount, shouldShowInnerBlocks } = useSelect( |
104 | | - ( select ) => { |
105 | | - const { |
106 | | - getGlobalBlockCount, |
107 | | - getClientIdsOfDescendants, |
108 | | - __unstableGetEditorMode, |
109 | | - } = select( blockEditorStore ); |
110 | | - const draggedBlockCount = |
111 | | - draggedClientIds?.length > 0 |
112 | | - ? getClientIdsOfDescendants( draggedClientIds ).length + 1 |
113 | | - : 0; |
114 | | - return { |
115 | | - visibleBlockCount: getGlobalBlockCount() - draggedBlockCount, |
116 | | - shouldShowInnerBlocks: __unstableGetEditorMode() !== 'zoom-out', |
117 | | - }; |
118 | | - }, |
119 | | - [ draggedClientIds ] |
120 | | - ); |
121 | | - |
122 | | - const { updateBlockSelection } = useBlockSelection(); |
123 | | - |
124 | | - const [ expandedState, setExpandedState ] = useReducer( expanded, {} ); |
125 | | - |
126 | | - const { ref: dropZoneRef, target: blockDropTarget } = useListViewDropZone(); |
127 | | - const elementRef = useRef(); |
128 | | - const treeGridRef = useMergeRefs( [ elementRef, dropZoneRef, ref ] ); |
129 | | - |
130 | | - const isMounted = useRef( false ); |
131 | | - const { setSelectedTreeId } = useListViewExpandSelectedItem( { |
132 | | - firstSelectedBlockClientId: selectedClientIds[ 0 ], |
133 | | - setExpandedState, |
134 | | - } ); |
135 | | - const selectEditorBlock = useCallback( |
136 | | - ( event, blockClientId ) => { |
137 | | - updateBlockSelection( event, blockClientId ); |
138 | | - setSelectedTreeId( blockClientId ); |
139 | | - if ( onSelect ) { |
140 | | - onSelect( getBlock( blockClientId ) ); |
141 | | - } |
142 | | - }, |
143 | | - [ setSelectedTreeId, updateBlockSelection, onSelect, getBlock ] |
144 | | - ); |
145 | | - useEffect( () => { |
146 | | - isMounted.current = true; |
147 | | - }, [] ); |
148 | | - |
149 | | - // List View renders a fixed number of items and relies on each having a fixed item height of 36px. |
150 | | - // If this value changes, we should also change the itemHeight value set in useFixedWindowList. |
151 | | - // See: https://github.com/WordPress/gutenberg/pull/35230 for additional context. |
152 | | - const [ fixedListWindow ] = useFixedWindowList( |
153 | | - elementRef, |
154 | | - BLOCK_LIST_ITEM_HEIGHT, |
155 | | - visibleBlockCount, |
156 | | - { |
157 | | - useWindowing: true, |
158 | | - windowOverscan: 40, |
159 | | - } |
160 | | - ); |
161 | | - |
162 | | - const expand = useCallback( |
163 | | - ( clientId ) => { |
164 | | - if ( ! clientId ) { |
165 | | - return; |
166 | | - } |
167 | | - setExpandedState( { type: 'expand', clientIds: [ clientId ] } ); |
168 | | - }, |
169 | | - [ setExpandedState ] |
170 | | - ); |
171 | | - const collapse = useCallback( |
172 | | - ( clientId ) => { |
173 | | - if ( ! clientId ) { |
174 | | - return; |
175 | | - } |
176 | | - setExpandedState( { type: 'collapse', clientIds: [ clientId ] } ); |
177 | | - }, |
178 | | - [ setExpandedState ] |
179 | | - ); |
180 | | - const expandRow = useCallback( |
181 | | - ( row ) => { |
182 | | - expand( row?.dataset?.block ); |
183 | | - }, |
184 | | - [ expand ] |
185 | | - ); |
186 | | - const collapseRow = useCallback( |
187 | | - ( row ) => { |
188 | | - collapse( row?.dataset?.block ); |
189 | | - }, |
190 | | - [ collapse ] |
191 | | - ); |
192 | | - const focusRow = useCallback( |
193 | | - ( event, startRow, endRow ) => { |
194 | | - if ( event.shiftKey ) { |
195 | | - updateBlockSelection( |
196 | | - event, |
197 | | - startRow?.dataset?.block, |
198 | | - endRow?.dataset?.block |
199 | | - ); |
200 | | - } |
201 | | - }, |
202 | | - [ updateBlockSelection ] |
203 | | - ); |
204 | | - |
205 | | - const contextValue = useMemo( |
206 | | - () => ( { |
207 | | - isTreeGridMounted: isMounted.current, |
208 | | - draggedClientIds, |
209 | | - expandedState, |
210 | | - expand, |
211 | | - collapse, |
212 | | - BlockSettingsMenu, |
213 | | - listViewInstanceId: instanceId, |
214 | | - renderAdditionalBlockUI, |
215 | | - } ), |
216 | | - [ |
217 | | - draggedClientIds, |
218 | | - expandedState, |
219 | | - expand, |
220 | | - collapse, |
221 | | - BlockSettingsMenu, |
222 | | - instanceId, |
223 | | - renderAdditionalBlockUI, |
224 | | - ] |
225 | | - ); |
226 | | - |
227 | | - // If there are no blocks to show, do not render the list view. |
228 | | - if ( ! clientIdsTree.length ) { |
229 | | - return null; |
230 | | - } |
231 | | - |
232 | | - return ( |
233 | | - <AsyncModeProvider value={ true }> |
234 | | - <ListViewDropIndicator |
235 | | - listViewRef={ elementRef } |
236 | | - blockDropTarget={ blockDropTarget } |
237 | | - /> |
238 | | - <TreeGrid |
239 | | - id={ id } |
240 | | - className="block-editor-list-view-tree" |
241 | | - aria-label={ __( 'Block navigation structure' ) } |
242 | | - ref={ treeGridRef } |
243 | | - onCollapseRow={ collapseRow } |
244 | | - onExpandRow={ expandRow } |
245 | | - onFocusRow={ focusRow } |
246 | | - applicationAriaLabel={ __( 'Block navigation structure' ) } |
247 | | - // eslint-disable-next-line jsx-a11y/aria-props |
248 | | - aria-description={ description } |
249 | | - > |
250 | | - <ListViewContext.Provider value={ contextValue }> |
251 | | - <ListViewBranch |
252 | | - blocks={ clientIdsTree } |
253 | | - parentId={ rootClientId } |
254 | | - selectBlock={ selectEditorBlock } |
255 | | - showBlockMovers={ showBlockMovers } |
256 | | - fixedListWindow={ fixedListWindow } |
257 | | - selectedClientIds={ selectedClientIds } |
258 | | - isExpanded={ isExpanded } |
259 | | - shouldShowInnerBlocks={ shouldShowInnerBlocks } |
260 | | - showAppender={ showAppender } |
261 | | - /> |
262 | | - </ListViewContext.Provider> |
263 | | - </TreeGrid> |
264 | | - </AsyncModeProvider> |
265 | | - ); |
266 | | -} |
267 | | -export const PrivateListView = forwardRef( ListViewComponent ); |
| 14 | +const { PrivateListView } = unlock( blockEditorPrivateApis ); |
268 | 15 |
|
269 | 16 | export default forwardRef( ( props, ref ) => { |
270 | 17 | return ( |
271 | 18 | <PrivateListView |
272 | 19 | ref={ ref } |
273 | 20 | { ...props } |
274 | 21 | showAppender={ false } |
275 | | - blockSettingsMenu={ BlockSettingsDropdown } |
276 | 22 | rootClientId={ null } |
277 | 23 | onSelect={ null } |
278 | | - renderAdditionalBlockUICallback={ null } |
| 24 | + renderAdditionalBlockUI={ null } |
279 | 25 | /> |
280 | 26 | ); |
281 | 27 | } ); |
0 commit comments