Skip to content

Commit

Permalink
List View: experiment with only rendering a fixed number of items
Browse files Browse the repository at this point in the history
  • Loading branch information
gwwar committed Oct 22, 2021
1 parent 30689b8 commit 0dbac74
Show file tree
Hide file tree
Showing 9 changed files with 397 additions and 59 deletions.
18 changes: 17 additions & 1 deletion bin/plugin/commands/performance.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const config = require( '../config' );
* @property {number[]} inserterOpen Average time to open global inserter.
* @property {number[]} inserterSearch Average time to search the inserter.
* @property {number[]} inserterHover Average time to move mouse between two block item in the inserter.
* @property {number[]} listViewOpen Average time to open listView
*/

/**
Expand All @@ -52,7 +53,7 @@ const config = require( '../config' );
* @property {number=} firstContentfulPaint Represents the time when the browser first renders any text or media.
* @property {number=} firstBlock Represents the time when Puppeteer first sees a block selector in the DOM.
* @property {number=} type Average type time.
* @property {number=} minType Minium type time.
* @property {number=} minType Minimum type time.
* @property {number=} maxType Maximum type time.
* @property {number=} focus Average block selection time.
* @property {number=} minFocus Min block selection time.
Expand All @@ -66,6 +67,9 @@ const config = require( '../config' );
* @property {number=} inserterHover Average time to move mouse between two block item in the inserter.
* @property {number=} minInserterHover Min time to move mouse between two block item in the inserter.
* @property {number=} maxInserterHover Max time to move mouse between two block item in the inserter.
* @property {number=} listViewOpen Average time to open list view.
* @property {number=} minListViewOpen Min time to open list view.
* @property {number=} maxListViewOpen Max time to open list view.
*/

/**
Expand Down Expand Up @@ -136,6 +140,9 @@ function curateResults( results ) {
inserterHover: average( results.inserterHover ),
minInserterHover: Math.min( ...results.inserterHover ),
maxInserterHover: Math.max( ...results.inserterHover ),
listViewOpen: average( results.listViewOpen ),
minListViewOpen: Math.min( ...results.listViewOpen ),
maxListViewOpen: Math.max( ...results.listViewOpen ),
};
}

Expand Down Expand Up @@ -378,6 +385,15 @@ async function runPerformanceTests( branches, options ) {
maxInserterHover: rawResults.map(
( r ) => r[ branch ].maxInserterHover
),
listViewOpen: rawResults.map(
( r ) => r[ branch ].listViewOpen
),
minListViewOpen: rawResults.map(
( r ) => r[ branch ].minListViewOpen
),
maxListViewOpen: rawResults.map(
( r ) => r[ branch ].maxListViewOpen
),
},
median
);
Expand Down
2 changes: 2 additions & 0 deletions packages/block-editor/src/components/list-view/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export default function ListViewBlock( {
showBlockMovers,
path,
isExpanded,
style,
} ) {
const cellRef = useRef( null );
const [ isHovered, setIsHovered ] = useState( false );
Expand Down Expand Up @@ -184,6 +185,7 @@ export default function ListViewBlock( {
className="block-editor-list-view-block__contents-cell"
colSpan={ colSpan }
ref={ cellRef }
style={ style }
>
{ ( { ref, tabIndex, onFocus } ) => (
<div className="block-editor-list-view-block__contents-container">
Expand Down
181 changes: 130 additions & 51 deletions packages/block-editor/src/components/list-view/branch.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/**
* External dependencies
*/
import { map, compact } from 'lodash';
import { compact } from 'lodash';

