UNPKG

@wordpress/block-editor

Version:
436 lines (375 loc) 15.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.addAttribute = addAttribute; exports.useLayoutClasses = useLayoutClasses; exports.useLayoutStyles = useLayoutStyles; exports.withLayoutStyles = exports.withInspectorControls = exports.withChildLayoutStyles = void 0; var _element = require("@wordpress/element"); var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _classnames = _interopRequireDefault(require("classnames")); var _lodash = require("lodash"); var _compose = require("@wordpress/compose"); var _hooks = require("@wordpress/hooks"); var _blocks = require("@wordpress/blocks"); var _data = require("@wordpress/data"); var _components = require("@wordpress/components"); var _i18n = require("@wordpress/i18n"); var _store = require("../store"); var _components2 = require("../components"); var _useSetting = _interopRequireDefault(require("../components/use-setting")); var _layout = require("../components/block-list/layout"); var _blockList = _interopRequireDefault(require("../components/block-list")); var _layouts = require("../layouts"); var _blockEditingMode = require("../components/block-editing-mode"); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ const layoutBlockSupportKey = '__experimentalLayout'; /** * Generates the utility classnames for the given block's layout attributes. * * @param { Object } blockAttributes Block attributes. * @param { string } blockName Block name. * * @return { Array } Array of CSS classname strings. */ function useLayoutClasses(blockAttributes = {}, blockName = '') { const rootPaddingAlignment = (0, _data.useSelect)(select => { const { getSettings } = select(_store.store); return getSettings().__experimentalFeatures?.useRootPaddingAwareAlignments; }, []); const globalLayoutSettings = (0, _useSetting.default)('layout') || {}; const { layout } = blockAttributes; const { default: defaultBlockLayout } = (0, _blocks.getBlockSupport)(blockName, layoutBlockSupportKey) || {}; const usedLayout = layout?.inherit || layout?.contentSize || layout?.wideSize ? { ...layout, type: 'constrained' } : layout || defaultBlockLayout || {}; const layoutClassnames = []; if (globalLayoutSettings?.definitions?.[usedLayout?.type || 'default']?.className) { const baseClassName = globalLayoutSettings?.definitions?.[usedLayout?.type || 'default']?.className; const compoundClassName = `wp-block-${blockName.split('/').pop()}-${baseClassName}`; layoutClassnames.push(baseClassName, compoundClassName); } if ((usedLayout?.inherit || usedLayout?.contentSize || usedLayout?.type === 'constrained') && rootPaddingAlignment) { layoutClassnames.push('has-global-padding'); } if (usedLayout?.orientation) { layoutClassnames.push(`is-${(0, _lodash.kebabCase)(usedLayout.orientation)}`); } if (usedLayout?.justifyContent) { layoutClassnames.push(`is-content-justification-${(0, _lodash.kebabCase)(usedLayout.justifyContent)}`); } if (usedLayout?.flexWrap && usedLayout.flexWrap === 'nowrap') { layoutClassnames.push('is-nowrap'); } return layoutClassnames; } /** * Generates a CSS rule with the given block's layout styles. * * @param { Object } blockAttributes Block attributes. * @param { string } blockName Block name. * @param { string } selector A selector to use in generating the CSS rule. * * @return { string } CSS rule. */ function useLayoutStyles(blockAttributes = {}, blockName, selector) { const { layout = {}, style = {} } = blockAttributes; // Update type for blocks using legacy layouts. const usedLayout = layout?.inherit || layout?.contentSize || layout?.wideSize ? { ...layout, type: 'constrained' } : layout || {}; const fullLayoutType = (0, _layouts.getLayoutType)(usedLayout?.type || 'default'); const globalLayoutSettings = (0, _useSetting.default)('layout') || {}; const blockGapSupport = (0, _useSetting.default)('spacing.blockGap'); const hasBlockGapSupport = blockGapSupport !== null; const css = fullLayoutType?.getLayoutStyle?.({ blockName, selector, layout, layoutDefinitions: globalLayoutSettings?.definitions, style, hasBlockGapSupport }); return css; } function LayoutPanel({ setAttributes, attributes, name: blockName }) { const { layout } = attributes; const defaultThemeLayout = (0, _useSetting.default)('layout'); const { themeSupportsLayout } = (0, _data.useSelect)(select => { const { getSettings } = select(_store.store); return { themeSupportsLayout: getSettings().supportsLayout }; }, []); const blockEditingMode = (0, _blockEditingMode.useBlockEditingMode)(); const layoutBlockSupport = (0, _blocks.getBlockSupport)(blockName, layoutBlockSupportKey, {}); const { allowSwitching, allowEditing = true, allowInheriting = true, default: defaultBlockLayout } = layoutBlockSupport; if (!allowEditing) { return null; } // Only show the inherit toggle if it's supported, // a default theme layout is set (e.g. one that provides `contentSize` and/or `wideSize` values), // and either the default / flow or the constrained layout type is in use, as the toggle switches from one to the other. const showInheritToggle = !!(allowInheriting && !!defaultThemeLayout && (!layout?.type || layout?.type === 'default' || layout?.type === 'constrained' || layout?.inherit)); const usedLayout = layout || defaultBlockLayout || {}; const { inherit = false, type = 'default', contentSize = null } = usedLayout; /** * `themeSupportsLayout` is only relevant to the `default/flow` or * `constrained` layouts and it should not be taken into account when other * `layout` types are used. */ if ((type === 'default' || type === 'constrained') && !themeSupportsLayout) { return null; } const layoutType = (0, _layouts.getLayoutType)(type); const constrainedType = (0, _layouts.getLayoutType)('constrained'); const displayControlsForLegacyLayouts = !usedLayout.type && (contentSize || inherit); const hasContentSizeOrLegacySettings = !!inherit || !!contentSize; const onChangeType = newType => setAttributes({ layout: { type: newType } }); const onChangeLayout = newLayout => setAttributes({ layout: newLayout }); return (0, _element.createElement)(_element.Fragment, null, (0, _element.createElement)(_components2.InspectorControls, null, (0, _element.createElement)(_components.PanelBody, { title: (0, _i18n.__)('Layout') }, showInheritToggle && (0, _element.createElement)(_element.Fragment, null, (0, _element.createElement)(_components.ToggleControl, { __nextHasNoMarginBottom: true, className: "block-editor-hooks__toggle-control", label: (0, _i18n.__)('Inner blocks use content width'), checked: layoutType?.name === 'constrained' || hasContentSizeOrLegacySettings, onChange: () => setAttributes({ layout: { type: layoutType?.name === 'constrained' || hasContentSizeOrLegacySettings ? 'default' : 'constrained' } }), help: layoutType?.name === 'constrained' || hasContentSizeOrLegacySettings ? (0, _i18n.__)('Nested blocks use content width with options for full and wide widths.') : (0, _i18n.__)('Nested blocks will fill the width of this container. Toggle to constrain.') })), !inherit && allowSwitching && (0, _element.createElement)(LayoutTypeSwitcher, { type: type, onChange: onChangeType }), layoutType && layoutType.name !== 'default' && (0, _element.createElement)(layoutType.inspectorControls, { layout: usedLayout, onChange: onChangeLayout, layoutBlockSupport: layoutBlockSupport }), constrainedType && displayControlsForLegacyLayouts && (0, _element.createElement)(constrainedType.inspectorControls, { layout: usedLayout, onChange: onChangeLayout, layoutBlockSupport: layoutBlockSupport }))), !inherit && blockEditingMode === 'default' && layoutType && (0, _element.createElement)(layoutType.toolBarControls, { layout: usedLayout, onChange: onChangeLayout, layoutBlockSupport: layoutBlockSupport })); } function LayoutTypeSwitcher({ type, onChange }) { return (0, _element.createElement)(_components.ButtonGroup, null, (0, _layouts.getLayoutTypes)().map(({ name, label }) => { return (0, _element.createElement)(_components.Button, { key: name, isPressed: type === name, onClick: () => onChange(name) }, label); })); } /** * Filters registered block settings, extending attributes to include `layout`. * * @param {Object} settings Original block settings. * * @return {Object} Filtered block settings. */ function addAttribute(settings) { var _settings$attributes$; if ('type' in ((_settings$attributes$ = settings.attributes?.layout) !== null && _settings$attributes$ !== void 0 ? _settings$attributes$ : {})) { return settings; } if ((0, _blocks.hasBlockSupport)(settings, layoutBlockSupportKey)) { settings.attributes = { ...settings.attributes, layout: { type: 'object' } }; } return settings; } /** * Override the default edit UI to include layout controls * * @param {Function} BlockEdit Original component. * * @return {Function} Wrapped component. */ const withInspectorControls = (0, _compose.createHigherOrderComponent)(BlockEdit => props => { const { name: blockName } = props; const supportLayout = (0, _blocks.hasBlockSupport)(blockName, layoutBlockSupportKey); const blockEditingMode = (0, _blockEditingMode.useBlockEditingMode)(); return [supportLayout && blockEditingMode === 'default' && (0, _element.createElement)(LayoutPanel, (0, _extends2.default)({ key: "layout" }, props)), (0, _element.createElement)(BlockEdit, (0, _extends2.default)({ key: "edit" }, props))]; }, 'withInspectorControls'); /** * Override the default block element to add the layout styles. * * @param {Function} BlockListBlock Original component. * * @return {Function} Wrapped component. */ exports.withInspectorControls = withInspectorControls; const withLayoutStyles = (0, _compose.createHigherOrderComponent)(BlockListBlock => props => { const { name, attributes } = props; const hasLayoutBlockSupport = (0, _blocks.hasBlockSupport)(name, layoutBlockSupportKey); const disableLayoutStyles = (0, _data.useSelect)(select => { const { getSettings } = select(_store.store); return !!getSettings().disableLayoutStyles; }); const shouldRenderLayoutStyles = hasLayoutBlockSupport && !disableLayoutStyles; const id = (0, _compose.useInstanceId)(BlockListBlock); const defaultThemeLayout = (0, _useSetting.default)('layout') || {}; const element = (0, _element.useContext)(_blockList.default.__unstableElementContext); const { layout } = attributes; const { default: defaultBlockLayout } = (0, _blocks.getBlockSupport)(name, layoutBlockSupportKey) || {}; const usedLayout = layout?.inherit || layout?.contentSize || layout?.wideSize ? { ...layout, type: 'constrained' } : layout || defaultBlockLayout || {}; const layoutClasses = hasLayoutBlockSupport ? useLayoutClasses(attributes, name) : null; // Higher specificity to override defaults from theme.json. const selector = `.wp-container-${id}.wp-container-${id}`; const blockGapSupport = (0, _useSetting.default)('spacing.blockGap'); const hasBlockGapSupport = blockGapSupport !== null; // Get CSS string for the current layout type. // The CSS and `style` element is only output if it is not empty. let css; if (shouldRenderLayoutStyles) { const fullLayoutType = (0, _layouts.getLayoutType)(usedLayout?.type || 'default'); css = fullLayoutType?.getLayoutStyle?.({ blockName: name, selector, layout: usedLayout, layoutDefinitions: defaultThemeLayout?.definitions, style: attributes?.style, hasBlockGapSupport }); } // Attach a `wp-container-` id-based class name as well as a layout class name such as `is-layout-flex`. const layoutClassNames = (0, _classnames.default)({ [`wp-container-${id}`]: shouldRenderLayoutStyles && !!css // Only attach a container class if there is generated CSS to be attached. }, layoutClasses); return (0, _element.createElement)(_element.Fragment, null, shouldRenderLayoutStyles && element && !!css && (0, _element.createPortal)((0, _element.createElement)(_layout.LayoutStyle, { blockName: name, selector: selector, css: css, layout: usedLayout, style: attributes?.style }), element), (0, _element.createElement)(BlockListBlock, (0, _extends2.default)({}, props, { __unstableLayoutClassNames: layoutClassNames }))); }, 'withLayoutStyles'); /** * Override the default block element to add the child layout styles. * * @param {Function} BlockListBlock Original component. * * @return {Function} Wrapped component. */ exports.withLayoutStyles = withLayoutStyles; const withChildLayoutStyles = (0, _compose.createHigherOrderComponent)(BlockListBlock => props => { const { attributes } = props; const { style: { layout = {} } = {} } = attributes; const { selfStretch, flexSize } = layout; const hasChildLayout = selfStretch || flexSize; const disableLayoutStyles = (0, _data.useSelect)(select => { const { getSettings } = select(_store.store); return !!getSettings().disableLayoutStyles; }); const shouldRenderChildLayoutStyles = hasChildLayout && !disableLayoutStyles; const element = (0, _element.useContext)(_blockList.default.__unstableElementContext); const id = (0, _compose.useInstanceId)(BlockListBlock); const selector = `.wp-container-content-${id}`; let css = ''; if (selfStretch === 'fixed' && flexSize) { css += `${selector} { flex-basis: ${flexSize}; box-sizing: border-box; }`; } else if (selfStretch === 'fill') { css += `${selector} { flex-grow: 1; }`; } // Attach a `wp-container-content` id-based classname. const className = (0, _classnames.default)(props?.className, { [`wp-container-content-${id}`]: shouldRenderChildLayoutStyles && !!css // Only attach a container class if there is generated CSS to be attached. }); return (0, _element.createElement)(_element.Fragment, null, shouldRenderChildLayoutStyles && element && !!css && (0, _element.createPortal)((0, _element.createElement)("style", null, css), element), (0, _element.createElement)(BlockListBlock, (0, _extends2.default)({}, props, { className: className }))); }, 'withChildLayoutStyles'); exports.withChildLayoutStyles = withChildLayoutStyles; (0, _hooks.addFilter)('blocks.registerBlockType', 'core/layout/addAttribute', addAttribute); (0, _hooks.addFilter)('editor.BlockListBlock', 'core/editor/layout/with-layout-styles', withLayoutStyles); (0, _hooks.addFilter)('editor.BlockListBlock', 'core/editor/layout/with-child-layout-styles', withChildLayoutStyles); (0, _hooks.addFilter)('editor.BlockEdit', 'core/editor/layout/with-inspector-controls', withInspectorControls); //# sourceMappingURL=layout.js.map