@wordpress/block-editor
Version:
436 lines (375 loc) • 15.4 kB
JavaScript
"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