UNPKG

@wordpress/block-editor

Version:
652 lines (650 loc) 23.8 kB
"use strict"; 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