: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
* @typedef {{[setting:string]:any}} WPLinkControlSettingsValue
* Custom settings values associated with a link.
* @typedef WPLinkControlSetting
* @property {string} id Identifier to use as property for setting value.
* @property {string} title Human-readable label to show in user interface.
* Properties associated with a link control value, composed as a union of the
* default properties and any custom settings values.
* @typedef {WPLinkControlDefaultValue&WPLinkControlSettingsValue} WPLinkControlValue
/** @typedef {(nextValue:WPLinkControlValue)=>void} WPLinkControlOnChangeProp */
* Properties associated with a search suggestion used within the LinkControl.
* @typedef WPLinkControlSuggestion
* @property {string} id Identifier to use to uniquely identify the suggestion.
* @property {string} type Identifies the type of the suggestion (eg: `post`,
* @property {string} title Human-readable label to show in user interface.
* @property {string} url A URL for the suggestion.
/** @typedef {(title:string)=>WPLinkControlSuggestion} WPLinkControlCreateSuggestionProp */
* @typedef WPLinkControlProps
* @property {(WPLinkControlSetting[])=} settings An array of settings objects. Each object will used to
* render a `ToggleControl` for that setting.
* @property {boolean=} forceIsEditingLink If passed as either `true` or `false`, controls the
* internal editing state of the component to respective
* show or not show the URL input field.
* @property {WPLinkControlValue=} value Current link value.
* @property {WPLinkControlOnChangeProp=} onChange Value change handler, called with the updated value if
* the user selects a new link or updates settings.
* @property {boolean=} noDirectEntry Whether to allow turning a URL-like search query directly into a link.
* @property {boolean=} showSuggestions Whether to present suggestions when typing the URL.
* @property {boolean=} showInitialSuggestions Whether to present initial suggestions immediately.
* @property {boolean=} withCreateSuggestion Whether to allow creation of link value from suggestion.
* @property {Object=} suggestionsQuery Query parameters to pass along to wp.blockEditor.__experimentalFetchLinkSuggestions.
* @property {boolean=} noURLSuggestion Whether to add a fallback suggestion which treats the search query as a URL.
* @property {boolean=} hasTextControl Whether to add a text field to the UI to update the value.title.
* @property {string|Function|undefined} createSuggestionButtonText The text to use in the button that calls createSuggestion.
* @property {Function} renderControlBottom Optional controls to be rendered at the bottom of the component.
const link_control_noop = () => {};
const PREFERENCE_SCOPE = 'core/block-editor';
const PREFERENCE_KEY = 'linkControlSettingsDrawer';
* Renders a link control. A link control is a controlled input which maintains
* a value associated with a link (HTML anchor element) and relevant settings
* for how that link is expected to behave.
* @param {WPLinkControlProps} props Component props.
settings = DEFAULT_LINK_SETTINGS,
onChange = link_control_noop,
inputValue: propInputValue = '',
createSuggestionButtonText,
renderControlBottom = null
if (withCreateSuggestion === undefined && createSuggestion) {
withCreateSuggestion = true;
const [settingsOpen, setSettingsOpen] = (0,external_wp_element_namespaceObject.useState)(false);
advancedSettingsPreference
} = (0,external_wp_data_namespaceObject.useSelect)(select => {
const prefsStore = select(external_wp_preferences_namespaceObject.store);
advancedSettingsPreference: (_prefsStore$get = prefsStore.get(PREFERENCE_SCOPE, PREFERENCE_KEY)) !== null && _prefsStore$get !== void 0 ? _prefsStore$get : false
} = (0,external_wp_data_namespaceObject.useDispatch)(external_wp_preferences_namespaceObject.store);
* Sets the open/closed state of the Advanced Settings Drawer,
* optionlly persisting the state to the user's preferences.
* Note that Block Editor components can be consumed by non-WordPress
* environments which may not have preferences setup.
* Therefore a local state is also used as a fallback.
* @param {boolean} prefVal the open/closed state of the Advanced Settings Drawer.
const setSettingsOpenWithPreference = prefVal => {
setPreference(PREFERENCE_SCOPE, PREFERENCE_KEY, prefVal);
setSettingsOpen(prefVal);
// Block Editor components can be consumed by non-WordPress environments
// which may not have these preferences setup.
// Therefore a local state is used as a fallback.
const isSettingsOpen = advancedSettingsPreference || settingsOpen;
const isMounting = (0,external_wp_element_namespaceObject.useRef)(true);
const wrapperNode = (0,external_wp_element_namespaceObject.useRef)();
const textInputRef = (0,external_wp_element_namespaceObject.useRef)();
const isEndingEditWithFocus = (0,external_wp_element_namespaceObject.useRef)(false);
const settingsKeys = settings.map(({
const [internalControlValue, setInternalControlValue, setInternalURLInputValue, setInternalTextInputValue, createSetInternalSettingValueHandler] = useInternalValue(value);
const valueHasChanges = value && !(0,external_wp_isShallowEqual_namespaceObject.isShallowEqualObjects)(internalControlValue, value);
const [isEditingLink, setIsEditingLink] = (0,external_wp_element_namespaceObject.useState)(forceIsEditingLink !== undefined ? forceIsEditingLink : !value || !value.url);
} = useCreatePage(createSuggestion);
(0,external_wp_element_namespaceObject.useEffect)(() => {
if (forceIsEditingLink === undefined) {
setIsEditingLink(forceIsEditingLink);
}, [forceIsEditingLink]);
(0,external_wp_element_namespaceObject.useEffect)(() => {
// We don't auto focus into the Link UI on mount
// because otherwise using the keyboard to select text
// *within* the link format is not possible.
if (isMounting.current) {
// - switching between editable and non editable LinkControl
// ...then move focus to the *first* element to avoid focus loss
// and to ensure focus is *within* the Link UI.
const nextFocusTarget = external_wp_dom_namespaceObject.focus.focusable.find(wrapperNode.current)[0] || wrapperNode.current;
isEndingEditWithFocus.current = false;
}, [isEditingLink, isCreatingPage]);
// The component mounting reference is maintained separately
// to correctly reset values in `StrictMode`.
(0,external_wp_element_namespaceObject.useEffect)(() => {
isMounting.current = false;
isMounting.current = true;
const hasLinkValue = value?.url?.trim()?.length > 0;
* Cancels editing state and marks that focus may need to be restored after
* the next render, if focus was within the wrapper when editing finished.
const stopEditing = () => {
isEndingEditWithFocus.current = !!wrapperNode.current?.contains(wrapperNode.current.ownerDocument.activeElement);
const handleSelectSuggestion = updatedValue => {
// Suggestions may contains "settings" values (e.g. `opensInNewTab`)
// which should not overide any existing settings values set by the
// user. This filters out any settings values from the suggestion.
const nonSettingsChanges = Object.keys(updatedValue).reduce((acc, key) => {
if (!settingsKeys.includes(key)) {
acc[key] = updatedValue[key];
// As title is not a setting, it must be manually applied
// in such a way as to preserve the users changes over
// any "title" value provided by the "suggestion".
title: internalControlValue?.title || updatedValue?.title
const handleSubmit = () => {
// Submit the original value with new stored values applied
// on top. URL is a special case as it may also be a prop.
url: currentUrlInputValue
const handleSubmitWithEnter = event => {
if (keyCode === external_wp_keycodes_namespaceObject.ENTER && !currentInputIsEmpty // Disallow submitting empty values.
const resetInternalValues = () => {
setInternalControlValue(value);
const handleCancel = event => {
// Ensure that any unsubmitted input changes are reset.
// If there is a link then exist editing mode and show preview.
// If there is no link value, then remove the link entirely.
const currentUrlInputValue = propInputValue || internalControlValue?.url || '';
const currentInputIsEmpty = !currentUrlInputValue?.trim()?.length;
const shownUnlinkControl = onRemove && value && !isEditingLink && !isCreatingPage;
const showActions = isEditingLink && hasLinkValue;
// Only show text control once a URL value has been committed
// and it isn't just empty whitespace.
// See https://github.com/WordPress/gutenberg/pull/33849/#issuecomment-932194927.
const showTextControl = hasLinkValue && hasTextControl;
const isEditing = (isEditingLink || !value) && !isCreatingPage;
const isDisabled = !valueHasChanges || currentInputIsEmpty;
const showSettings = !!settings?.length && isEditingLink && hasLinkValue;
return /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)("div", {
className: "block-editor-link-control",
children: [isCreatingPage && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)("div", {
className: "block-editor-link-control__loading",
children: [/*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.Spinner, {}), " ", (0,external_wp_i18n_namespaceObject.__)('Creating'), "\u2026"]
}), isEditing && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)(external_ReactJSXRuntime_namespaceObject.Fragment, {
children: [/*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)("div", {
'block-editor-link-control__search-input-wrapper': true,
'has-text-control': showTextControl,
'has-actions': showActions
children: [showTextControl && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.TextControl, {
__nextHasNoMarginBottom: true,
className: "block-editor-link-control__field block-editor-link-control__text-content",
label: (0,external_wp_i18n_namespaceObject.__)('Text'),
value: internalControlValue?.title,
onChange: setInternalTextInputValue,
onKeyDown: handleSubmitWithEnter,
}), /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(search_input, {
className: "block-editor-link-control__field block-editor-link-control__search-input",
placeholder: searchInputPlaceholder,
value: currentUrlInputValue,
withCreateSuggestion: withCreateSuggestion,
onCreateSuggestion: createPage,
onChange: setInternalURLInputValue,
onSelect: handleSelectSuggestion,
showInitialSuggestions: showInitialSuggestions,
allowDirectEntry: !noDirectEntry,
showSuggestions: showSuggestions,
suggestionsQuery: suggestionsQuery,
withURLSuggestion: !noURLSuggestion,
createSuggestionButtonText: createSuggestionButtonText,
hideLabelFromVision: !showTextControl
}), !showActions && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)("div", {
className: "block-editor-link-control__search-enter",
children: /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.Button, {
onClick: isDisabled ? link_control_noop : handleSubmit,
label: (0,external_wp_i18n_namespaceObject.__)('Submit'),
className: "block-editor-link-control__search-submit",
"aria-disabled": isDisabled
}), errorMessage && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.Notice, {
className: "block-editor-link-control__search-error",
}), value && !isEditingLink && !isCreatingPage && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(LinkPreview, {
// force remount when URL changes to avoid race conditions for rich previews
onEditClick: () => setIsEditingLink(true),
hasRichPreviews: hasRichPreviews,
hasUnlinkControl: shownUnlinkControl,
}, value?.url), showSettings && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)("div", {
className: "block-editor-link-control__tools",
children: !currentInputIsEmpty && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(settings_drawer, {
settingsOpen: isSettingsOpen,
setSettingsOpen: setSettingsOpenWithPreference,
children: /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(link_control_settings, {
value: internalControlValue,
onChange: createSetInternalSettingValueHandler(settingsKeys)
}), showActions && /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)(external_wp_components_namespaceObject.__experimentalHStack, {
className: "block-editor-link-control__search-actions",
children: [/*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.Button, {
children: (0,external_wp_i18n_namespaceObject.__)('Cancel')
}), /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.Button, {
onClick: isDisabled ? link_control_noop : handleSubmit,
className: "block-editor-link-control__search-submit",
"aria-disabled": isDisabled,
children: (0,external_wp_i18n_namespaceObject.__)('Save')
}), !isCreatingPage && renderControlBottom && renderControlBottom()]
LinkControl.ViewerFill = ViewerFill;
LinkControl.DEFAULT_LINK_SETTINGS = DEFAULT_LINK_SETTINGS;
/* harmony default export */ const link_control = (LinkControl);
;// CONCATENATED MODULE: ./node_modules/@wordpress/block-editor/build-module/components/media-replace-flow/index.js
const media_replace_flow_noop = () => {};
const MediaReplaceFlow = ({
onFilesUpload = media_replace_flow_noop,
name = (0,external_wp_i18n_namespaceObject.__)('Replace'),
const mediaUpload = (0,external_wp_data_namespaceObject.useSelect)(select => {
return select(store).getSettings().mediaUpload;
const canUpload = !!mediaUpload;
const editMediaButtonRef = (0,external_wp_element_namespaceObject.useRef)();
const errorNoticeID = `block-editor/media-replace-flow/error-notice/${++uniqueId}`;
const onUploadError = message => {
const safeMessage = (0,external_wp_dom_namespaceObject.__unstableStripHTML)(message);
// We need to set a timeout for showing the notice
// so that VoiceOver and possibly other screen readers
// can announce the error afer the toolbar button
// regains focus once the upload dialog closes.
// Otherwise VO simply skips over the notice and announces
// the focused element and the open menu.
createNotice('error', safeMessage, {
const selectMedia = (media, closeMenu) => {
if (useFeaturedImage && onToggleFeaturedImage) {
// Calling `onSelect` after the state update since it might unmount the component.
(0,external_wp_a11y_namespaceObject.speak)((0,external_wp_i18n_namespaceObject.__)('The media file has been replaced'));
removeNotice(errorNoticeID);
const uploadFiles = (event, closeMenu) => {
const files = event.target.files;
onFileChange: ([media]) => {
selectMedia(media, closeMenu);
const openOnArrowDown = event => {
if (event.keyCode === external_wp_keycodes_namespaceObject.DOWN) {
const onlyAllowsImages = () => {
if (!allowedTypes || allowedTypes.length === 0) {
return allowedTypes.every(allowedType => allowedType === 'image' || allowedType.startsWith('image/'));
const gallery = multiple && onlyAllowsImages();
return /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.Dropdown, {
popoverProps: popoverProps,
contentClassName: "block-editor-media-replace-flow__options",
}) => /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(external_wp_components_namespaceObject.ToolbarButton, {
onKeyDown: openOnArrowDown,
}) => /*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)(external_ReactJSXRuntime_namespaceObject.Fragment, {
children: [/*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)(external_wp_components_namespaceObject.NavigableMenu, {
className: "block-editor-media-replace-flow__media-upload-menu",
children: [/*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsxs)(check, {
children: [/*#__PURE__*/(0,external_ReactJSXRuntime_namespaceObject.jsx)(media_upload, {
addToGallery: addToGallery,
value: multiple ? mediaIds : mediaId,