refactor: move web utils to packages (#7145)
* refactor: move web utils to packages * fix: build and lint errors * chore: update drag handle plugin * chore: update table cell type to fix build errors * fix: build errors * chore: sync few changes * fix: build errors * chore: minor fixes related to duplicate assets imports * fix: build errors * chore: minor changes
This commit is contained in:
parent
dffcc6dc10
commit
2014400bed
614 changed files with 1999 additions and 3030 deletions
89
packages/editor/src/core/helpers/parser.ts
Normal file
89
packages/editor/src/core/helpers/parser.ts
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// plane imports
|
||||
import { TDocumentPayload, TDuplicateAssetData, TDuplicateAssetResponse } from "@plane/types";
|
||||
import { TEditorAssetType } from "@plane/types/src/enums";
|
||||
// local imports
|
||||
import { convertHTMLDocumentToAllFormats } from "./yjs-utils";
|
||||
|
||||
/**
|
||||
* @description function to extract all image assets from HTML content
|
||||
* @param htmlContent
|
||||
* @returns {string[]} array of image asset sources
|
||||
*/
|
||||
export const extractImageAssetsFromHTMLContent = (htmlContent: string): string[] => {
|
||||
// create a DOM parser
|
||||
const parser = new DOMParser();
|
||||
// parse the HTML string into a DOM document
|
||||
const doc = parser.parseFromString(htmlContent, "text/html");
|
||||
// get all image components
|
||||
const imageComponents = doc.querySelectorAll("image-component");
|
||||
// collect all unique image sources
|
||||
const imageSources = new Set<string>();
|
||||
// extract sources from image components
|
||||
imageComponents.forEach((component) => {
|
||||
const src = component.getAttribute("src");
|
||||
if (src) imageSources.add(src);
|
||||
});
|
||||
return Array.from(imageSources);
|
||||
};
|
||||
|
||||
/**
|
||||
* @description function to replace image assets in HTML content with new IDs
|
||||
* @param props
|
||||
* @returns {string} HTML content with replaced image assets
|
||||
*/
|
||||
export const replaceImageAssetsInHTMLContent = (props: {
|
||||
htmlContent: string;
|
||||
assetMap: Record<string, string>;
|
||||
}): string => {
|
||||
const { htmlContent, assetMap } = props;
|
||||
// create a DOM parser
|
||||
const parser = new DOMParser();
|
||||
// parse the HTML string into a DOM document
|
||||
const doc = parser.parseFromString(htmlContent, "text/html");
|
||||
// replace sources in image components
|
||||
const imageComponents = doc.querySelectorAll("image-component");
|
||||
imageComponents.forEach((component) => {
|
||||
const oldSrc = component.getAttribute("src");
|
||||
if (oldSrc && assetMap[oldSrc]) {
|
||||
component.setAttribute("src", assetMap[oldSrc]);
|
||||
}
|
||||
});
|
||||
// serialize the document back into a string
|
||||
return doc.body.innerHTML;
|
||||
};
|
||||
|
||||
export const getEditorContentWithReplacedImageAssets = async (props: {
|
||||
descriptionHTML: string;
|
||||
entityId: string;
|
||||
entityType: TEditorAssetType;
|
||||
projectId: string | undefined;
|
||||
variant: "rich" | "document";
|
||||
duplicateAssetService: (params: TDuplicateAssetData) => Promise<TDuplicateAssetResponse>;
|
||||
}): Promise<TDocumentPayload> => {
|
||||
const { descriptionHTML, entityId, entityType, projectId, variant, duplicateAssetService } = props;
|
||||
let replacedDescription = descriptionHTML;
|
||||
// step 1: extract image assets from the description
|
||||
const imageAssets = extractImageAssetsFromHTMLContent(descriptionHTML);
|
||||
if (imageAssets.length !== 0) {
|
||||
// step 2: duplicate the image assets
|
||||
const duplicateAssetsResponse = await duplicateAssetService({
|
||||
entity_id: entityId,
|
||||
entity_type: entityType,
|
||||
project_id: projectId,
|
||||
asset_ids: imageAssets,
|
||||
});
|
||||
if (Object.keys(duplicateAssetsResponse ?? {}).length > 0) {
|
||||
// step 3: replace the image assets in the description
|
||||
replacedDescription = replaceImageAssetsInHTMLContent({
|
||||
htmlContent: descriptionHTML,
|
||||
assetMap: duplicateAssetsResponse,
|
||||
});
|
||||
}
|
||||
}
|
||||
// step 4: convert the description to the document payload
|
||||
const documentPayload = convertHTMLDocumentToAllFormats({
|
||||
document_html: replacedDescription,
|
||||
variant,
|
||||
});
|
||||
return documentPayload;
|
||||
};
|
||||
|
|
@ -3,6 +3,7 @@ import { generateHTML, generateJSON } from "@tiptap/html";
|
|||
import { prosemirrorJSONToYDoc, yXmlFragmentToProseMirrorRootNode } from "y-prosemirror";
|
||||
import * as Y from "yjs";
|
||||
// extensions
|
||||
import { TDocumentPayload } from "@plane/types";
|
||||
import {
|
||||
CoreEditorExtensionsWithoutProps,
|
||||
DocumentEditorExtensionsWithoutProps,
|
||||
|
|
@ -140,3 +141,50 @@ export const getAllDocumentFormatsFromDocumentEditorBinaryData = (
|
|||
contentHTML,
|
||||
};
|
||||
};
|
||||
|
||||
type TConvertHTMLDocumentToAllFormatsArgs = {
|
||||
document_html: string;
|
||||
variant: "rich" | "document";
|
||||
};
|
||||
|
||||
/**
|
||||
* @description Converts HTML content to all supported document formats (JSON, HTML, and binary)
|
||||
* @param {TConvertHTMLDocumentToAllFormatsArgs} args - Arguments containing HTML content and variant type
|
||||
* @param {string} args.document_html - The HTML content to convert
|
||||
* @param {"rich" | "document"} args.variant - The type of editor variant to use for conversion
|
||||
* @returns {TDocumentPayload} Object containing the document in all supported formats
|
||||
* @throws {Error} If an invalid variant is provided
|
||||
*/
|
||||
export const convertHTMLDocumentToAllFormats = (args: TConvertHTMLDocumentToAllFormatsArgs): TDocumentPayload => {
|
||||
const { document_html, variant } = args;
|
||||
|
||||
let allFormats: TDocumentPayload;
|
||||
|
||||
if (variant === "rich") {
|
||||
// Convert HTML to binary format for rich text editor
|
||||
const contentBinary = getBinaryDataFromRichTextEditorHTMLString(document_html);
|
||||
// Generate all document formats from the binary data
|
||||
const { contentBinaryEncoded, contentHTML, contentJSON } =
|
||||
getAllDocumentFormatsFromRichTextEditorBinaryData(contentBinary);
|
||||
allFormats = {
|
||||
description: contentJSON,
|
||||
description_html: contentHTML,
|
||||
description_binary: contentBinaryEncoded,
|
||||
};
|
||||
} else if (variant === "document") {
|
||||
// Convert HTML to binary format for document editor
|
||||
const contentBinary = getBinaryDataFromDocumentEditorHTMLString(document_html);
|
||||
// Generate all document formats from the binary data
|
||||
const { contentBinaryEncoded, contentHTML, contentJSON } =
|
||||
getAllDocumentFormatsFromDocumentEditorBinaryData(contentBinary);
|
||||
allFormats = {
|
||||
description: contentJSON,
|
||||
description_html: contentHTML,
|
||||
description_binary: contentBinaryEncoded,
|
||||
};
|
||||
} else {
|
||||
throw new Error(`Invalid variant provided: ${variant}`);
|
||||
}
|
||||
|
||||
return allFormats;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
import { Fragment, Slice, Node, Schema } from "@tiptap/pm/model";
|
||||
import { NodeSelection } from "@tiptap/pm/state";
|
||||
// @ts-expect-error __serializeForClipboard's is not exported
|
||||
import { __serializeForClipboard, EditorView } from "@tiptap/pm/view";
|
||||
import { EditorView } from "@tiptap/pm/view";
|
||||
// constants
|
||||
import { CORE_EXTENSIONS } from "@/constants/extension";
|
||||
// extensions
|
||||
|
|
@ -417,7 +416,7 @@ const handleNodeSelection = (
|
|||
}
|
||||
|
||||
const slice = view.state.selection.content();
|
||||
const { dom, text } = __serializeForClipboard(view, slice);
|
||||
const { dom, text } = view.serializeForClipboard(slice);
|
||||
|
||||
if (event instanceof DragEvent && event.dataTransfer) {
|
||||
event.dataTransfer.clearData();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue