Skip to content

Commit a2e7f8d

Browse files
authored
Experimental list view duplicate for use in navigation list view experiment (#45544)
* Adds the gutenberg-off-canvas-navigation-editor experiment * export the experimental OffCanvasEditor
1 parent e5df3db commit a2e7f8d

18 files changed

+2183
-0
lines changed

packages/block-editor/src/components/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export { default as __experimentalLinkControlSearchResults } from './link-contro
7373
export { default as __experimentalLinkControlSearchItem } from './link-control/search-item';
7474
export { default as LineHeightControl } from './line-height-control';
7575
export { default as __experimentalListView } from './list-view';
76+
export { default as __experimentalOffCanvasEditor } from './off-canvas-editor';
7677
export { default as MediaReplaceFlow } from './media-replace-flow';
7778
export { default as MediaPlaceholder } from './media-placeholder';
7879
export { default as MediaUpload } from './media-upload';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Experimental Off Canvas Editor
2+
3+
The __ExperimentalOffCanvasEditor component is a modified ListView compoent. It provides an overview of the hierarchical structure of all blocks in the editor. The blocks are presented vertically one below the other. It enables editing of hierarchy and addition of elements in the block tree without selecting the block instance on the canvas.
4+
5+
It is an experimental component which may end up completely merged into the ListView component via configuration props.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import classnames from 'classnames';
5+
6+
/**
7+
* WordPress dependencies
8+
*/
9+
import { useSelect } from '@wordpress/data';
10+
import { forwardRef } from '@wordpress/element';
11+
12+
/**
13+
* Internal dependencies
14+
*/
15+
import ListViewBlockSelectButton from './block-select-button';
16+
import BlockDraggable from '../block-draggable';
17+
import { store as blockEditorStore } from '../../store';
18+
19+
const ListViewBlockContents = forwardRef(
20+
(
21+
{
22+
onClick,
23+
onToggleExpanded,
24+
block,
25+
isSelected,
26+
position,
27+
siblingBlockCount,
28+
level,
29+
isExpanded,
30+
selectedClientIds,
31+
...props
32+
},
33+
ref
34+
) => {
35+
const { clientId } = block;
36+
37+
const { blockMovingClientId, selectedBlockInBlockEditor } = useSelect(
38+
( select ) => {
39+
const { hasBlockMovingClientId, getSelectedBlockClientId } =
40+
select( blockEditorStore );
41+
return {
42+
blockMovingClientId: hasBlockMovingClientId(),
43+
selectedBlockInBlockEditor: getSelectedBlockClientId(),
44+
};
45+
},
46+
[ clientId ]
47+
);
48+
49+
const isBlockMoveTarget =
50+
blockMovingClientId && selectedBlockInBlockEditor === clientId;
51+
52+
const className = classnames( 'block-editor-list-view-block-contents', {
53+
'is-dropping-before': isBlockMoveTarget,
54+
} );
55+
56+
// Only include all selected blocks if the currently clicked on block
57+
// is one of the selected blocks. This ensures that if a user attempts
58+
// to drag a block that isn't part of the selection, they're still able
59+
// to drag it and rearrange its position.
60+
const draggableClientIds = selectedClientIds.includes( clientId )
61+
? selectedClientIds
62+
: [ clientId ];
63+
64+
return (
65+
<BlockDraggable clientIds={ draggableClientIds }>
66+
{ ( { draggable, onDragStart, onDragEnd } ) => (
67+
<ListViewBlockSelectButton
68+
ref={ ref }
69+
className={ className }
70+
block={ block }
71+
onClick={ onClick }
72+
onToggleExpanded={ onToggleExpanded }
73+
isSelected={ isSelected }
74+
position={ position }
75+
siblingBlockCount={ siblingBlockCount }
76+
level={ level }
77+
draggable={ draggable }
78+
onDragStart={ onDragStart }
79+
onDragEnd={ onDragEnd }
80+
isExpanded={ isExpanded }
81+
{ ...props }
82+
/>
83+
) }
84+
</BlockDraggable>
85+
);
86+
}
87+
);
88+
89+
export default ListViewBlockContents;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import classnames from 'classnames';
5+
6+
/**
7+
* WordPress dependencies
8+
*/
9+
import {
10+
Button,
11+
__experimentalHStack as HStack,
12+
__experimentalTruncate as Truncate,
13+
} from '@wordpress/components';
14+
import { forwardRef } from '@wordpress/element';
15+
import { Icon, lock } from '@wordpress/icons';
16+
import { SPACE, ENTER } from '@wordpress/keycodes';
17+
18+
/**
19+
* Internal dependencies
20+
*/
21+
import BlockIcon from '../block-icon';
22+
import useBlockDisplayInformation from '../use-block-display-information';
23+
import useBlockDisplayTitle from '../block-title/use-block-display-title';
24+
import ListViewExpander from './expander';
25+
import { useBlockLock } from '../block-lock';
26+
27+
function ListViewBlockSelectButton(
28+
{
29+
className,
30+
block: { clientId },
31+
onClick,
32+
onToggleExpanded,
33+
tabIndex,
34+
onFocus,
35+
onDragStart,
36+
onDragEnd,
37+
draggable,
38+
},
39+
ref
40+
) {
41+
const blockInformation = useBlockDisplayInformation( clientId );
42+
const blockTitle = useBlockDisplayTitle( {
43+
clientId,
44+
context: 'list-view',
45+
} );
46+
const { isLocked } = useBlockLock( clientId );
47+
48+
// The `href` attribute triggers the browser's native HTML drag operations.
49+
// When the link is dragged, the element's outerHTML is set in DataTransfer object as text/html.
50+
// We need to clear any HTML drag data to prevent `pasteHandler` from firing
51+
// inside the `useOnBlockDrop` hook.
52+
const onDragStartHandler = ( event ) => {
53+
event.dataTransfer.clearData();
54+
onDragStart?.( event );
55+
};
56+
57+
function onKeyDownHandler( event ) {
58+
if ( event.keyCode === ENTER || event.keyCode === SPACE ) {
59+
onClick( event );
60+
}
61+
}
62+
63+
return (
64+
<>
65+
<Button
66+
className={ classnames(
67+
'block-editor-list-view-block-select-button',
68+
className
69+
) }
70+
onClick={ onClick }
71+
onKeyDown={ onKeyDownHandler }
72+
ref={ ref }
73+
tabIndex={ tabIndex }
74+
onFocus={ onFocus }
75+
onDragStart={ onDragStartHandler }
76+
onDragEnd={ onDragEnd }
77+
draggable={ draggable }
78+
href={ `#block-${ clientId }` }
79+
aria-hidden={ true }
80+
>
81+
<ListViewExpander onClick={ onToggleExpanded } />
82+
<BlockIcon icon={ blockInformation?.icon } showColors />
83+
<HStack
84+
alignment="center"
85+
className="block-editor-list-view-block-select-button__label-wrapper"
86+
justify="flex-start"
87+
spacing={ 1 }
88+
>
89+
<span className="block-editor-list-view-block-select-button__title">
90+
<Truncate ellipsizeMode="auto">{ blockTitle }</Truncate>
91+
</span>
92+
{ blockInformation?.anchor && (
93+
<span className="block-editor-list-view-block-select-button__anchor-wrapper">
94+
<Truncate
95+
className="block-editor-list-view-block-select-button__anchor"
96+
ellipsizeMode="auto"
97+
>
98+
{ blockInformation.anchor }
99+
</Truncate>
100+
</span>
101+
) }
102+
{ isLocked && (
103+
<span className="block-editor-list-view-block-select-button__lock">
104+
<Icon icon={ lock } />
105+
</span>
106+
) }
107+
</HStack>
108+
</Button>
109+
</>
110+
);
111+
}
112+
113+
export default forwardRef( ListViewBlockSelectButton );

0 commit comments

Comments
 (0)