@wordpress/block-editor
Version:
457 lines (455 loc) • 15.5 kB
JavaScript
"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/url-input/index.js
var url_input_exports = {};
__export(url_input_exports, {
default: () => url_input_default
});
module.exports = __toCommonJS(url_input_exports);
var import_clsx = __toESM(require("clsx"));
var import_i18n = require("@wordpress/i18n");
var import_element = require("@wordpress/element");
var import_keycodes = require("@wordpress/keycodes");
var import_components = require("@wordpress/components");
var import_compose = require("@wordpress/compose");
var import_data = require("@wordpress/data");
var import_url = require("@wordpress/url");
var import_store = require("../../store");
var import_jsx_runtime = require("react/jsx-runtime");
var import_react = require("react");
function isFunction(maybeFunc) {
return typeof maybeFunc === "function";
}
var URLInput = class extends import_element.Component {
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
this.onFocus = this.onFocus.bind(this);
this.onKeyDown = this.onKeyDown.bind(this);
this.selectLink = this.selectLink.bind(this);
this.handleOnClick = this.handleOnClick.bind(this);
this.bindSuggestionNode = this.bindSuggestionNode.bind(this);
this.autocompleteRef = props.autocompleteRef || (0, import_element.createRef)();
this.inputRef = props.inputRef || (0, import_element.createRef)();
this.updateSuggestions = (0, import_compose.debounce)(
this.updateSuggestions.bind(this),
200
);
this.suggestionNodes = [];
this.suggestionsRequest = null;
this.state = {
suggestions: [],
showSuggestions: false,
suggestionsValue: null,
selectedSuggestion: null,
suggestionsListboxId: "",
suggestionOptionIdPrefix: ""
};
}
componentDidUpdate(prevProps) {
const { showSuggestions, selectedSuggestion } = this.state;
const { value, __experimentalShowInitialSuggestions = false } = this.props;
if (showSuggestions && selectedSuggestion !== null && this.suggestionNodes[selectedSuggestion]) {
this.suggestionNodes[selectedSuggestion].scrollIntoView({
behavior: "instant",
block: "nearest",
inline: "nearest"
});
}
if (prevProps.value !== value && !this.props.disableSuggestions) {
if (value?.length) {
this.updateSuggestions(value);
} else if (__experimentalShowInitialSuggestions) {
this.updateSuggestions();
}
}
}
componentDidMount() {
if (this.shouldShowInitialSuggestions()) {
this.updateSuggestions();
}
}
componentWillUnmount() {
this.suggestionsRequest?.cancel?.();
this.suggestionsRequest = null;
}
bindSuggestionNode(index) {
return (ref) => {
this.suggestionNodes[index] = ref;
};
}
shouldShowInitialSuggestions() {
const { __experimentalShowInitialSuggestions = false, value } = this.props;
return __experimentalShowInitialSuggestions && !(value && value.length);
}
updateSuggestions(value = "") {
const {
__experimentalFetchLinkSuggestions: fetchLinkSuggestions,
__experimentalHandleURLSuggestions: handleURLSuggestions
} = this.props;
if (!fetchLinkSuggestions) {
return;
}
const isInitialSuggestions = !value?.length;
value = value.trim();
if (!isInitialSuggestions && (value.length < 2 || !handleURLSuggestions && (0, import_url.isURL)(value))) {
this.suggestionsRequest?.cancel?.();
this.suggestionsRequest = null;
this.setState({
suggestions: [],
showSuggestions: false,
suggestionsValue: value,
selectedSuggestion: null,
loading: false
});
return;
}
this.setState({
selectedSuggestion: null,
loading: true
});
const request = fetchLinkSuggestions(value, {
isInitialSuggestions
});
request.then((suggestions) => {
if (this.suggestionsRequest !== request) {
return;
}
this.setState({
suggestions,
suggestionsValue: value,
loading: false,
showSuggestions: !!suggestions.length
});
if (!!suggestions.length) {
this.props.debouncedSpeak(
(0, import_i18n.sprintf)(
/* translators: %d: number of results. */
(0, import_i18n._n)(
"%d result found, use up and down arrow keys to navigate.",
"%d results found, use up and down arrow keys to navigate.",
suggestions.length
),
suggestions.length
),
"assertive"
);
} else {
this.props.debouncedSpeak(
(0, import_i18n.__)("No results."),
"assertive"
);
}
}).catch(() => {
if (this.suggestionsRequest !== request) {
return;
}
this.setState({
loading: false
});
}).finally(() => {
if (this.suggestionsRequest === request) {
this.suggestionsRequest = null;
}
});
this.suggestionsRequest = request;
}
onChange(newValue) {
this.props.onChange(newValue);
}
onFocus() {
const { suggestions } = this.state;
const { disableSuggestions, value } = this.props;
if (value && !disableSuggestions && !(suggestions && suggestions.length) && this.suggestionsRequest === null) {
this.updateSuggestions(value);
}
}
onKeyDown(event) {
this.props.onKeyDown?.(event);
const { showSuggestions, selectedSuggestion, suggestions, loading } = this.state;
if (!showSuggestions || !suggestions.length || loading) {
switch (event.keyCode) {
// When UP is pressed, if the caret is at the start of the text, move it to the 0
// position.
case import_keycodes.UP: {
if (0 !== event.target.selectionStart) {
event.preventDefault();
event.target.setSelectionRange(0, 0);
}
break;
}
// When DOWN is pressed, if the caret is not at the end of the text, move it to the
// last position.
case import_keycodes.DOWN: {
if (this.props.value.length !== event.target.selectionStart) {
event.preventDefault();
event.target.setSelectionRange(
this.props.value.length,
this.props.value.length
);
}
break;
}
// Submitting while loading should trigger onSubmit.
case import_keycodes.ENTER: {
if (this.props.onSubmit) {
event.preventDefault();
this.props.onSubmit(null, event);
}
break;
}
}
return;
}
const suggestion = this.state.suggestions[this.state.selectedSuggestion];
switch (event.keyCode) {
case import_keycodes.UP: {
event.preventDefault();
const previousIndex = !selectedSuggestion ? suggestions.length - 1 : selectedSuggestion - 1;
this.setState({
selectedSuggestion: previousIndex
});
break;
}
case import_keycodes.DOWN: {
event.preventDefault();
const nextIndex = selectedSuggestion === null || selectedSuggestion === suggestions.length - 1 ? 0 : selectedSuggestion + 1;
this.setState({
selectedSuggestion: nextIndex
});
break;
}
case import_keycodes.TAB: {
if (this.state.selectedSuggestion !== null) {
this.selectLink(suggestion);
this.props.speak((0, import_i18n.__)("Link selected."));
}
break;
}
case import_keycodes.ENTER: {
event.preventDefault();
if (this.state.selectedSuggestion !== null) {
this.selectLink(suggestion);
if (this.props.onSubmit) {
this.props.onSubmit(suggestion, event);
}
} else if (this.props.onSubmit) {
this.props.onSubmit(null, event);
}
break;
}
}
}
selectLink(suggestion) {
this.props.onChange(suggestion.url, suggestion);
this.setState({
selectedSuggestion: null,
showSuggestions: false
});
}
handleOnClick(suggestion) {
this.selectLink(suggestion);
this.inputRef.current.focus();
}
static getDerivedStateFromProps({
value,
instanceId,
disableSuggestions,
__experimentalShowInitialSuggestions = false
}, { showSuggestions }) {
let shouldShowSuggestions = showSuggestions;
const hasValue = value && value.length;
if (!__experimentalShowInitialSuggestions && !hasValue) {
shouldShowSuggestions = false;
}
if (disableSuggestions === true) {
shouldShowSuggestions = false;
}
return {
showSuggestions: shouldShowSuggestions,
suggestionsListboxId: `block-editor-url-input-suggestions-${instanceId}`,
suggestionOptionIdPrefix: `block-editor-url-input-suggestion-${instanceId}`
};
}
render() {
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
this.renderControl(),
this.renderSuggestions()
] });
}
renderControl() {
const {
label = null,
className,
isFullWidth,
instanceId,
placeholder = (0, import_i18n.__)("Paste URL or type to search"),
__experimentalRenderControl: renderControl,
value = "",
hideLabelFromVision = false,
help = null,
disabled = false
} = this.props;
const {
loading,
showSuggestions,
selectedSuggestion,
suggestionsListboxId,
suggestionOptionIdPrefix
} = this.state;
const inputId = `url-input-control-${instanceId}`;
const controlProps = {
id: inputId,
// Passes attribute to label for the for attribute
label,
className: (0, import_clsx.default)("block-editor-url-input", className, {
"is-full-width": isFullWidth
}),
hideLabelFromVision
};
const inputProps = {
id: inputId,
value,
required: true,
type: "text",
onChange: disabled ? () => {
} : this.onChange,
// Disable onChange when disabled
onFocus: disabled ? () => {
} : this.onFocus,
// Disable onFocus when disabled
placeholder,
onKeyDown: disabled ? () => {
} : this.onKeyDown,
// Disable onKeyDown when disabled
role: "combobox",
"aria-label": label ? void 0 : (0, import_i18n.__)("URL"),
// Ensure input always has an accessible label
"aria-expanded": showSuggestions,
"aria-autocomplete": "list",
"aria-owns": suggestionsListboxId,
"aria-activedescendant": selectedSuggestion !== null ? `${suggestionOptionIdPrefix}-${selectedSuggestion}` : void 0,
ref: this.inputRef,
disabled,
suffix: this.props.suffix,
help
};
if (renderControl) {
return renderControl(controlProps, inputProps, loading);
}
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_components.BaseControl, { __nextHasNoMarginBottom: true, ...controlProps, children: [
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.__experimentalInputControl, { ...inputProps, __next40pxDefaultSize: true }),
loading && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Spinner, {})
] });
}
renderSuggestions() {
const {
className,
__experimentalRenderSuggestions: renderSuggestions
} = this.props;
const {
showSuggestions,
suggestions,
suggestionsValue,
selectedSuggestion,
suggestionsListboxId,
suggestionOptionIdPrefix,
loading
} = this.state;
if (!showSuggestions || suggestions.length === 0) {
return null;
}
const suggestionsListProps = {
id: suggestionsListboxId,
ref: this.autocompleteRef,
role: "listbox"
};
const buildSuggestionItemProps = (suggestion, index) => {
return {
role: "option",
tabIndex: "-1",
id: `${suggestionOptionIdPrefix}-${index}`,
ref: this.bindSuggestionNode(index),
"aria-selected": index === selectedSuggestion ? true : void 0
};
};
if (isFunction(renderSuggestions)) {
return renderSuggestions({
suggestions,
selectedSuggestion,
suggestionsListProps,
buildSuggestionItemProps,
isLoading: loading,
handleSuggestionClick: this.handleOnClick,
isInitialSuggestions: !suggestionsValue?.length,
currentInputValue: suggestionsValue
});
}
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_components.Popover, { placement: "bottom", focusOnMount: false, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
"div",
{
...suggestionsListProps,
className: (0, import_clsx.default)("block-editor-url-input__suggestions", {
[`${className}__suggestions`]: className
}),
children: suggestions.map((suggestion, index) => /* @__PURE__ */ (0, import_react.createElement)(
import_components.Button,
{
__next40pxDefaultSize: true,
...buildSuggestionItemProps(suggestion, index),
key: suggestion.id,
className: (0, import_clsx.default)(
"block-editor-url-input__suggestion",
{
"is-selected": index === selectedSuggestion
}
),
onClick: () => this.handleOnClick(suggestion)
},
suggestion.title
))
}
) });
}
};
var url_input_default = (0, import_compose.compose)(
import_compose.withSafeTimeout,
import_components.withSpokenMessages,
import_compose.withInstanceId,
(0, import_data.withSelect)((select, props) => {
if (isFunction(props.__experimentalFetchLinkSuggestions)) {
return;
}
const { getSettings } = select(import_store.store);
return {
__experimentalFetchLinkSuggestions: getSettings().__experimentalFetchLinkSuggestions
};
})
)(URLInput);
//# sourceMappingURL=index.js.map