From f8d85fd6333f7f8d4db25803aa1cdd438fe06f07 Mon Sep 17 00:00:00 2001 From: Aaryan Khandelwal Date: Fri, 13 Sep 2024 15:50:26 +0530 Subject: [PATCH] refactor: custom image extension folder structure --- .../components/image-loader.tsx | 3 - .../image-upload/index.tsx | 1 - .../custom-image-component/index.ts | 1 - .../components/image-block.tsx} | 28 ++------- .../components/image-node.tsx} | 22 +++---- .../components}/image-uploader.tsx | 5 +- .../custom-image/components/index.ts | 3 + .../custom-image.ts | 15 +++-- .../src/core/extensions/custom-image/index.ts | 3 + .../custom-image/read-only-custom-image.ts | 60 +++++++++++++++++++ .../editor/src/core/extensions/extensions.tsx | 4 +- .../image/image-component-without-props.tsx | 2 +- packages/editor/src/core/extensions/index.ts | 1 + .../core/extensions/read-only-extensions.tsx | 2 + space/helpers/string.helper.ts | 4 +- web/helpers/string.helper.ts | 4 +- 16 files changed, 104 insertions(+), 54 deletions(-) delete mode 100644 packages/editor/src/core/extensions/custom-image-component/components/image-loader.tsx delete mode 100644 packages/editor/src/core/extensions/custom-image-component/image-upload/index.tsx delete mode 100644 packages/editor/src/core/extensions/custom-image-component/index.ts rename packages/editor/src/core/extensions/{custom-image-component/components/image-block-view.tsx => custom-image/components/image-block.tsx} (84%) rename packages/editor/src/core/extensions/{custom-image-component/image-upload/image-upload.tsx => custom-image/components/image-node.tsx} (87%) rename packages/editor/src/core/extensions/{custom-image-component/image-upload => custom-image/components}/image-uploader.tsx (97%) create mode 100644 packages/editor/src/core/extensions/custom-image/components/index.ts rename packages/editor/src/core/extensions/{custom-image-component => custom-image}/custom-image.ts (93%) create mode 100644 packages/editor/src/core/extensions/custom-image/index.ts create mode 100644 packages/editor/src/core/extensions/custom-image/read-only-custom-image.ts diff --git a/packages/editor/src/core/extensions/custom-image-component/components/image-loader.tsx b/packages/editor/src/core/extensions/custom-image-component/components/image-loader.tsx deleted file mode 100644 index 52c62493aa..0000000000 --- a/packages/editor/src/core/extensions/custom-image-component/components/image-loader.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export const ImageShimmer: React.FC<{ width: string; height: string }> = ({ width, height }) => ( -
-); diff --git a/packages/editor/src/core/extensions/custom-image-component/image-upload/index.tsx b/packages/editor/src/core/extensions/custom-image-component/image-upload/index.tsx deleted file mode 100644 index 0d0b9c2638..0000000000 --- a/packages/editor/src/core/extensions/custom-image-component/image-upload/index.tsx +++ /dev/null @@ -1 +0,0 @@ -export * from "./image-upload"; diff --git a/packages/editor/src/core/extensions/custom-image-component/index.ts b/packages/editor/src/core/extensions/custom-image-component/index.ts deleted file mode 100644 index f72f24fd8f..0000000000 --- a/packages/editor/src/core/extensions/custom-image-component/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./custom-image"; diff --git a/packages/editor/src/core/extensions/custom-image-component/components/image-block-view.tsx b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx similarity index 84% rename from packages/editor/src/core/extensions/custom-image-component/components/image-block-view.tsx rename to packages/editor/src/core/extensions/custom-image/components/image-block.tsx index 1f8c7e54c2..9de98dfae9 100644 --- a/packages/editor/src/core/extensions/custom-image-component/components/image-block-view.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-block.tsx @@ -1,29 +1,13 @@ import React, { useRef, useState, useCallback, useLayoutEffect } from "react"; -import { Node as ProsemirrorNode } from "@tiptap/pm/model"; import { NodeSelection } from "@tiptap/pm/state"; -import { Editor } from "@tiptap/react"; +// extensions +import { CustomImageNodeViewProps } from "@/extensions/custom-image"; // helpers import { cn } from "@/helpers/common"; -// components -import { ImageShimmer } from "./image-loader"; - -interface ImageBlockViewProps { - editor: Editor; - getPos: () => number; - node: ProsemirrorNode & { - attrs: { - src: string; - width: string; - height: string; - }; - }; - updateAttributes: (attrs: Record) => void; - selected: boolean; -} const MIN_SIZE = 100; -export const ImageComponent: React.FC = (props) => { +export const CustomImageBlock: React.FC = (props) => { const { node, updateAttributes, selected, getPos, editor } = props; const { src, width, height } = node.attrs; @@ -111,7 +95,7 @@ export const ImageComponent: React.FC = (props) => { height: size.height, }} > - {isLoading && } + {isLoading &&
} = (props) => { height: size.height, }} /> - {selected &&
} + {editor.isEditable && selected &&
} <>
= (props) => {
); }; - -export default ImageComponent; diff --git a/packages/editor/src/core/extensions/custom-image-component/image-upload/image-upload.tsx b/packages/editor/src/core/extensions/custom-image/components/image-node.tsx similarity index 87% rename from packages/editor/src/core/extensions/custom-image-component/image-upload/image-upload.tsx rename to packages/editor/src/core/extensions/custom-image/components/image-node.tsx index d79162749a..b65d890f56 100644 --- a/packages/editor/src/core/extensions/custom-image-component/image-upload/image-upload.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-node.tsx @@ -1,11 +1,15 @@ import React, { useCallback, useEffect, useRef, useState } from "react"; import { Node as ProsemirrorNode } from "@tiptap/pm/model"; import { Editor, NodeViewWrapper } from "@tiptap/react"; -import { UploadEntity, UploadImageExtensionStorage } from "@/extensions/custom-image-component"; -import ImageComponent from "@/extensions/custom-image-component/components/image-block-view"; -import { ImageUploader } from "./image-uploader"; +// extensions +import { + CustomImageBlock, + CustomImageUploader, + UploadEntity, + UploadImageExtensionStorage, +} from "@/extensions/custom-image"; -interface ImageUploadProps { +export type CustomImageNodeViewProps = { getPos: () => number; editor: Editor; node: ProsemirrorNode & { @@ -17,9 +21,9 @@ interface ImageUploadProps { }; updateAttributes: (attrs: Record) => void; selected: boolean; -} +}; -export const CustomImage = (props: ImageUploadProps) => { +export const CustomImageNode = (props: CustomImageNodeViewProps) => { const { getPos, editor, node, updateAttributes, selected } = props; const fileInputRef = useRef(null); @@ -89,7 +93,7 @@ export const CustomImage = (props: ImageUploadProps) => {
{isUploaded ? ( - { selected={selected} /> ) : ( - { ); }; - -export default CustomImage; diff --git a/packages/editor/src/core/extensions/custom-image-component/image-upload/image-uploader.tsx b/packages/editor/src/core/extensions/custom-image/components/image-uploader.tsx similarity index 97% rename from packages/editor/src/core/extensions/custom-image-component/image-upload/image-uploader.tsx rename to packages/editor/src/core/extensions/custom-image/components/image-uploader.tsx index efdc1f789e..d288630c63 100644 --- a/packages/editor/src/core/extensions/custom-image-component/image-upload/image-uploader.tsx +++ b/packages/editor/src/core/extensions/custom-image/components/image-uploader.tsx @@ -5,6 +5,7 @@ import { ImageIcon } from "lucide-react"; import { cn } from "@/helpers/common"; // hooks import { useUploader, useFileUpload, useDropZone } from "@/hooks/use-file-upload"; +// plugins import { isFileValid } from "@/plugins/image"; type RefType = React.RefObject | ((instance: HTMLInputElement | null) => void); @@ -17,7 +18,7 @@ const assignRef = (ref: RefType, value: HTMLInputElement | null) => { } }; -export const ImageUploader = (props: { +export const CustomImageUploader = (props: { onUpload: (url: string) => void; editor: Editor; fileInputRef: RefType; @@ -87,5 +88,3 @@ export const ImageUploader = (props: {
); }; - -export default ImageUploader; diff --git a/packages/editor/src/core/extensions/custom-image/components/index.ts b/packages/editor/src/core/extensions/custom-image/components/index.ts new file mode 100644 index 0000000000..d16be13c86 --- /dev/null +++ b/packages/editor/src/core/extensions/custom-image/components/index.ts @@ -0,0 +1,3 @@ +export * from "./image-block"; +export * from "./image-node"; +export * from "./image-uploader"; diff --git a/packages/editor/src/core/extensions/custom-image-component/custom-image.ts b/packages/editor/src/core/extensions/custom-image/custom-image.ts similarity index 93% rename from packages/editor/src/core/extensions/custom-image-component/custom-image.ts rename to packages/editor/src/core/extensions/custom-image/custom-image.ts index 6d9f5cf8bd..4f0b96e7e8 100644 --- a/packages/editor/src/core/extensions/custom-image-component/custom-image.ts +++ b/packages/editor/src/core/extensions/custom-image/custom-image.ts @@ -2,11 +2,12 @@ import { mergeAttributes } from "@tiptap/core"; import { Image } from "@tiptap/extension-image"; import { ReactNodeViewRenderer } from "@tiptap/react"; import { v4 as uuidv4 } from "uuid"; - -import { TFileHandler } from "@/types"; - -import { CustomImage } from "./image-upload"; +// extensions +import { CustomImageNode } from "@/extensions/custom-image"; +// plugins import { isFileValid } from "@/plugins/image"; +// types +import { TFileHandler } from "@/types"; declare module "@tiptap/core" { interface Commands { @@ -23,7 +24,7 @@ export interface UploadImageExtensionStorage { export type UploadEntity = { event: "insert" } | { event: "drop"; file: File }; -export const CustomImageComponent = (props: TFileHandler) => { +export const CustomImageExtension = (props: TFileHandler) => { const { upload } = props; return Image.extend<{}, UploadImageExtensionStorage>({ @@ -124,9 +125,7 @@ export const CustomImageComponent = (props: TFileHandler) => { }, addNodeView() { - return ReactNodeViewRenderer(CustomImage); + return ReactNodeViewRenderer(CustomImageNode); }, }); }; - -export default CustomImageComponent; diff --git a/packages/editor/src/core/extensions/custom-image/index.ts b/packages/editor/src/core/extensions/custom-image/index.ts new file mode 100644 index 0000000000..de2bb38789 --- /dev/null +++ b/packages/editor/src/core/extensions/custom-image/index.ts @@ -0,0 +1,3 @@ +export * from "./components"; +export * from "./custom-image"; +export * from "./read-only-custom-image"; diff --git a/packages/editor/src/core/extensions/custom-image/read-only-custom-image.ts b/packages/editor/src/core/extensions/custom-image/read-only-custom-image.ts new file mode 100644 index 0000000000..51235eeee7 --- /dev/null +++ b/packages/editor/src/core/extensions/custom-image/read-only-custom-image.ts @@ -0,0 +1,60 @@ +import { mergeAttributes } from "@tiptap/core"; +import { Image } from "@tiptap/extension-image"; +import { ReactNodeViewRenderer } from "@tiptap/react"; +// components +import { CustomImageNode, UploadImageExtensionStorage } from "@/extensions/custom-image"; + +export const CustomReadOnlyImageExtension = () => + Image.extend<{}, UploadImageExtensionStorage>({ + name: "imageComponent", + selectable: true, + group: "block", + atom: true, + draggable: true, + + addAttributes() { + return { + ...this.parent?.(), + width: { + default: "35%", + }, + src: { + default: null, + }, + height: { + default: "auto", + }, + ["data-type"]: { + default: this.name, + }, + ["data-file"]: { + default: null, + }, + ["id"]: { + default: null, + }, + }; + }, + + parseHTML() { + return [ + { + tag: "image-component", + }, + ]; + }, + + renderHTML({ HTMLAttributes }) { + return ["image-component", mergeAttributes(HTMLAttributes)]; + }, + + addStorage() { + return { + fileMap: new Map(), + }; + }, + + addNodeView() { + return ReactNodeViewRenderer(CustomImageNode); + }, + }); diff --git a/packages/editor/src/core/extensions/extensions.tsx b/packages/editor/src/core/extensions/extensions.tsx index 411f7359c6..41406b7954 100644 --- a/packages/editor/src/core/extensions/extensions.tsx +++ b/packages/editor/src/core/extensions/extensions.tsx @@ -13,6 +13,7 @@ import { CustomCodeInlineExtension, CustomCodeMarkPlugin, CustomHorizontalRule, + CustomImageExtension, CustomKeymap, CustomLinkExtension, CustomMention, @@ -30,7 +31,6 @@ import { import { isValidHttpUrl } from "@/helpers/common"; // types import { DeleteImage, IMentionHighlight, IMentionSuggestion, RestoreImage, UploadImage } from "@/types"; -import { CustomImageComponent } from "./custom-image-component"; type TArguments = { enableHistory: boolean; @@ -106,7 +106,7 @@ export const CoreEditorExtensions = ({ class: "rounded-md", }, }), - CustomImageComponent({ + CustomImageExtension({ delete: deleteFile, restore: restoreFile, upload: uploadFile, diff --git a/packages/editor/src/core/extensions/image/image-component-without-props.tsx b/packages/editor/src/core/extensions/image/image-component-without-props.tsx index e3eedff06c..dfe58d4adc 100644 --- a/packages/editor/src/core/extensions/image/image-component-without-props.tsx +++ b/packages/editor/src/core/extensions/image/image-component-without-props.tsx @@ -1,6 +1,6 @@ import { mergeAttributes } from "@tiptap/core"; import { Image } from "@tiptap/extension-image"; -import { UploadImageExtensionStorage } from "@/extensions/custom-image-component"; +import { UploadImageExtensionStorage } from "@/extensions/custom-image"; export const CustomImageComponentWithoutProps = () => Image.extend<{}, UploadImageExtensionStorage>({ diff --git a/packages/editor/src/core/extensions/index.ts b/packages/editor/src/core/extensions/index.ts index 41f8189b0b..658dd2f799 100644 --- a/packages/editor/src/core/extensions/index.ts +++ b/packages/editor/src/core/extensions/index.ts @@ -1,5 +1,6 @@ export * from "./code"; export * from "./code-inline"; +export * from "./custom-image"; export * from "./custom-link"; export * from "./custom-list-keymap"; export * from "./image"; diff --git a/packages/editor/src/core/extensions/read-only-extensions.tsx b/packages/editor/src/core/extensions/read-only-extensions.tsx index 68e3f7d688..2898b6cdc0 100644 --- a/packages/editor/src/core/extensions/read-only-extensions.tsx +++ b/packages/editor/src/core/extensions/read-only-extensions.tsx @@ -19,6 +19,7 @@ import { TableRow, Table, CustomMention, + CustomReadOnlyImageExtension, } from "@/extensions"; // helpers import { isValidHttpUrl } from "@/helpers/common"; @@ -74,6 +75,7 @@ export const CoreReadOnlyEditorExtensions = (mentionConfig: { class: "rounded-md", }, }), + CustomReadOnlyImageExtension(), TiptapUnderline, TextStyle, TaskList.configure({ diff --git a/space/helpers/string.helper.ts b/space/helpers/string.helper.ts index 6c60e4bb9f..5c704c44c3 100644 --- a/space/helpers/string.helper.ts +++ b/space/helpers/string.helper.ts @@ -69,7 +69,9 @@ export const isCommentEmpty = (comment: string | undefined): boolean => { // return true if comment is undefined if (!comment) return true; return ( - comment?.trim() === "" || comment === "

" || isEmptyHtmlString(comment ?? "", ["img", "mention-component"]) + comment?.trim() === "" || + comment === "

" || + isEmptyHtmlString(comment ?? "", ["img", "mention-component", "image-component"]) ); }; diff --git a/web/helpers/string.helper.ts b/web/helpers/string.helper.ts index 00d601c74e..1182feeb0a 100644 --- a/web/helpers/string.helper.ts +++ b/web/helpers/string.helper.ts @@ -249,7 +249,9 @@ export const isCommentEmpty = (comment: string | undefined): boolean => { // return true if comment is undefined if (!comment) return true; return ( - comment?.trim() === "" || comment === "

" || isEmptyHtmlString(comment ?? "", ["img", "mention-component"]) + comment?.trim() === "" || + comment === "

" || + isEmptyHtmlString(comment ?? "", ["img", "mention-component", "image-component"]) ); };