Skip to content

Commit

Permalink
Extracted scroll and expand logic to hook
Browse files Browse the repository at this point in the history
  • Loading branch information
ramonjd committed Jan 10, 2022
1 parent 620068d commit bc16cda
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 71 deletions.
81 changes: 16 additions & 65 deletions packages/block-editor/src/components/list-view/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ import {
forwardRef,
} from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { getScrollContainer } from '@wordpress/dom';

/**
* Internal dependencies
*/
import ListViewBranch, { countBlocks } from './branch';
import ListViewBranch from './branch';
import { ListViewContext } from './context';
import ListViewDropIndicator from './drop-indicator';
import useListViewClientIds from './use-list-view-client-ids';
import useListViewDropZone from './use-list-view-drop-zone';
import useListViewOpenSelectedItem from './use-list-view-open-selected-item';
import { store as blockEditorStore } from '../../store';

const noop = () => {};
Expand All @@ -45,7 +45,7 @@ const expanded = ( state, action ) => {
return state;
};

const BLOCK_LIST_ITEM_HEIGHT = 36;
export const BLOCK_LIST_ITEM_HEIGHT = 36;

/**
* Wrap `ListViewRows` with `TreeGrid`. ListViewRows is a
Expand Down Expand Up @@ -79,9 +79,7 @@ function ListView(
clientIdsTree,
draggedClientIds,
selectedClientIds,
selectedBlockParentClientIds,
} = useListViewClientIds( blocks );
const selectedTreeId = useRef( null );
const { selectBlock } = useDispatch( blockEditorStore );
const { visibleBlockCount } = useSelect(
( select ) => {
Expand All @@ -98,20 +96,26 @@ function ListView(
},
[ draggedClientIds ]
);
const [ expandedState, setExpandedState ] = useReducer( expanded, {} );
const { ref: dropZoneRef, target: blockDropTarget } = useListViewDropZone();
const elementRef = useRef();
const treeGridRef = useMergeRefs( [ elementRef, dropZoneRef, ref ] );
const isMounted = useRef( false );
const { setSelectedTreeId } = useListViewOpenSelectedItem( {
firstSelectedBlockClientId: selectedClientIds[ 0 ],
clientIdsTree,
scrollContainerElement: elementRef?.current,
expandedState,
setExpandedState,
} );
const selectEditorBlock = useCallback(
( clientId ) => {
selectBlock( clientId );
onSelect( clientId );
selectedTreeId.current = clientId;
setSelectedTreeId( clientId );
},
[ selectBlock, onSelect ]
);
const [ expandedState, setExpandedState ] = useReducer( expanded, {} );
const { ref: dropZoneRef, target: blockDropTarget } = useListViewDropZone();
const elementRef = useRef();
const treeGridRef = useMergeRefs( [ elementRef, dropZoneRef, ref ] );
const isMounted = useRef( false );

useEffect( () => {
isMounted.current = true;
}, [] );
Expand Down Expand Up @@ -182,59 +186,6 @@ function ListView(
collapse,
]
);
// @TODO create custom hooks.
useEffect( () => {
// If the selectedTreeId is the same as the selected block,
// it means that the block was selected usin the block list tree.
if ( selectedTreeId.current === selectedClientIds[ 0 ] ) {
return;
}

// If the selected block has parents, get the top-level parent.
if (
Array.isArray( selectedBlockParentClientIds ) &&
selectedBlockParentClientIds.length
) {
// If the selected block has parents,
// expand the tree branch.
setExpandedState( {
type: 'expand',
clientIds: selectedBlockParentClientIds,
} );
}

if ( Array.isArray( selectedClientIds ) && selectedClientIds.length ) {
const scrollContainer = getScrollContainer( elementRef.current );

// Grab the selected id. This is the point at which we can
// stop counting blocks in the tree.
let selectedId = selectedClientIds[ 0 ];

// If the selected block has parents, get the top-level parent.
if (
Array.isArray( selectedBlockParentClientIds ) &&
selectedBlockParentClientIds.length
) {
selectedId = selectedBlockParentClientIds[ 0 ];
}

// Count expanded blocks in the tree up until the selected block,
// so we can calculate the scroll container top.
let listItemHeightFactor = 0;
clientIdsTree.every( ( item ) => {
if ( item?.clientId === selectedId ) {
return false;
}
listItemHeightFactor += countBlocks( item, expandedState, [] );
return true;
} );

// @TODO if selected block is already visible in the list prevent scroll.
scrollContainer?.scrollTo( {
top: listItemHeightFactor * BLOCK_LIST_ITEM_HEIGHT,
} );
}
}, [ selectedClientIds[ 0 ] ] );

return (
<AsyncModeProvider value={ true }>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,12 @@ export default function useListViewClientIds( blocks ) {
getDraggedBlockClientIds,
getSelectedBlockClientIds,
__unstableGetClientIdsTree,
getBlockParents,
} = select( blockEditorStore );
const selectedBlockClientIds = getSelectedBlockClientIds();

return {
selectedClientIds: getSelectedBlockClientIds(),
draggedClientIds: getDraggedBlockClientIds(),
clientIdsTree: blocks ? blocks : __unstableGetClientIdsTree(),
selectedBlockParentClientIds: getBlockParents(
selectedBlockClientIds[ 0 ],
false
),
};
},
[ blocks ]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/**
* WordPress dependencies
*/
import { useLayoutEffect, useEffect, useState } from '@wordpress/element';
import { getScrollContainer } from '@wordpress/dom';
import { useSelect } from '@wordpress/data';

