Skip to content

Commit

Permalink
List View: Scroll selected block into view when single block selectio…
Browse files Browse the repository at this point in the history
…n changes (#46895)

* List View: Try scrolling selected blocks into view when single block selection changes

* Use getScrollContainer and calculate real top position of scrollable area instead of using a hard-coded value

* Try rearranging things so that the ref is always attached at the row level

* Move placeholder to its own file

* Tidy up a little

* Tidy comments

* Remove unneeded optional chaining

Co-authored-by: Kai Hao <[email protected]>

* Simplify and improve logic based on feedback

Co-authored-by: Kai Hao <[email protected]>

* Remove unneeded optional chaining

Co-authored-by: Kai Hao <[email protected]>

* Revert placeholder component, update showBlock logic so that selected blocks are rendered as real ListViewBlock components

---------

Co-authored-by: Kai Hao <[email protected]>
  • Loading branch information
2 people authored and ntsekouras committed Feb 20, 2023
1 parent 8a24a11 commit 7951add
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 31 deletions.
12 changes: 12 additions & 0 deletions packages/block-editor/src/components/list-view/block.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { sprintf, __ } from '@wordpress/i18n';
* Internal dependencies
*/
import ListViewLeaf from './leaf';
import useListViewScrollIntoView from './use-list-view-scroll-into-view';
import {
BlockMoverUpButton,
BlockMoverDownButton,
Expand Down Expand Up @@ -57,6 +58,7 @@ function ListViewBlock( {
isSyncedBranch,
} ) {
const cellRef = useRef( null );
const rowRef = useRef( null );
const [ isHovered, setIsHovered ] = useState( false );
const { clientId } = block;

Expand Down Expand Up @@ -220,6 +222,15 @@ function ListViewBlock( {
? selectedClientIds
: [ clientId ];

// Pass in a ref to the row, so that it can be scrolled
// into view when selected. For long lists, the placeholder for the
// selected block is also observed, within ListViewLeafPlaceholder.
useListViewScrollIntoView( {
isSelected,
rowItemRef: rowRef,
selectedClientIds,
} );

return (
<ListViewLeaf
className={ classes }
Expand All @@ -235,6 +246,7 @@ function ListViewBlock( {
data-block={ clientId }
isExpanded={ canExpand ? isExpanded : undefined }
aria-selected={ !! isSelected || forceSelectionContentLock }
ref={ rowRef }
>
<TreeGridCell
className="block-editor-list-view-block__contents-cell"
Expand Down
3 changes: 1 addition & 2 deletions packages/block-editor/src/components/list-view/branch.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,8 +155,6 @@ function ListViewBranch( props ) {

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

const showBlock = isDragged || blockInView;

// Make updates to the selected or dragged blocks synchronous,
// but asynchronous for any other block.
const isSelected = isClientIdSelected(
Expand All @@ -165,6 +163,7 @@ function ListViewBranch( props ) {
);
const isSelectedBranch =
isBranchSelected || ( isSelected && hasNestedBlocks );
const showBlock = isDragged || blockInView || isSelected;
return (
<AsyncModeProvider key={ clientId } value={ ! isSelected }>
{ showBlock && (
Expand Down
72 changes: 43 additions & 29 deletions packages/block-editor/src/components/list-view/leaf.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import classnames from 'classnames';
* WordPress dependencies
*/
import { __experimentalTreeGridRow as TreeGridRow } from '@wordpress/components';
import { useMergeRefs } from '@wordpress/compose';
import { forwardRef } from '@wordpress/element';

/**
* Internal dependencies
Expand All @@ -16,33 +18,45 @@ import useMovingAnimation from '../use-moving-animation';

const AnimatedTreeGridRow = animated( TreeGridRow );

export default function ListViewLeaf( {
isSelected,
position,
level,
rowCount,
children,
className,
path,
...props
} ) {
const ref = useMovingAnimation( {
isSelected,
adjustScrolling: false,
enableAnimation: true,
triggerAnimationOnChange: path,
} );
const ListViewLeaf = forwardRef(
(
{
isSelected,
position,
level,
rowCount,
children,
className,
path,
...props
},
ref
) => {
const animationRef = useMovingAnimation( {
isSelected,
adjustScrolling: false,
enableAnimation: true,
triggerAnimationOnChange: path,
} );

return (
<AnimatedTreeGridRow
ref={ ref }
className={ classnames( 'block-editor-list-view-leaf', className ) }
level={ level }
positionInSet={ position }
setSize={ rowCount }
{ ...props }
>
{ children }
</AnimatedTreeGridRow>
);
}
const mergedRef = useMergeRefs( [ ref, animationRef ] );

return (
<AnimatedTreeGridRow
ref={ mergedRef }
className={ classnames(
'block-editor-list-view-leaf',
className
) }
level={ level }
positionInSet={ position }
setSize={ rowCount }
{ ...props }
>
{ children }
</AnimatedTreeGridRow>
);
}
);

export default ListViewLeaf;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* WordPress dependencies
*/
import { getScrollContainer } from '@wordpress/dom';
import { useLayoutEffect } from '@wordpress/element';

export default function useListViewScrollIntoView( {
isSelected,
selectedClientIds,
rowItemRef,
} ) {
const isSingleSelection = selectedClientIds.length === 1;

useLayoutEffect( () => {
// Skip scrolling into view if this particular block isn't selected,
// or if more than one block is selected overall. This is to avoid
// scrolling the view in a multi selection where the user has intentionally
// selected multiple blocks within the list view, but the initially
// selected block may be out of view.
if ( ! isSelected || ! isSingleSelection || ! rowItemRef.current ) {
return;
}

const scrollContainer = getScrollContainer( rowItemRef.current );
const { ownerDocument } = rowItemRef.current;

const windowScroll =
scrollContainer === ownerDocument.body ||
scrollContainer === ownerDocument.documentElement;

// If the there is no scroll container, of if the scroll container is the window,
// do not scroll into view, as the block is already in view.
if ( windowScroll || ! scrollContainer ) {
return;
}

const rowRect = rowItemRef.current.getBoundingClientRect();
const scrollContainerRect = scrollContainer.getBoundingClientRect();

// If the selected block is not currently visible, scroll to it.
if (
rowRect.top < scrollContainerRect.top ||
rowRect.bottom > scrollContainerRect.bottom
) {
rowItemRef.current.scrollIntoView();
}
}, [ isSelected, isSingleSelection, rowItemRef ] );
}

0 comments on commit 7951add

Please sign in to comment.