UNPKG

@wordpress/block-editor

Version:
406 lines (393 loc) 14 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.addAttribute = addAttribute; exports.default = void 0; exports.useLayoutClasses = useLayoutClasses; exports.useLayoutStyles = useLayoutStyles; exports.withLayoutStyles = void 0; var _clsx = _interopRequireDefault(require("clsx")); 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 _useSettings = require("../components/use-settings"); var _layouts = require("../layouts"); var _blockEditingMode = require("../components/block-editing-mode"); var _definitions = require("../layouts/definitions"); var _utils = require("./utils"); var _lockUnlock = require("../lock-unlock"); var _jsxRuntime = require("react/jsx-runtime"); /** * External dependencies */ /** * WordPress dependencies */ /** * Internal dependencies */ const layoutBlockSupportKey = 'layout'; const { kebabCase } = (0, _lockUnlock.unlock)(_components.privateApis); function hasLayoutBlockSupport(blockName) { return (0, _blocks.hasBlockSupport)(blockName, 'layout') || (0, _blocks.hasBlockSupport)(blockName, '__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 { 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 (_definitions.LAYOUT_DEFINITIONS[usedLayout?.type || 'default']?.className) { const baseClassName = _definitions.LAYOUT_DEFINITIONS[usedLayout?.type || 'default']?.className; const splitBlockName = blockName.split('/'); const fullBlockName = splitBlockName[0] === 'core' ? splitBlockName.pop() : splitBlockName.join('-'); const compoundClassName = `wp-block-${fullBlockName}-${baseClassName}`; layoutClassnames.push(baseClassName, compoundClassName); } const hasGlobalPadding = (0, _data.useSelect)(select => { return (usedLayout?.inherit || usedLayout?.contentSize || usedLayout?.type === 'constrained') && select(_store.store).getSettings().__experimentalFeatures?.useRootPaddingAwareAlignments; }, [usedLayout?.contentSize, usedLayout?.inherit, usedLayout?.type]); if (hasGlobalPadding) { layoutClassnames.push('has-global-padding'); } if (usedLayout?.orientation) { layoutClassnames.push(`is-${kebabCase(usedLayout.orientation)}`); } if (usedLayout?.justifyContent) { layoutClassnames.push(`is-content-justification-${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 [blockGapSupport] = (0, _useSettings.useSettings)('spacing.blockGap'); const hasBlockGapSupport = blockGapSupport !== null; return fullLayoutType?.getLayoutStyle?.({ blockName, selector, layout, style, hasBlockGapSupport }); } function LayoutPanelPure({ layout, setAttributes, name: blockName, clientId }) { const settings = (0, _utils.useBlockSettings)(blockName); // Block settings come from theme.json under settings.[blockName]. const { layout: layoutSettings } = settings; const { themeSupportsLayout } = (0, _data.useSelect)(select => { const { getSettings } = select(_store.store); return { themeSupportsLayout: getSettings().supportsLayout }; }, []); const blockEditingMode = (0, _blockEditingMode.useBlockEditingMode)(); if (blockEditingMode !== 'default') { return null; } // Layout block support comes from the block's block.json. const layoutBlockSupport = (0, _blocks.getBlockSupport)(blockName, layoutBlockSupportKey, {}); const blockSupportAndThemeSettings = { ...layoutSettings, ...layoutBlockSupport }; const { allowSwitching, allowEditing = true, allowInheriting = true, default: defaultBlockLayout } = blockSupportAndThemeSettings; if (!allowEditing) { return null; } /* * Try to find the layout type from either the * block's layout settings or any saved layout config. */ const blockSupportAndLayout = { ...layoutBlockSupport, ...layout }; const { type, default: { type: defaultType = 'default' } = {} } = blockSupportAndLayout; const blockLayoutType = type || defaultType; // Only show the inherit toggle if it's supported, // 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 && (!blockLayoutType || blockLayoutType === 'default' || blockLayoutType === 'constrained' || blockSupportAndLayout.inherit)); const usedLayout = layout || defaultBlockLayout || {}; const { inherit = false, 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 ((blockLayoutType === 'default' || blockLayoutType === 'constrained') && !themeSupportsLayout) { return null; } const layoutType = (0, _layouts.getLayoutType)(blockLayoutType); 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 /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_components2.InspectorControls, { children: /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.PanelBody, { title: (0, _i18n.__)('Layout'), children: [showInheritToggle && /*#__PURE__*/(0, _jsxRuntime.jsx)(_jsxRuntime.Fragment, { children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.ToggleControl, { __nextHasNoMarginBottom: true, 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.') }) }), !inherit && allowSwitching && /*#__PURE__*/(0, _jsxRuntime.jsx)(LayoutTypeSwitcher, { type: blockLayoutType, onChange: onChangeType }), layoutType && layoutType.name !== 'default' && /*#__PURE__*/(0, _jsxRuntime.jsx)(layoutType.inspectorControls, { layout: usedLayout, onChange: onChangeLayout, layoutBlockSupport: blockSupportAndThemeSettings, name: blockName, clientId: clientId }), constrainedType && displayControlsForLegacyLayouts && /*#__PURE__*/(0, _jsxRuntime.jsx)(constrainedType.inspectorControls, { layout: usedLayout, onChange: onChangeLayout, layoutBlockSupport: blockSupportAndThemeSettings, name: blockName, clientId: clientId })] }) }), !inherit && layoutType && /*#__PURE__*/(0, _jsxRuntime.jsx)(layoutType.toolBarControls, { layout: usedLayout, onChange: onChangeLayout, layoutBlockSupport: layoutBlockSupport, name: blockName, clientId: clientId })] }); } var _default = exports.default = { shareWithChildBlocks: true, edit: LayoutPanelPure, attributeKeys: ['layout'], hasSupport(name) { return hasLayoutBlockSupport(name); } }; function LayoutTypeSwitcher({ type, onChange }) { return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.__experimentalToggleGroupControl, { __next40pxDefaultSize: true, isBlock: true, label: (0, _i18n.__)('Layout type'), __nextHasNoMarginBottom: true, hideLabelFromVision: true, isAdaptiveWidth: true, value: type, onChange: onChange, children: (0, _layouts.getLayoutTypes)().map(({ name, label }) => { return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.__experimentalToggleGroupControlOption, { value: name, label: label }, name); }) }); } /** * 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 (hasLayoutBlockSupport(settings)) { settings.attributes = { ...settings.attributes, layout: { type: 'object' } }; } return settings; } function BlockWithLayoutStyles({ block: BlockListBlock, props, blockGapSupport, layoutClasses }) { const { name, attributes } = props; const id = (0, _compose.useInstanceId)(BlockListBlock); 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 selectorPrefix = `wp-container-${kebabCase(name)}-is-layout-`; // Higher specificity to override defaults from theme.json. const selector = `.${selectorPrefix}${id}`; 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. const fullLayoutType = (0, _layouts.getLayoutType)(usedLayout?.type || 'default'); const css = fullLayoutType?.getLayoutStyle?.({ blockName: name, selector, layout: usedLayout, 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, _clsx.default)({ [`${selectorPrefix}${id}`]: !!css // Only attach a container class if there is generated CSS to be attached. }, layoutClasses); (0, _utils.useStyleOverride)({ css }); return /*#__PURE__*/(0, _jsxRuntime.jsx)(BlockListBlock, { ...props, __unstableLayoutClassNames: layoutClassNames }); } /** * Override the default block element to add the layout styles. * * @param {Function} BlockListBlock Original component. * * @return {Function} Wrapped component. */ const withLayoutStyles = exports.withLayoutStyles = (0, _compose.createHigherOrderComponent)(BlockListBlock => props => { const { clientId, name, attributes } = props; const blockSupportsLayout = hasLayoutBlockSupport(name); const layoutClasses = useLayoutClasses(attributes, name); const extraProps = (0, _data.useSelect)(select => { // The callback returns early to avoid block editor subscription. if (!blockSupportsLayout) { return; } const { getSettings, getBlockSettings } = (0, _lockUnlock.unlock)(select(_store.store)); const { disableLayoutStyles } = getSettings(); if (disableLayoutStyles) { return; } const [blockGapSupport] = getBlockSettings(clientId, 'spacing.blockGap'); return { blockGapSupport }; }, [blockSupportsLayout, clientId]); if (!extraProps) { return /*#__PURE__*/(0, _jsxRuntime.jsx)(BlockListBlock, { ...props, __unstableLayoutClassNames: blockSupportsLayout ? layoutClasses : undefined }); } return /*#__PURE__*/(0, _jsxRuntime.jsx)(BlockWithLayoutStyles, { block: BlockListBlock, props: props, layoutClasses: layoutClasses, ...extraProps }); }, 'withLayoutStyles'); (0, _hooks.addFilter)('blocks.registerBlockType', 'core/layout/addAttribute', addAttribute); (0, _hooks.addFilter)('editor.BlockListBlock', 'core/editor/layout/with-layout-styles', withLayoutStyles); //# sourceMappingURL=layout.js.map