summaryrefslogtreecommitdiffstats
path: root/wp-includes/js/dist/rich-text.js
diff options
context:
space:
mode:
Diffstat (limited to 'wp-includes/js/dist/rich-text.js')
-rw-r--r--wp-includes/js/dist/rich-text.js1255
1 files changed, 481 insertions, 774 deletions
diff --git a/wp-includes/js/dist/rich-text.js b/wp-includes/js/dist/rich-text.js
index e0aa83e..3e953e5 100644
--- a/wp-includes/js/dist/rich-text.js
+++ b/wp-includes/js/dist/rich-text.js
@@ -136,307 +136,9 @@ function formatTypes(state = {}, action) {
formatTypes
}));
-;// CONCATENATED MODULE: ./node_modules/rememo/rememo.js
-
-
-/** @typedef {(...args: any[]) => *[]} GetDependants */
-
-/** @typedef {() => void} Clear */
-
-/**
- * @typedef {{
- * getDependants: GetDependants,
- * clear: Clear
- * }} EnhancedSelector
- */
-
-/**
- * Internal cache entry.
- *
- * @typedef CacheNode
- *
- * @property {?CacheNode|undefined} [prev] Previous node.
- * @property {?CacheNode|undefined} [next] Next node.
- * @property {*[]} args Function arguments for cache entry.
- * @property {*} val Function result.
- */
-
-/**
- * @typedef Cache
- *
- * @property {Clear} clear Function to clear cache.
- * @property {boolean} [isUniqueByDependants] Whether dependants are valid in
- * considering cache uniqueness. A cache is unique if dependents are all arrays
- * or objects.
- * @property {CacheNode?} [head] Cache head.
- * @property {*[]} [lastDependants] Dependants from previous invocation.
- */
-
-/**
- * Arbitrary value used as key for referencing cache object in WeakMap tree.
- *
- * @type {{}}
- */
-var LEAF_KEY = {};
-
-/**
- * Returns the first argument as the sole entry in an array.
- *
- * @template T
- *
- * @param {T} value Value to return.
- *
- * @return {[T]} Value returned as entry in array.
- */
-function arrayOf(value) {
- return [value];
-}
-
-/**
- * Returns true if the value passed is object-like, or false otherwise. A value
- * is object-like if it can support property assignment, e.g. object or array.
- *
- * @param {*} value Value to test.
- *
- * @return {boolean} Whether value is object-like.
- */
-function isObjectLike(value) {
- return !!value && 'object' === typeof value;
-}
-
-/**
- * Creates and returns a new cache object.
- *
- * @return {Cache} Cache object.
- */
-function createCache() {
- /** @type {Cache} */
- var cache = {
- clear: function () {
- cache.head = null;
- },
- };
-
- return cache;
-}
-
-/**
- * Returns true if entries within the two arrays are strictly equal by
- * reference from a starting index.
- *
- * @param {*[]} a First array.
- * @param {*[]} b Second array.
- * @param {number} fromIndex Index from which to start comparison.
- *
- * @return {boolean} Whether arrays are shallowly equal.
- */
-function isShallowEqual(a, b, fromIndex) {
- var i;
-
- if (a.length !== b.length) {
- return false;
- }
-
- for (i = fromIndex; i < a.length; i++) {
- if (a[i] !== b[i]) {
- return false;
- }
- }
-
- return true;
-}
-
-/**
- * Returns a memoized selector function. The getDependants function argument is
- * called before the memoized selector and is expected to return an immutable
- * reference or array of references on which the selector depends for computing
- * its own return value. The memoize cache is preserved only as long as those
- * dependant references remain the same. If getDependants returns a different
- * reference(s), the cache is cleared and the selector value regenerated.
- *
- * @template {(...args: *[]) => *} S
- *
- * @param {S} selector Selector function.
- * @param {GetDependants=} getDependants Dependant getter returning an array of
- * references used in cache bust consideration.
- */
-/* harmony default export */ function rememo(selector, getDependants) {
- /** @type {WeakMap<*,*>} */
- var rootCache;
-
- /** @type {GetDependants} */
- var normalizedGetDependants = getDependants ? getDependants : arrayOf;
-
- /**
- * Returns the cache for a given dependants array. When possible, a WeakMap
- * will be used to create a unique cache for each set of dependants. This
- * is feasible due to the nature of WeakMap in allowing garbage collection
- * to occur on entries where the key object is no longer referenced. Since
- * WeakMap requires the key to be an object, this is only possible when the
- * dependant is object-like. The root cache is created as a hierarchy where
- * each top-level key is the first entry in a dependants set, the value a
- * WeakMap where each key is the next dependant, and so on. This continues
- * so long as the dependants are object-like. If no dependants are object-
- * like, then the cache is shared across all invocations.
- *
- * @see isObjectLike
- *
- * @param {*[]} dependants Selector dependants.
- *
- * @return {Cache} Cache object.
- */
- function getCache(dependants) {
- var caches = rootCache,
- isUniqueByDependants = true,
- i,
- dependant,
- map,
- cache;
-
- for (i = 0; i < dependants.length; i++) {
- dependant = dependants[i];
-
- // Can only compose WeakMap from object-like key.
- if (!isObjectLike(dependant)) {
- isUniqueByDependants = false;
- break;
- }
-
- // Does current segment of cache already have a WeakMap?
- if (caches.has(dependant)) {
- // Traverse into nested WeakMap.
- caches = caches.get(dependant);
- } else {
- // Create, set, and traverse into a new one.
- map = new WeakMap();
- caches.set(dependant, map);
- caches = map;
- }
- }
-
- // We use an arbitrary (but consistent) object as key for the last item
- // in the WeakMap to serve as our running cache.
- if (!caches.has(LEAF_KEY)) {
- cache = createCache();
- cache.isUniqueByDependants = isUniqueByDependants;
- caches.set(LEAF_KEY, cache);
- }
-
- return caches.get(LEAF_KEY);
- }
-
- /**
- * Resets root memoization cache.
- */
- function clear() {
- rootCache = new WeakMap();
- }
-
- /* eslint-disable jsdoc/check-param-names */
- /**
- * The augmented selector call, considering first whether dependants have
- * changed before passing it to underlying memoize function.
- *
- * @param {*} source Source object for derivation.
- * @param {...*} extraArgs Additional arguments to pass to selector.
- *
- * @return {*} Selector result.
- */
- /* eslint-enable jsdoc/check-param-names */
- function callSelector(/* source, ...extraArgs */) {
- var len = arguments.length,
- cache,
- node,
- i,
- args,
- dependants;
-
- // Create copy of arguments (avoid leaking deoptimization).
- args = new Array(len);
- for (i = 0; i < len; i++) {
- args[i] = arguments[i];
- }
-
- dependants = normalizedGetDependants.apply(null, args);
- cache = getCache(dependants);
-
- // If not guaranteed uniqueness by dependants (primitive type), shallow
- // compare against last dependants and, if references have changed,
- // destroy cache to recalculate result.
- if (!cache.isUniqueByDependants) {
- if (
- cache.lastDependants &&
- !isShallowEqual(dependants, cache.lastDependants, 0)
- ) {
- cache.clear();
- }
-
- cache.lastDependants = dependants;
- }
-
- node = cache.head;
- while (node) {
- // Check whether node arguments match arguments
- if (!isShallowEqual(node.args, args, 1)) {
- node = node.next;
- continue;
- }
-
- // At this point we can assume we've found a match
-
- // Surface matched node to head if not already
- if (node !== cache.head) {
- // Adjust siblings to point to each other.
- /** @type {CacheNode} */ (node.prev).next = node.next;
- if (node.next) {
- node.next.prev = node.prev;
- }
-
- node.next = cache.head;
- node.prev = null;
- /** @type {CacheNode} */ (cache.head).prev = node;
- cache.head = node;
- }
-
- // Return immediately
- return node.val;
- }
-
- // No cached value found. Continue to insertion phase:
-
- node = /** @type {CacheNode} */ ({
- // Generate the result from original function
- val: selector.apply(null, args),
- });
-
- // Avoid including the source object in the cache.
- args[0] = null;
- node.args = args;
-
- // Don't need to check whether node is already head, since it would
- // have been returned above already if it was
-
- // Shift existing head down list
- if (cache.head) {
- cache.head.prev = node;
- node.next = cache.head;
- }
-
- cache.head = node;
-
- return node.val;
- }
-
- callSelector.getDependants = normalizedGetDependants;
- callSelector.clear = clear;
- clear();
-
- return /** @type {S & EnhancedSelector} */ (callSelector);
-}
-
;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/store/selectors.js
/**
- * External dependencies
+ * WordPress dependencies
*/
@@ -473,7 +175,7 @@ function isShallowEqual(a, b, fromIndex) {
*
* @return {Array} Format types.
*/
-const getFormatTypes = rememo(state => Object.values(state.formatTypes), state => [state.formatTypes]);
+const getFormatTypes = (0,external_wp_data_namespaceObject.createSelector)(state => Object.values(state.formatTypes), state => [state.formatTypes]);
/**
* Returns a format type by name.
@@ -1185,7 +887,9 @@ function toTree({
}
if (character === OBJECT_REPLACEMENT_CHARACTER) {
const replacement = replacements[i];
- if (!replacement) continue;
+ if (!replacement) {
+ continue;
+ }
const {
type,
attributes,
@@ -1902,7 +1606,9 @@ function createFromElement({
});
continue;
}
- if (format) delete format.formatType;
+ if (format) {
+ delete format.formatType;
+ }
const value = createFromElement({
element: node,
range,
@@ -3024,7 +2730,9 @@ function useAnchorRef({
} = settings;
const activeFormat = name ? getActiveFormat(value, name) : undefined;
return (0,external_wp_element_namespaceObject.useMemo)(() => {
- if (!ref.current) return;
+ if (!ref.current) {
+ return;
+ }
const {
ownerDocument: {
defaultView
@@ -3088,9 +2796,15 @@ function getFormatElement(range, editableContentElement, tagName, className) {
if (element.nodeType !== element.ELEMENT_NODE) {
element = element.parentElement;
}
- if (!element) return;
- if (element === editableContentElement) return;
- if (!editableContentElement.contains(element)) return;
+ if (!element) {
+ return;
+ }
+ if (element === editableContentElement) {
+ return;
+ }
+ if (!editableContentElement.contains(element)) {
+ return;
+ }
const selector = tagName + (className ? '.' + className : '');
// .closest( selector ), but with a boundary. Check if the element matches
@@ -3143,7 +2857,9 @@ function createVirtualAnchorElement(range, editableContentElement) {
* @return {HTMLElement|VirtualAnchorElement|undefined} The anchor.
*/
function getAnchor(editableContentElement, tagName, className) {
- if (!editableContentElement) return;
+ if (!editableContentElement) {
+ return;
+ }
const {
ownerDocument
} = editableContentElement;
@@ -3151,12 +2867,20 @@ function getAnchor(editableContentElement, tagName, className) {
defaultView
} = ownerDocument;
const selection = defaultView.getSelection();
- if (!selection) return;
- if (!selection.rangeCount) return;
+ if (!selection) {
+ return;
+ }
+ if (!selection.rangeCount) {
+ return;
+ }
const range = selection.getRangeAt(0);
- if (!range || !range.startContainer) return;
+ if (!range || !range.startContainer) {
+ return;
+ }
const formatElement = getFormatElement(range, editableContentElement, tagName, className);
- if (formatElement) return formatElement;
+ if (formatElement) {
+ return formatElement;
+ }
return createVirtualAnchorElement(range, editableContentElement);
}
@@ -3184,7 +2908,9 @@ function useAnchor({
const [anchor, setAnchor] = (0,external_wp_element_namespaceObject.useState)(() => getAnchor(editableContentElement, tagName, className));
const wasActive = (0,external_wp_compose_namespaceObject.usePrevious)(isActive);
(0,external_wp_element_namespaceObject.useLayoutEffect)(() => {
- if (!editableContentElement) return;
+ if (!editableContentElement) {
+ return;
+ }
function callback() {
setAnchor(getAnchor(editableContentElement, tagName, className));
}
@@ -3253,7 +2979,9 @@ const whiteSpace = 'pre-wrap';
const minWidth = '1px';
function useDefaultStyle() {
return (0,external_wp_element_namespaceObject.useCallback)(element => {
- if (!element) return;
+ if (!element) {
+ return;
+ }
element.style.whiteSpace = whiteSpace;
element.style.minWidth = minWidth;
}, []);
@@ -3315,216 +3043,243 @@ function useBoundaryStyle({
return ref;
}
-;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-copy-handler.js
+;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/event-listeners/copy-handler.js
/**
- * WordPress dependencies
+ * Internal dependencies
*/
-/**
- * Internal dependencies
- */
+/* harmony default export */ const copy_handler = (props => element => {
+ function onCopy(event) {
+ const {
+ record
+ } = props.current;
+ const {
+ ownerDocument
+ } = element;
+ if (isCollapsed(record.current) || !element.contains(ownerDocument.activeElement)) {
+ return;
+ }
+ const selectedRecord = slice(record.current);
+ const plainText = getTextContent(selectedRecord);
+ const html = toHTMLString({
+ value: selectedRecord
+ });
+ event.clipboardData.setData('text/plain', plainText);
+ event.clipboardData.setData('text/html', html);
+ event.clipboardData.setData('rich-text', 'true');
+ event.preventDefault();
+ if (event.type === 'cut') {
+ ownerDocument.execCommand('delete');
+ }
+ }
+ const {
+ defaultView
+ } = element.ownerDocument;
+ defaultView.addEventListener('copy', onCopy);
+ defaultView.addEventListener('cut', onCopy);
+ return () => {
+ defaultView.removeEventListener('copy', onCopy);
+ defaultView.removeEventListener('cut', onCopy);
+ };
+});
+;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/event-listeners/select-object.js
+/* harmony default export */ const select_object = (() => element => {
+ function onClick(event) {
+ const {
+ target
+ } = event;
+ // If the child element has no text content, it must be an object.
+ if (target === element || target.textContent && target.isContentEditable) {
+ return;
+ }
+ const {
+ ownerDocument
+ } = target;
+ const {
+ defaultView
+ } = ownerDocument;
+ const selection = defaultView.getSelection();
-function useCopyHandler(props) {
- const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
- propsRef.current = props;
- return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
- function onCopy(event) {
- const {
- record
- } = propsRef.current;
- const {
- ownerDocument
- } = element;
- if (isCollapsed(record.current) || !element.contains(ownerDocument.activeElement)) {
- return;
- }
- const selectedRecord = slice(record.current);
- const plainText = getTextContent(selectedRecord);
- const html = toHTMLString({
- value: selectedRecord
- });
- event.clipboardData.setData('text/plain', plainText);
- event.clipboardData.setData('text/html', html);
- event.clipboardData.setData('rich-text', 'true');
- event.preventDefault();
- if (event.type === 'cut') {
- ownerDocument.execCommand('delete');
- }
+ // If it's already selected, do nothing and let default behavior happen.
+ // This means it's "click-through".
+ if (selection.containsNode(target)) {
+ return;
}
- element.addEventListener('copy', onCopy);
- element.addEventListener('cut', onCopy);
- return () => {
- element.removeEventListener('copy', onCopy);
- element.removeEventListener('cut', onCopy);
- };
- }, []);
-}
+ const range = ownerDocument.createRange();
+ // If the target is within a non editable element, select the non
+ // editable element.
+ const nodeToSelect = target.isContentEditable ? target : target.closest('[contenteditable]');
+ range.selectNode(nodeToSelect);
+ selection.removeAllRanges();
+ selection.addRange(range);
+ event.preventDefault();
+ }
+ function onFocusIn(event) {
+ // When there is incoming focus from a link, select the object.
+ if (event.relatedTarget && !element.contains(event.relatedTarget) && event.relatedTarget.tagName === 'A') {
+ onClick(event);
+ }
+ }
+ element.addEventListener('click', onClick);
+ element.addEventListener('focusin', onFocusIn);
+ return () => {
+ element.removeEventListener('click', onClick);
+ element.removeEventListener('focusin', onFocusIn);
+ };
+});
;// CONCATENATED MODULE: external ["wp","keycodes"]
const external_wp_keycodes_namespaceObject = window["wp"]["keycodes"];
-;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-format-boundaries.js
+;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/event-listeners/format-boundaries.js
/**
* WordPress dependencies
*/
-
-
/**
* Internal dependencies
*/
const EMPTY_ACTIVE_FORMATS = [];
-function useFormatBoundaries(props) {
- const [, forceRender] = (0,external_wp_element_namespaceObject.useReducer)(() => ({}));
- const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
- propsRef.current = props;
- return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
- function onKeyDown(event) {
- const {
- keyCode,
- shiftKey,
- altKey,
- metaKey,
- ctrlKey
- } = event;
- if (
- // Only override left and right keys without modifiers pressed.
- shiftKey || altKey || metaKey || ctrlKey || keyCode !== external_wp_keycodes_namespaceObject.LEFT && keyCode !== external_wp_keycodes_namespaceObject.RIGHT) {
- return;
- }
- const {
- record,
- applyRecord
- } = propsRef.current;
- const {
- text,
- formats,
- start,
- end,
- activeFormats: currentActiveFormats = []
- } = record.current;
- const collapsed = isCollapsed(record.current);
- const {
- ownerDocument
- } = element;
- const {
- defaultView
- } = ownerDocument;
- // To do: ideally, we should look at visual position instead.
- const {
- direction
- } = defaultView.getComputedStyle(element);
- const reverseKey = direction === 'rtl' ? external_wp_keycodes_namespaceObject.RIGHT : external_wp_keycodes_namespaceObject.LEFT;
- const isReverse = event.keyCode === reverseKey;
-
- // If the selection is collapsed and at the very start, do nothing if
- // navigating backward.
- // If the selection is collapsed and at the very end, do nothing if
- // navigating forward.
- if (collapsed && currentActiveFormats.length === 0) {
- if (start === 0 && isReverse) {
- return;
- }
- if (end === text.length && !isReverse) {
- return;
- }
- }
-
- // If the selection is not collapsed, let the browser handle collapsing
- // the selection for now. Later we could expand this logic to set
- // boundary positions if needed.
- if (!collapsed) {
+/* harmony default export */ const format_boundaries = (props => element => {
+ function onKeyDown(event) {
+ const {
+ keyCode,
+ shiftKey,
+ altKey,
+ metaKey,
+ ctrlKey
+ } = event;
+ if (
+ // Only override left and right keys without modifiers pressed.
+ shiftKey || altKey || metaKey || ctrlKey || keyCode !== external_wp_keycodes_namespaceObject.LEFT && keyCode !== external_wp_keycodes_namespaceObject.RIGHT) {
+ return;
+ }
+ const {
+ record,
+ applyRecord,
+ forceRender
+ } = props.current;
+ const {
+ text,
+ formats,
+ start,
+ end,
+ activeFormats: currentActiveFormats = []
+ } = record.current;
+ const collapsed = isCollapsed(record.current);
+ const {
+ ownerDocument
+ } = element;
+ const {
+ defaultView
+ } = ownerDocument;
+ // To do: ideally, we should look at visual position instead.
+ const {
+ direction
+ } = defaultView.getComputedStyle(element);
+ const reverseKey = direction === 'rtl' ? external_wp_keycodes_namespaceObject.RIGHT : external_wp_keycodes_namespaceObject.LEFT;
+ const isReverse = event.keyCode === reverseKey;
+
+ // If the selection is collapsed and at the very start, do nothing if
+ // navigating backward.
+ // If the selection is collapsed and at the very end, do nothing if
+ // navigating forward.
+ if (collapsed && currentActiveFormats.length === 0) {
+ if (start === 0 && isReverse) {
return;
}
- const formatsBefore = formats[start - 1] || EMPTY_ACTIVE_FORMATS;
- const formatsAfter = formats[start] || EMPTY_ACTIVE_FORMATS;
- const destination = isReverse ? formatsBefore : formatsAfter;
- const isIncreasing = currentActiveFormats.every((format, index) => format === destination[index]);
- let newActiveFormatsLength = currentActiveFormats.length;
- if (!isIncreasing) {
- newActiveFormatsLength--;
- } else if (newActiveFormatsLength < destination.length) {
- newActiveFormatsLength++;
- }
- if (newActiveFormatsLength === currentActiveFormats.length) {
- record.current._newActiveFormats = destination;
+ if (end === text.length && !isReverse) {
return;
}
- event.preventDefault();
- const origin = isReverse ? formatsAfter : formatsBefore;
- const source = isIncreasing ? destination : origin;
- const newActiveFormats = source.slice(0, newActiveFormatsLength);
- const newValue = {
- ...record.current,
- activeFormats: newActiveFormats
- };
- record.current = newValue;
- applyRecord(newValue);
- forceRender();
}
- element.addEventListener('keydown', onKeyDown);
- return () => {
- element.removeEventListener('keydown', onKeyDown);
+
+ // If the selection is not collapsed, let the browser handle collapsing
+ // the selection for now. Later we could expand this logic to set
+ // boundary positions if needed.
+ if (!collapsed) {
+ return;
+ }
+ const formatsBefore = formats[start - 1] || EMPTY_ACTIVE_FORMATS;
+ const formatsAfter = formats[start] || EMPTY_ACTIVE_FORMATS;
+ const destination = isReverse ? formatsBefore : formatsAfter;
+ const isIncreasing = currentActiveFormats.every((format, index) => format === destination[index]);
+ let newActiveFormatsLength = currentActiveFormats.length;
+ if (!isIncreasing) {
+ newActiveFormatsLength--;
+ } else if (newActiveFormatsLength < destination.length) {
+ newActiveFormatsLength++;
+ }
+ if (newActiveFormatsLength === currentActiveFormats.length) {
+ record.current._newActiveFormats = destination;
+ return;
+ }
+ event.preventDefault();
+ const origin = isReverse ? formatsAfter : formatsBefore;
+ const source = isIncreasing ? destination : origin;
+ const newActiveFormats = source.slice(0, newActiveFormatsLength);
+ const newValue = {
+ ...record.current,
+ activeFormats: newActiveFormats
};
- }, []);
-}
+ record.current = newValue;
+ applyRecord(newValue);
+ forceRender();
+ }
+ element.addEventListener('keydown', onKeyDown);
+ return () => {
+ element.removeEventListener('keydown', onKeyDown);
+ };
+});
-;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-select-object.js
+;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/event-listeners/delete.js
/**
* WordPress dependencies
*/
-function useSelectObject() {
- return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
- function onClick(event) {
- const {
- target
- } = event;
- // If the child element has no text content, it must be an object.
- if (target === element || target.textContent && target.isContentEditable) {
- return;
- }
- const {
- ownerDocument
- } = target;
- const {
- defaultView
- } = ownerDocument;
- const selection = defaultView.getSelection();
-
- // If it's already selected, do nothing and let default behavior
- // happen. This means it's "click-through".
- if (selection.containsNode(target)) return;
- const range = ownerDocument.createRange();
- // If the target is within a non editable element, select the non
- // editable element.
- const nodeToSelect = target.isContentEditable ? target : target.closest('[contenteditable]');
- range.selectNode(nodeToSelect);
- selection.removeAllRanges();
- selection.addRange(range);
- event.preventDefault();
+/**
+ * Internal dependencies
+ */
+
+/* harmony default export */ const event_listeners_delete = (props => element => {
+ function onKeyDown(event) {
+ const {
+ keyCode
+ } = event;
+ const {
+ createRecord,
+ handleChange
+ } = props.current;
+ if (event.defaultPrevented) {
+ return;
}
- function onFocusIn(event) {
- // When there is incoming focus from a link, select the object.
- if (event.relatedTarget && !element.contains(event.relatedTarget) && event.relatedTarget.tagName === 'A') {
- onClick(event);
- }
+ if (keyCode !== external_wp_keycodes_namespaceObject.DELETE && keyCode !== external_wp_keycodes_namespaceObject.BACKSPACE) {
+ return;
}
- element.addEventListener('click', onClick);
- element.addEventListener('focusin', onFocusIn);
- return () => {
- element.removeEventListener('click', onClick);
- element.removeEventListener('focusin', onFocusIn);
- };
- }, []);
-}
+ const currentValue = createRecord();
+ const {
+ start,
+ end,
+ text
+ } = currentValue;
+
+ // Always handle full content deletion ourselves.
+ if (start === 0 && end !== 0 && end === text.length) {
+ handleChange(remove_remove(currentValue));
+ event.preventDefault();
+ }
+ }
+ element.addEventListener('keydown', onKeyDown);
+ return () => {
+ element.removeEventListener('keydown', onKeyDown);
+ };
+});
;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/update-formats.js
/**
@@ -3583,13 +3338,7 @@ function updateFormats({
return value;
}
-;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-input-and-selection.js
-/**
- * WordPress dependencies
- */
-
-
-
+;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/event-listeners/input-and-selection.js
/**
* Internal dependencies
*/
@@ -3604,7 +3353,7 @@ function updateFormats({
* @type {Set}
*/
const INSERTION_INPUT_TYPES_TO_IGNORE = new Set(['insertParagraph', 'insertOrderedList', 'insertUnorderedList', 'insertHorizontalRule', 'insertLink']);
-const use_input_and_selection_EMPTY_ACTIVE_FORMATS = [];
+const input_and_selection_EMPTY_ACTIVE_FORMATS = [];
const PLACEHOLDER_ATTR_NAME = 'data-rich-text-placeholder';
/**
@@ -3628,210 +3377,204 @@ function fixPlaceholderSelection(defaultView) {
}
selection.collapseToStart();
}
-function useInputAndSelection(props) {
- const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
- propsRef.current = props;
- return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
- const {
- ownerDocument
- } = element;
+/* harmony default export */ const input_and_selection = (props => element => {
+ const {
+ ownerDocument
+ } = element;
+ const {
+ defaultView
+ } = ownerDocument;
+ let isComposing = false;
+ function onInput(event) {
+ // Do not trigger a change if characters are being composed. Browsers
+ // will usually emit a final `input` event when the characters are
+ // composed. As of December 2019, Safari doesn't support
+ // nativeEvent.isComposing.
+ if (isComposing) {
+ return;
+ }
+ let inputType;
+ if (event) {
+ inputType = event.inputType;
+ }
const {
- defaultView
- } = ownerDocument;
- let isComposing = false;
- function onInput(event) {
- // Do not trigger a change if characters are being composed.
- // Browsers will usually emit a final `input` event when the
- // characters are composed.
- // As of December 2019, Safari doesn't support
- // nativeEvent.isComposing.
- if (isComposing) {
- return;
- }
- let inputType;
- if (event) {
- inputType = event.inputType;
- }
- const {
- record,
- applyRecord,
- createRecord,
- handleChange
- } = propsRef.current;
-
- // The browser formatted something or tried to insert HTML.
- // Overwrite it. It will be handled later by the format library if
- // needed.
- if (inputType && (inputType.indexOf('format') === 0 || INSERTION_INPUT_TYPES_TO_IGNORE.has(inputType))) {
- applyRecord(record.current);
- return;
- }
- const currentValue = createRecord();
- const {
- start,
- activeFormats: oldActiveFormats = []
- } = record.current;
-
- // Update the formats between the last and new caret position.
- const change = updateFormats({
- value: currentValue,
- start,
- end: currentValue.start,
- formats: oldActiveFormats
- });
- handleChange(change);
+ record,
+ applyRecord,
+ createRecord,
+ handleChange
+ } = props.current;
+
+ // The browser formatted something or tried to insert HTML. Overwrite
+ // it. It will be handled later by the format library if needed.
+ if (inputType && (inputType.indexOf('format') === 0 || INSERTION_INPUT_TYPES_TO_IGNORE.has(inputType))) {
+ applyRecord(record.current);
+ return;
}
+ const currentValue = createRecord();
+ const {
+ start,
+ activeFormats: oldActiveFormats = []
+ } = record.current;
- /**
- * Syncs the selection to local state. A callback for the
- * `selectionchange` event.
- */
- function handleSelectionChange() {
- const {
- record,
- applyRecord,
- createRecord,
- onSelectionChange
- } = propsRef.current;
-
- // Check if the implementor disabled editing. `contentEditable`
- // does disable input, but not text selection, so we must ignore
- // selection changes.
- if (element.contentEditable !== 'true') {
- return;
- }
-
- // Ensure the active element is the rich text element.
- if (ownerDocument.activeElement !== element) {
- // If it is not, we can stop listening for selection changes.
- // We resume listening when the element is focused.
- ownerDocument.removeEventListener('selectionchange', handleSelectionChange);
- return;
- }
-
- // In case of a keyboard event, ignore selection changes during
- // composition.
- if (isComposing) {
- return;
- }
- const {
- start,
- end,
- text
- } = createRecord();
- const oldRecord = record.current;
-
- // Fallback mechanism for IE11, which doesn't support the input event.
- // Any input results in a selection change.
- if (text !== oldRecord.text) {
- onInput();
- return;
- }
- if (start === oldRecord.start && end === oldRecord.end) {
- // Sometimes the browser may set the selection on the placeholder
- // element, in which case the caret is not visible. We need to set
- // the caret before the placeholder if that's the case.
- if (oldRecord.text.length === 0 && start === 0) {
- fixPlaceholderSelection(defaultView);
- }
- return;
- }
- const newValue = {
- ...oldRecord,
- start,
- end,
- // _newActiveFormats may be set on arrow key navigation to control
- // the right boundary position. If undefined, getActiveFormats will
- // give the active formats according to the browser.
- activeFormats: oldRecord._newActiveFormats,
- _newActiveFormats: undefined
- };
- const newActiveFormats = getActiveFormats(newValue, use_input_and_selection_EMPTY_ACTIVE_FORMATS);
-
- // Update the value with the new active formats.
- newValue.activeFormats = newActiveFormats;
+ // Update the formats between the last and new caret position.
+ const change = updateFormats({
+ value: currentValue,
+ start,
+ end: currentValue.start,
+ formats: oldActiveFormats
+ });
+ handleChange(change);
+ }
- // It is important that the internal value is updated first,
- // otherwise the value will be wrong on render!
- record.current = newValue;
- applyRecord(newValue, {
- domOnly: true
- });
- onSelectionChange(start, end);
+ /**
+ * Syncs the selection to local state. A callback for the `selectionchange`
+ * event.
+ */
+ function handleSelectionChange() {
+ const {
+ record,
+ applyRecord,
+ createRecord,
+ onSelectionChange
+ } = props.current;
+
+ // Check if the implementor disabled editing. `contentEditable` does
+ // disable input, but not text selection, so we must ignore selection
+ // changes.
+ if (element.contentEditable !== 'true') {
+ return;
}
- function onCompositionStart() {
- isComposing = true;
- // Do not update the selection when characters are being composed as
- // this rerenders the component and might destroy internal browser
- // editing state.
+
+ // Ensure the active element is the rich text element.
+ if (ownerDocument.activeElement !== element) {
+ // If it is not, we can stop listening for selection changes. We
+ // resume listening when the element is focused.
ownerDocument.removeEventListener('selectionchange', handleSelectionChange);
- // Remove the placeholder. Since the rich text value doesn't update
- // during composition, the placeholder doesn't get removed. There's
- // no need to re-add it, when the value is updated on compositionend
- // it will be re-added when the value is empty.
- element.querySelector(`[${PLACEHOLDER_ATTR_NAME}]`)?.remove();
+ return;
}
- function onCompositionEnd() {
- isComposing = false;
- // Ensure the value is up-to-date for browsers that don't emit a final
- // input event after composition.
- onInput({
- inputType: 'insertText'
- });
- // Tracking selection changes can be resumed.
- ownerDocument.addEventListener('selectionchange', handleSelectionChange);
+
+ // In case of a keyboard event, ignore selection changes during
+ // composition.
+ if (isComposing) {
+ return;
}
- function onFocus() {
- const {
- record,
- isSelected,
- onSelectionChange,
- applyRecord
- } = propsRef.current;
-
- // When the whole editor is editable, let writing flow handle
- // selection.
- if (element.parentElement.closest('[contenteditable="true"]')) {
- return;
- }
- if (!isSelected) {
- // We know for certain that on focus, the old selection is invalid.
- // It will be recalculated on the next mouseup, keyup, or touchend
- // event.
- const index = undefined;
- record.current = {
- ...record.current,
- start: index,
- end: index,
- activeFormats: use_input_and_selection_EMPTY_ACTIVE_FORMATS
- };
- } else {
- applyRecord(record.current, {
- domOnly: true
- });
+ const {
+ start,
+ end,
+ text
+ } = createRecord();
+ const oldRecord = record.current;
+
+ // Fallback mechanism for IE11, which doesn't support the input event.
+ // Any input results in a selection change.
+ if (text !== oldRecord.text) {
+ onInput();
+ return;
+ }
+ if (start === oldRecord.start && end === oldRecord.end) {
+ // Sometimes the browser may set the selection on the placeholder
+ // element, in which case the caret is not visible. We need to set
+ // the caret before the placeholder if that's the case.
+ if (oldRecord.text.length === 0 && start === 0) {
+ fixPlaceholderSelection(defaultView);
}
- onSelectionChange(record.current.start, record.current.end);
- ownerDocument.addEventListener('selectionchange', handleSelectionChange);
+ return;
}
- element.addEventListener('input', onInput);
- element.addEventListener('compositionstart', onCompositionStart);
- element.addEventListener('compositionend', onCompositionEnd);
- element.addEventListener('focus', onFocus);
- return () => {
- element.removeEventListener('input', onInput);
- element.removeEventListener('compositionstart', onCompositionStart);
- element.removeEventListener('compositionend', onCompositionEnd);
- element.removeEventListener('focus', onFocus);
+ const newValue = {
+ ...oldRecord,
+ start,
+ end,
+ // _newActiveFormats may be set on arrow key navigation to control
+ // the right boundary position. If undefined, getActiveFormats will
+ // give the active formats according to the browser.
+ activeFormats: oldRecord._newActiveFormats,
+ _newActiveFormats: undefined
};
- }, []);
-}
+ const newActiveFormats = getActiveFormats(newValue, input_and_selection_EMPTY_ACTIVE_FORMATS);
-;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-selection-change-compat.js
-/**
- * WordPress dependencies
- */
+ // Update the value with the new active formats.
+ newValue.activeFormats = newActiveFormats;
+ // It is important that the internal value is updated first,
+ // otherwise the value will be wrong on render!
+ record.current = newValue;
+ applyRecord(newValue, {
+ domOnly: true
+ });
+ onSelectionChange(start, end);
+ }
+ function onCompositionStart() {
+ isComposing = true;
+ // Do not update the selection when characters are being composed as
+ // this rerenders the component and might destroy internal browser
+ // editing state.
+ ownerDocument.removeEventListener('selectionchange', handleSelectionChange);
+ // Remove the placeholder. Since the rich text value doesn't update
+ // during composition, the placeholder doesn't get removed. There's no
+ // need to re-add it, when the value is updated on compositionend it
+ // will be re-added when the value is empty.
+ element.querySelector(`[${PLACEHOLDER_ATTR_NAME}]`)?.remove();
+ }
+ function onCompositionEnd() {
+ isComposing = false;
+ // Ensure the value is up-to-date for browsers that don't emit a final
+ // input event after composition.
+ onInput({
+ inputType: 'insertText'
+ });
+ // Tracking selection changes can be resumed.
+ ownerDocument.addEventListener('selectionchange', handleSelectionChange);
+ }
+ function onFocus() {
+ const {
+ record,
+ isSelected,
+ onSelectionChange,
+ applyRecord
+ } = props.current;
+
+ // When the whole editor is editable, let writing flow handle
+ // selection.
+ if (element.parentElement.closest('[contenteditable="true"]')) {
+ return;
+ }
+ if (!isSelected) {
+ // We know for certain that on focus, the old selection is invalid.
+ // It will be recalculated on the next mouseup, keyup, or touchend
+ // event.
+ const index = undefined;
+ record.current = {
+ ...record.current,
+ start: index,
+ end: index,
+ activeFormats: input_and_selection_EMPTY_ACTIVE_FORMATS
+ };
+ } else {
+ applyRecord(record.current, {
+ domOnly: true
+ });
+ }
+ onSelectionChange(record.current.start, record.current.end);
+
+ // There is no selection change event when the element is focused, so
+ // we need to manually trigger it. The selection is also not available
+ // yet in this call stack.
+ window.queueMicrotask(handleSelectionChange);
+ ownerDocument.addEventListener('selectionchange', handleSelectionChange);
+ }
+ element.addEventListener('input', onInput);
+ element.addEventListener('compositionstart', onCompositionStart);
+ element.addEventListener('compositionend', onCompositionEnd);
+ element.addEventListener('focus', onFocus);
+ return () => {
+ element.removeEventListener('input', onInput);
+ element.removeEventListener('compositionstart', onCompositionStart);
+ element.removeEventListener('compositionend', onCompositionEnd);
+ element.removeEventListener('focus', onFocus);
+ };
+});
+;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/event-listeners/selection-change-compat.js
/**
* Internal dependencies
*/
@@ -3842,97 +3585,73 @@ function useInputAndSelection(props) {
* changing the selection by mouse or keyboard. This hook makes sure that, if we
* detect no `selectionchange` or `input` event between the up and down events,
* we fire a `selectionchange` event.
- *
- * @return {import('@wordpress/compose').RefEffect} A ref effect attaching the
- * listeners.
*/
-function useSelectionChangeCompat() {
- return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
- const {
- ownerDocument
- } = element;
- const {
- defaultView
- } = ownerDocument;
- const selection = defaultView?.getSelection();
- let range;
- function getRange() {
- return selection.rangeCount ? selection.getRangeAt(0) : null;
- }
- function onDown(event) {
- const type = event.type === 'keydown' ? 'keyup' : 'pointerup';
- function onCancel() {
- ownerDocument.removeEventListener(type, onUp);
- ownerDocument.removeEventListener('selectionchange', onCancel);
- ownerDocument.removeEventListener('input', onCancel);
- }
- function onUp() {
- onCancel();
- if (isRangeEqual(range, getRange())) return;
- ownerDocument.dispatchEvent(new Event('selectionchange'));
+/* harmony default export */ const selection_change_compat = (() => element => {
+ const {
+ ownerDocument
+ } = element;
+ const {
+ defaultView
+ } = ownerDocument;
+ const selection = defaultView?.getSelection();
+ let range;
+ function getRange() {
+ return selection.rangeCount ? selection.getRangeAt(0) : null;
+ }
+ function onDown(event) {
+ const type = event.type === 'keydown' ? 'keyup' : 'pointerup';
+ function onCancel() {
+ ownerDocument.removeEventListener(type, onUp);
+ ownerDocument.removeEventListener('selectionchange', onCancel);
+ ownerDocument.removeEventListener('input', onCancel);
+ }
+ function onUp() {
+ onCancel();
+ if (isRangeEqual(range, getRange())) {
+ return;
}
- ownerDocument.addEventListener(type, onUp);
- ownerDocument.addEventListener('selectionchange', onCancel);
- ownerDocument.addEventListener('input', onCancel);
- range = getRange();
- }
- element.addEventListener('pointerdown', onDown);
- element.addEventListener('keydown', onDown);
- return () => {
- element.removeEventListener('pointerdown', onDown);
- element.removeEventListener('keydown', onDown);
- };
- }, []);
-}
+ ownerDocument.dispatchEvent(new Event('selectionchange'));
+ }
+ ownerDocument.addEventListener(type, onUp);
+ ownerDocument.addEventListener('selectionchange', onCancel);
+ ownerDocument.addEventListener('input', onCancel);
+ range = getRange();
+ }
+ element.addEventListener('pointerdown', onDown);
+ element.addEventListener('keydown', onDown);
+ return () => {
+ element.removeEventListener('pointerdown', onDown);
+ element.removeEventListener('keydown', onDown);
+ };
+});
-;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/use-delete.js
+;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/event-listeners/index.js
/**
* WordPress dependencies
*/
-
/**
* Internal dependencies
*/
-function useDelete(props) {
+
+
+
+
+
+const allEventListeners = [copy_handler, select_object, format_boundaries, event_listeners_delete, input_and_selection, selection_change_compat];
+function useEventListeners(props) {
const propsRef = (0,external_wp_element_namespaceObject.useRef)(props);
propsRef.current = props;
+ const refEffects = (0,external_wp_element_namespaceObject.useMemo)(() => allEventListeners.map(refEffect => refEffect(propsRef)), [propsRef]);
return (0,external_wp_compose_namespaceObject.useRefEffect)(element => {
- function onKeyDown(event) {
- const {
- keyCode
- } = event;
- const {
- createRecord,
- handleChange
- } = propsRef.current;
- if (event.defaultPrevented) {
- return;
- }
- if (keyCode !== external_wp_keycodes_namespaceObject.DELETE && keyCode !== external_wp_keycodes_namespaceObject.BACKSPACE) {
- return;
- }
- const currentValue = createRecord();
- const {
- start,
- end,
- text
- } = currentValue;
-
- // Always handle full content deletion ourselves.
- if (start === 0 && end !== 0 && end === text.length) {
- handleChange(remove_remove(currentValue));
- event.preventDefault();
- }
- }
- element.addEventListener('keydown', onKeyDown);
+ const cleanups = refEffects.map(effect => effect(element));
return () => {
- element.removeEventListener('keydown', onKeyDown);
+ cleanups.forEach(cleanup => cleanup());
};
- }, []);
+ }, [refEffects]);
}
;// CONCATENATED MODULE: ./node_modules/@wordpress/rich-text/build-module/component/index.js
@@ -3952,11 +3671,6 @@ function useDelete(props) {
-
-
-
-
-
function useRichText({
value = '',
selectionStart,
@@ -4114,22 +3828,15 @@ function useRichText({
}, [hadSelectionUpdate.current]);
const mergedRefs = (0,external_wp_compose_namespaceObject.useMergeRefs)([ref, useDefaultStyle(), useBoundaryStyle({
record
- }), useCopyHandler({
- record
- }), useSelectObject(), useFormatBoundaries({
- record,
- applyRecord
- }), useDelete({
- createRecord,
- handleChange
- }), useInputAndSelection({
+ }), useEventListeners({
record,
+ handleChange,
applyRecord,
createRecord,
- handleChange,
isSelected,
- onSelectionChange
- }), useSelectionChangeCompat(), (0,external_wp_compose_namespaceObject.useRefEffect)(() => {
+ onSelectionChange,
+ forceRender
+ }), (0,external_wp_compose_namespaceObject.useRefEffect)(() => {
applyFromProps();
didMount.current = true;
}, [placeholder, ...__unstableDependencies])]);