@wordpress/block-library
Version:
Block library for the WordPress editor.
626 lines (625 loc) • 19.5 kB
JavaScript
// packages/block-library/src/site-logo/edit.js
import clsx from "clsx";
import { isBlobURL } from "@wordpress/blob";
import {
createInterpolateElement,
useEffect,
useState
} from "@wordpress/element";
import { __, isRTL } from "@wordpress/i18n";
import {
RangeControl,
ResizableBox,
Spinner,
ToggleControl,
ToolbarButton,
Placeholder,
Button,
DropZone,
FlexItem,
__experimentalToolsPanel as ToolsPanel,
__experimentalToolsPanelItem as ToolsPanelItem,
__experimentalItemGroup as ItemGroup,
__experimentalHStack as HStack,
__experimentalTruncate as Truncate
} from "@wordpress/components";
import { useViewportMatch } from "@wordpress/compose";
import {
BlockControls,
InspectorControls,
MediaPlaceholder,
MediaReplaceFlow,
useBlockProps,
store as blockEditorStore,
__experimentalImageEditor as ImageEditor,
useBlockEditingMode
} from "@wordpress/block-editor";
import { useSelect, useDispatch } from "@wordpress/data";
import { store as coreStore } from "@wordpress/core-data";
import { crop, upload } from "@wordpress/icons";
import { store as noticesStore } from "@wordpress/notices";
import { MIN_SIZE } from "../image/constants";
import { useToolsPanelDropdownMenuProps } from "../utils/hooks";
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
var ALLOWED_MEDIA_TYPES = ["image"];
var ACCEPT_MEDIA_STRING = "image/*";
var SiteLogo = ({
alt,
attributes: { align, width, height, isLink, linkTarget, shouldSyncIcon },
isSelected,
setAttributes,
setLogo,
logoUrl,
siteUrl,
logoId,
iconId,
setIcon,
canUserEdit
}) => {
const isLargeViewport = useViewportMatch("medium");
const isWideAligned = ["wide", "full"].includes(align);
const isResizable = !isWideAligned && isLargeViewport;
const [{ naturalWidth, naturalHeight }, setNaturalSize] = useState({});
const [isEditingImage, setIsEditingImage] = useState(false);
const { toggleSelection } = useDispatch(blockEditorStore);
const dropdownMenuProps = useToolsPanelDropdownMenuProps();
const blockEditingMode = useBlockEditingMode();
const isContentOnlyMode = blockEditingMode === "contentOnly";
const { imageEditing, maxWidth, title } = useSelect((select) => {
const settings = select(blockEditorStore).getSettings();
const siteEntities = select(coreStore).getEntityRecord(
"root",
"__unstableBase"
);
return {
title: siteEntities?.name,
imageEditing: settings.imageEditing,
maxWidth: settings.maxWidth
};
}, []);
useEffect(() => {
if (shouldSyncIcon && logoId !== iconId) {
setAttributes({ shouldSyncIcon: false });
}
}, []);
useEffect(() => {
if (!isSelected) {
setIsEditingImage(false);
}
}, [isSelected]);
function onResizeStart() {
toggleSelection(false);
}
function onResizeStop() {
toggleSelection(true);
}
const img = /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(
"img",
{
className: "custom-logo",
src: logoUrl,
alt,
onLoad: (event) => {
setNaturalSize({
naturalWidth: event.target.naturalWidth,
naturalHeight: event.target.naturalHeight
});
}
}
),
isBlobURL(logoUrl) && /* @__PURE__ */ jsx(Spinner, {})
] });
let imgWrapper = img;
if (isLink) {
imgWrapper = /* @__PURE__ */ jsx(
"a",
{
href: siteUrl,
className: "custom-logo-link",
rel: "home",
title,
onClick: (event) => event.preventDefault(),
children: img
}
);
}
if (!isResizable || !naturalWidth || !naturalHeight) {
return /* @__PURE__ */ jsx("div", { style: { width, height }, children: imgWrapper });
}
const defaultWidth = 120;
const currentWidth = width || defaultWidth;
const ratio = naturalWidth / naturalHeight;
const currentHeight = currentWidth / ratio;
const minWidth = naturalWidth < naturalHeight ? MIN_SIZE : Math.ceil(MIN_SIZE * ratio);
const minHeight = naturalHeight < naturalWidth ? MIN_SIZE : Math.ceil(MIN_SIZE / ratio);
const maxWidthBuffer = maxWidth * 2.5;
let showRightHandle = false;
let showLeftHandle = false;
if (align === "center") {
showRightHandle = true;
showLeftHandle = true;
} else if (isRTL()) {
if (align === "left") {
showRightHandle = true;
} else {
showLeftHandle = true;
}
} else {
if (align === "right") {
showLeftHandle = true;
} else {
showRightHandle = true;
}
}
const canEditImage = logoId && naturalWidth && naturalHeight && imageEditing;
const shouldShowCropAndDimensions = !isContentOnlyMode;
let imgEdit;
if (canEditImage && isEditingImage) {
imgEdit = /* @__PURE__ */ jsx(
ImageEditor,
{
id: logoId,
url: logoUrl,
width: currentWidth,
height: currentHeight,
naturalHeight,
naturalWidth,
onSaveImage: (imageAttributes) => {
setLogo(imageAttributes.id);
},
onFinishEditing: () => {
setIsEditingImage(false);
}
}
);
} else {
imgEdit = /* @__PURE__ */ jsx(
ResizableBox,
{
size: {
width: currentWidth,
height: currentHeight
},
showHandle: isSelected && shouldShowCropAndDimensions,
minWidth,
maxWidth: maxWidthBuffer,
minHeight,
maxHeight: maxWidthBuffer / ratio,
lockAspectRatio: true,
enable: {
top: false,
right: showRightHandle,
bottom: true,
left: showLeftHandle
},
onResizeStart,
onResizeStop: (event, direction, elt, delta) => {
onResizeStop();
setAttributes({
width: parseInt(currentWidth + delta.width, 10),
height: parseInt(currentHeight + delta.height, 10)
});
},
children: imgWrapper
}
);
}
const shouldUseNewUrl = !window?.__experimentalUseCustomizerSiteLogoUrl;
const siteIconSettingsUrl = shouldUseNewUrl ? siteUrl + "/wp-admin/options-general.php" : siteUrl + "/wp-admin/customize.php?autofocus[section]=title_tagline";
const syncSiteIconHelpText = createInterpolateElement(
__(
"Site Icons are what you see in browser tabs, bookmark bars, and within the WordPress mobile apps. To use a custom icon that is different from your site logo, use the <a>Site Icon settings</a>."
),
{
a: (
// eslint-disable-next-line jsx-a11y/anchor-has-content
/* @__PURE__ */ jsx(
"a",
{
href: siteIconSettingsUrl,
target: "_blank",
rel: "noopener noreferrer"
}
)
)
}
);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(InspectorControls, { children: /* @__PURE__ */ jsxs(
ToolsPanel,
{
label: __("Settings"),
dropdownMenuProps,
children: [
/* @__PURE__ */ jsx(
ToolsPanelItem,
{
isShownByDefault: true,
hasValue: () => !!width,
label: __("Image width"),
onDeselect: () => setAttributes({ width: void 0 }),
children: /* @__PURE__ */ jsx(
RangeControl,
{
__nextHasNoMarginBottom: true,
__next40pxDefaultSize: true,
label: __("Image width"),
onChange: (newWidth) => setAttributes({ width: newWidth }),
min: minWidth,
max: maxWidthBuffer,
initialPosition: Math.min(
defaultWidth,
maxWidthBuffer
),
value: width || "",
disabled: !isResizable
}
)
}
),
/* @__PURE__ */ jsx(
ToolsPanelItem,
{
isShownByDefault: true,
hasValue: () => !isLink,
label: __("Link image to home"),
onDeselect: () => setAttributes({ isLink: true }),
children: /* @__PURE__ */ jsx(
ToggleControl,
{
__nextHasNoMarginBottom: true,
label: __("Link image to home"),
onChange: () => setAttributes({ isLink: !isLink }),
checked: isLink
}
)
}
),
isLink && /* @__PURE__ */ jsx(
ToolsPanelItem,
{
isShownByDefault: true,
hasValue: () => linkTarget === "_blank",
label: __("Open in new tab"),
onDeselect: () => setAttributes({ linkTarget: "_self" }),
children: /* @__PURE__ */ jsx(
ToggleControl,
{
__nextHasNoMarginBottom: true,
label: __("Open in new tab"),
onChange: (value) => setAttributes({
linkTarget: value ? "_blank" : "_self"
}),
checked: linkTarget === "_blank"
}
)
}
),
canUserEdit && /* @__PURE__ */ jsx(
ToolsPanelItem,
{
isShownByDefault: true,
hasValue: () => !!shouldSyncIcon,
label: __("Use as Site Icon"),
onDeselect: () => {
setAttributes({ shouldSyncIcon: false });
setIcon(void 0);
},
children: /* @__PURE__ */ jsx(
ToggleControl,
{
__nextHasNoMarginBottom: true,
label: __("Use as Site Icon"),
onChange: (value) => {
setAttributes({ shouldSyncIcon: value });
setIcon(value ? logoId : void 0);
},
checked: !!shouldSyncIcon,
help: syncSiteIconHelpText
}
)
}
)
]
}
) }),
canEditImage && !isEditingImage && shouldShowCropAndDimensions && /* @__PURE__ */ jsx(BlockControls, { group: "block", children: /* @__PURE__ */ jsx(
ToolbarButton,
{
onClick: () => setIsEditingImage(true),
icon: crop,
label: __("Crop")
}
) }),
imgEdit
] });
};
function SiteLogoReplaceFlow({ mediaURL, ...mediaReplaceProps }) {
return /* @__PURE__ */ jsx(
MediaReplaceFlow,
{
...mediaReplaceProps,
mediaURL,
allowedTypes: ALLOWED_MEDIA_TYPES,
accept: ACCEPT_MEDIA_STRING
}
);
}
var InspectorLogoPreview = ({ media, itemGroupProps }) => {
const {
alt_text: alt,
source_url: logoUrl,
slug: logoSlug,
media_details: logoMediaDetails
} = media ?? {};
const logoLabel = logoMediaDetails?.sizes?.full?.file || logoSlug;
return /* @__PURE__ */ jsx(ItemGroup, { ...itemGroupProps, as: "span", children: /* @__PURE__ */ jsxs(HStack, { justify: "flex-start", as: "span", children: [
/* @__PURE__ */ jsx("img", { src: logoUrl, alt }),
/* @__PURE__ */ jsx(FlexItem, { as: "span", children: /* @__PURE__ */ jsx(
Truncate,
{
numberOfLines: 1,
className: "block-library-site-logo__inspector-media-replace-title",
children: logoLabel
}
) })
] }) });
};
function LogoEdit({
attributes,
className,
setAttributes,
isSelected
}) {
const { width, shouldSyncIcon } = attributes;
const {
siteLogoId,
canUserEdit,
url,
siteIconId,
mediaItemData,
isRequestingMediaItem
} = useSelect((select) => {
const { canUser, getEntityRecord, getEditedEntityRecord } = select(coreStore);
const _canUserEdit = canUser("update", {
kind: "root",
name: "site"
});
const siteSettings = _canUserEdit ? getEditedEntityRecord("root", "site") : void 0;
const siteData = getEntityRecord("root", "__unstableBase");
const _siteLogoId = _canUserEdit ? siteSettings?.site_logo : siteData?.site_logo;
const _siteIconId = siteSettings?.site_icon;
const mediaItem = _siteLogoId && select(coreStore).getEntityRecord(
"postType",
"attachment",
_siteLogoId,
{
context: "view"
}
);
const _isRequestingMediaItem = !!_siteLogoId && !select(coreStore).hasFinishedResolution("getEntityRecord", [
"postType",
"attachment",
_siteLogoId,
{ context: "view" }
]);
return {
siteLogoId: _siteLogoId,
canUserEdit: _canUserEdit,
url: siteData?.home,
mediaItemData: mediaItem,
isRequestingMediaItem: _isRequestingMediaItem,
siteIconId: _siteIconId
};
}, []);
const { getSettings } = useSelect(blockEditorStore);
const [temporaryURL, setTemporaryURL] = useState();
const dropdownMenuProps = useToolsPanelDropdownMenuProps();
const { editEntityRecord } = useDispatch(coreStore);
const setLogo = (newValue, shouldForceSync = false) => {
if (shouldSyncIcon || shouldForceSync) {
setIcon(newValue);
}
editEntityRecord("root", "site", void 0, {
site_logo: newValue
});
};
const setIcon = (newValue) => (
// The new value needs to be `null` to reset the Site Icon.
editEntityRecord("root", "site", void 0, {
site_icon: newValue ?? null
})
);
const { alt_text: alt, source_url: logoUrl } = mediaItemData ?? {};
const onInitialSelectLogo = (media) => {
if (shouldSyncIcon === void 0) {
const shouldForceSync = !siteIconId;
setAttributes({ shouldSyncIcon: shouldForceSync });
onSelectLogo(media, shouldForceSync);
return;
}
onSelectLogo(media);
};
const onSelectLogo = (media, shouldForceSync = false) => {
if (!media) {
return;
}
if (!media.id && media.url) {
setTemporaryURL(media.url);
setLogo(void 0);
return;
}
setLogo(media.id, shouldForceSync);
};
const onRemoveLogo = () => {
setLogo(null);
setAttributes({ width: void 0 });
};
const { createErrorNotice } = useDispatch(noticesStore);
const onUploadError = (message) => {
createErrorNotice(message, { type: "snackbar" });
setTemporaryURL();
};
const onFilesDrop = (filesList) => {
getSettings().mediaUpload({
allowedTypes: ALLOWED_MEDIA_TYPES,
filesList,
onFileChange([image]) {
if (isBlobURL(image?.url)) {
setTemporaryURL(image.url);
return;
}
onInitialSelectLogo(image);
},
onError: onUploadError,
multiple: false
});
};
const mediaReplaceFlowProps = {
mediaURL: logoUrl,
name: !logoUrl ? __("Choose logo") : __("Replace"),
onSelect: onSelectLogo,
onError: onUploadError,
onReset: onRemoveLogo
};
const controls = canUserEdit && /* @__PURE__ */ jsx(BlockControls, { group: "other", children: /* @__PURE__ */ jsx(SiteLogoReplaceFlow, { ...mediaReplaceFlowProps }) });
let logoImage;
const isLoading = siteLogoId === void 0 || isRequestingMediaItem;
if (isLoading) {
logoImage = /* @__PURE__ */ jsx(Spinner, {});
}
useEffect(() => {
if (logoUrl && temporaryURL) {
setTemporaryURL();
}
}, [logoUrl, temporaryURL]);
if (!!logoUrl || !!temporaryURL) {
logoImage = /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(
SiteLogo,
{
alt,
attributes,
className,
isSelected,
setAttributes,
logoUrl: temporaryURL || logoUrl,
setLogo,
logoId: mediaItemData?.id || siteLogoId,
siteUrl: url,
setIcon,
iconId: siteIconId,
canUserEdit
}
),
canUserEdit && /* @__PURE__ */ jsx(DropZone, { onFilesDrop })
] });
}
const placeholder = (content) => {
const placeholderClassName = clsx(
"block-editor-media-placeholder",
className
);
return /* @__PURE__ */ jsx(
Placeholder,
{
className: placeholderClassName,
preview: logoImage,
withIllustration: true,
style: {
width
},
children: content
}
);
};
const classes = clsx(className, {
"is-default-size": !width,
"is-transient": temporaryURL
});
const blockProps = useBlockProps({ className: classes });
const mediaInspectorPanel = (canUserEdit || logoUrl) && /* @__PURE__ */ jsx(InspectorControls, { children: /* @__PURE__ */ jsx(
ToolsPanel,
{
label: __("Media"),
dropdownMenuProps,
children: !canUserEdit ? /* @__PURE__ */ jsx(
"div",
{
className: "block-library-site-logo__inspector-media-replace-container",
style: { gridColumn: "1 / -1" },
children: /* @__PURE__ */ jsx(
InspectorLogoPreview,
{
media: mediaItemData,
itemGroupProps: {
isBordered: true,
className: "block-library-site-logo__inspector-readonly-logo-preview"
}
}
)
}
) : /* @__PURE__ */ jsx(
ToolsPanelItem,
{
hasValue: () => !!logoUrl,
label: __("Logo"),
isShownByDefault: true,
children: /* @__PURE__ */ jsxs("div", { className: "block-library-site-logo__inspector-media-replace-container", children: [
/* @__PURE__ */ jsx(
SiteLogoReplaceFlow,
{
...mediaReplaceFlowProps,
name: !!logoUrl ? /* @__PURE__ */ jsx(
InspectorLogoPreview,
{
media: mediaItemData
}
) : __("Choose logo"),
renderToggle: (props) => /* @__PURE__ */ jsx(Button, { ...props, __next40pxDefaultSize: true, children: temporaryURL ? /* @__PURE__ */ jsx(Spinner, {}) : props.children })
}
),
/* @__PURE__ */ jsx(DropZone, { onFilesDrop })
] })
}
)
}
) });
return /* @__PURE__ */ jsxs("div", { ...blockProps, children: [
controls,
mediaInspectorPanel,
(!!logoUrl || !!temporaryURL) && logoImage,
(isLoading || !temporaryURL && !logoUrl && !canUserEdit) && /* @__PURE__ */ jsx(Placeholder, { className: "site-logo_placeholder", withIllustration: true, children: isLoading && /* @__PURE__ */ jsx("span", { className: "components-placeholder__preview", children: /* @__PURE__ */ jsx(Spinner, {}) }) }),
!isLoading && !temporaryURL && !logoUrl && canUserEdit && /* @__PURE__ */ jsx(
MediaPlaceholder,
{
onSelect: onInitialSelectLogo,
accept: ACCEPT_MEDIA_STRING,
allowedTypes: ALLOWED_MEDIA_TYPES,
onError: onUploadError,
placeholder,
mediaLibraryButton: ({ open }) => {
return /* @__PURE__ */ jsx(
Button,
{
__next40pxDefaultSize: true,
icon: upload,
variant: "primary",
label: __("Choose logo"),
showTooltip: true,
tooltipPosition: "middle right",
onClick: () => {
open();
}
}
);
}
}
)
] });
}
export {
LogoEdit as default
};
//# sourceMappingURL=edit.js.map