UNPKG

@wordpress/block-library

Version:
281 lines (275 loc) 9.53 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = exports.LinkUI = void 0; exports.getSuggestionsQuery = getSuggestionsQuery; var _dom = require("@wordpress/dom"); var _components = require("@wordpress/components"); var _i18n = require("@wordpress/i18n"); var _blockEditor = require("@wordpress/block-editor"); var _element = require("@wordpress/element"); var _coreData = require("@wordpress/core-data"); var _htmlEntities = require("@wordpress/html-entities"); var _data = require("@wordpress/data"); var _icons = require("@wordpress/icons"); var _compose = require("@wordpress/compose"); var _lockUnlock = require("../lock-unlock"); var _jsxRuntime = require("react/jsx-runtime"); /** * WordPress dependencies */ /** * Internal dependencies */ const { PrivateQuickInserter: QuickInserter } = (0, _lockUnlock.unlock)(_blockEditor.privateApis); /** * Given the Link block's type attribute, return the query params to give to * /wp/v2/search. * * @param {string} type Link block's type attribute. * @param {string} kind Link block's entity of kind (post-type|taxonomy) * @return {{ type?: string, subtype?: string }} Search query params. */ function getSuggestionsQuery(type, kind) { switch (type) { case 'post': case 'page': return { type: 'post', subtype: type }; case 'category': return { type: 'term', subtype: 'category' }; case 'tag': return { type: 'term', subtype: 'post_tag' }; case 'post_format': return { type: 'post-format' }; default: if (kind === 'taxonomy') { return { type: 'term', subtype: type }; } if (kind === 'post-type') { return { type: 'post', subtype: type }; } return { // for custom link which has no type // always show pages as initial suggestions initialSuggestionsSearchOptions: { type: 'post', subtype: 'page', perPage: 20 } }; } } function LinkUIBlockInserter({ clientId, onBack }) { const { rootBlockClientId } = (0, _data.useSelect)(select => { const { getBlockRootClientId } = select(_blockEditor.store); return { rootBlockClientId: getBlockRootClientId(clientId) }; }, [clientId]); const focusOnMountRef = (0, _compose.useFocusOnMount)('firstElement'); const dialogTitleId = (0, _compose.useInstanceId)(_blockEditor.LinkControl, `link-ui-block-inserter__title`); const dialogDescriptionId = (0, _compose.useInstanceId)(_blockEditor.LinkControl, `link-ui-block-inserter__description`); if (!clientId) { return null; } return /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { className: "link-ui-block-inserter", role: "dialog", "aria-labelledby": dialogTitleId, "aria-describedby": dialogDescriptionId, ref: focusOnMountRef, children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.VisuallyHidden, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("h2", { id: dialogTitleId, children: (0, _i18n.__)('Add block') }), /*#__PURE__*/(0, _jsxRuntime.jsx)("p", { id: dialogDescriptionId, children: (0, _i18n.__)('Choose a block to add to your Navigation.') })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, { className: "link-ui-block-inserter__back", icon: (0, _i18n.isRTL)() ? _icons.chevronRightSmall : _icons.chevronLeftSmall, onClick: e => { e.preventDefault(); onBack(); }, size: "small", children: (0, _i18n.__)('Back') }), /*#__PURE__*/(0, _jsxRuntime.jsx)(QuickInserter, { rootClientId: rootBlockClientId, clientId: clientId, isAppender: false, prioritizePatterns: false, selectBlockOnInsert: true, hasSearch: false })] }); } function UnforwardedLinkUI(props, ref) { const { label, url, opensInNewTab, type, kind } = props.link; const postType = type || 'page'; const [addingBlock, setAddingBlock] = (0, _element.useState)(false); const [focusAddBlockButton, setFocusAddBlockButton] = (0, _element.useState)(false); const { saveEntityRecord } = (0, _data.useDispatch)(_coreData.store); const permissions = (0, _coreData.useResourcePermissions)({ kind: 'postType', name: postType }); async function handleCreate(pageTitle) { const page = await saveEntityRecord('postType', postType, { title: pageTitle, status: 'draft' }); return { id: page.id, type: postType, // Make `title` property consistent with that in `fetchLinkSuggestions` where the `rendered` title (containing HTML entities) // is also being decoded. By being consistent in both locations we avoid having to branch in the rendering output code. // Ideally in the future we will update both APIs to utilise the "raw" form of the title which is better suited to edit contexts. // e.g. // - title.raw = "Yes & No" // - title.rendered = "Yes &#038; No" // - decodeEntities( title.rendered ) = "Yes & No" // See: // - https://github.com/WordPress/gutenberg/pull/41063 // - https://github.com/WordPress/gutenberg/blob/a1e1fdc0e6278457e9f4fc0b31ac6d2095f5450b/packages/core-data/src/fetch/__experimental-fetch-link-suggestions.js#L212-L218 title: (0, _htmlEntities.decodeEntities)(page.title.rendered), url: page.link, kind: 'post-type' }; } // Memoize link value to avoid overriding the LinkControl's internal state. // This is a temporary fix. See https://github.com/WordPress/gutenberg/issues/50976#issuecomment-1568226407. const link = (0, _element.useMemo)(() => ({ url, opensInNewTab, title: label && (0, _dom.__unstableStripHTML)(label) }), [label, opensInNewTab, url]); const dialogTitleId = (0, _compose.useInstanceId)(LinkUI, `link-ui-link-control__title`); const dialogDescriptionId = (0, _compose.useInstanceId)(LinkUI, `link-ui-link-control__description`); return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.Popover, { ref: ref, placement: "bottom", onClose: props.onClose, anchor: props.anchor, shift: true, children: [!addingBlock && /*#__PURE__*/(0, _jsxRuntime.jsxs)("div", { role: "dialog", "aria-labelledby": dialogTitleId, "aria-describedby": dialogDescriptionId, children: [/*#__PURE__*/(0, _jsxRuntime.jsxs)(_components.VisuallyHidden, { children: [/*#__PURE__*/(0, _jsxRuntime.jsx)("h2", { id: dialogTitleId, children: (0, _i18n.__)('Add link') }), /*#__PURE__*/(0, _jsxRuntime.jsx)("p", { id: dialogDescriptionId, children: (0, _i18n.__)('Search for and add a link to your Navigation.') })] }), /*#__PURE__*/(0, _jsxRuntime.jsx)(_blockEditor.LinkControl, { hasTextControl: true, hasRichPreviews: true, value: link, showInitialSuggestions: true, withCreateSuggestion: permissions.canCreate, createSuggestion: handleCreate, createSuggestionButtonText: searchTerm => { let format; if (type === 'post') { /* translators: %s: search term. */ format = (0, _i18n.__)('Create draft post: <mark>%s</mark>'); } else { /* translators: %s: search term. */ format = (0, _i18n.__)('Create draft page: <mark>%s</mark>'); } return (0, _element.createInterpolateElement)((0, _i18n.sprintf)(format, searchTerm), { mark: /*#__PURE__*/(0, _jsxRuntime.jsx)("mark", {}) }); }, noDirectEntry: !!type, noURLSuggestion: !!type, suggestionsQuery: getSuggestionsQuery(type, kind), onChange: props.onChange, onRemove: props.onRemove, onCancel: props.onCancel, renderControlBottom: () => !link?.url?.length && /*#__PURE__*/(0, _jsxRuntime.jsx)(LinkUITools, { focusAddBlockButton: focusAddBlockButton, setAddingBlock: () => { setAddingBlock(true); setFocusAddBlockButton(false); } }) })] }), addingBlock && /*#__PURE__*/(0, _jsxRuntime.jsx)(LinkUIBlockInserter, { clientId: props.clientId, onBack: () => { setAddingBlock(false); setFocusAddBlockButton(true); } })] }); } const LinkUI = exports.LinkUI = (0, _element.forwardRef)(UnforwardedLinkUI); const LinkUITools = ({ setAddingBlock, focusAddBlockButton }) => { const blockInserterAriaRole = 'listbox'; const addBlockButtonRef = (0, _element.useRef)(); // Focus the add block button when the popover is opened. (0, _element.useEffect)(() => { if (focusAddBlockButton) { addBlockButtonRef.current?.focus(); } }, [focusAddBlockButton]); return /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.__experimentalVStack, { className: "link-ui-tools", children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_components.Button, { __next40pxDefaultSize: true, ref: addBlockButtonRef, icon: _icons.plus, onClick: e => { e.preventDefault(); setAddingBlock(true); }, "aria-haspopup": blockInserterAriaRole, children: (0, _i18n.__)('Add block') }) }); }; var _default = exports.default = LinkUITools; //# sourceMappingURL=link-ui.js.map