-
Notifications
You must be signed in to change notification settings - Fork 4.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Try inserting blocks directly in empty grid cells #63108
Changes from 12 commits
f29737c
cae62a3
5f3a7ea
cab1e9a
c80bcd3
1e01b30
b857803
4f075f6
04d59ee
570f94a
1aedd5e
e3da319
9a626e3
443d239
fc33a46
0fd3672
7f64054
add8ca3
28ebf19
715c50d
dc8e9f9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,9 +15,10 @@ import { __experimentalUseDropZone as useDropZone } from '@wordpress/compose'; | |
*/ | ||
import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs'; | ||
import BlockPopoverCover from '../block-popover/cover'; | ||
import { range, GridRect, getGridInfo } from './utils'; | ||
import { range, GridRect, getGridInfo, getComputedCSS } from './utils'; | ||
import { store as blockEditorStore } from '../../store'; | ||
import { useGetNumberOfBlocksBeforeCell } from './use-get-number-of-blocks-before-cell'; | ||
import ButtonBlockAppender from '../button-block-appender'; | ||
|
||
export function GridVisualizer( { clientId, contentRef, parentLayout } ) { | ||
const isDistractionFree = useSelect( | ||
|
@@ -44,6 +45,28 @@ export function GridVisualizer( { clientId, contentRef, parentLayout } ) { | |
); | ||
} | ||
|
||
const checkIfCellOccupied = ( gridElement, column, row ) => { | ||
const cell = Array.from( gridElement.children ).find( ( child ) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bit worried about performance here. We're iterating through the grid's children once per cell. So for a 16x16 grid with blocks in every cell that's An alternative approach might be to calculate I might try that if you don't mind me pushing to this branch. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, go ahead! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the bug you're seeing with appender still appearing when a paragraph is added must happening in this function, because the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How's this look? 9a626e3 Not very scientific but it feels a bit snappier to me clicking around. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmm, I'm seeing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But the issue with the appender still rendering is fixed! And the code looks much neater There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oops. Fixed. |
||
const [ columnStart, columnSpan ] = | ||
getComputedCSS( child, 'grid-column' ).match( /\d+/g ) || []; | ||
const [ rowStart, rowSpan ] = | ||
getComputedCSS( child, 'grid-row' ).match( /\d+/g ) || []; | ||
const columnEnd = columnSpan | ||
? parseInt( columnStart, 10 ) + parseInt( columnSpan, 10 ) - 1 | ||
: columnStart; | ||
const rowEnd = rowSpan | ||
? parseInt( rowStart, 10 ) + parseInt( rowSpan, 10 ) - 1 | ||
: rowStart; | ||
return ( | ||
column >= columnStart && | ||
column <= columnEnd && | ||
row >= rowStart && | ||
row <= rowEnd | ||
); | ||
} ); | ||
return cell !== undefined; | ||
}; | ||
|
||
const GridVisualizerGrid = forwardRef( | ||
( { clientId, gridElement, isManualGrid }, ref ) => { | ||
const [ gridInfo, setGridInfo ] = useState( () => | ||
|
@@ -52,6 +75,17 @@ const GridVisualizerGrid = forwardRef( | |
const [ isDroppingAllowed, setIsDroppingAllowed ] = useState( false ); | ||
const [ highlightedRect, setHighlightedRect ] = useState( null ); | ||
|
||
const { | ||
updateBlockAttributes, | ||
moveBlocksToPosition, | ||
__unstableMarkNextChangeAsNotPersistent, | ||
} = useDispatch( blockEditorStore ); | ||
|
||
const getNumberOfBlocksBeforeCell = useGetNumberOfBlocksBeforeCell( | ||
clientId, | ||
gridInfo.numColumns | ||
); | ||
|
||
useEffect( () => { | ||
const observers = []; | ||
for ( const element of [ gridElement, ...gridElement.children ] ) { | ||
|
@@ -99,25 +133,84 @@ const GridVisualizerGrid = forwardRef( | |
{ isManualGrid | ||
? range( 1, gridInfo.numRows ).map( ( row ) => | ||
range( 1, gridInfo.numColumns ).map( | ||
( column ) => ( | ||
<GridVisualizerCell | ||
key={ `${ row }-${ column }` } | ||
color={ gridInfo.currentColor } | ||
> | ||
<GridVisualizerDropZone | ||
column={ column } | ||
row={ row } | ||
gridClientId={ clientId } | ||
gridInfo={ gridInfo } | ||
highlightedRect={ | ||
highlightedRect | ||
( column ) => { | ||
const isCellOccupied = | ||
checkIfCellOccupied( | ||
gridElement, | ||
column, | ||
row | ||
); | ||
const isHighlighted = | ||
highlightedRect?.contains( | ||
column, | ||
row | ||
) ?? false; | ||
return ( | ||
<GridVisualizerCell | ||
key={ `${ row }-${ column }` } | ||
color={ gridInfo.currentColor } | ||
className={ | ||
isHighlighted && | ||
'is-highlighted' | ||
} | ||
setHighlightedRect={ | ||
setHighlightedRect | ||
} | ||
/> | ||
</GridVisualizerCell> | ||
) | ||
> | ||
{ isCellOccupied ? ( | ||
<GridVisualizerDropZone | ||
column={ column } | ||
row={ row } | ||
gridClientId={ | ||
clientId | ||
} | ||
gridInfo={ gridInfo } | ||
setHighlightedRect={ | ||
setHighlightedRect | ||
} | ||
/> | ||
) : ( | ||
<GridVisualizerAppender | ||
column={ column } | ||
row={ row } | ||
gridClientId={ | ||
clientId | ||
} | ||
gridInfo={ gridInfo } | ||
setHighlightedRect={ | ||
setHighlightedRect | ||
} | ||
onSelectCallback={ ( | ||
block | ||
) => { | ||
updateBlockAttributes( | ||
block.clientId, | ||
{ | ||
style: { | ||
layout: { | ||
columnStart: | ||
column, | ||
rowStart: | ||
row, | ||
}, | ||
}, | ||
} | ||
); | ||
__unstableMarkNextChangeAsNotPersistent(); | ||
moveBlocksToPosition( | ||
[ | ||
block.clientId, | ||
], | ||
clientId, | ||
clientId, | ||
getNumberOfBlocksBeforeCell( | ||
column, | ||
row | ||
) | ||
); | ||
} } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd move this callback logic into |
||
/> | ||
) } | ||
</GridVisualizerCell> | ||
); | ||
} | ||
) | ||
) | ||
: Array.from( | ||
|
@@ -135,28 +228,32 @@ const GridVisualizerGrid = forwardRef( | |
} | ||
); | ||
|
||
function GridVisualizerCell( { color, children } ) { | ||
function GridVisualizerCell( { color, children, className } ) { | ||
return ( | ||
<div | ||
className="block-editor-grid-visualizer__cell" | ||
className={ clsx( | ||
'block-editor-grid-visualizer__cell', | ||
className | ||
) } | ||
style={ { | ||
boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${ color } 20%, #0000)`, | ||
color, | ||
} } | ||
> | ||
{ children } | ||
</div> | ||
); | ||
} | ||
|
||
function GridVisualizerDropZone( { | ||
function useGridVisualizerDropZone( | ||
column, | ||
row, | ||
gridClientId, | ||
gridInfo, | ||
highlightedRect, | ||
setHighlightedRect, | ||
} ) { | ||
const { getBlockAttributes } = useSelect( blockEditorStore ); | ||
setHighlightedRect | ||
) { | ||
const { getBlockAttributes, getBlockRootClientId } = | ||
useSelect( blockEditorStore ); | ||
const { | ||
updateBlockAttributes, | ||
moveBlocksToPosition, | ||
|
@@ -168,7 +265,7 @@ function GridVisualizerDropZone( { | |
gridInfo.numColumns | ||
); | ||
|
||
const ref = useDropZoneWithValidation( { | ||
return useDropZoneWithValidation( { | ||
validateDrag( srcClientId ) { | ||
const attributes = getBlockAttributes( srcClientId ); | ||
const rect = new GridRect( { | ||
|
@@ -221,21 +318,58 @@ function GridVisualizerDropZone( { | |
__unstableMarkNextChangeAsNotPersistent(); | ||
moveBlocksToPosition( | ||
[ srcClientId ], | ||
gridClientId, | ||
getBlockRootClientId( srcClientId ), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's this change for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We need to allow blocks from outside the grid (so with a different rootClientId) to be dropped inside the grid too. |
||
gridClientId, | ||
getNumberOfBlocksBeforeCell( column, row ) | ||
); | ||
}, | ||
} ); | ||
} | ||
|
||
const isHighlighted = highlightedRect?.contains( column, row ) ?? false; | ||
|
||
function GridVisualizerDropZone( { | ||
column, | ||
row, | ||
gridClientId, | ||
gridInfo, | ||
setHighlightedRect, | ||
} ) { | ||
return ( | ||
<div | ||
ref={ ref } | ||
className={ clsx( 'block-editor-grid-visualizer__drop-zone', { | ||
'is-highlighted': isHighlighted, | ||
} ) } | ||
className="block-editor-grid-visualizer__drop-zone" | ||
ref={ useGridVisualizerDropZone( | ||
column, | ||
row, | ||
gridClientId, | ||
gridInfo, | ||
setHighlightedRect | ||
) } | ||
/> | ||
); | ||
} | ||
|
||
function GridVisualizerAppender( { | ||
column, | ||
row, | ||
gridClientId, | ||
gridInfo, | ||
setHighlightedRect, | ||
onSelectCallback, | ||
} ) { | ||
return ( | ||
<ButtonBlockAppender | ||
rootClientId={ gridClientId } | ||
className="block-editor-grid-visualizer__appender" | ||
ref={ useGridVisualizerDropZone( | ||
column, | ||
row, | ||
gridClientId, | ||
gridInfo, | ||
setHighlightedRect | ||
) } | ||
style={ { | ||
color: gridInfo.currentColor, | ||
} } | ||
onSelectCallback={ onSelectCallback } | ||
/> | ||
); | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -150,6 +150,7 @@ class PrivateInserter extends Component { | |
prioritizePatterns, | ||
onSelectOrClose, | ||
selectBlockOnInsert, | ||
onSelectCallback, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This component has |
||
} = this.props; | ||
|
||
if ( isQuick ) { | ||
|
@@ -167,6 +168,9 @@ class PrivateInserter extends Component { | |
onSelectOrClose( firstBlock ); | ||
} | ||
onClose(); | ||
if ( onSelectCallback ) { | ||
onSelectCallback( firstBlock ); | ||
} | ||
} } | ||
rootClientId={ rootClientId } | ||
clientId={ clientId } | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the
Callback
suffix necessary? Theon
prefix already hints that it's a function so I'd simply call thisonSelect
.