From 7e5c0d9de66ea4219babad7d891cf1b8cc3b919b Mon Sep 17 00:00:00 2001 From: oiov Date: Mon, 20 Oct 2025 11:34:05 +0800 Subject: [PATCH] refact: email sending editor --- app/(marketing)/[slug]/page.tsx | 2 +- app/layout.tsx | 2 +- components/email/EmailEditor.tsx | 142 +++ components/email/SendEmailModal.tsx | 126 +- components/shared/icons.tsx | 2 + .../tiptap/tiptap-icons/align-center-icon.tsx | 38 + .../tiptap-icons/align-justify-icon.tsx | 38 + .../tiptap/tiptap-icons/align-left-icon.tsx | 38 + .../tiptap/tiptap-icons/align-right-icon.tsx | 38 + .../tiptap/tiptap-icons/arrow-left-icon.tsx | 24 + .../shared/tiptap/tiptap-icons/ban-icon.tsx | 26 + .../tiptap/tiptap-icons/blockquote-icon.tsx | 44 + .../shared/tiptap/tiptap-icons/bold-icon.tsx | 26 + .../tiptap/tiptap-icons/chevron-down-icon.tsx | 26 + .../shared/tiptap/tiptap-icons/close-icon.tsx | 24 + .../tiptap/tiptap-icons/code-block-icon.tsx | 38 + .../shared/tiptap/tiptap-icons/code2-icon.tsx | 32 + .../tiptap-icons/corner-down-left-icon.tsx | 26 + .../tiptap-icons/external-link-icon.tsx | 28 + .../tiptap/tiptap-icons/heading-five-icon.tsx | 28 + .../tiptap/tiptap-icons/heading-four-icon.tsx | 28 + .../tiptap/tiptap-icons/heading-icon.tsx | 24 + .../tiptap/tiptap-icons/heading-one-icon.tsx | 28 + .../tiptap/tiptap-icons/heading-six-icon.tsx | 30 + .../tiptap-icons/heading-three-icon.tsx | 36 + .../tiptap/tiptap-icons/heading-two-icon.tsx | 28 + .../tiptap/tiptap-icons/highlighter-icon.tsx | 26 + .../tiptap/tiptap-icons/image-plus-icon.tsx | 26 + .../tiptap/tiptap-icons/italic-icon.tsx | 24 + .../shared/tiptap/tiptap-icons/link-icon.tsx | 28 + .../shared/tiptap/tiptap-icons/list-icon.tsx | 56 + .../tiptap/tiptap-icons/list-ordered-icon.tsx | 56 + .../tiptap/tiptap-icons/list-todo-icon.tsx | 50 + .../tiptap/tiptap-icons/moon-star-icon.tsx | 30 + .../shared/tiptap/tiptap-icons/redo2-icon.tsx | 26 + .../tiptap/tiptap-icons/strike-icon.tsx | 28 + .../tiptap/tiptap-icons/subscript-icon.tsx | 38 + .../shared/tiptap/tiptap-icons/sun-icon.tsx | 58 + .../tiptap/tiptap-icons/superscript-icon.tsx | 38 + .../shared/tiptap/tiptap-icons/trash-icon.tsx | 26 + .../tiptap/tiptap-icons/underline-icon.tsx | 26 + .../shared/tiptap/tiptap-icons/undo2-icon.tsx | 26 + .../blockquote-node/blockquote-node.scss | 37 + .../code-block-node/code-block-node.scss | 54 + .../heading-node/heading-node.scss | 38 + .../horizontal-rule-node-extension.ts | 14 + .../horizontal-rule-node.scss | 25 + .../tiptap-node/image-node/image-node.scss | 31 + .../image-upload-node-extension.ts | 162 +++ .../image-upload-node/image-upload-node.scss | 249 ++++ .../image-upload-node/image-upload-node.tsx | 557 ++++++++ .../tiptap-node/image-upload-node/index.tsx | 1 + .../tiptap-node/list-node/list-node.scss | 160 +++ .../paragraph-node/paragraph-node.scss | 272 ++++ .../tiptap-templates/simple/data/content.json | 477 +++++++ .../simple/simple-editor.scss | 83 ++ .../tiptap-templates/simple/simple-editor.tsx | 157 +++ .../tiptap-templates/simple/theme-toggle.tsx | 47 + .../badge/badge-colors.scss | 395 ++++++ .../badge/badge-group.scss | 16 + .../tiptap-ui-primitive/badge/badge.scss | 99 ++ .../tiptap-ui-primitive/badge/badge.tsx | 47 + .../tiptap-ui-primitive/badge/index.tsx | 1 + .../button/button-colors.scss | 429 +++++++ .../button/button-group.scss | 22 + .../tiptap-ui-primitive/button/button.scss | 314 +++++ .../tiptap-ui-primitive/button/button.tsx | 115 ++ .../tiptap-ui-primitive/button/index.tsx | 1 + .../tiptap/tiptap-ui-primitive/card/card.scss | 77 ++ .../tiptap/tiptap-ui-primitive/card/card.tsx | 85 ++ .../tiptap/tiptap-ui-primitive/card/index.tsx | 1 + .../dropdown-menu/dropdown-menu.scss | 63 + .../dropdown-menu/dropdown-menu.tsx | 102 ++ .../dropdown-menu/index.tsx | 1 + .../tiptap-ui-primitive/input/index.tsx | 1 + .../tiptap-ui-primitive/input/input.scss | 45 + .../tiptap-ui-primitive/input/input.tsx | 27 + .../tiptap-ui-primitive/popover/index.tsx | 1 + .../tiptap-ui-primitive/popover/popover.scss | 63 + .../tiptap-ui-primitive/popover/popover.tsx | 40 + .../tiptap-ui-primitive/separator/index.tsx | 1 + .../separator/separator.scss | 23 + .../separator/separator.tsx | 36 + .../tiptap-ui-primitive/spacer/index.tsx | 1 + .../tiptap-ui-primitive/spacer/spacer.tsx | 28 + .../tiptap-ui-primitive/toolbar/index.tsx | 1 + .../tiptap-ui-primitive/toolbar/toolbar.scss | 98 ++ .../tiptap-ui-primitive/toolbar/toolbar.tsx | 127 ++ .../tiptap-ui-primitive/tooltip/index.tsx | 1 + .../tiptap-ui-primitive/tooltip/tooltip.scss | 43 + .../tiptap-ui-primitive/tooltip/tooltip.tsx | 234 ++++ .../blockquote-button/blockquote-button.tsx | 122 ++ .../tiptap-ui/blockquote-button/index.tsx | 2 + .../blockquote-button/use-blockquote.ts | 238 ++++ .../code-block-button/code-block-button.tsx | 122 ++ .../tiptap-ui/code-block-button/index.tsx | 2 + .../code-block-button/use-code-block.ts | 245 ++++ .../color-highlight-button.scss | 49 + .../color-highlight-button.tsx | 143 +++ .../color-highlight-button/index.tsx | 2 + .../use-color-highlight.ts | 238 ++++ .../color-highlight-popover.tsx | 210 +++ .../color-highlight-popover/index.tsx | 1 + .../heading-button/heading-button.tsx | 128 ++ .../tiptap/tiptap-ui/heading-button/index.tsx | 2 + .../tiptap-ui/heading-button/use-heading.ts | 311 +++++ .../heading-dropdown-menu.tsx | 132 ++ .../tiptap-ui/heading-dropdown-menu/index.tsx | 2 + .../use-heading-dropdown-menu.ts | 130 ++ .../image-upload-button.tsx | 120 ++ .../tiptap-ui/image-upload-button/index.tsx | 2 + .../image-upload-button/use-image-upload.ts | 194 +++ .../tiptap/tiptap-ui/link-popover/index.tsx | 2 + .../tiptap-ui/link-popover/link-popover.tsx | 313 +++++ .../link-popover/use-link-popover.ts | 276 ++++ .../tiptap/tiptap-ui/list-button/index.tsx | 2 + .../tiptap-ui/list-button/list-button.tsx | 126 ++ .../tiptap/tiptap-ui/list-button/use-list.ts | 306 +++++ .../tiptap-ui/list-dropdown-menu/index.tsx | 1 + .../list-dropdown-menu/list-dropdown-menu.tsx | 131 ++ .../use-list-dropdown-menu.ts | 216 ++++ .../tiptap/tiptap-ui/mark-button/index.tsx | 2 + .../tiptap-ui/mark-button/mark-button.tsx | 126 ++ .../tiptap/tiptap-ui/mark-button/use-mark.ts | 212 ++++ .../tiptap-ui/text-align-button/index.tsx | 2 + .../text-align-button/text-align-button.tsx | 142 +++ .../text-align-button/use-text-align.ts | 219 ++++ .../tiptap-ui/undo-redo-button/index.tsx | 2 + .../undo-redo-button/undo-redo-button.tsx | 123 ++ .../undo-redo-button/use-undo-redo.ts | 182 +++ hooks/use-composed-ref.ts | 47 + hooks/use-cursor-visibility.ts | 71 ++ hooks/use-element-rect.ts | 166 +++ hooks/use-menu-navigation.ts | 196 +++ hooks/use-mobile.ts | 19 + hooks/use-scrolling.ts | 75 ++ hooks/use-throttled-callback.ts | 47 + hooks/use-tiptap-editor.ts | 49 + hooks/use-unmount.ts | 21 + hooks/use-window-size.ts | 93 ++ lib/tiptap-utils.ts | 396 ++++++ middleware.ts | 12 - package.json | 18 +- pnpm-lock.yaml | 1125 +++++++++++++++-- public/sw.js.map | 2 +- styles/_keyframe-animations.scss | 91 ++ styles/_variables.scss | 296 +++++ styles/{globals.css => globals.scss} | 3 + styles/{mdx.css => mdx.scss} | 0 149 files changed, 13678 insertions(+), 213 deletions(-) create mode 100644 components/email/EmailEditor.tsx create mode 100644 components/shared/tiptap/tiptap-icons/align-center-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/align-justify-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/align-left-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/align-right-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/arrow-left-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/ban-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/blockquote-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/bold-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/chevron-down-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/close-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/code-block-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/code2-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/corner-down-left-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/external-link-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/heading-five-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/heading-four-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/heading-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/heading-one-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/heading-six-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/heading-three-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/heading-two-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/highlighter-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/image-plus-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/italic-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/link-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/list-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/list-ordered-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/list-todo-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/moon-star-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/redo2-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/strike-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/subscript-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/sun-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/superscript-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/trash-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/underline-icon.tsx create mode 100644 components/shared/tiptap/tiptap-icons/undo2-icon.tsx create mode 100644 components/shared/tiptap/tiptap-node/blockquote-node/blockquote-node.scss create mode 100644 components/shared/tiptap/tiptap-node/code-block-node/code-block-node.scss create mode 100644 components/shared/tiptap/tiptap-node/heading-node/heading-node.scss create mode 100644 components/shared/tiptap/tiptap-node/horizontal-rule-node/horizontal-rule-node-extension.ts create mode 100644 components/shared/tiptap/tiptap-node/horizontal-rule-node/horizontal-rule-node.scss create mode 100644 components/shared/tiptap/tiptap-node/image-node/image-node.scss create mode 100644 components/shared/tiptap/tiptap-node/image-upload-node/image-upload-node-extension.ts create mode 100644 components/shared/tiptap/tiptap-node/image-upload-node/image-upload-node.scss create mode 100644 components/shared/tiptap/tiptap-node/image-upload-node/image-upload-node.tsx create mode 100644 components/shared/tiptap/tiptap-node/image-upload-node/index.tsx create mode 100644 components/shared/tiptap/tiptap-node/list-node/list-node.scss create mode 100644 components/shared/tiptap/tiptap-node/paragraph-node/paragraph-node.scss create mode 100644 components/shared/tiptap/tiptap-templates/simple/data/content.json create mode 100644 components/shared/tiptap/tiptap-templates/simple/simple-editor.scss create mode 100644 components/shared/tiptap/tiptap-templates/simple/simple-editor.tsx create mode 100644 components/shared/tiptap/tiptap-templates/simple/theme-toggle.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/badge/badge-colors.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/badge/badge-group.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/badge/badge.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/badge/badge.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/badge/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/button/button-colors.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/button/button-group.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/button/button.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/button/button.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/button/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/card/card.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/card/card.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/card/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/dropdown-menu/dropdown-menu.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/dropdown-menu/dropdown-menu.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/dropdown-menu/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/input/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/input/input.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/input/input.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/popover/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/popover/popover.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/popover/popover.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/separator/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/separator/separator.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/separator/separator.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/spacer/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/spacer/spacer.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/toolbar/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/toolbar/toolbar.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/toolbar/toolbar.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/tooltip/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui-primitive/tooltip/tooltip.scss create mode 100644 components/shared/tiptap/tiptap-ui-primitive/tooltip/tooltip.tsx create mode 100644 components/shared/tiptap/tiptap-ui/blockquote-button/blockquote-button.tsx create mode 100644 components/shared/tiptap/tiptap-ui/blockquote-button/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/blockquote-button/use-blockquote.ts create mode 100644 components/shared/tiptap/tiptap-ui/code-block-button/code-block-button.tsx create mode 100644 components/shared/tiptap/tiptap-ui/code-block-button/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/code-block-button/use-code-block.ts create mode 100644 components/shared/tiptap/tiptap-ui/color-highlight-button/color-highlight-button.scss create mode 100644 components/shared/tiptap/tiptap-ui/color-highlight-button/color-highlight-button.tsx create mode 100644 components/shared/tiptap/tiptap-ui/color-highlight-button/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/color-highlight-button/use-color-highlight.ts create mode 100644 components/shared/tiptap/tiptap-ui/color-highlight-popover/color-highlight-popover.tsx create mode 100644 components/shared/tiptap/tiptap-ui/color-highlight-popover/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/heading-button/heading-button.tsx create mode 100644 components/shared/tiptap/tiptap-ui/heading-button/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/heading-button/use-heading.ts create mode 100644 components/shared/tiptap/tiptap-ui/heading-dropdown-menu/heading-dropdown-menu.tsx create mode 100644 components/shared/tiptap/tiptap-ui/heading-dropdown-menu/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/heading-dropdown-menu/use-heading-dropdown-menu.ts create mode 100644 components/shared/tiptap/tiptap-ui/image-upload-button/image-upload-button.tsx create mode 100644 components/shared/tiptap/tiptap-ui/image-upload-button/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/image-upload-button/use-image-upload.ts create mode 100644 components/shared/tiptap/tiptap-ui/link-popover/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/link-popover/link-popover.tsx create mode 100644 components/shared/tiptap/tiptap-ui/link-popover/use-link-popover.ts create mode 100644 components/shared/tiptap/tiptap-ui/list-button/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/list-button/list-button.tsx create mode 100644 components/shared/tiptap/tiptap-ui/list-button/use-list.ts create mode 100644 components/shared/tiptap/tiptap-ui/list-dropdown-menu/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/list-dropdown-menu/list-dropdown-menu.tsx create mode 100644 components/shared/tiptap/tiptap-ui/list-dropdown-menu/use-list-dropdown-menu.ts create mode 100644 components/shared/tiptap/tiptap-ui/mark-button/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/mark-button/mark-button.tsx create mode 100644 components/shared/tiptap/tiptap-ui/mark-button/use-mark.ts create mode 100644 components/shared/tiptap/tiptap-ui/text-align-button/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/text-align-button/text-align-button.tsx create mode 100644 components/shared/tiptap/tiptap-ui/text-align-button/use-text-align.ts create mode 100644 components/shared/tiptap/tiptap-ui/undo-redo-button/index.tsx create mode 100644 components/shared/tiptap/tiptap-ui/undo-redo-button/undo-redo-button.tsx create mode 100644 components/shared/tiptap/tiptap-ui/undo-redo-button/use-undo-redo.ts create mode 100644 hooks/use-composed-ref.ts create mode 100644 hooks/use-cursor-visibility.ts create mode 100644 hooks/use-element-rect.ts create mode 100644 hooks/use-menu-navigation.ts create mode 100644 hooks/use-mobile.ts create mode 100644 hooks/use-scrolling.ts create mode 100644 hooks/use-throttled-callback.ts create mode 100644 hooks/use-tiptap-editor.ts create mode 100644 hooks/use-unmount.ts create mode 100644 hooks/use-window-size.ts create mode 100644 lib/tiptap-utils.ts create mode 100644 styles/_keyframe-animations.scss create mode 100644 styles/_variables.scss rename styles/{globals.css => globals.scss} (98%) rename styles/{mdx.css => mdx.scss} (100%) diff --git a/app/(marketing)/[slug]/page.tsx b/app/(marketing)/[slug]/page.tsx index 91ae751..1814a37 100644 --- a/app/(marketing)/[slug]/page.tsx +++ b/app/(marketing)/[slug]/page.tsx @@ -3,7 +3,7 @@ import { allPages } from "contentlayer/generated"; import { Mdx } from "@/components/content/mdx-components"; -import "@/styles/mdx.css"; +import "@/styles/mdx.scss"; import { Metadata } from "next"; diff --git a/app/layout.tsx b/app/layout.tsx index d2a2fcc..6f37427 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,4 +1,4 @@ -import "@/styles/globals.css"; +import "@/styles/globals.scss"; import { fontHeading, fontSans, fontSatoshi } from "@/assets/fonts"; import { SessionProvider } from "next-auth/react"; diff --git a/components/email/EmailEditor.tsx b/components/email/EmailEditor.tsx new file mode 100644 index 0000000..c3ae846 --- /dev/null +++ b/components/email/EmailEditor.tsx @@ -0,0 +1,142 @@ +import * as React from "react"; +// --- Lib --- +import { Highlight } from "@tiptap/extension-highlight"; +import { Image } from "@tiptap/extension-image"; +import { TaskItem, TaskList } from "@tiptap/extension-list"; +import { Subscript } from "@tiptap/extension-subscript"; +import { Superscript } from "@tiptap/extension-superscript"; +import { TextAlign } from "@tiptap/extension-text-align"; +import { Typography } from "@tiptap/extension-typography"; +import { Selection } from "@tiptap/extensions"; +import { EditorContent, EditorContext, useEditor } from "@tiptap/react"; +// --- Tiptap Core Extensions --- +import { StarterKit } from "@tiptap/starter-kit"; + +import { handleImageUpload, MAX_FILE_SIZE } from "@/lib/tiptap-utils"; +import { useCursorVisibility } from "@/hooks/use-cursor-visibility"; +// --- Hooks --- +import { useIsMobile } from "@/hooks/use-mobile"; +import { useWindowSize } from "@/hooks/use-window-size"; +import { HorizontalRule } from "@/components/shared/tiptap/tiptap-node/horizontal-rule-node/horizontal-rule-node-extension"; +// --- Tiptap Node --- +import { ImageUploadNode } from "@/components/shared/tiptap/tiptap-node/image-upload-node/image-upload-node-extension"; +// import content from "@/components/shared/tiptap/tiptap-templates/simple/data/content.json"; +import { Toolbar } from "@/components/shared/tiptap/tiptap-ui-primitive/toolbar"; + +import { Icons } from "../shared/icons"; +import { + MainToolbarContent, + MobileToolbarContent, +} from "../shared/tiptap/tiptap-templates/simple/simple-editor"; + +export function EmailEditor({ + onGetEditorValue, +}: { + onGetEditorValue: (html: string, text: string) => void; +}) { + const isMobile = useIsMobile(); + const { height } = useWindowSize(); + const [mobileView, setMobileView] = React.useState< + "main" | "highlighter" | "link" + >("main"); + const toolbarRef = React.useRef(null); + + const editor = useEditor({ + immediatelyRender: false, + shouldRerenderOnTransaction: false, + editorProps: { + attributes: { + autocomplete: "off", + autocorrect: "off", + autocapitalize: "off", + "aria-label": "Main content area, start typing to enter text.", + class: "simple-editor", + }, + }, + extensions: [ + StarterKit.configure({ + horizontalRule: false, + link: { + openOnClick: false, + enableClickSelection: true, + }, + }), + HorizontalRule, + TextAlign.configure({ types: ["heading", "paragraph"] }), + TaskList, + TaskItem.configure({ nested: true }), + Highlight.configure({ multicolor: true }), + Image, + Typography, + Superscript, + Subscript, + Selection, + ImageUploadNode.configure({ + accept: "image/*", + maxSize: MAX_FILE_SIZE, + limit: 3, + upload: handleImageUpload, + onError: (error) => console.error("Upload failed:", error), + }), + ], + content: "Hi", + onUpdate: ({ editor }) => { + const html = editor.getHTML(); + const text = editor.getText(); + onGetEditorValue(html, text); + }, + }); + + const rect = useCursorVisibility({ + editor, + overlayHeight: toolbarRef.current?.getBoundingClientRect().height ?? 0, + }); + + React.useEffect(() => { + if (!isMobile && mobileView !== "main") { + setMobileView("main"); + } + }, [isMobile, mobileView]); + editor?.getHTML(); + return ( +
+ + + {mobileView === "main" ? ( + setMobileView("highlighter")} + onLinkClick={() => setMobileView("link")} + isMobile={isMobile} + /> + ) : ( + setMobileView("main")} + /> + )} + + + + + +
+ ); +} diff --git a/components/email/SendEmailModal.tsx b/components/email/SendEmailModal.tsx index ab0fdc4..69cab4c 100644 --- a/components/email/SendEmailModal.tsx +++ b/components/email/SendEmailModal.tsx @@ -1,7 +1,7 @@ "use client"; import { useState, useTransition } from "react"; -import dynamic from "next/dynamic"; +import { useTranslations } from "next-intl"; import { toast } from "sonner"; import { Icons } from "../shared/icons"; @@ -10,17 +10,11 @@ import { Drawer, DrawerClose, DrawerContent, - DrawerFooter, DrawerHeader, DrawerTitle, } from "../ui/drawer"; import { Input } from "../ui/input"; - -import "react-quill/dist/quill.snow.css"; - -import { useTranslations } from "next-intl"; - -const ReactQuill = dynamic(() => import("react-quill"), { ssr: false }); +import { EmailEditor } from "./EmailEditor"; interface SendEmailModalProps { className?: string; @@ -36,7 +30,12 @@ export function SendEmailModal({ onSuccess, }: SendEmailModalProps) { const [isOpen, setIsOpen] = useState(false); - const [sendForm, setSendForm] = useState({ to: "", subject: "", html: "" }); + const [sendForm, setSendForm] = useState({ + to: "", + subject: "", + html: "", + text: "", + }); const [isPending, startTransition] = useTransition(); const t = useTranslations("Email"); @@ -46,8 +45,10 @@ export function SendEmailModal({ toast.error("No email address selected"); return; } + console.log("sendForm", sendForm); + // return; if (!sendForm.to || !sendForm.subject || !sendForm.html) { - toast.error("Please fill in all fields"); + toast.error("Please fill in all required fields"); return; } @@ -60,13 +61,14 @@ export function SendEmailModal({ to: sendForm.to, subject: sendForm.subject, html: sendForm.html, + text: sendForm.text, }), }); if (response.ok) { toast.success("Email sent successfully"); setIsOpen(false); - setSendForm({ to: "", subject: "", html: "" }); + setSendForm({ to: "", subject: "", html: "", text: "" }); onSuccess?.(); } else { toast.error("Failed to send email", { @@ -94,28 +96,62 @@ export function SendEmailModal({ )} - - + + {t("Send Email")}{" "} - - - - - + + + + -
-
-