UNPKG

@wordpress/block-editor

Version:
345 lines (344 loc) 11.5 kB
// packages/block-editor/src/hooks/layout.js import clsx from "clsx"; import { createHigherOrderComponent, useInstanceId } from "@wordpress/compose"; import { addFilter } from "@wordpress/hooks"; import { getBlockSupport, hasBlockSupport } from "@wordpress/blocks"; import { useSelect } from "@wordpress/data"; import { __experimentalToggleGroupControl as ToggleGroupControl, __experimentalToggleGroupControlOption as ToggleGroupControlOption, ToggleControl, PanelBody, privateApis as componentsPrivateApis } from "@wordpress/components"; import { __ } from "@wordpress/i18n"; import { store as blockEditorStore } from "../store"; import { InspectorControls } from "../components"; import { useSettings } from "../components/use-settings"; import { getLayoutType, getLayoutTypes } from "../layouts"; import { useBlockEditingMode } from "../components/block-editing-mode"; import { LAYOUT_DEFINITIONS } from "../layouts/definitions"; import { useBlockSettings, useStyleOverride } from "./utils"; import { unlock } from "../lock-unlock"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; var layoutBlockSupportKey = "layout"; var { kebabCase } = unlock(componentsPrivateApis); function hasLayoutBlockSupport(blockName) { return hasBlockSupport(blockName, "layout") || hasBlockSupport(blockName, "__experimentalLayout"); } function useLayoutClasses(blockAttributes = {}, blockName = "") { const { layout } = blockAttributes; const { default: defaultBlockLayout } = getBlockSupport(blockName, layoutBlockSupportKey) || {}; const usedLayout = layout?.inherit || layout?.contentSize || layout?.wideSize ? { ...layout, type: "constrained" } : layout || defaultBlockLayout || {}; const layoutClassnames = []; if (LAYOUT_DEFINITIONS[usedLayout?.type || "default"]?.className) { const baseClassName = 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 = useSelect( (select) => { if (!usedLayout?.inherit && !usedLayout?.contentSize && usedLayout?.type !== "constrained") { return false; } return select(blockEditorStore).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; } function useLayoutStyles(blockAttributes = {}, blockName, selector) { const { layout = {}, style = {} } = blockAttributes; const usedLayout = layout?.inherit || layout?.contentSize || layout?.wideSize ? { ...layout, type: "constrained" } : layout || {}; const fullLayoutType = getLayoutType(usedLayout?.type || "default"); const [blockGapSupport] = useSettings("spacing.blockGap"); const hasBlockGapSupport = blockGapSupport !== null; return fullLayoutType?.getLayoutStyle?.({ blockName, selector, layout, style, hasBlockGapSupport }); } function LayoutPanelPure({ layout, setAttributes, name: blockName, clientId }) { const settings = useBlockSettings(blockName); const { layout: layoutSettings } = settings; const { themeSupportsLayout } = useSelect((select) => { const { getSettings } = select(blockEditorStore); return { themeSupportsLayout: getSettings().supportsLayout }; }, []); const blockEditingMode = useBlockEditingMode(); if (blockEditingMode !== "default") { return null; } const layoutBlockSupport = getBlockSupport( blockName, layoutBlockSupportKey, {} ); const blockSupportAndThemeSettings = { ...layoutSettings, ...layoutBlockSupport }; const { allowSwitching, allowEditing = true, allowInheriting = true, default: defaultBlockLayout } = blockSupportAndThemeSettings; if (!allowEditing) { return null; } const blockSupportAndLayout = { ...layoutBlockSupport, ...layout }; const { type, default: { type: defaultType = "default" } = {} } = blockSupportAndLayout; const blockLayoutType = type || defaultType; const showInheritToggle = !!(allowInheriting && (!blockLayoutType || blockLayoutType === "default" || blockLayoutType === "constrained" || blockSupportAndLayout.inherit)); const usedLayout = layout || defaultBlockLayout || {}; const { inherit = false, contentSize = null } = usedLayout; if ((blockLayoutType === "default" || blockLayoutType === "constrained") && !themeSupportsLayout) { return null; } const layoutType = getLayoutType(blockLayoutType); const constrainedType = 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__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx(InspectorControls, { children: /* @__PURE__ */ jsxs(PanelBody, { title: __("Layout"), children: [ showInheritToggle && /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx( ToggleControl, { __nextHasNoMarginBottom: true, label: __("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 ? __( "Nested blocks use content width with options for full and wide widths." ) : __( "Nested blocks will fill the width of this container." ) } ) }), !inherit && allowSwitching && /* @__PURE__ */ jsx( LayoutTypeSwitcher, { type: blockLayoutType, onChange: onChangeType } ), layoutType && layoutType.name !== "default" && /* @__PURE__ */ jsx( layoutType.inspectorControls, { layout: usedLayout, onChange: onChangeLayout, layoutBlockSupport: blockSupportAndThemeSettings, name: blockName, clientId } ), constrainedType && displayControlsForLegacyLayouts && /* @__PURE__ */ jsx( constrainedType.inspectorControls, { layout: usedLayout, onChange: onChangeLayout, layoutBlockSupport: blockSupportAndThemeSettings, name: blockName, clientId } ) ] }) }), !inherit && layoutType && /* @__PURE__ */ jsx( layoutType.toolBarControls, { layout: usedLayout, onChange: onChangeLayout, layoutBlockSupport, name: blockName, clientId } ) ] }); } var layout_default = { shareWithChildBlocks: true, edit: LayoutPanelPure, attributeKeys: ["layout"], hasSupport(name) { return hasLayoutBlockSupport(name); } }; function LayoutTypeSwitcher({ type, onChange }) { return /* @__PURE__ */ jsx( ToggleGroupControl, { __next40pxDefaultSize: true, isBlock: true, label: __("Layout type"), __nextHasNoMarginBottom: true, hideLabelFromVision: true, isAdaptiveWidth: true, value: type, onChange, children: getLayoutTypes().map(({ name, label }) => { return /* @__PURE__ */ jsx( ToggleGroupControlOption, { value: name, label }, name ); }) } ); } function addAttribute(settings) { if ("type" in (settings.attributes?.layout ?? {})) { 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 = useInstanceId(BlockListBlock); const { layout } = attributes; const { default: defaultBlockLayout } = getBlockSupport(name, layoutBlockSupportKey) || {}; const usedLayout = layout?.inherit || layout?.contentSize || layout?.wideSize ? { ...layout, type: "constrained" } : layout || defaultBlockLayout || {}; const selectorPrefix = `wp-container-${kebabCase(name)}-is-layout-`; const selector = `.${selectorPrefix}${id}`; const hasBlockGapSupport = blockGapSupport !== null; const fullLayoutType = getLayoutType(usedLayout?.type || "default"); const css = fullLayoutType?.getLayoutStyle?.({ blockName: name, selector, layout: usedLayout, style: attributes?.style, hasBlockGapSupport }); const layoutClassNames = clsx( { [`${selectorPrefix}${id}`]: !!css // Only attach a container class if there is generated CSS to be attached. }, layoutClasses ); useStyleOverride({ css }); return /* @__PURE__ */ jsx( BlockListBlock, { ...props, __unstableLayoutClassNames: layoutClassNames } ); } var withLayoutStyles = createHigherOrderComponent( (BlockListBlock) => (props) => { const { clientId, name, attributes } = props; const blockSupportsLayout = hasLayoutBlockSupport(name); const layoutClasses = useLayoutClasses(attributes, name); const extraProps = useSelect( (select) => { if (!blockSupportsLayout) { return; } const { getSettings, getBlockSettings } = unlock( select(blockEditorStore) ); const { disableLayoutStyles } = getSettings(); if (disableLayoutStyles) { return; } const [blockGapSupport] = getBlockSettings( clientId, "spacing.blockGap" ); return { blockGapSupport }; }, [blockSupportsLayout, clientId] ); if (!extraProps) { return /* @__PURE__ */ jsx( BlockListBlock, { ...props, __unstableLayoutClassNames: blockSupportsLayout ? layoutClasses : void 0 } ); } return /* @__PURE__ */ jsx( BlockWithLayoutStyles, { block: BlockListBlock, props, layoutClasses, ...extraProps } ); }, "withLayoutStyles" ); addFilter( "blocks.registerBlockType", "core/layout/addAttribute", addAttribute ); addFilter( "editor.BlockListBlock", "core/editor/layout/with-layout-styles", withLayoutStyles ); export { addAttribute, layout_default as default, useLayoutClasses, useLayoutStyles, withLayoutStyles }; //# sourceMappingURL=layout.js.map