UNPKG

communication-react-19

Version:

React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)

186 lines 7.81 kB
// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. /** * Plugin event type for RoosterJS plugins * @private */ export var PluginEventType; (function (PluginEventType) { PluginEventType["EditorReady"] = "editorReady"; PluginEventType["BeforeDispose"] = "beforeDispose"; PluginEventType["ContentChanged"] = "contentChanged"; PluginEventType["BeforeSetContent"] = "beforeSetContent"; PluginEventType["Input"] = "input"; PluginEventType["KeyDown"] = "keyDown"; PluginEventType["BeforeCutCopy"] = "beforeCutCopy"; PluginEventType["BeforePaste"] = "beforePaste"; PluginEventType["ZoomChanged"] = "zoomChanged"; PluginEventType["MouseUp"] = "mouseUp"; PluginEventType["CompositionEnd"] = "compositionEnd"; })(PluginEventType || (PluginEventType = {})); /** * ContentChanged event source for RoosterJS * @private */ export var ContentChangedEventSource; (function (ContentChangedEventSource) { ContentChangedEventSource["Paste"] = "Paste"; })(ContentChangedEventSource || (ContentChangedEventSource = {})); /** * Applies the border format to the specified element. * If the element is an HTMLTableCellElement, it skips setting editing info * and to use classes instead of inline styles. * For all other cases, the default format applier is used. */ export const borderApplier = (format, element, context) => { if (element instanceof HTMLTableCellElement) { // don't set format for table cell // as it will set inline styles for them // we want to use classes instead } else if (context.defaultFormatAppliers.border) { // apply default formats for all other cases context.defaultFormatAppliers.border(format, element, context); } }; /** * Applies the dataset format to the given HTML element. * If the element is an HTMLTableElement, it skips setting editing info * and to use classes instead of inline styles. * For all other cases, it applies the default formats. */ export const dataSetApplier = (format, element, context) => { if (element instanceof HTMLTableElement) { // don't set editing info for tables // as it will set inline styles for them // we want to use classes instead } else if (context.defaultFormatAppliers.dataset) { // apply default formats for all other cases context.defaultFormatAppliers.dataset(format, element, context); } }; /* @conditional-compile-remove(rich-text-editor-image-upload) */ /** * @internal */ export const getPreviousInlineImages = (content) => { if (!content) { return []; } const previousInlineImages = []; const document = new DOMParser().parseFromString(content !== null && content !== void 0 ? content : '', 'text/html'); document.querySelectorAll('img').forEach((img) => { const imageAttributes = getInlineImageAttributes(img); previousInlineImages.push(imageAttributes); }); return previousInlineImages; }; /* @conditional-compile-remove(rich-text-editor-image-upload) */ /** * @internal */ export const getRemovedInlineImages = (content, previousInlineImages) => { const document = new DOMParser().parseFromString(content !== null && content !== void 0 ? content : '', 'text/html'); const currentContentIds = Array.from(document.querySelectorAll('img')).map((img) => img.id); previousInlineImages = previousInlineImages === null || previousInlineImages === void 0 ? void 0 : previousInlineImages.filter((img) => !!img.id && !(currentContentIds === null || currentContentIds === void 0 ? void 0 : currentContentIds.includes(img.id))); const removedInlineImages = [...previousInlineImages]; return removedInlineImages; }; /* @conditional-compile-remove(rich-text-editor-image-upload) */ /** * @internal */ export const getInsertedInlineImages = (content, previousInlineImages) => { const document = new DOMParser().parseFromString(content !== null && content !== void 0 ? content : '', 'text/html'); const currentContentInlineImages = Array.from(document.querySelectorAll('img')); const previousContentIds = Array.from(previousInlineImages).map((img) => img.id); // if check is updated, also update getRemovedInlineImages const insertedInlineImages = currentContentInlineImages.filter((img) => !previousContentIds.includes(img.id)); return insertedInlineImages; }; /* @conditional-compile-remove(rich-text-editor-image-upload) */ /** * @internal */ export const getInlineImageAttributes = (image) => { const imageAttributes = {}; image.getAttributeNames().forEach((attrName) => { const attrValue = image.getAttribute(attrName); if (attrValue) { imageAttributes[attrName] = attrValue; } }); return imageAttributes; }; /* @conditional-compile-remove(rich-text-editor) */ /** * @internal */ /** * Update the scroll position of the editor to ensure the content is visible. */ export const scrollToBottomRichTextEditor = () => { // Get the current selection in the document const selection = document.getSelection(); // Check if a selection exists and it has at least one range if (!selection || selection.rangeCount <= 0) { // If no selection or range, exit the function return; } // Get the first range of the selection // A user can normally only select one range at a time, so the rangeCount will usually be 1 const range = selection.getRangeAt(0); // If the common ancestor container of the range is the document itself, // it might mean that the editable element is getting removed from the DOM // In such cases, especially in Safari, trying to modify the range might throw a HierarchyRequest error if (range.commonAncestorContainer === document) { return; } // Create a temporary span element to use as an anchor for scrolling // We can't use the anchor node directly because if it's a Text node, calling scrollIntoView() on it will throw an error const tempElement = document.createElement('span'); // Collapse the range to its end point // This means the start and end points of the range will be the same, and it will not contain any content range.collapse(false); // Insert the temporary element at the cursor's position at the end of the range range.insertNode(tempElement); // Scroll the temporary element into view // the element will be aligned at the center of the scroll container, otherwise, text and images may be positioned incorrectly tempElement.scrollIntoView({ block: 'center' }); tempElement.remove(); }; /* @conditional-compile-remove(rich-text-editor-image-upload) */ /** * Revoke the blob urls in the removedInlineImages and remove them from the currentLocalBlobMap * @internal */ export const removeLocalBlobs = (currentLocalBlobMap, removedInlineImages) => { removedInlineImages.forEach((image) => { if (!image.id) { return; } removeSingleLocalBlob(currentLocalBlobMap, image.id); }); }; /* @conditional-compile-remove(rich-text-editor-image-upload) */ /** * Revoke all the blob urls in the currentLocalBlobMap and clean up the currentLocalBlobMap * @internal */ export const cleanAllLocalBlobs = (currentLocalBlobMap) => { Object.keys(currentLocalBlobMap).forEach((imageId) => { removeSingleLocalBlob(currentLocalBlobMap, imageId); }); }; /* @conditional-compile-remove(rich-text-editor-image-upload) */ const removeSingleLocalBlob = (currentLocalBlobMap, imageId) => { const blobUrl = currentLocalBlobMap[imageId]; if (blobUrl) { URL.revokeObjectURL(blobUrl); delete currentLocalBlobMap[imageId]; } }; //# sourceMappingURL=RichTextEditorUtils.js.map