@wordpress/block-editor
Version:
522 lines (451 loc) • 17.5 kB
JavaScript
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