"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = __experimentalRichText; exports.useRichText = useRichText; var _element = require("@wordpress/element"); var _compose = require("@wordpress/compose"); var _data = require("@wordpress/data"); var _create = require("../create"); var _toDom = require("../to-dom"); var _toHtmlString = require("../to-html-string"); var _useDefaultStyle = require("./use-default-style"); var _useBoundaryStyle = require("./use-boundary-style"); var _eventListeners = require("./event-listeners"); /** * WordPress dependencies */ /** * Internal dependencies */ function useRichText({ value = '', selectionStart, selectionEnd, placeholder, onSelectionChange, preserveWhiteSpace, onChange, __unstableDisableFormats: disableFormats, __unstableIsSelected: isSelected, __unstableDependencies = [], __unstableAfterParse, __unstableBeforeSerialize, __unstableAddInvisibleFormats }) { const registry = (0, _data.useRegistry)(); const [, forceRender] = (0, _element.useReducer)(() => ({})); const ref = (0, _element.useRef)(); function createRecord() { const { ownerDocument: { defaultView } } = ref.current; const selection = defaultView.getSelection(); const range = selection.rangeCount > 0 ? selection.getRangeAt(0) : null; return (0, _create.create)({ element: ref.current, range, __unstableIsEditableTree: true }); } function applyRecord(newRecord, { domOnly } = {}) { (0, _toDom.apply)({ value: newRecord, current: ref.current, prepareEditableTree: __unstableAddInvisibleFormats, __unstableDomOnly: domOnly, placeholder }); } // Internal values are updated synchronously, unlike props and state. const _value = (0, _element.useRef)(value); const record = (0, _element.useRef)(); function setRecordFromProps() { _value.current = value; record.current = value; if (!(value instanceof _create.RichTextData)) { record.current = value ? _create.RichTextData.fromHTMLString(value, { preserveWhiteSpace }) : _create.RichTextData.empty(); } // To do: make rich text internally work with RichTextData. record.current = { text: record.current.text, formats: record.current.formats, replacements: record.current.replacements }; if (disableFormats) { record.current.formats = Array(value.length); record.current.replacements = Array(value.length); } if (__unstableAfterParse) { record.current.formats = __unstableAfterParse(record.current); } record.current.start = selectionStart; record.current.end = selectionEnd; } const hadSelectionUpdate = (0, _element.useRef)(false); if (!record.current) { hadSelectionUpdate.current = isSelected; setRecordFromProps(); } else if (selectionStart !== record.current.start || selectionEnd !== record.current.end) { hadSelectionUpdate.current = isSelected; record.current = { ...record.current, start: selectionStart, end: selectionEnd, activeFormats: undefined }; } /** * Sync the value to global state. The node tree and selection will also be * updated if differences are found. * * @param {Object} newRecord The record to sync and apply. */ function handleChange(newRecord) { record.current = newRecord; applyRecord(newRecord); if (disableFormats) { _value.current = newRecord.text; } else { const newFormats = __unstableBeforeSerialize ? __unstableBeforeSerialize(newRecord) : newRecord.formats; newRecord = { ...newRecord, formats: newFormats }; if (typeof value === 'string') { _value.current = (0, _toHtmlString.toHTMLString)({ value: newRecord, preserveWhiteSpace }); } else { _value.current = new _create.RichTextData(newRecord); } } const { start, end, formats, text } = record.current; // Selection must be updated first, so it is recorded in history when // the content change happens. // We batch both calls to only attempt to rerender once. registry.batch(() => { onSelectionChange(start, end); onChange(_value.current, { __unstableFormats: formats, __unstableText: text }); }); forceRender(); } function applyFromProps() { setRecordFromProps(); applyRecord(record.current); } const didMount = (0, _element.useRef)(false); // Value updates must happen synchonously to avoid overwriting newer values. (0, _element.useLayoutEffect)(() => { if (didMount.current && value !== _value.current) { applyFromProps(); forceRender(); } }, [value]); // Value updates must happen synchonously to avoid overwriting newer values. (0, _element.useLayoutEffect)(() => { if (!hadSelectionUpdate.current) { return; } if (ref.current.ownerDocument.activeElement !== ref.current) { ref.current.focus(); } applyRecord(record.current); hadSelectionUpdate.current = false; }, [hadSelectionUpdate.current]); const mergedRefs = (0, _compose.useMergeRefs)([ref, (0, _useDefaultStyle.useDefaultStyle)(), (0, _useBoundaryStyle.useBoundaryStyle)({ record }), (0, _eventListeners.useEventListeners)({ record, handleChange, applyRecord, createRecord, isSelected, onSelectionChange, forceRender }), (0, _compose.useRefEffect)(() => { applyFromProps(); didMount.current = true; }, [placeholder, ...__unstableDependencies])]); return { value: record.current, // A function to get the most recent value so event handlers in // useRichText implementations have access to it. For example when // listening to input events, we internally update the state, but this // state is not yet available to the input event handler because React // may re-render asynchronously. getValue: () => record.current, onChange: handleChange, ref: mergedRefs }; } function __experimentalRichText() {} //# sourceMappingURL=index.js.map