@wordpress/format-library
Version:
Format library for the WordPress editor.
166 lines (162 loc) • 4.8 kB
JavaScript
/**
* External dependencies
*/
import Clipboard from '@react-native-clipboard/clipboard';
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
import { withSpokenMessages } from '@wordpress/components';
import { RichTextToolbarButton } from '@wordpress/block-editor';
import { applyFormat, getActiveFormat, getTextContent, isCollapsed, removeFormat, slice } from '@wordpress/rich-text';
import { isURL } from '@wordpress/url';
import { link as linkIcon } from '@wordpress/icons';
/**
* Internal dependencies
*/
import ModalLinkUI from './modal';
import { isValidHref } from './utils';
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
const name = 'core/link';
export const link = {
name,
title: __('Link'),
tagName: 'a',
className: null,
attributes: {
url: 'href',
target: 'target',
rel: 'rel'
},
edit: withSpokenMessages(class LinkEdit extends Component {
constructor() {
super(...arguments);
this.addLink = this.addLink.bind(this);
this.stopAddingLink = this.stopAddingLink.bind(this);
this.onRemoveFormat = this.onRemoveFormat.bind(this);
this.getURLFromClipboard = this.getURLFromClipboard.bind(this);
this.state = {
addingLink: false
};
}
addLink() {
const {
value,
onChange
} = this.props;
const text = getTextContent(slice(value));
if (text && isURL(text) && isValidHref(text)) {
const newValue = applyFormat(value, {
type: name,
attributes: {
url: text
}
});
newValue.start = newValue.end;
newValue.activeFormats = [];
onChange({
...newValue,
needsSelectionUpdate: true
});
} else {
this.setState({
addingLink: true
});
this.getURLFromClipboard();
}
}
stopAddingLink() {
this.setState({
addingLink: false,
clipboardURL: undefined
});
}
getLinkSelection() {
const {
value,
isActive
} = this.props;
const startFormat = getActiveFormat(value, 'core/link');
// If the link isn't selected, get the link manually by looking around the cursor
// TODO: handle partly selected links.
if (startFormat && isCollapsed(value) && isActive) {
let startIndex = value.start;
let endIndex = value.end;
while (value.formats[startIndex]?.find(format => format?.type === startFormat.type)) {
startIndex--;
}
endIndex++;
while (value.formats[endIndex]?.find(format => format?.type === startFormat.type)) {
endIndex++;
}
return {
...value,
start: startIndex + 1,
end: endIndex
};
}
return value;
}
onRemoveFormat() {
const {
onChange,
speak,
value
} = this.props;
const startFormat = getActiveFormat(value, 'core/link');
// Before we try to remove anything we check if there is something at the caret position to remove.
if (isCollapsed(value) && startFormat === undefined) {
return;
}
const linkSelection = this.getLinkSelection();
onChange(removeFormat(linkSelection, name));
speak(__('Link removed.'), 'assertive');
}
async getURLFromClipboard() {
const clipboardText = await Clipboard.getString();
if (!clipboardText) {
return;
}
// Check if pasted text is URL.
if (!isURL(clipboardText)) {
return;
}
this.setState({
clipboardURL: clipboardText
});
}
render() {
const {
isActive,
activeAttributes,
onChange
} = this.props;
const linkSelection = this.getLinkSelection();
// If no URL is set and we have a clipboard URL let's use it.
if (!activeAttributes.url && this.state.clipboardURL) {
activeAttributes.url = this.state.clipboardURL;
}
return /*#__PURE__*/_jsxs(_Fragment, {
children: [/*#__PURE__*/_jsx(ModalLinkUI, {
isVisible: this.state.addingLink,
isActive: isActive,
activeAttributes: activeAttributes,
onClose: this.stopAddingLink,
onChange: onChange,
onRemove: this.onRemoveFormat,
value: linkSelection
}), /*#__PURE__*/_jsx(RichTextToolbarButton, {
name: "link",
icon: linkIcon,
title: __('Link'),
onClick: this.addLink,
isActive: isActive,
shortcutType: "primary",
shortcutCharacter: "k"
})]
});
}
})
};
//# sourceMappingURL=index.native.js.map