Skip to content
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

Add Grid interactivity #59052

Merged
merged 20 commits into from
Feb 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
956b676
Add grid visualization
noisysocks Feb 11, 2024
c7f6986
Show grid visualization when child is selected
noisysocks Feb 14, 2024
185dc1a
Allow dragging to set column and row span
noisysocks Feb 14, 2024
3ce2c39
Don't need this ref
noisysocks Feb 14, 2024
895b33d
Just disable left/top resizing for now
noisysocks Feb 15, 2024
088cf0f
Clean up CSS
noisysocks Feb 15, 2024
a8deab4
Merge remote-tracking branch 'origin/trunk' into try/interactive-grid…
noisysocks Feb 19, 2024
0a22979
Add shift=false to popovers
noisysocks Feb 19, 2024
0bd1e2c
Accommodate variably sized columns/rows by using grid-template to cal…
noisysocks Feb 19, 2024
4db7ab0
BlockPopover: Use ResizeObserver to match size of covered block
noisysocks Feb 19, 2024
e6567ab
Fix error due to undefined selectedElement
noisysocks Feb 20, 2024
a036614
Update GridVisualizer when grid or its children resize
noisysocks Feb 20, 2024
df65b36
Add experimental flag
noisysocks Feb 20, 2024
cbc5b50
Fix formatting
noisysocks Feb 21, 2024
e1c2c92
Go away spaces
noisysocks Feb 21, 2024
f0c111a
BlockPopover: Remove __unstableRefreshSize prop and improve how __uns…
noisysocks Feb 21, 2024
4e50848
BlockPopover: Remove __unstableCoverTarget in favour of BlockPopoverC…
noisysocks Feb 21, 2024
e48fe99
Merge branch 'update/remove-__unstableRefreshSize' into try/interacti…
noisysocks Feb 21, 2024
fad46c9
Merge remote-tracking branch 'origin/trunk' into try/interactive-grid…
noisysocks Feb 22, 2024
fcdbc90
Use BlockPopoverCover
noisysocks Feb 22, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/experimental/editor-settings.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ function gutenberg_enable_experiments() {
if ( $gutenberg_experiments && array_key_exists( 'gutenberg-color-randomizer', $gutenberg_experiments ) ) {
wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableColorRandomizer = true', 'before' );
}
if ( $gutenberg_experiments && array_key_exists( 'gutenberg-grid-interactivity', $gutenberg_experiments ) ) {
wp_add_inline_script( 'wp-block-editor', 'window.__experimentalEnableGridInteractivity = true', 'before' );
}
if ( gutenberg_is_experiment_enabled( 'gutenberg-no-tinymce' ) ) {
wp_add_inline_script( 'wp-block-library', 'window.__experimentalDisableTinymce = true', 'before' );
}
Expand Down
14 changes: 14 additions & 0 deletions lib/experiments-page.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ function gutenberg_initialize_experiments_settings() {
'id' => 'gutenberg-color-randomizer',
)
);

add_settings_field(
'gutenberg-form-blocks',
__( 'Form and input blocks ', 'gutenberg' ),
Expand All @@ -101,6 +102,19 @@ function gutenberg_initialize_experiments_settings() {
'id' => 'gutenberg-form-blocks',
)
);

add_settings_field(
'gutenberg-grid-interactivity',
__( 'Grid interactivty ', 'gutenberg' ),
'gutenberg_display_experiment_field',
'gutenberg-experiments',
'gutenberg_experiments_section',
array(
'label' => __( 'Test enhancements to the Grid block that let you move and resize items in the editor canvas.', 'gutenberg' ),
'id' => 'gutenberg-grid-interactivity',
)
);