/**
* WordPress dependencies
Expand All @@ -14,6 +14,38 @@ import { Fragment } from '@wordpress/element';
import ListViewBlock from './block';
import { useListViewContext } from './context';

function countBlocks( block, expandedState, draggedClientIds ) {
const isDragged = draggedClientIds?.includes( block.clientId );
if ( isDragged ) {
return 0;
}
const isExpanded = expandedState[ block.clientId ] ?? true;
if ( isExpanded ) {
return (
1 +
block.innerBlocks.reduce(
countReducer( expandedState, draggedClientIds ),
0
)
);
}
return 1;
}
const countReducer = ( expandedState, draggedClientIds ) => (
count,
block
) => {
const isDragged = draggedClientIds?.includes( block.clientId );
if ( isDragged ) {
return count;
}
const isExpanded = expandedState[ block.clientId ] ?? true;
if ( isExpanded && block.innerBlocks.length > 0 ) {
return count + countBlocks( block, expandedState, draggedClientIds );
}
return count + 1;
};

export default function ListViewBranch( props ) {
const {
blocks,
Expand All @@ -22,62 +54,109 @@ export default function ListViewBranch( props ) {
showNestedBlocks,
level = 1,
path = '',
listPosition = 0,
fixedListWindow,
} = props;

const { expandedState, draggedClientIds } = useListViewContext();
const {
expandedState,
draggedClientIds,
__experimentalPersistentListViewFeatures,
} = useListViewContext();

const filteredBlocks = compact( blocks );
const blockCount = filteredBlocks.length;
let nextPosition = listPosition;

const listItems = [];
for ( let index = 0; index < filteredBlocks.length; index++ ) {
const block = filteredBlocks[ index ];
const { clientId, innerBlocks } = block;

if ( index > 0 ) {
nextPosition += countBlocks(
filteredBlocks[ index - 1 ],
expandedState,
draggedClientIds
);
}

const usesWindowing = __experimentalPersistentListViewFeatures;
const {
start,
end,
itemInView,
startPadding,
endPadding,
} = fixedListWindow;

const blockInView = ! usesWindowing || itemInView( nextPosition );

const isDragging = draggedClientIds?.length > 0;
if (
usesWindowing &&
! isDragging &&
! blockInView &&
nextPosition > start
) {
// found the end of the window, don't bother processing the rest of the items
break;
}
const style = usesWindowing
? {
paddingTop: start === nextPosition ? startPadding : 0,
paddingBottom: end === nextPosition ? endPadding : 0,
}
: undefined;

const position = index + 1;
const updatedPath =
path.length > 0 ? `${ path }_${ position }` : `${ position }`;
const hasNestedBlocks =
showNestedBlocks && !! innerBlocks && !! innerBlocks.length;

const isExpanded = hasNestedBlocks
? expandedState[ clientId ] ?? true
: undefined;

// Make updates to the selected or dragged blocks synchronous,
// but asynchronous for any other block.
const isDragged = !! draggedClientIds?.includes( clientId );

return (
<>
{ map( filteredBlocks, ( block, index ) => {
const { clientId, innerBlocks } = block;
const position = index + 1;
// This string value is used to trigger an animation change.
// This may be removed if we use a different animation library in the future.
const updatedPath =
path.length > 0
? `${ path }_${ position }`
: `${ position }`;
const hasNestedBlocks =
showNestedBlocks && !! innerBlocks && !! innerBlocks.length;

const isExpanded = hasNestedBlocks
? expandedState[ clientId ] ?? true
: undefined;

const isDragged = !! draggedClientIds?.includes( clientId );

return (
<Fragment key={ clientId }>
<ListViewBlock
block={ block }
selectBlock={ selectBlock }
isDragged={ isDragged }
level={ level }
position={ position }
rowCount={ blockCount }
siblingBlockCount={ blockCount }
showBlockMovers={ showBlockMovers }
path={ updatedPath }
isExpanded={ isExpanded }
/>
{ hasNestedBlocks && isExpanded && ! isDragged && (
<ListViewBranch
blocks={ innerBlocks }
selectBlock={ selectBlock }
showBlockMovers={ showBlockMovers }
showNestedBlocks={ showNestedBlocks }
level={ level + 1 }
path={ updatedPath }
/>
) }
</Fragment>
);
} ) }
</>
);
listItems.push(
<Fragment key={ clientId }>
{ ( isDragged || blockInView ) && (
<ListViewBlock
block={ block }
selectBlock={ selectBlock }
isDragged={ isDragged }
level={ level }
position={ position }
rowCount={ blockCount }
siblingBlockCount={ blockCount }
showBlockMovers={ showBlockMovers }
path={ updatedPath }
isExpanded={ isExpanded }
listPosition={ nextPosition }
style={ style }
/>
) }
{ hasNestedBlocks && isExpanded && ! isDragged && (
<ListViewBranch
blocks={ innerBlocks }
selectBlock={ selectBlock }
showBlockMovers={ showBlockMovers }
showNestedBlocks={ showNestedBlocks }
level={ level + 1 }
path={ updatedPath }
listPosition={ nextPosition + 1 }
fixedListWindow={ fixedListWindow }
/>
) }
</Fragment>
);
}
return <>{ listItems }</>;
}

ListViewBranch.defaultProps = {
Expand Down
39 changes: 36 additions & 3 deletions packages/block-editor/src/components/list-view/index.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
/**
* WordPress dependencies
*/

import { useMergeRefs } from '@wordpress/compose';
import {
useMergeRefs,
__experimentalUseFixedWindowList as useFixedWindowList,
} from '@wordpress/compose';
import { __experimentalTreeGrid as TreeGrid } from '@wordpress/components';
import { AsyncModeProvider, useDispatch } from '@wordpress/data';
import { AsyncModeProvider, useDispatch, useSelect } from '@wordpress/data';
import {
useCallback,
useEffect,
Expand Down Expand Up @@ -67,6 +69,21 @@ function ListView(
) {
const { clientIdsTree, draggedClientIds } = useListViewClientIds( blocks );
const { selectBlock } = useDispatch( 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 selectEditorBlock = useCallback(
( clientId ) => {
selectBlock( clientId );
Expand All @@ -85,6 +102,16 @@ function ListView(
isMounted.current = true;
}, [] );

const [ fixedListWindow ] = useFixedWindowList(
elementRef,
36,
visibleBlockCount,
{
windowOverscan: 1,
useWindowing: __experimentalPersistentListViewFeatures,
}
);

const expand = useCallback(
( clientId ) => {
if ( ! clientId ) {
Expand Down Expand Up @@ -151,13 +178,19 @@ function ListView(
ref={ treeGridRef }
onCollapseRow={ collapseRow }
onExpandRow={ expandRow }
aria-rowcount={
__experimentalPersistentListViewFeatures
? visibleBlockCount
: undefined
}
>
<ListViewContext.Provider value={ contextValue }>
<ListViewBranch
blocks={ clientIdsTree }
selectBlock={ selectEditorBlock }
showNestedBlocks={ showNestedBlocks }
showBlockMovers={ showBlockMovers }
fixedListWindow={ fixedListWindow }
{ ...props }
/>
</ListViewContext.Provider>
Expand Down
Loading

0 comments on commit 0dbac74

Please sign in to comment.