UNPKG

@wordpress/block-library

Version:
293 lines (292 loc) 10 kB
// packages/block-library/src/navigation-link/shared/controls.js import { __experimentalToolsPanel as ToolsPanel, __experimentalToolsPanelItem as ToolsPanelItem, __experimentalInputControl as InputControl, Button, CheckboxControl, TextControl, TextareaControl } from "@wordpress/components"; import { __, sprintf } from "@wordpress/i18n"; import { useRef, useEffect, useState } from "@wordpress/element"; import { useInstanceId } from "@wordpress/compose"; import { safeDecodeURI } from "@wordpress/url"; import { __unstableStripHTML as stripHTML } from "@wordpress/dom"; import { linkOff as unlinkIcon } from "@wordpress/icons"; import { useDispatch } from "@wordpress/data"; import { store as blockEditorStore } from "@wordpress/block-editor"; import { useToolsPanelDropdownMenuProps } from "../../utils/hooks"; import { updateAttributes } from "./update-attributes"; import { useEntityBinding } from "./use-entity-binding"; import { jsx, jsxs } from "react/jsx-runtime"; function getEntityTypeName(type, kind) { if (kind === "post-type") { switch (type) { case "post": return __("post"); case "page": return __("page"); default: return type || __("post"); } } if (kind === "taxonomy") { switch (type) { case "category": return __("category"); case "tag": return __("tag"); default: return type || __("term"); } } return type || __("item"); } function Controls({ attributes, setAttributes, clientId }) { const { label, url, description, rel, opensInNewTab } = attributes; const lastURLRef = useRef(url); const dropdownMenuProps = useToolsPanelDropdownMenuProps(); const urlInputRef = useRef(); const shouldFocusURLInputRef = useRef(false); const inputId = useInstanceId(Controls, "link-input"); const helpTextId = `${inputId}__help`; const [inputValue, setInputValue] = useState(url); useEffect(() => { setInputValue(url); lastURLRef.current = url; }, [url]); const { hasUrlBinding, isBoundEntityAvailable, clearBinding } = useEntityBinding({ clientId, attributes }); const { updateBlockAttributes } = useDispatch(blockEditorStore); const unsyncBoundLink = () => { clearBinding(); updateBlockAttributes(clientId, { url: lastURLRef.current, // set the lastURLRef as the new editable value so we avoid bugs from empty link states id: void 0 }); }; useEffect(() => { if (!hasUrlBinding && shouldFocusURLInputRef.current) { urlInputRef.current?.select(); } shouldFocusURLInputRef.current = false; }, [hasUrlBinding]); return /* @__PURE__ */ jsxs( ToolsPanel, { label: __("Settings"), resetAll: () => { setAttributes({ label: "", url: "", description: "", rel: "", opensInNewTab: false }); }, dropdownMenuProps, children: [ /* @__PURE__ */ jsx( ToolsPanelItem, { hasValue: () => !!label, label: __("Text"), onDeselect: () => setAttributes({ label: "" }), isShownByDefault: true, children: /* @__PURE__ */ jsx( TextControl, { __nextHasNoMarginBottom: true, __next40pxDefaultSize: true, label: __("Text"), value: label ? stripHTML(label) : "", onChange: (labelValue) => { setAttributes({ label: labelValue }); }, autoComplete: "off" } ) } ), /* @__PURE__ */ jsx( ToolsPanelItem, { hasValue: () => !!url, label: __("Link"), onDeselect: () => setAttributes({ url: "" }), isShownByDefault: true, children: /* @__PURE__ */ jsx( InputControl, { ref: urlInputRef, __nextHasNoMarginBottom: true, __next40pxDefaultSize: true, id: inputId, label: __("Link"), value: (() => { if (hasUrlBinding && !isBoundEntityAvailable) { return ""; } return inputValue ? safeDecodeURI(inputValue) : ""; })(), autoComplete: "off", type: "url", disabled: hasUrlBinding, "aria-invalid": hasUrlBinding && !isBoundEntityAvailable ? "true" : void 0, "aria-describedby": helpTextId, className: hasUrlBinding && !isBoundEntityAvailable ? "navigation-link-control__input-with-error-suffix" : void 0, onChange: (newValue) => { if (isBoundEntityAvailable) { return; } setInputValue(newValue); }, onFocus: () => { if (isBoundEntityAvailable) { return; } lastURLRef.current = url; }, onBlur: () => { if (isBoundEntityAvailable) { return; } const finalValue = !inputValue ? lastURLRef.current : inputValue; setInputValue(finalValue); updateAttributes({ url: finalValue }, setAttributes, { ...attributes, url: lastURLRef.current }); }, help: hasUrlBinding && !isBoundEntityAvailable ? /* @__PURE__ */ jsx( MissingEntityHelp, { id: helpTextId, type: attributes.type, kind: attributes.kind } ) : isBoundEntityAvailable && /* @__PURE__ */ jsx( BindingHelpText, { type: attributes.type, kind: attributes.kind } ), suffix: hasUrlBinding && /* @__PURE__ */ jsx( Button, { icon: unlinkIcon, onClick: () => { unsyncBoundLink(); shouldFocusURLInputRef.current = true; }, "aria-describedby": helpTextId, showTooltip: true, label: __("Unsync and edit"), __next40pxDefaultSize: true, className: hasUrlBinding && !isBoundEntityAvailable ? "navigation-link-control__error-suffix-button" : void 0 } ) } ) } ), /* @__PURE__ */ jsx( ToolsPanelItem, { hasValue: () => !!opensInNewTab, label: __("Open in new tab"), onDeselect: () => setAttributes({ opensInNewTab: false }), isShownByDefault: true, children: /* @__PURE__ */ jsx( CheckboxControl, { __nextHasNoMarginBottom: true, label: __("Open in new tab"), checked: opensInNewTab, onChange: (value) => setAttributes({ opensInNewTab: value }) } ) } ), /* @__PURE__ */ jsx( ToolsPanelItem, { hasValue: () => !!description, label: __("Description"), onDeselect: () => setAttributes({ description: "" }), isShownByDefault: true, children: /* @__PURE__ */ jsx( TextareaControl, { __nextHasNoMarginBottom: true, label: __("Description"), value: description || "", onChange: (descriptionValue) => { setAttributes({ description: descriptionValue }); }, help: __( "The description will be displayed in the menu if the current theme supports it." ) } ) } ), /* @__PURE__ */ jsx( ToolsPanelItem, { hasValue: () => !!rel, label: __("Rel attribute"), onDeselect: () => setAttributes({ rel: "" }), isShownByDefault: true, children: /* @__PURE__ */ jsx( TextControl, { __nextHasNoMarginBottom: true, __next40pxDefaultSize: true, label: __("Rel attribute"), value: rel || "", onChange: (relValue) => { setAttributes({ rel: relValue }); }, autoComplete: "off", help: __( "The relationship of the linked URL as space-separated link types." ) } ) } ) ] } ); } function BindingHelpText({ type, kind }) { const entityType = getEntityTypeName(type, kind); return sprintf( /* translators: %s is the entity type (e.g., "page", "post", "category") */ __("Synced with the selected %s."), entityType ); } function MissingEntityHelpText({ type, kind }) { const entityType = getEntityTypeName(type, kind); return sprintf( /* translators: %s is the entity type (e.g., "page", "post", "category") */ __("Synced %s is missing. Please update or remove this link."), entityType ); } function MissingEntityHelp({ id, type, kind }) { return /* @__PURE__ */ jsx("span", { id, className: "navigation-link-control__error-text", children: /* @__PURE__ */ jsx(MissingEntityHelpText, { type, kind }) }); } export { BindingHelpText, Controls, MissingEntityHelpText }; //# sourceMappingURL=controls.js.map