add_settings_field(
'gutenberg-no-tinymce',
__( 'Disable TinyMCE and Classic block', 'gutenberg' ),
Expand Down
3 changes: 3 additions & 0 deletions packages/base-styles/_z-index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ $z-layers: (
// Above the block list and the header.
".block-editor-block-popover": 31,

// Below the block toolbar.
".block-editor-grid-visualizer": 30,

// Show snackbars above everything (similar to popovers)
".components-snackbar-list": 100000,

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* WordPress dependencies
*/
import { ResizableBox } from '@wordpress/components';

/**
* Internal dependencies
*/
import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';
import BlockPopoverCover from '../block-popover/cover';
import { getComputedCSS } from './utils';

export function GridItemResizer( { clientId, onChange } ) {
const blockElement = useBlockElement( clientId );
if ( ! blockElement ) {
return null;
}
return (
<BlockPopoverCover
className="block-editor-grid-item-resizer"
clientId={ clientId }
__unstablePopoverSlot="block-toolbar"
noisysocks marked this conversation as resolved.
Show resolved Hide resolved
>
<ResizableBox
className="block-editor-grid-item-resizer__box"
size={ {
width: '100%',
height: '100%',
} }
enable={ {
bottom: true,
bottomLeft: false,
bottomRight: false,
left: false,
right: true,
top: false,
topLeft: false,
topRight: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So what happens if we enable "left" and "top"? I'm guessing it breaks somehow? 😄

Copy link
Member Author

@noisysocks noisysocks Feb 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a bug in ResizableBox or something because it just goes crazy 🤪 We can look at adding it later if it feels like we need it.

} }
onResizeStop={ ( event, direction, boxElement ) => {
const gridElement = blockElement.parentElement;
const columnGap = parseFloat(
getComputedCSS( gridElement, 'column-gap' )
);
const rowGap = parseFloat(
getComputedCSS( gridElement, 'row-gap' )
);
const gridColumnLines = getGridLines(
getComputedCSS( gridElement, 'grid-template-columns' ),
columnGap
);
const gridRowLines = getGridLines(
getComputedCSS( gridElement, 'grid-template-rows' ),
rowGap
);
const columnStart = getClosestLine(
gridColumnLines,
blockElement.offsetLeft
);
const rowStart = getClosestLine(
gridRowLines,
blockElement.offsetTop
);
const columnEnd = getClosestLine(
gridColumnLines,
blockElement.offsetLeft + boxElement.offsetWidth
);
const rowEnd = getClosestLine(
gridRowLines,
blockElement.offsetTop + boxElement.offsetHeight
);
noisysocks marked this conversation as resolved.
Show resolved Hide resolved
onChange( {
columnSpan: Math.max( columnEnd - columnStart, 1 ),
rowSpan: Math.max( rowEnd - rowStart, 1 ),
} );
} }
/>
</BlockPopoverCover>
);
}

function getGridLines( template, gap ) {
const lines = [ 0 ];
for ( const size of template.split( ' ' ) ) {
const line = parseFloat( size );
lines.push( lines[ lines.length - 1 ] + line + gap );
}
return lines;
}

function getClosestLine( lines, position ) {
return lines.reduce(
( closest, line, index ) =>
Math.abs( line - position ) <
Math.abs( lines[ closest ] - position )
? index
: closest,
0
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* WordPress dependencies
*/
import { useState, useEffect } from '@wordpress/element';

/**
* Internal dependencies
*/
import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';
import BlockPopoverCover from '../block-popover/cover';
import { getComputedCSS } from './utils';

export function GridVisualizer( { clientId } ) {
const blockElement = useBlockElement( clientId );
if ( ! blockElement ) {
return null;
}
return (
<BlockPopoverCover
className="block-editor-grid-visualizer"
clientId={ clientId }
__unstablePopoverSlot="block-toolbar"
>
<GridVisualizerGrid blockElement={ blockElement } />
</BlockPopoverCover>
);
}

function GridVisualizerGrid( { blockElement } ) {
const [ gridInfo, setGridInfo ] = useState( () =>
getGridInfo( blockElement )
);
useEffect( () => {
const observers = [];
for ( const element of [ blockElement, ...blockElement.children ] ) {
const observer = new window.ResizeObserver( () => {
setGridInfo( getGridInfo( blockElement ) );
} );
observer.observe( element );
observers.push( observer );
}
return () => {
for ( const observer of observers ) {
observer.disconnect();
}
};
}, [ blockElement ] );
return (
<div
className="block-editor-grid-visualizer__grid"
style={ gridInfo.style }
>
{ Array.from( { length: gridInfo.numItems }, ( _, i ) => (
<div key={ i } className="block-editor-grid-visualizer__item" />
) ) }
</div>
);
}

function getGridInfo( blockElement ) {
const gridTemplateColumns = getComputedCSS(
blockElement,
'grid-template-columns'
);
const gridTemplateRows = getComputedCSS(
blockElement,
'grid-template-rows'
);
const numColumns = gridTemplateColumns.split( ' ' ).length;
const numRows = gridTemplateRows.split( ' ' ).length;
const numItems = numColumns * numRows;
return {
numItems,
style: {
gridTemplateColumns,
gridTemplateRows,
gap: getComputedCSS( blockElement, 'gap' ),
padding: getComputedCSS( blockElement, 'padding' ),
},
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { GridVisualizer } from './grid-visualizer';
export { GridItemResizer } from './grid-item-resizer';
33 changes: 33 additions & 0 deletions packages/block-editor/src/components/grid-visualizer/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// TODO: Specificity hacks to get rid of all these darn !importants.

.block-editor-grid-visualizer {
z-index: z-index(".block-editor-grid-visualizer") !important;
}

.block-editor-grid-visualizer .components-popover__content * {
pointer-events: none !important;
}

.block-editor-grid-visualizer__grid {
display: grid;
}

.block-editor-grid-visualizer__item {
border: $border-width dashed $gray-300;
}

.block-editor-grid-item-resizer {
z-index: z-index(".block-editor-grid-visualizer") !important;
}

.block-editor-grid-item-resizer .components-popover__content * {
pointer-events: none !important;
}

.block-editor-grid-item-resizer__box {
border: $border-width solid var(--wp-admin-theme-color);

.components-resizable-box__handle {
pointer-events: all !important;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export function getComputedCSS( element, property ) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the third instance of this function in the block-editor package, might be worth consolidating (as a separate task)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The refactor in #59227 brings it down to 2! Getting close... 😂

return element.ownerDocument.defaultView
.getComputedStyle( element )
.getPropertyValue( property );
}
1 change: 1 addition & 0 deletions packages/block-editor/src/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ createBlockEditFilter(
contentLockUI,
blockHooks,
blockRenaming,
childLayout,
].filter( Boolean )
);
createBlockListBlockFilter( [
Expand Down
38 changes: 38 additions & 0 deletions packages/block-editor/src/hooks/layout-child.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { useSelect } from '@wordpress/data';
import { store as blockEditorStore } from '../store';
import { useStyleOverride } from './utils';
import { useLayout } from '../components/block-list/layout';
import { GridVisualizer, GridItemResizer } from '../components/grid-visualizer';

function useBlockPropsChildLayoutStyles( { style } ) {
const shouldRenderChildLayoutStyles = useSelect( ( select ) => {
Expand Down Expand Up @@ -96,8 +97,45 @@ function useBlockPropsChildLayoutStyles( { style } ) {
return { className: `wp-container-content-${ id }` };
}

function ChildLayoutControlsPure( { clientId, style, setAttributes } ) {
const parentLayout = useLayout() || {};
const rootClientId = useSelect(
( select ) => {
return select( blockEditorStore ).getBlockRootClientId( clientId );
},
[ clientId ]
);
if ( parentLayout.type !== 'grid' ) {
return null;
}
if ( ! window.__experimentalEnableGridInteractivity ) {
return null;
}
return (
<>
<GridVisualizer clientId={ rootClientId } />
<GridItemResizer
clientId={ clientId }
onChange={ ( { columnSpan, rowSpan } ) => {
setAttributes( {
style: {
...style,
layout: {
...style?.layout,
columnSpan,
rowSpan,
},
},
} );
} }
/>
</>
);
}

export default {
useBlockProps: useBlockPropsChildLayoutStyles,
edit: ChildLayoutControlsPure,
attributeKeys: [ 'style' ],
hasSupport() {
return true;
Expand Down
13 changes: 12 additions & 1 deletion packages/block-editor/src/hooks/layout.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,12 @@ export function useLayoutStyles( blockAttributes = {}, blockName, selector ) {
return css;
}

function LayoutPanelPure( { layout, setAttributes, name: blockName } ) {
function LayoutPanelPure( {
layout,
setAttributes,
name: blockName,
clientId,
} ) {
const settings = useBlockSettings( blockName );
// Block settings come from theme.json under settings.[blockName].
const { layout: layoutSettings } = settings;
Expand Down Expand Up @@ -266,13 +271,17 @@ function LayoutPanelPure( { layout, setAttributes, name: blockName } ) {
layout={ usedLayout }
onChange={ onChangeLayout }
layoutBlockSupport={ blockSupportAndThemeSettings }
name={ blockName }
clientId={ clientId }
/>
) }
{ constrainedType && displayControlsForLegacyLayouts && (
<constrainedType.inspectorControls
layout={ usedLayout }
onChange={ onChangeLayout }
layoutBlockSupport={ blockSupportAndThemeSettings }
name={ blockName }
clientId={ clientId }
/>
) }
</PanelBody>
Expand All @@ -282,6 +291,8 @@ function LayoutPanelPure( { layout, setAttributes, name: blockName } ) {
layout={ usedLayout }
onChange={ onChangeLayout }
layoutBlockSupport={ layoutBlockSupport }
name={ blockName }
clientId={ clientId }
/>
) }
</>
Expand Down
Loading
Loading