/**
* Internal dependencies
*/
import { BLOCK_LIST_ITEM_HEIGHT } from './';
import { countBlocks } from './branch';
import { store as blockEditorStore } from '../../store';

export default function useListViewOpenSelectedItem( {
firstSelectedBlockClientId,
clientIdsTree,
blockListItemHeight = BLOCK_LIST_ITEM_HEIGHT,
scrollContainerElement,
expandedState,
setExpandedState,
} ) {
const [ selectedTreeId, setSelectedTreeId ] = useState( null );
const scrollContainer = getScrollContainer( scrollContainerElement );
const { selectedBlockParentClientIds } = useSelect(
( select ) => {
const { getBlockParents } = select( blockEditorStore );
return {
selectedBlockParentClientIds: getBlockParents(
firstSelectedBlockClientId,
false
),
};
},
[ firstSelectedBlockClientId ]
);

const parentClientIds =
Array.isArray( selectedBlockParentClientIds ) &&
selectedBlockParentClientIds.length
? selectedBlockParentClientIds
: null;

// Track the expanded state of any parents.
// To calculate the number of expanded items correctly.
let parentExpandedState = null;
if ( parentClientIds ) {
parentExpandedState = expandedState[ parentClientIds[ 0 ] ];
}

useEffect( () => {
// If the selectedTreeId is the same as the selected block,
// it means that the block was selected using the block list tree.
if ( selectedTreeId === firstSelectedBlockClientId ) {
return;
}

// If the selected block has parents, get the top-level parent.
if ( parentClientIds ) {
// If the selected block has parents,
// expand the tree branch.
setExpandedState( {
type: 'expand',
clientIds: selectedBlockParentClientIds,
} );
}
}, [ firstSelectedBlockClientId ] );

useLayoutEffect( () => {
// If the selectedTreeId is the same as the selected block,
// it means that the block was selected using the block list tree.
if ( selectedTreeId === firstSelectedBlockClientId ) {
return;
}
if (
scrollContainer &&
!! firstSelectedBlockClientId &&
Array.isArray( clientIdsTree ) &&
clientIdsTree.length
) {
// Grab the selected id. This is the point at which we can
// stop counting blocks in the tree.
let selectedId = firstSelectedBlockClientId;

// If the selected block has parents, get the top-level parent.
if ( parentClientIds ) {
selectedId = parentClientIds[ 0 ];
// If the selected block has parents,
// check to see if the selected tree is expanded
// so we can accurately calculate the scroll container top value.
if ( ! parentExpandedState ) {
return;
}
}

// Count expanded blocks in the tree up until the selected block,
// so we can calculate the scroll container top value.
let listItemHeightFactor = 0;
clientIdsTree.every( ( item ) => {
if ( item?.clientId === selectedId ) {
return false;
}
listItemHeightFactor += countBlocks( item, expandedState, [] );
return true;
} );

// New scroll value is the number of expanded items
// multiplied by the item height
// plus the number of expanded children in the selected block
// multiplied by the item height.
const newScrollTopValue =
listItemHeightFactor * blockListItemHeight +
( parentClientIds ? parentClientIds.length : 1 ) *
blockListItemHeight;

const shouldScrollDown =
newScrollTopValue >
scrollContainer.scrollTop + scrollContainer.clientHeight;

const shouldScrollUp =
newScrollTopValue < scrollContainer.scrollTop;

if ( ! shouldScrollUp && ! shouldScrollDown ) {
return;
}

// @TODO This doesn't yet work when nested blocks are selected.
// We're still using the top parent block to calculate/trigger redraw.
// If selected block is already visible in the list prevent scroll.
scrollContainer?.scrollTo( {
top: newScrollTopValue,
} );
}
}, [ firstSelectedBlockClientId, parentExpandedState ] );

return {
setSelectedTreeId,
};
}

0 comments on commit bc16cda

Please sign in to comment.