UNPKG

@wordpress/block-library

Version:
231 lines (211 loc) 7.52 kB
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