UNPKG

@wordpress/block-editor

Version:
522 lines (451 loc) 17.5 kB
import { createElement } from "@wordpress/element"; /** * External dependencies */ import classnames from 'classnames'; /** * WordPress dependencies */ import { __ } from '@wordpress/i18n'; import { __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalBoxControl as BoxControl, __experimentalHStack as HStack, __experimentalVStack as VStack, __experimentalUnitControl as UnitControl, __experimentalUseCustomUnits as useCustomUnits, __experimentalView as View } from '@wordpress/components'; import { Icon, positionCenter, stretchWide } from '@wordpress/icons'; import { useCallback, Platform } from '@wordpress/element'; /** * Internal dependencies */ import { getValueFromVariable } from './utils'; import SpacingSizesControl from '../spacing-sizes-control'; import HeightControl from '../height-control'; import ChildLayoutControl from '../child-layout-control'; import { cleanEmptyObject } from '../../hooks/utils'; import { setImmutably } from '../../utils/object'; const AXIAL_SIDES = ['horizontal', 'vertical']; export function useHasDimensionsPanel(settings) { const hasContentSize = useHasContentSize(settings); const hasWideSize = useHasWideSize(settings); const hasPadding = useHasPadding(settings); const hasMargin = useHasMargin(settings); const hasGap = useHasGap(settings); const hasMinHeight = useHasMinHeight(settings); const hasChildLayout = useHasChildLayout(settings); return Platform.OS === 'web' && (hasContentSize || hasWideSize || hasPadding || hasMargin || hasGap || hasMinHeight || hasChildLayout); } function useHasContentSize(settings) { return settings?.layout?.contentSize; } function useHasWideSize(settings) { return settings?.layout?.wideSize; } function useHasPadding(settings) { return settings?.spacing?.padding; } function useHasMargin(settings) { return settings?.spacing?.margin; } function useHasGap(settings) { return settings?.spacing?.blockGap; } function useHasMinHeight(settings) { return settings?.dimensions?.minHeight; } function useHasChildLayout(settings) { var _settings$parentLayou; const { type: parentLayoutType = 'default', default: { type: defaultParentLayoutType = 'default' } = {}, allowSizingOnChildren = false } = (_settings$parentLayou = settings?.parentLayout) !== null && _settings$parentLayou !== void 0 ? _settings$parentLayou : {}; const support = (defaultParentLayoutType === 'flex' || parentLayoutType === 'flex') && allowSizingOnChildren; return !!settings?.layout && support; } function useHasSpacingPresets(settings) { var _ref, _ref2; const { custom, theme, default: defaultPresets } = settings?.spacing?.spacingSizes || {}; const presets = (_ref = (_ref2 = custom !== null && custom !== void 0 ? custom : theme) !== null && _ref2 !== void 0 ? _ref2 : defaultPresets) !== null && _ref !== void 0 ? _ref : []; return presets.length > 0; } function filterValuesBySides(values, sides) { // If no custom side configuration, all sides are opted into by default. // Without any values, we have nothing to filter either. if (!sides || !values) { return values; } // Only include sides opted into within filtered values. const filteredValues = {}; sides.forEach(side => { if (side === 'vertical') { filteredValues.top = values.top; filteredValues.bottom = values.bottom; } if (side === 'horizontal') { filteredValues.left = values.left; filteredValues.right = values.right; } filteredValues[side] = values?.[side]; }); return filteredValues; } function splitStyleValue(value) { // Check for shorthand value (a string value). if (value && typeof value === 'string') { // Convert to value for individual sides for BoxControl. return { top: value, right: value, bottom: value, left: value }; } return value; } function splitGapValue(value) { // Check for shorthand value (a string value). if (value && typeof value === 'string') { // If the value is a string, treat it as a single side (top) for the spacing controls. return { top: value }; } if (value) { return { ...value, right: value?.left, bottom: value?.top }; } return value; } function DimensionsToolsPanel({ resetAllFilter, onChange, value, panelId, children }) { const resetAll = () => { const updatedValue = resetAllFilter(value); onChange(updatedValue); }; return createElement(ToolsPanel, { label: __('Dimensions'), resetAll: resetAll, panelId: panelId }, children); } const DEFAULT_CONTROLS = { contentSize: true, wideSize: true, padding: true, margin: true, blockGap: true, minHeight: true, childLayout: true }; export default function DimensionsPanel({ as: Wrapper = DimensionsToolsPanel, value, onChange, inheritedValue = value, settings, panelId, defaultControls = DEFAULT_CONTROLS, onVisualize = () => {}, // Special case because the layout controls are not part of the dimensions panel // in global styles but not in block inspector. includeLayoutControls = false }) { var _settings$parentLayou2, _defaultControls$cont, _defaultControls$wide, _defaultControls$padd, _defaultControls$marg, _defaultControls$bloc, _defaultControls$minH, _defaultControls$chil; const decodeValue = rawValue => { if (rawValue && typeof rawValue === 'object') { return Object.keys(rawValue).reduce((acc, key) => { acc[key] = getValueFromVariable({ settings }, '', rawValue[key]); return acc; }, {}); } return getValueFromVariable({ settings }, '', rawValue); }; const showSpacingPresetsControl = useHasSpacingPresets(settings); const units = useCustomUnits({ availableUnits: settings?.spacing?.units || ['%', 'px', 'em', 'rem', 'vw'] }); // Content Size const showContentSizeControl = useHasContentSize(settings) && includeLayoutControls; const contentSizeValue = decodeValue(inheritedValue?.layout?.contentSize); const setContentSizeValue = newValue => { onChange(setImmutably(value, ['layout', 'contentSize'], newValue || undefined)); }; const hasUserSetContentSizeValue = () => !!value?.layout?.contentSize; const resetContentSizeValue = () => setContentSizeValue(undefined); // Wide Size const showWideSizeControl = useHasWideSize(settings) && includeLayoutControls; const wideSizeValue = decodeValue(inheritedValue?.layout?.wideSize); const setWideSizeValue = newValue => { onChange(setImmutably(value, ['layout', 'wideSize'], newValue || undefined)); }; const hasUserSetWideSizeValue = () => !!value?.layout?.wideSize; const resetWideSizeValue = () => setWideSizeValue(undefined); // Padding const showPaddingControl = useHasPadding(settings); const rawPadding = decodeValue(inheritedValue?.spacing?.padding); const paddingValues = splitStyleValue(rawPadding); const paddingSides = Array.isArray(settings?.spacing?.padding) ? settings?.spacing?.padding : settings?.spacing?.padding?.sides; const isAxialPadding = paddingSides && paddingSides.some(side => AXIAL_SIDES.includes(side)); const setPaddingValues = newPaddingValues => { const padding = filterValuesBySides(newPaddingValues, paddingSides); onChange(setImmutably(value, ['spacing', 'padding'], padding)); }; const hasPaddingValue = () => !!value?.spacing?.padding && Object.keys(value?.spacing?.padding).length; const resetPaddingValue = () => setPaddingValues(undefined); const onMouseOverPadding = () => onVisualize('padding'); // Margin const showMarginControl = useHasMargin(settings); const rawMargin = decodeValue(inheritedValue?.spacing?.margin); const marginValues = splitStyleValue(rawMargin); const marginSides = Array.isArray(settings?.spacing?.margin) ? settings?.spacing?.margin : settings?.spacing?.margin?.sides; const isAxialMargin = marginSides && marginSides.some(side => AXIAL_SIDES.includes(side)); const setMarginValues = newMarginValues => { const margin = filterValuesBySides(newMarginValues, marginSides); onChange(setImmutably(value, ['spacing', 'margin'], margin)); }; const hasMarginValue = () => !!value?.spacing?.margin && Object.keys(value?.spacing?.margin).length; const resetMarginValue = () => setMarginValues(undefined); const onMouseOverMargin = () => onVisualize('margin'); // Block Gap const showGapControl = useHasGap(settings); const gapValue = decodeValue(inheritedValue?.spacing?.blockGap); const gapValues = splitGapValue(gapValue); const gapSides = Array.isArray(settings?.spacing?.blockGap) ? settings?.spacing?.blockGap : settings?.spacing?.blockGap?.sides; const isAxialGap = gapSides && gapSides.some(side => AXIAL_SIDES.includes(side)); const setGapValue = newGapValue => { onChange(setImmutably(value, ['spacing', 'blockGap'], newGapValue)); }; const setGapValues = nextBoxGapValue => { if (!nextBoxGapValue) { setGapValue(null); } // If axial gap is not enabled, treat the 'top' value as the shorthand gap value. if (!isAxialGap && nextBoxGapValue?.hasOwnProperty('top')) { setGapValue(nextBoxGapValue.top); } else { setGapValue({ top: nextBoxGapValue?.top, left: nextBoxGapValue?.left }); } }; const resetGapValue = () => setGapValue(undefined); const hasGapValue = () => !!value?.spacing?.blockGap; // Min Height const showMinHeightControl = useHasMinHeight(settings); const minHeightValue = decodeValue(inheritedValue?.dimensions?.minHeight); const setMinHeightValue = newValue => { onChange(setImmutably(value, ['dimensions', 'minHeight'], newValue)); }; const resetMinHeightValue = () => { setMinHeightValue(undefined); }; const hasMinHeightValue = () => !!value?.dimensions?.minHeight; // Child Layout const showChildLayoutControl = useHasChildLayout(settings); const childLayout = inheritedValue?.layout; const { orientation = 'horizontal' } = (_settings$parentLayou2 = settings?.parentLayout) !== null && _settings$parentLayou2 !== void 0 ? _settings$parentLayou2 : {}; const childLayoutOrientationLabel = orientation === 'horizontal' ? __('Width') : __('Height'); const setChildLayout = newChildLayout => { onChange({ ...value, layout: { ...value?.layout, ...newChildLayout } }); }; const resetChildLayoutValue = () => { setChildLayout({ selfStretch: undefined, flexSize: undefined }); }; const hasChildLayoutValue = () => !!value?.layout; const resetAllFilter = useCallback(previousValue => { return { ...previousValue, layout: cleanEmptyObject({ ...previousValue?.layout, contentSize: undefined, wideSize: undefined, selfStretch: undefined, flexSize: undefined }), spacing: { ...previousValue?.spacing, padding: undefined, margin: undefined, blockGap: undefined }, dimensions: { ...previousValue?.dimensions, minHeight: undefined } }; }, []); const onMouseLeaveControls = () => onVisualize(false); return createElement(Wrapper, { resetAllFilter: resetAllFilter, value: value, onChange: onChange, panelId: panelId }, (showContentSizeControl || showWideSizeControl) && createElement("span", { className: "span-columns" }, __('Set the width of the main content area.')), showContentSizeControl && createElement(ToolsPanelItem, { className: "single-column", label: __('Content size'), hasValue: hasUserSetContentSizeValue, onDeselect: resetContentSizeValue, isShownByDefault: (_defaultControls$cont = defaultControls.contentSize) !== null && _defaultControls$cont !== void 0 ? _defaultControls$cont : DEFAULT_CONTROLS.contentSize, panelId: panelId }, createElement(HStack, { alignment: "flex-end", justify: "flex-start" }, createElement(UnitControl, { label: __('Content'), labelPosition: "top", __unstableInputWidth: "80px", value: contentSizeValue || '', onChange: nextContentSize => { setContentSizeValue(nextContentSize); }, units: units }), createElement(View, null, createElement(Icon, { icon: positionCenter })))), showWideSizeControl && createElement(ToolsPanelItem, { className: "single-column", label: __('Wide size'), hasValue: hasUserSetWideSizeValue, onDeselect: resetWideSizeValue, isShownByDefault: (_defaultControls$wide = defaultControls.wideSize) !== null && _defaultControls$wide !== void 0 ? _defaultControls$wide : DEFAULT_CONTROLS.wideSize, panelId: panelId }, createElement(HStack, { alignment: "flex-end", justify: "flex-start" }, createElement(UnitControl, { label: __('Wide'), labelPosition: "top", __unstableInputWidth: "80px", value: wideSizeValue || '', onChange: nextWideSize => { setWideSizeValue(nextWideSize); }, units: units }), createElement(View, null, createElement(Icon, { icon: stretchWide })))), showPaddingControl && createElement(ToolsPanelItem, { hasValue: hasPaddingValue, label: __('Padding'), onDeselect: resetPaddingValue, isShownByDefault: (_defaultControls$padd = defaultControls.padding) !== null && _defaultControls$padd !== void 0 ? _defaultControls$padd : DEFAULT_CONTROLS.padding, className: classnames({ 'tools-panel-item-spacing': showSpacingPresetsControl }), panelId: panelId }, !showSpacingPresetsControl && createElement(BoxControl, { values: paddingValues, onChange: setPaddingValues, label: __('Padding'), sides: paddingSides, units: units, allowReset: false, splitOnAxis: isAxialPadding, onMouseOver: onMouseOverPadding, onMouseOut: onMouseLeaveControls }), showSpacingPresetsControl && createElement(SpacingSizesControl, { values: paddingValues, onChange: setPaddingValues, label: __('Padding'), sides: paddingSides, units: units, allowReset: false, onMouseOver: onMouseOverPadding, onMouseOut: onMouseLeaveControls })), showMarginControl && createElement(ToolsPanelItem, { hasValue: hasMarginValue, label: __('Margin'), onDeselect: resetMarginValue, isShownByDefault: (_defaultControls$marg = defaultControls.margin) !== null && _defaultControls$marg !== void 0 ? _defaultControls$marg : DEFAULT_CONTROLS.margin, className: classnames({ 'tools-panel-item-spacing': showSpacingPresetsControl }), panelId: panelId }, !showSpacingPresetsControl && createElement(BoxControl, { values: marginValues, onChange: setMarginValues, label: __('Margin'), sides: marginSides, units: units, allowReset: false, splitOnAxis: isAxialMargin, onMouseOver: onMouseOverMargin, onMouseOut: onMouseLeaveControls }), showSpacingPresetsControl && createElement(SpacingSizesControl, { values: marginValues, onChange: setMarginValues, label: __('Margin'), sides: marginSides, units: units, allowReset: false, onMouseOver: onMouseOverMargin, onMouseOut: onMouseLeaveControls })), showGapControl && createElement(ToolsPanelItem, { hasValue: hasGapValue, label: __('Block spacing'), onDeselect: resetGapValue, isShownByDefault: (_defaultControls$bloc = defaultControls.blockGap) !== null && _defaultControls$bloc !== void 0 ? _defaultControls$bloc : DEFAULT_CONTROLS.blockGap, className: classnames({ 'tools-panel-item-spacing': showSpacingPresetsControl }), panelId: panelId }, !showSpacingPresetsControl && (isAxialGap ? createElement(BoxControl, { label: __('Block spacing'), min: 0, onChange: setGapValues, units: units, sides: gapSides, values: gapValues, allowReset: false, splitOnAxis: isAxialGap }) : createElement(UnitControl, { label: __('Block spacing'), __unstableInputWidth: "80px", min: 0, onChange: setGapValue, units: units, value: gapValue })), showSpacingPresetsControl && createElement(SpacingSizesControl, { label: __('Block spacing'), min: 0, onChange: setGapValues, showSideInLabel: false, sides: isAxialGap ? gapSides : ['top'] // Use 'top' as the shorthand property in non-axial configurations. , values: gapValues, allowReset: false })), showMinHeightControl && createElement(ToolsPanelItem, { hasValue: hasMinHeightValue, label: __('Min. height'), onDeselect: resetMinHeightValue, isShownByDefault: (_defaultControls$minH = defaultControls.minHeight) !== null && _defaultControls$minH !== void 0 ? _defaultControls$minH : DEFAULT_CONTROLS.minHeight, panelId: panelId }, createElement(HeightControl, { label: __('Min. height'), value: minHeightValue, onChange: setMinHeightValue })), showChildLayoutControl && createElement(VStack, { as: ToolsPanelItem, spacing: 2, hasValue: hasChildLayoutValue, label: childLayoutOrientationLabel, onDeselect: resetChildLayoutValue, isShownByDefault: (_defaultControls$chil = defaultControls.childLayout) !== null && _defaultControls$chil !== void 0 ? _defaultControls$chil : DEFAULT_CONTROLS.childLayout, panelId: panelId }, createElement(ChildLayoutControl, { value: childLayout, onChange: setChildLayout, parentLayout: settings?.parentLayout }))); } //# sourceMappingURL=dimensions-panel.js.map