@wordpress/block-editor
Version:
626 lines (625 loc) • 21.3 kB
JavaScript
// packages/block-editor/src/components/global-styles/dimensions-panel.js
import clsx from "clsx";
import { __ } from "@wordpress/i18n";
import {
__experimentalToolsPanel as ToolsPanel,
__experimentalToolsPanelItem as ToolsPanelItem,
BoxControl,
__experimentalUnitControl as UnitControl,
__experimentalUseCustomUnits as useCustomUnits,
__experimentalInputControlPrefixWrapper as InputControlPrefixWrapper
} from "@wordpress/components";
import { Icon, alignNone, stretchWide } from "@wordpress/icons";
import { useCallback, useState, Platform } from "@wordpress/element";
import { getValueFromVariable } from "@wordpress/global-styles-engine";
import { useToolsPanelDropdownMenuProps } from "./utils";
import SpacingSizesControl from "../spacing-sizes-control";
import HeightControl from "../height-control";
import ChildLayoutControl from "../child-layout-control";
import AspectRatioTool from "../dimensions-tool/aspect-ratio-tool";
import { cleanEmptyObject } from "../../hooks/utils";
import { setImmutably } from "../../utils/object";
import { jsx, jsxs } from "react/jsx-runtime";
var AXIAL_SIDES = ["horizontal", "vertical"];
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 hasWidth = useHasWidth(settings);
const hasAspectRatio = useHasAspectRatio(settings);
const hasChildLayout = useHasChildLayout(settings);
return Platform.OS === "web" && (hasContentSize || hasWideSize || hasPadding || hasMargin || hasGap || hasMinHeight || hasWidth || hasAspectRatio || 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 useHasWidth(settings) {
return settings?.dimensions?.width;
}
function useHasAspectRatio(settings) {
return settings?.dimensions?.aspectRatio;
}
function useHasChildLayout(settings) {
const {
type: parentLayoutType = "default",
default: { type: defaultParentLayoutType = "default" } = {},
allowSizingOnChildren = false
} = settings?.parentLayout ?? {};
const support = (defaultParentLayoutType === "flex" || parentLayoutType === "flex" || defaultParentLayoutType === "grid" || parentLayoutType === "grid") && allowSizingOnChildren;
return !!settings?.layout && support;
}
function useHasSpacingPresets(settings) {
const { defaultSpacingSizes, spacingSizes } = settings?.spacing || {};
return defaultSpacingSizes !== false && spacingSizes?.default?.length > 0 || spacingSizes?.theme?.length > 0 || spacingSizes?.custom?.length > 0;
}
function filterValuesBySides(values, sides) {
if (!sides || !values) {
return 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) {
if (value && typeof value === "string") {
return {
top: value,
right: value,
bottom: value,
left: value
};
}
return value;
}
function splitGapValue(value, isAxialGap) {
if (!value) {
return value;
}
if (typeof value === "string") {
return isAxialGap ? { top: value, right: value, bottom: value, left: value } : { top: value };
}
return {
...value,
right: value?.left,
bottom: value?.top
};
}
function DimensionsToolsPanel({
resetAllFilter,
onChange,
value,
panelId,
children
}) {
const dropdownMenuProps = useToolsPanelDropdownMenuProps();
const resetAll = () => {
const updatedValue = resetAllFilter(value);
onChange(updatedValue);
};
return /* @__PURE__ */ jsx(
ToolsPanel,
{
label: __("Dimensions"),
resetAll,
panelId,
dropdownMenuProps,
children
}
);
}
var DEFAULT_CONTROLS = {
contentSize: true,
wideSize: true,
padding: true,
margin: true,
blockGap: true,
minHeight: true,
width: true,
aspectRatio: true,
childLayout: true
};
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
}) {
const { dimensions, spacing } = settings;
const decodeValue = (rawValue) => {
if (rawValue && typeof rawValue === "object") {
return Object.keys(rawValue).reduce((acc, key) => {
acc[key] = getValueFromVariable(
{ settings: { dimensions, spacing } },
"",
rawValue[key]
);
return acc;
}, {});
}
return getValueFromVariable(
{ settings: { dimensions, spacing } },
"",
rawValue
);
};
const showSpacingPresetsControl = useHasSpacingPresets(settings);
const units = useCustomUnits({
availableUnits: settings?.spacing?.units || [
"%",
"px",
"em",
"rem",
"vw"
]
});
const minimumMargin = -Infinity;
const [minMarginValue, setMinMarginValue] = useState(minimumMargin);
const showContentSizeControl = useHasContentSize(settings) && includeLayoutControls;
const contentSizeValue = decodeValue(inheritedValue?.layout?.contentSize);
const setContentSizeValue = (newValue) => {
onChange(
setImmutably(
value,
["layout", "contentSize"],
newValue || void 0
)
);
};
const hasUserSetContentSizeValue = () => !!value?.layout?.contentSize;
const resetContentSizeValue = () => setContentSizeValue(void 0);
const showWideSizeControl = useHasWideSize(settings) && includeLayoutControls;
const wideSizeValue = decodeValue(inheritedValue?.layout?.wideSize);
const setWideSizeValue = (newValue) => {
onChange(
setImmutably(
value,
["layout", "wideSize"],
newValue || void 0
)
);
};
const hasUserSetWideSizeValue = () => !!value?.layout?.wideSize;
const resetWideSizeValue = () => setWideSizeValue(void 0);
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(void 0);
const onMouseOverPadding = () => onVisualize("padding");
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(void 0);
const onMouseOverMargin = () => onVisualize("margin");
const showGapControl = useHasGap(settings);
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 gapValue = decodeValue(inheritedValue?.spacing?.blockGap);
const gapValues = splitGapValue(gapValue, isAxialGap);
const setGapValue = (newGapValue) => {
onChange(
setImmutably(value, ["spacing", "blockGap"], newGapValue)
);
};
const setGapValues = (nextBoxGapValue) => {
if (!nextBoxGapValue) {
setGapValue(null);
}
if (!isAxialGap && nextBoxGapValue?.hasOwnProperty("top")) {
setGapValue(nextBoxGapValue.top);
} else {
setGapValue({
top: nextBoxGapValue?.top,
left: nextBoxGapValue?.left
});
}
};
const resetGapValue = () => setGapValue(void 0);
const hasGapValue = () => !!value?.spacing?.blockGap;
const showMinHeightControl = useHasMinHeight(settings);
const minHeightValue = decodeValue(inheritedValue?.dimensions?.minHeight);
const setMinHeightValue = (newValue) => {
const tempValue = setImmutably(
value,
["dimensions", "minHeight"],
newValue
);
onChange(
setImmutably(
tempValue,
["dimensions", "aspectRatio"],
void 0
)
);
};
const resetMinHeightValue = () => {
setMinHeightValue(void 0);
};
const hasMinHeightValue = () => !!value?.dimensions?.minHeight;
const showWidthControl = useHasWidth(settings);
const widthValue = decodeValue(inheritedValue?.dimensions?.width);
const setWidthValue = (newValue) => {
onChange(setImmutably(value, ["dimensions", "width"], newValue));
};
const resetWidthValue = () => {
setWidthValue(void 0);
};
const hasWidthValue = () => !!value?.dimensions?.width;
const showAspectRatioControl = useHasAspectRatio(settings);
const aspectRatioValue = decodeValue(
inheritedValue?.dimensions?.aspectRatio
);
const setAspectRatioValue = (newValue) => {
const tempValue = setImmutably(
value,
["dimensions", "aspectRatio"],
newValue
);
onChange(
setImmutably(tempValue, ["dimensions", "minHeight"], void 0)
);
};
const hasAspectRatioValue = () => !!value?.dimensions?.aspectRatio;
const showChildLayoutControl = useHasChildLayout(settings);
const childLayout = inheritedValue?.layout;
const setChildLayout = (newChildLayout) => {
onChange({
...value,
layout: {
...newChildLayout
}
});
};
const resetAllFilter = useCallback((previousValue) => {
return {
...previousValue,
layout: cleanEmptyObject({
...previousValue?.layout,
contentSize: void 0,
wideSize: void 0,
selfStretch: void 0,
flexSize: void 0,
columnStart: void 0,
rowStart: void 0,
columnSpan: void 0,
rowSpan: void 0
}),
spacing: {
...previousValue?.spacing,
padding: void 0,
margin: void 0,
blockGap: void 0
},
dimensions: {
...previousValue?.dimensions,
minHeight: void 0,
aspectRatio: void 0,
width: void 0
}
};
}, []);
const onMouseLeaveControls = () => onVisualize(false);
return /* @__PURE__ */ jsxs(
Wrapper,
{
resetAllFilter,
value,
onChange,
panelId,
children: [
(showContentSizeControl || showWideSizeControl) && /* @__PURE__ */ jsx("span", { className: "span-columns", children: __("Set the width of the main content area.") }),
showContentSizeControl && /* @__PURE__ */ jsx(
ToolsPanelItem,
{
label: __("Content width"),
hasValue: hasUserSetContentSizeValue,
onDeselect: resetContentSizeValue,
isShownByDefault: defaultControls.contentSize ?? DEFAULT_CONTROLS.contentSize,
panelId,
children: /* @__PURE__ */ jsx(
UnitControl,
{
__next40pxDefaultSize: true,
label: __("Content width"),
labelPosition: "top",
value: contentSizeValue || "",
onChange: (nextContentSize) => {
setContentSizeValue(nextContentSize);
},
units,
prefix: /* @__PURE__ */ jsx(InputControlPrefixWrapper, { variant: "icon", children: /* @__PURE__ */ jsx(Icon, { icon: alignNone }) })
}
)
}
),
showWideSizeControl && /* @__PURE__ */ jsx(
ToolsPanelItem,
{
label: __("Wide width"),
hasValue: hasUserSetWideSizeValue,
onDeselect: resetWideSizeValue,
isShownByDefault: defaultControls.wideSize ?? DEFAULT_CONTROLS.wideSize,
panelId,
children: /* @__PURE__ */ jsx(
UnitControl,
{
__next40pxDefaultSize: true,
label: __("Wide width"),
labelPosition: "top",
value: wideSizeValue || "",
onChange: (nextWideSize) => {
setWideSizeValue(nextWideSize);
},
units,
prefix: /* @__PURE__ */ jsx(InputControlPrefixWrapper, { variant: "icon", children: /* @__PURE__ */ jsx(Icon, { icon: stretchWide }) })
}
)
}
),
showPaddingControl && /* @__PURE__ */ jsxs(
ToolsPanelItem,
{
hasValue: hasPaddingValue,
label: __("Padding"),
onDeselect: resetPaddingValue,
isShownByDefault: defaultControls.padding ?? DEFAULT_CONTROLS.padding,
className: clsx({
"tools-panel-item-spacing": showSpacingPresetsControl
}),
panelId,
children: [
!showSpacingPresetsControl && /* @__PURE__ */ jsx(
BoxControl,
{
__next40pxDefaultSize: true,
values: paddingValues,
onChange: setPaddingValues,
label: __("Padding"),
sides: paddingSides,
units,
allowReset: false,
splitOnAxis: isAxialPadding,
inputProps: {
onMouseOver: onMouseOverPadding,
onMouseOut: onMouseLeaveControls
}
}
),
showSpacingPresetsControl && /* @__PURE__ */ jsx(
SpacingSizesControl,
{
values: paddingValues,
onChange: setPaddingValues,
label: __("Padding"),
sides: paddingSides,
units,
allowReset: false,
onMouseOver: onMouseOverPadding,
onMouseOut: onMouseLeaveControls
}
)
]
}
),
showMarginControl && /* @__PURE__ */ jsxs(
ToolsPanelItem,
{
hasValue: hasMarginValue,
label: __("Margin"),
onDeselect: resetMarginValue,
isShownByDefault: defaultControls.margin ?? DEFAULT_CONTROLS.margin,
className: clsx({
"tools-panel-item-spacing": showSpacingPresetsControl
}),
panelId,
children: [
!showSpacingPresetsControl && /* @__PURE__ */ jsx(
BoxControl,
{
__next40pxDefaultSize: true,
values: marginValues,
onChange: setMarginValues,
inputProps: {
min: minMarginValue,
onDragStart: () => {
setMinMarginValue(0);
},
onDragEnd: () => {
setMinMarginValue(minimumMargin);
},
onMouseOver: onMouseOverMargin,
onMouseOut: onMouseLeaveControls
},
label: __("Margin"),
sides: marginSides,
units,
allowReset: false,
splitOnAxis: isAxialMargin
}
),
showSpacingPresetsControl && /* @__PURE__ */ jsx(
SpacingSizesControl,
{
values: marginValues,
onChange: setMarginValues,
minimumCustomValue: -Infinity,
label: __("Margin"),
sides: marginSides,
units,
allowReset: false,
onMouseOver: onMouseOverMargin,
onMouseOut: onMouseLeaveControls
}
)
]
}
),
showGapControl && /* @__PURE__ */ jsxs(
ToolsPanelItem,
{
hasValue: hasGapValue,
label: __("Block spacing"),
onDeselect: resetGapValue,
isShownByDefault: defaultControls.blockGap ?? DEFAULT_CONTROLS.blockGap,
className: clsx({
"tools-panel-item-spacing": showSpacingPresetsControl,
"single-column": (
// If UnitControl is used, should be single-column.
!showSpacingPresetsControl && !isAxialGap
)
}),
panelId,
children: [
!showSpacingPresetsControl && (isAxialGap ? /* @__PURE__ */ jsx(
BoxControl,
{
__next40pxDefaultSize: true,
label: __("Block spacing"),
min: 0,
onChange: setGapValues,
units,
sides: gapSides,
values: gapValues,
allowReset: false,
splitOnAxis: isAxialGap
}
) : /* @__PURE__ */ jsx(
UnitControl,
{
__next40pxDefaultSize: true,
label: __("Block spacing"),
min: 0,
onChange: setGapValue,
units,
value: gapValue
}
)),
showSpacingPresetsControl && /* @__PURE__ */ jsx(
SpacingSizesControl,
{
label: __("Block spacing"),
min: 0,
onChange: setGapValues,
showSideInLabel: false,
sides: isAxialGap ? gapSides : ["top"],
values: gapValues,
allowReset: false
}
)
]
}
),
showChildLayoutControl && /* @__PURE__ */ jsx(
ChildLayoutControl,
{
value: childLayout,
onChange: setChildLayout,
parentLayout: settings?.parentLayout,
panelId,
isShownByDefault: defaultControls.childLayout ?? DEFAULT_CONTROLS.childLayout
}
),
showMinHeightControl && /* @__PURE__ */ jsx(
ToolsPanelItem,
{
hasValue: hasMinHeightValue,
label: __("Minimum height"),
onDeselect: resetMinHeightValue,
isShownByDefault: defaultControls.minHeight ?? DEFAULT_CONTROLS.minHeight,
panelId,
children: /* @__PURE__ */ jsx(
HeightControl,
{
label: __("Minimum height"),
value: minHeightValue,
onChange: setMinHeightValue
}
)
}
),
showWidthControl && /* @__PURE__ */ jsx(
ToolsPanelItem,
{
hasValue: hasWidthValue,
label: __("Width"),
onDeselect: resetWidthValue,
isShownByDefault: defaultControls.width ?? DEFAULT_CONTROLS.width,
panelId,
children: /* @__PURE__ */ jsx(
HeightControl,
{
label: __("Width"),
value: widthValue,
onChange: setWidthValue
}
)
}
),
showAspectRatioControl && /* @__PURE__ */ jsx(
AspectRatioTool,
{
hasValue: hasAspectRatioValue,
value: aspectRatioValue,
onChange: setAspectRatioValue,
panelId,
isShownByDefault: defaultControls.aspectRatio ?? DEFAULT_CONTROLS.aspectRatio
}
)
]
}
);
}
export {
DimensionsPanel as default,
useHasDimensionsPanel
};
//# sourceMappingURL=dimensions-panel.js.map