@wordpress/block-library
Version:
Block library for the WordPress editor.
231 lines (211 loc) • 7.52 kB
JavaScript
import { createElement, Fragment } from "@wordpress/element";
/**
* External dependencies
*/
import classnames from 'classnames';
import { get } from 'lodash';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Notice, PanelBody, RangeControl, ToggleControl } from '@wordpress/components';
import { InspectorControls, useInnerBlocksProps, BlockControls, BlockVerticalAlignmentToolbar, __experimentalBlockVariationPicker, useBlockProps, store as blockEditorStore } from '@wordpress/block-editor';
import { withDispatch, useDispatch, useSelect } from '@wordpress/data';
import { createBlock, createBlocksFromInnerBlocksTemplate, store as blocksStore } from '@wordpress/blocks';
/**
* Internal dependencies
*/
import { hasExplicitPercentColumnWidths, getMappedColumnWidths, getRedistributedColumnWidths, toWidthPrecision } from './utils';
/**
* Allowed blocks constant is passed to InnerBlocks precisely as specified here.
* The contents of the array should never change.
* The array should contain the name of each block that is allowed.
* In columns block, the only block we allow is 'core/column'.
*
* @constant
* @type {string[]}
*/
const ALLOWED_BLOCKS = ['core/column'];
function ColumnsEditContainer(_ref) {
let {
attributes,
setAttributes,
updateAlignment,
updateColumns,
clientId
} = _ref;
const {
isStackedOnMobile,
verticalAlignment
} = attributes;
const {
count
} = useSelect(select => {
return {
count: select(blockEditorStore).getBlockCount(clientId)
};
}, [clientId]);
const classes = classnames({
[`are-vertically-aligned-${verticalAlignment}`]: verticalAlignment,
[`is-not-stacked-on-mobile`]: !isStackedOnMobile
});
const blockProps = useBlockProps({
className: classes
});
const innerBlocksProps = useInnerBlocksProps(blockProps, {
allowedBlocks: ALLOWED_BLOCKS,
orientation: 'horizontal',
renderAppender: false
});
return createElement(Fragment, null, createElement(BlockControls, null, createElement(BlockVerticalAlignmentToolbar, {
onChange: updateAlignment,
value: verticalAlignment
})), createElement(InspectorControls, null, createElement(PanelBody, null, createElement(RangeControl, {
label: __('Columns'),
value: count,
onChange: value => updateColumns(count, value),
min: 1,
max: Math.max(6, count)
}), count > 6 && createElement(Notice, {
status: "warning",
isDismissible: false
}, __('This column count exceeds the recommended amount and may cause visual breakage.')), createElement(ToggleControl, {
label: __('Stack on mobile'),
checked: isStackedOnMobile,
onChange: () => setAttributes({
isStackedOnMobile: !isStackedOnMobile
})
}))), createElement("div", innerBlocksProps));
}
const ColumnsEditContainerWrapper = withDispatch((dispatch, ownProps, registry) => ({
/**
* Update all child Column blocks with a new vertical alignment setting
* based on whatever alignment is passed in. This allows change to parent
* to overide anything set on a individual column basis.
*
* @param {string} verticalAlignment the vertical alignment setting
*/
updateAlignment(verticalAlignment) {
const {
clientId,
setAttributes
} = ownProps;
const {
updateBlockAttributes
} = dispatch(blockEditorStore);
const {
getBlockOrder
} = registry.select(blockEditorStore); // Update own alignment.
setAttributes({
verticalAlignment
}); // Update all child Column Blocks to match.
const innerBlockClientIds = getBlockOrder(clientId);
innerBlockClientIds.forEach(innerBlockClientId => {
updateBlockAttributes(innerBlockClientId, {
verticalAlignment
});
});
},
/**
* Updates the column count, including necessary revisions to child Column
* blocks to grant required or redistribute available space.
*
* @param {number} previousColumns Previous column count.
* @param {number} newColumns New column count.
*/
updateColumns(previousColumns, newColumns) {
const {
clientId
} = ownProps;
const {
replaceInnerBlocks
} = dispatch(blockEditorStore);
const {
getBlocks
} = registry.select(blockEditorStore);
let innerBlocks = getBlocks(clientId);
const hasExplicitWidths = hasExplicitPercentColumnWidths(innerBlocks); // Redistribute available width for existing inner blocks.
const isAddingColumn = newColumns > previousColumns;
if (isAddingColumn && hasExplicitWidths) {
// If adding a new column, assign width to the new column equal to
// as if it were `1 / columns` of the total available space.
const newColumnWidth = toWidthPrecision(100 / newColumns); // Redistribute in consideration of pending block insertion as
// constraining the available working width.
const widths = getRedistributedColumnWidths(innerBlocks, 100 - newColumnWidth);
innerBlocks = [...getMappedColumnWidths(innerBlocks, widths), ...Array.from({
length: newColumns - previousColumns
}).map(() => {
return createBlock('core/column', {
width: `${newColumnWidth}%`
});
})];
} else if (isAddingColumn) {
innerBlocks = [...innerBlocks, ...Array.from({
length: newColumns - previousColumns
}).map(() => {
return createBlock('core/column');
})];
} else {
// The removed column will be the last of the inner blocks.
innerBlocks = innerBlocks.slice(0, -(previousColumns - newColumns));
if (hasExplicitWidths) {
// Redistribute as if block is already removed.
const widths = getRedistributedColumnWidths(innerBlocks, 100);
innerBlocks = getMappedColumnWidths(innerBlocks, widths);
}
}
replaceInnerBlocks(clientId, innerBlocks);
}
}))(ColumnsEditContainer);
function Placeholder(_ref2) {
let {
clientId,
name,
setAttributes
} = _ref2;
const {
blockType,
defaultVariation,
variations
} = useSelect(select => {
const {
getBlockVariations,
getBlockType,
getDefaultBlockVariation
} = select(blocksStore);
return {
blockType: getBlockType(name),
defaultVariation: getDefaultBlockVariation(name, 'block'),
variations: getBlockVariations(name, 'block')
};
}, [name]);
const {
replaceInnerBlocks
} = useDispatch(blockEditorStore);
const blockProps = useBlockProps();
return createElement("div", blockProps, createElement(__experimentalBlockVariationPicker, {
icon: get(blockType, ['icon', 'src']),
label: get(blockType, ['title']),
variations: variations,
onSelect: function () {
let nextVariation = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultVariation;
if (nextVariation.attributes) {
setAttributes(nextVariation.attributes);
}
if (nextVariation.innerBlocks) {
replaceInnerBlocks(clientId, createBlocksFromInnerBlocksTemplate(nextVariation.innerBlocks), true);
}
},
allowSkip: true
}));
}
const ColumnsEdit = props => {
const {
clientId
} = props;
const hasInnerBlocks = useSelect(select => select(blockEditorStore).getBlocks(clientId).length > 0, [clientId]);
const Component = hasInnerBlocks ? ColumnsEditContainerWrapper : Placeholder;
return createElement(Component, props);
};
export default ColumnsEdit;
//# sourceMappingURL=edit.js.map