@wordpress/block-editor
Version:
652 lines (650 loc) • 23.8 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// packages/block-editor/src/components/background-image-control/index.js
var background_image_control_exports = {};
__export(background_image_control_exports, {
backgroundPositionToCoords: () => backgroundPositionToCoords,
coordsToBackgroundPosition: () => coordsToBackgroundPosition,
default: () => BackgroundImagePanel
});
module.exports = __toCommonJS(background_image_control_exports);
var import_clsx = __toESM(require("clsx"));
var import_components = require("@wordpress/components");
var import_icons = require("@wordpress/icons");
var import_i18n = require("@wordpress/i18n");
var import_notices = require("@wordpress/notices");
var import_url = require("@wordpress/url");
var import_element = require("@wordpress/element");
var import_data = require("@wordpress/data");
var import_dom = require("@wordpress/dom");
var import_blob = require("@wordpress/blob");
var import_global_styles_engine = require("@wordpress/global-styles-engine");
var import_background_panel = require("../global-styles/background-panel");
var import_object = require("../../utils/object");
var import_media_replace_flow = __toESM(require("../media-replace-flow"));
var import_store = require("../../store");
var import_private_keys = require("../../store/private-keys");
var import_jsx_runtime = require("react/jsx-runtime");
var IMAGE_BACKGROUND_TYPE = "image";
var BACKGROUND_POPOVER_PROPS = {
placement: "left-start",
offset: 36,
shift: true,
className: "block-editor-global-styles-background-panel__popover"
};
var noop = () => {
};
var focusToggleButton = (containerRef) => {
window.requestAnimationFrame(() => {
const [toggleButton] = import_dom.focus.tabbable.find(containerRef?.current);
if (!toggleButton) {
return;
}
toggleButton.focus();
});
};
function backgroundSizeHelpText(value) {
if (value === "cover" || value === void 0) {
return (0, import_i18n.__)("Image covers the space evenly.");
}
if (value === "contain") {
return (0, import_i18n.__)("Image is contained without distortion.");
}
return (0, import_i18n.__)("Image has a fixed width.");
}
var coordsToBackgroundPosition = (value) => {
if (!value || isNaN(value.x) && isNaN(value.y)) {
return void 0;
}
const x = isNaN(value.x) ? 0.5 : value.x;
const y = isNaN(value.y) ? 0.5 : value.y;
return `${x * 100}% ${y * 100}%`;
};
var backgroundPositionToCoords = (value) => {
if (!value) {
return { x: void 0, y: void 0 };
}
let [x, y] = value.split(" ").map((v) => parseFloat(v) / 100);
x = isNaN(x) ? void 0 : x;
y = isNaN(y) ? x : y;
return { x, y };
};
function InspectorImagePreviewItem({
as = "span",
imgUrl,
toggleProps = {},
filename,
label,
onToggleCallback = noop
}) {
const { isOpen, ...restToggleProps } = toggleProps;
(0, import_element.useEffect)(() => {
if (typeof isOpen !== "undefined") {
onToggleCallback(isOpen);
}
}, [isOpen, onToggleCallback]);
const renderPreviewContent = () => {
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
import_components.__experimentalHStack,
{
justify: "flex-start",
as: "span",
className: "block-editor-global-styles-background-panel__inspector-preview-inner",
children: [
imgUrl && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"span",
{
className: "block-editor-global-styles-background-panel__inspector-image-indicator-wrapper",
"aria-hidden": true,
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"span",
{
className: "block-editor-global-styles-background-panel__inspector-image-indicator",
style: {
backgroundImage: `url(${imgUrl})`
}
}
)
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_components.FlexItem, { as: "span", style: imgUrl ? {} : { flexGrow: 1 }, children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.__experimentalTruncate,
{
numberOfLines: 1,
className: "block-editor-global-styles-background-panel__inspector-media-replace-title",
children: label
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.VisuallyHidden, { as: "span", children: imgUrl ? (0, import_i18n.sprintf)(
/* translators: %s: file name */
(0, import_i18n.__)("Background image: %s"),
filename || label
) : (0, import_i18n.__)("No background image selected") })
] })
]
}
);
};
return as === "button" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Button, { __next40pxDefaultSize: true, ...restToggleProps, children: renderPreviewContent() }) : renderPreviewContent();
}
function BackgroundControlsPanel({
label,
filename,
url: imgUrl,
children,
onToggle: onToggleCallback = noop,
hasImageValue,
onReset,
containerRef
}) {
if (!hasImageValue) {
return;
}
const imgLabel = label || (0, import_url.getFilename)(imgUrl) || (0, import_i18n.__)("Add background image");
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.Dropdown,
{
popoverProps: BACKGROUND_POPOVER_PROPS,
renderToggle: ({ onToggle, isOpen }) => {
const toggleProps = {
onClick: onToggle,
className: "block-editor-global-styles-background-panel__dropdown-toggle",
"aria-expanded": isOpen,
"aria-label": (0, import_i18n.__)(
"Background size, position and repeat options."
),
isOpen
};
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
InspectorImagePreviewItem,
{
imgUrl,
filename,
label: imgLabel,
toggleProps,
as: "button",
onToggleCallback
}
),
onReset && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.Button,
{
__next40pxDefaultSize: true,
label: (0, import_i18n.__)("Reset"),
className: "block-editor-global-styles-background-panel__reset",
size: "small",
icon: import_icons.reset,
onClick: () => {
onReset();
if (isOpen) {
onToggle();
}
focusToggleButton(containerRef);
}
}
)
] });
},
renderContent: () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.__experimentalDropdownContentWrapper,
{
className: "block-editor-global-styles-background-panel__dropdown-content-wrapper",
paddingSize: "medium",
children
}
)
}
);
}
function LoadingSpinner() {
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Placeholder, { className: "block-editor-global-styles-background-panel__loading", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Spinner, {}) });
}
function BackgroundImageControls({
onChange,
style,
inheritedValue,
onRemoveImage = noop,
onResetImage = noop,
displayInPanel,
defaultValues,
containerRef
}) {
const [isUploading, setIsUploading] = (0, import_element.useState)(false);
const { getSettings } = (0, import_data.useSelect)(import_store.store);
const { id, title, url } = style?.background?.backgroundImage || {
...inheritedValue?.background?.backgroundImage
};
const { createErrorNotice } = (0, import_data.useDispatch)(import_notices.store);
const onUploadError = (message) => {
createErrorNotice(message, { type: "snackbar" });
setIsUploading(false);
};
const resetBackgroundImage = () => onChange(
(0, import_object.setImmutably)(
style,
["background", "backgroundImage"],
void 0
)
);
const onSelectMedia = (media) => {
if (!media || !media.url) {
resetBackgroundImage();
setIsUploading(false);
return;
}
if ((0, import_blob.isBlobURL)(media.url)) {
setIsUploading(true);
return;
}
if (media.media_type && media.media_type !== IMAGE_BACKGROUND_TYPE || !media.media_type && media.type && media.type !== IMAGE_BACKGROUND_TYPE) {
onUploadError(
(0, import_i18n.__)("Only images can be used as a background image.")
);
return;
}
const sizeValue = style?.background?.backgroundSize || defaultValues?.backgroundSize;
const positionValue = style?.background?.backgroundPosition;
onChange(
(0, import_object.setImmutably)(style, ["background"], {
...style?.background,
backgroundImage: {
url: media.url,
id: media.id,
source: "file",
title: media.title || void 0
},
backgroundPosition: (
/*
* A background image uploaded and set in the editor receives a default background position of '50% 0',
* when the background image size is the equivalent of "Tile".
* This is to increase the chance that the image's focus point is visible.
* This is in-editor only to assist with the user experience.
*/
!positionValue && ("auto" === sizeValue || !sizeValue) ? "50% 0" : positionValue
),
backgroundSize: sizeValue
})
);
setIsUploading(false);
focusToggleButton(containerRef);
};
const onFilesDrop = (filesList) => {
getSettings().mediaUpload({
allowedTypes: [IMAGE_BACKGROUND_TYPE],
filesList,
onFileChange([image]) {
onSelectMedia(image);
},
onError: onUploadError,
multiple: false
});
};
const hasValue = (0, import_background_panel.hasBackgroundImageValue)(style);
const onRemove = () => onChange(
(0, import_object.setImmutably)(style, ["background"], {
backgroundImage: "none"
})
);
const canRemove = !hasValue && (0, import_background_panel.hasBackgroundImageValue)(inheritedValue);
const imgLabel = title || (0, import_url.getFilename)(url) || (0, import_i18n.__)("Add background image");
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "block-editor-global-styles-background-panel__image-tools-panel-item", children: [
isUploading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(LoadingSpinner, {}),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_media_replace_flow.default,
{
mediaId: id,
mediaURL: url,
allowedTypes: [IMAGE_BACKGROUND_TYPE],
accept: "image/*",
onSelect: onSelectMedia,
popoverProps: {
className: (0, import_clsx.default)({
"block-editor-global-styles-background-panel__media-replace-popover": displayInPanel
})
},
name: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
InspectorImagePreviewItem,
{
imgUrl: url,
filename: title,
label: imgLabel
}
),
renderToggle: (props) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Button, { ...props, __next40pxDefaultSize: true }),
onError: onUploadError,
onReset: () => {
focusToggleButton(containerRef);
onResetImage();
},
children: canRemove && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.MenuItem,
{
onClick: () => {
focusToggleButton(containerRef);
onRemove();
onRemoveImage();
},
children: (0, import_i18n.__)("Remove")
}
)
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.DropZone,
{
onFilesDrop,
label: (0, import_i18n.__)("Drop to upload")
}
)
] });
}
function BackgroundSizeControls({
onChange,
style,
inheritedValue,
defaultValues
}) {
const sizeValue = style?.background?.backgroundSize || inheritedValue?.background?.backgroundSize;
const repeatValue = style?.background?.backgroundRepeat || inheritedValue?.background?.backgroundRepeat;
const imageValue = style?.background?.backgroundImage?.url || inheritedValue?.background?.backgroundImage?.url;
const isUploadedImage = style?.background?.backgroundImage?.id;
const positionValue = style?.background?.backgroundPosition || inheritedValue?.background?.backgroundPosition;
const attachmentValue = style?.background?.backgroundAttachment || inheritedValue?.background?.backgroundAttachment;
let currentValueForToggle = !sizeValue && isUploadedImage ? defaultValues?.backgroundSize : sizeValue || "auto";
currentValueForToggle = !["cover", "contain", "auto"].includes(
currentValueForToggle
) ? "auto" : currentValueForToggle;
const repeatCheckedValue = !(repeatValue === "no-repeat" || currentValueForToggle === "cover" && repeatValue === void 0);
const updateBackgroundSize = (next) => {
let nextRepeat = repeatValue;
let nextPosition = positionValue;
if (next === "contain") {
nextRepeat = "no-repeat";
nextPosition = void 0;
}
if (next === "cover") {
nextRepeat = void 0;
nextPosition = void 0;
}
if ((currentValueForToggle === "cover" || currentValueForToggle === "contain") && next === "auto") {
nextRepeat = void 0;
if (!!style?.background?.backgroundImage?.id) {
nextPosition = "50% 0";
}
}
if (!next && currentValueForToggle === "auto") {
next = "auto";
}
onChange(
(0, import_object.setImmutably)(style, ["background"], {
...style?.background,
backgroundPosition: nextPosition,
backgroundRepeat: nextRepeat,
backgroundSize: next
})
);
};
const updateBackgroundPosition = (next) => {
onChange(
(0, import_object.setImmutably)(
style,
["background", "backgroundPosition"],
coordsToBackgroundPosition(next)
)
);
};
const toggleIsRepeated = () => onChange(
(0, import_object.setImmutably)(
style,
["background", "backgroundRepeat"],
repeatCheckedValue === true ? "no-repeat" : "repeat"
)
);
const toggleScrollWithPage = () => onChange(
(0, import_object.setImmutably)(
style,
["background", "backgroundAttachment"],
attachmentValue === "fixed" ? "scroll" : "fixed"
)
);
const backgroundPositionValue = !positionValue && isUploadedImage && "contain" === sizeValue ? defaultValues?.backgroundPosition : positionValue;
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_components.__experimentalVStack, { spacing: 3, className: "single-column", children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.FocalPointPicker,
{
__nextHasNoMarginBottom: true,
label: (0, import_i18n.__)("Focal point"),
url: imageValue,
value: backgroundPositionToCoords(backgroundPositionValue),
onChange: updateBackgroundPosition
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.ToggleControl,
{
__nextHasNoMarginBottom: true,
label: (0, import_i18n.__)("Fixed background"),
checked: attachmentValue === "fixed",
onChange: toggleScrollWithPage
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
import_components.__experimentalToggleGroupControl,
{
__nextHasNoMarginBottom: true,
size: "__unstable-large",
label: (0, import_i18n.__)("Size"),
value: currentValueForToggle,
onChange: updateBackgroundSize,
isBlock: true,
help: backgroundSizeHelpText(
sizeValue || defaultValues?.backgroundSize
),
children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.__experimentalToggleGroupControlOption,
{
value: "cover",
label: (0, import_i18n._x)(
"Cover",
"Size option for background image control"
)
},
"cover"
),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.__experimentalToggleGroupControlOption,
{
value: "contain",
label: (0, import_i18n._x)(
"Contain",
"Size option for background image control"
)
},
"contain"
),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.__experimentalToggleGroupControlOption,
{
value: "auto",
label: (0, import_i18n._x)(
"Tile",
"Size option for background image control"
)
},
"tile"
)
]
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_components.__experimentalHStack, { justify: "flex-start", spacing: 2, as: "span", children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.__experimentalUnitControl,
{
"aria-label": (0, import_i18n.__)("Background image width"),
onChange: updateBackgroundSize,
value: sizeValue,
size: "__unstable-large",
__unstableInputWidth: "100px",
min: 0,
placeholder: (0, import_i18n.__)("Auto"),
disabled: currentValueForToggle !== "auto" || currentValueForToggle === void 0
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
import_components.ToggleControl,
{
__nextHasNoMarginBottom: true,
label: (0, import_i18n.__)("Repeat"),
checked: repeatCheckedValue,
onChange: toggleIsRepeated,
disabled: currentValueForToggle === "cover"
}
)
] })
] });
}
function BackgroundImagePanel({
value,
onChange,
inheritedValue = value,
settings,
defaultValues = {}
}) {
const { globalStyles, _links } = (0, import_data.useSelect)((select) => {
const { getSettings } = select(import_store.store);
const _settings = getSettings();
return {
globalStyles: _settings[import_private_keys.globalStylesDataKey],
_links: _settings[import_private_keys.globalStylesLinksDataKey]
};
}, []);
const resolvedInheritedValue = (0, import_element.useMemo)(() => {
const resolvedValues = {
background: {}
};
if (!inheritedValue?.background) {
return inheritedValue;
}
Object.entries(inheritedValue?.background).forEach(
([key, backgroundValue]) => {
resolvedValues.background[key] = (0, import_global_styles_engine.getResolvedValue)(
backgroundValue,
{
styles: globalStyles,
_links
}
);
}
);
return resolvedValues;
}, [globalStyles, _links, inheritedValue]);
const resetBackground = () => onChange((0, import_object.setImmutably)(value, ["background"], {}));
const { title, url } = value?.background?.backgroundImage || {
...resolvedInheritedValue?.background?.backgroundImage
};
const hasImageValue = (0, import_background_panel.hasBackgroundImageValue)(value) || (0, import_background_panel.hasBackgroundImageValue)(resolvedInheritedValue);
const imageValue = value?.background?.backgroundImage || inheritedValue?.background?.backgroundImage;
const shouldShowBackgroundImageControls = hasImageValue && "none" !== imageValue && (settings?.background?.backgroundSize || settings?.background?.backgroundPosition || settings?.background?.backgroundRepeat);
const [isDropDownOpen, setIsDropDownOpen] = (0, import_element.useState)(false);
const containerRef = (0, import_element.useRef)();
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"div",
{
ref: containerRef,
className: (0, import_clsx.default)(
"block-editor-global-styles-background-panel__inspector-media-replace-container",
{
"is-open": isDropDownOpen
}
),
children: shouldShowBackgroundImageControls ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
BackgroundControlsPanel,
{
label: title,
filename: title,
url,
onToggle: setIsDropDownOpen,
hasImageValue,
onReset: resetBackground,
containerRef,
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_components.__experimentalVStack, { spacing: 3, className: "single-column", children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
BackgroundImageControls,
{
onChange,
style: value,
inheritedValue: resolvedInheritedValue,
displayInPanel: true,
onResetImage: () => {
setIsDropDownOpen(false);
resetBackground();
},
onRemoveImage: () => setIsDropDownOpen(false),
defaultValues,
containerRef
}
),
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
BackgroundSizeControls,
{
onChange,
style: value,
defaultValues,
inheritedValue: resolvedInheritedValue
}
)
] })
}
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
BackgroundImageControls,
{
onChange,
style: value,
inheritedValue: resolvedInheritedValue,
defaultValues,
onResetImage: () => {
setIsDropDownOpen(false);
resetBackground();
},
onRemoveImage: () => setIsDropDownOpen(false),
containerRef
}
)
}
);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
backgroundPositionToCoords,
coordsToBackgroundPosition
});
//# sourceMappingURL=index.js.map