Skip to content

Commit

Permalink
Block Library: Implement drag handles for columns resizing
Browse files Browse the repository at this point in the history
  • Loading branch information
aduth committed Jun 27, 2019
1 parent 8213f92 commit 1a5a171
Show file tree
Hide file tree
Showing 3 changed files with 228 additions and 73 deletions.
9 changes: 9 additions & 0 deletions packages/block-library/src/columns/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
getRedistributedColumnWidths,
toWidthPrecision,
} from './utils';
import ColumnsResizer from './resizer';

/**
* Allowed blocks constant is passed to InnerBlocks precisely as specified here.
Expand Down Expand Up @@ -111,6 +112,8 @@ export function ColumnsEdit( {
updateAlignment,
updateColumns,
clientId,
isSelected,
toggleSelection,
} ) {
const { verticalAlignment } = attributes;

Expand Down Expand Up @@ -175,6 +178,12 @@ export function ColumnsEdit( {
template={ count === 0 && ! forceUseTemplate ? null : template }
templateLock="all"
allowedBlocks={ ALLOWED_BLOCKS } />
{ isSelected && (
<ColumnsResizer
clientId={ clientId }
toggleSelection={ toggleSelection }
/>
) }
</div>
</>
);
Expand Down
186 changes: 113 additions & 73 deletions packages/block-library/src/columns/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -33,98 +33,138 @@

.wp-block-columns {
display: block;
}

> .editor-inner-blocks > .editor-block-list__layout {
display: flex;
.wp-block-columns > .editor-inner-blocks > .editor-block-list__layout,
.wp-block-columns__resizer {
display: flex;

// Responsiveness: Allow wrapping on mobile.
flex-wrap: wrap;
// Responsiveness: Allow wrapping on mobile.
flex-wrap: wrap;

@include break-medium() {
flex-wrap: nowrap;
@include break-medium() {
flex-wrap: nowrap;
}
// Set full heights on Columns to enable vertical alignment preview
> [data-type="core/column"],
> [data-type="core/column"] > .editor-block-list__block-edit,
> [data-type="core/column"] > .editor-block-list__block-edit > div[data-block],
> [data-type="core/column"] > .editor-block-list__block-edit .block-core-columns,
> .wp-block-columns__resize-box {
@include flex-full-height();
}
// Adjust the individual column block.
> [data-type="core/column"],
.wp-block-columns__resize-box {

// On mobile, only a single column is shown, so match adjacent block paddings.
padding-left: 0;
padding-right: 0;
margin-left: -$block-padding;
margin-right: -$block-padding;

// Prevent the columns from growing wider than their distributed sizes.
min-width: 0;

// Prevent long unbroken words from overflowing.
word-break: break-word; // For back-compat.
overflow-wrap: break-word; // New standard.

// Responsiveness: Show at most one columns on mobile.
flex-basis: 100%;

// Beyond mobile, allow 2 columns.
@include break-small() {
flex-grow: 0;
margin-left: $block-padding;
margin-right: $block-padding;

// TODO: This is not viable, since it will cause overflow of the
// columns content, but is necessary as demonstration of the impact
// on browser flex distribution on assigned width disparities.
flex-shrink: 0;
flex-basis: 50%;
}
// Set full heights on Columns to enable vertical alignment preview
> [data-type="core/column"],
> [data-type="core/column"] > .editor-block-list__block-edit,
> [data-type="core/column"] > .editor-block-list__block-edit > div[data-block],
> [data-type="core/column"] > .editor-block-list__block-edit .block-core-columns {
@include flex-full-height();

// Add space between columns. Themes can customize this if they wish to work differently.
// This has to match the same padding applied in style.scss.
// Only apply this beyond the mobile breakpoint, as there's only a single column on mobile.
@include break-small() {
&:nth-child(even) {
margin-left: calc(#{$grid-size-large * 2} + #{$block-padding});
}
}
// Adjust the individual column block.
> [data-type="core/column"] {

// On mobile, only a single column is shown, so match adjacent block paddings.
padding-left: 0;
padding-right: 0;
margin-left: -$block-padding;
margin-right: -$block-padding;

// Prevent the columns from growing wider than their distributed sizes.
min-width: 0;

// Prevent long unbroken words from overflowing.
word-break: break-word; // For back-compat.
overflow-wrap: break-word; // New standard.

// Responsiveness: Show at most one columns on mobile.
flex-basis: 100%;

// Beyond mobile, allow 2 columns.
@include break-small() {
flex-basis: calc(50% - (#{$grid-size-large} + #{$block-padding * 2}));
flex-grow: 0;
margin-left: $block-padding;
margin-right: $block-padding;

// When columns are in a single row, add space before all except the first.
@include break-medium() {
&:not(:first-child) {
margin-left: calc(#{$grid-size-large * 2} + #{$block-padding});
}
}

// Add space between columns. Themes can customize this if they wish to work differently.
// This has to match the same padding applied in style.scss.
// Only apply this beyond the mobile breakpoint, as there's only a single column on mobile.
@include break-small() {
&:nth-child(even) {
margin-left: calc(#{$grid-size-large * 2} + #{$block-padding});
}
> .editor-block-list__block-edit {
margin-top: 0;
margin-bottom: 0;

// Remove Block "padding" so individual Column is flush with parent Columns
&::before {
left: 0;
right: 0;
}

// When columns are in a single row, add space before all except the first.
@include break-medium() {
&:not(:first-child) {
margin-left: calc(#{$grid-size-large * 2} + #{$block-padding});
}
> .editor-block-contextual-toolbar {
margin-left: -$border-width;
}

> .editor-block-list__block-edit {
// Zero out margins.
> [data-block] {
margin-top: 0;
margin-bottom: 0;
}

// Remove Block "padding" so individual Column is flush with parent Columns
&::before {
left: 0;
right: 0;
}

> .editor-block-contextual-toolbar {
margin-left: -$border-width;
}

// Zero out margins.
> [data-block] {
margin-top: 0;
margin-bottom: 0;
}

// The Columns block is a flex-container, therefore it nullifies margin collapsing.
// Therefore, blocks inside this will appear to create a double margin.
// We compensate for this using negative margins.
> div > .block-core-columns > .editor-inner-blocks {
margin-top: -$default-block-margin;
margin-bottom: -$default-block-margin;
}
// The Columns block is a flex-container, therefore it nullifies margin collapsing.
// Therefore, blocks inside this will appear to create a double margin.
// We compensate for this using negative margins.
> div > .block-core-columns > .editor-inner-blocks {
margin-top: -$default-block-margin;
margin-bottom: -$default-block-margin;
}
}
}
}

.wp-block-columns__resizer {
display: none;

@include break-medium() {
display: flex;
}

position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
pointer-events: none;
}

.wp-block-columns__resize-box {
.wp-block-columns__resizer.is-resizing &:not(.is-active) {
opacity: 0;
}

.wp-block-columns__resizer & {
// Reset natural flex-basis assigned above, deferring instead to the
// width assigned by ResizableBox.
flex-basis: auto;
}

.components-resizable-box__handle {
pointer-events: auto;
margin-right: #{-1 * ($grid-size-large + $block-padding)};
}
}

/**
* Columns act as as a "passthrough container"
* and therefore has its vertical margins/padding removed via negative margins
Expand Down
106 changes: 106 additions & 0 deletions packages/block-library/src/columns/resizer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* External dependencies
*/
import { forEach, find, difference, over } from 'lodash';
import classnames from 'classnames';

/**
* WordPress dependencies
*/
import { useSelect, withDispatch } from '@wordpress/data';
import { ResizableBox } from '@wordpress/components';
import { useState } from '@wordpress/element';

/**
* Internal dependencies
*/
import {
getEffectiveColumnWidth,
toWidthPrecision,
getTotalColumnsWidth,
getColumnWidths,
getAdjacentBlocks,
getRedistributedColumnWidths,
} from './utils';

function ColumnsResizer( {
clientId,
toggleSelection,
updateBlockAttributes,
} ) {
const [ activeResizeClientId, setActiveResizeClientId ] = useState( null );

const { columns } = useSelect( ( select ) => ( {
columns: select( 'core/block-editor' ).getBlocks( clientId ),
} ), [ clientId ] );

const classes = classnames( 'wp-block-columns__resizer', {
'is-resizing': activeResizeClientId !== null,
} );

return (
<div className={ classes }>
{ columns.map( ( column, index ) => {
const width = getEffectiveColumnWidth( column, columns.length );

function onResizeStop( event, direction, element ) {
const nextWidth = toWidthPrecision( parseFloat( element.style.width ) );
const adjacentColumns = getAdjacentBlocks( columns, column.clientId );

// The occupied width is calculated as the sum of the new width
// and the total width of blocks _not_ in the adjacent set.
const occupiedWidth = nextWidth + getTotalColumnsWidth(
difference( columns, [
find( columns, { clientId: column.clientId } ),
...adjacentColumns,
] )
);

// Compute _all_ next column widths, in case the updated column
// is in the middle of a set of columns which don't yet have
// any explicit widths assigned (include updates to those not
// part of the adjacent blocks).
const nextColumnWidths = {
...getColumnWidths( columns, columns.length ),
[ column.clientId ]: nextWidth,
...getRedistributedColumnWidths( adjacentColumns, 100 - occupiedWidth, columns.length ),
};

forEach( nextColumnWidths, ( nextColumnWidth, columnClientId ) => {
updateBlockAttributes( columnClientId, { width: nextColumnWidth } );
} );
}

return (
<ResizableBox
key={ column.clientId }
enable={ {
right: index !== columns.length - 1,
} }
size={ { width: width + '%' } }
axis="x"
onResizeStart={ over( [
() => setActiveResizeClientId( column.clientId ),
() => toggleSelection( false ),
] ) }
onResizeStop={ over( [
() => setActiveResizeClientId( null ),
() => toggleSelection( true ),
onResizeStop,
] ) }
className={ classnames(
'wp-block-columns__resize-box',
'is-selected',
{ 'is-active': activeResizeClientId === column.clientId }
) }
/>
);
} ) }
</div>
);
}

export default withDispatch( ( dispatch ) => {
const { updateBlockAttributes } = dispatch( 'core/block-editor' );
return { updateBlockAttributes };
} )( ColumnsResizer );

0 comments on commit 1a5a171

Please sign in to comment.