diff --git a/packages/editor/core/src/index.ts b/packages/editor/core/src/index.ts index 55f68ac74..bdf533193 100644 --- a/packages/editor/core/src/index.ts +++ b/packages/editor/core/src/index.ts @@ -15,8 +15,8 @@ export { EditorContainer } from "./ui/components/editor-container"; export { EditorContentWrapper } from "./ui/components/editor-content"; // hooks -export { useEditor } from "./ui/hooks/useEditor"; -export { useReadOnlyEditor } from "./ui/hooks/useReadOnlyEditor"; +export { useEditor } from "./ui/hooks/use-editor"; +export { useReadOnlyEditor } from "./ui/hooks/use-read-only-editor"; // helper items export * from "./ui/menus/menu-items"; diff --git a/packages/editor/core/src/ui/extensions/image/image-resize.tsx b/packages/editor/core/src/ui/extensions/image/image-resize.tsx index 2545c7e44..2ede63961 100644 --- a/packages/editor/core/src/ui/extensions/image/image-resize.tsx +++ b/packages/editor/core/src/ui/extensions/image/image-resize.tsx @@ -1,4 +1,5 @@ import { Editor } from "@tiptap/react"; +import { useState } from "react"; import Moveable from "react-moveable"; export const ImageResizer = ({ editor }: { editor: Editor }) => { @@ -17,6 +18,8 @@ export const ImageResizer = ({ editor }: { editor: Editor }) => { } }; + const [aspectRatio, setAspectRatio] = useState(1); + return ( <> { keepRatio resizable throttleResize={0} + onResizeStart={() => { + const imageInfo = document.querySelector( + ".ProseMirror-selectednode", + ) as HTMLImageElement; + if (imageInfo) { + const originalWidth = Number(imageInfo.width); + const originalHeight = Number(imageInfo.height); + setAspectRatio(originalWidth / originalHeight); + } + }} onResize={({ target, width, height, delta }: any) => { - delta[0] && (target!.style.width = `${width}px`); - delta[1] && (target!.style.height = `${height}px`); + if (delta[0]) { + const newWidth = Math.max(width, 100); + const newHeight = newWidth / aspectRatio; + target!.style.width = `${newWidth}px`; + target!.style.height = `${newHeight}px`; + } + if (delta[1]) { + const newHeight = Math.max(height, 100); + const newWidth = newHeight * aspectRatio; + target!.style.height = `${newHeight}px`; + target!.style.width = `${newWidth}px`; + } }} onResizeEnd={() => { updateMediaSize(); diff --git a/packages/editor/core/src/ui/hooks/useEditor.tsx b/packages/editor/core/src/ui/hooks/use-editor.tsx similarity index 95% rename from packages/editor/core/src/ui/hooks/useEditor.tsx rename to packages/editor/core/src/ui/hooks/use-editor.tsx index 0c0a77ab3..bd349f3ef 100644 --- a/packages/editor/core/src/ui/hooks/useEditor.tsx +++ b/packages/editor/core/src/ui/hooks/use-editor.tsx @@ -4,7 +4,6 @@ import { CoreEditorProps } from "../props"; import { CoreEditorExtensions } from "../extensions"; import { EditorProps } from "@tiptap/pm/view"; import { getTrimmedHTML } from "../../lib/utils"; -import { useInitializedContent } from "./useInitializedContent"; import { DeleteImage, IMentionSuggestion, @@ -15,6 +14,7 @@ import { interface CustomEditorProps { uploadFile: UploadImage; restoreFile: RestoreImage; + text_html?: string; deleteFile: DeleteImage; cancelUploadImage?: () => any; setIsSubmitting?: ( @@ -38,6 +38,7 @@ export const useEditor = ({ cancelUploadImage, editorProps = {}, value, + text_html, extensions = [], onStart, onChange, @@ -78,11 +79,9 @@ export const useEditor = ({ onChange?.(editor.getJSON(), getTrimmedHTML(editor.getHTML())); }, }, - [], + [text_html], ); - useInitializedContent(editor, value); - const editorRef: MutableRefObject = useRef(null); editorRef.current = editor; diff --git a/packages/editor/core/src/ui/hooks/useReadOnlyEditor.tsx b/packages/editor/core/src/ui/hooks/use-read-only-editor.tsx similarity index 92% rename from packages/editor/core/src/ui/hooks/useReadOnlyEditor.tsx rename to packages/editor/core/src/ui/hooks/use-read-only-editor.tsx index f9e48dfd3..3207e5e56 100644 --- a/packages/editor/core/src/ui/hooks/useReadOnlyEditor.tsx +++ b/packages/editor/core/src/ui/hooks/use-read-only-editor.tsx @@ -5,8 +5,8 @@ import { MutableRefObject, useEffect, } from "react"; -import { CoreReadOnlyEditorExtensions } from "../../ui/read-only/extensions"; -import { CoreReadOnlyEditorProps } from "../../ui/read-only/props"; +import { CoreReadOnlyEditorExtensions } from "../read-only/extensions"; +import { CoreReadOnlyEditorProps } from "../read-only/props"; import { EditorProps } from "@tiptap/pm/view"; import { IMentionSuggestion } from "@plane/editor-types"; diff --git a/packages/editor/core/src/ui/hooks/useInitializedContent.tsx b/packages/editor/core/src/ui/hooks/useInitializedContent.tsx deleted file mode 100644 index 8e2ce1717..000000000 --- a/packages/editor/core/src/ui/hooks/useInitializedContent.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Editor } from "@tiptap/react"; -import { useEffect, useRef } from "react"; - -export const useInitializedContent = (editor: Editor | null, value: string) => { - const hasInitializedContent = useRef(false); - - useEffect(() => { - if (editor) { - const cleanedValue = - typeof value === "string" && value.trim() !== "" ? value : "

"; - if (cleanedValue !== "

" && !hasInitializedContent.current) { - editor.commands.setContent(cleanedValue); - hasInitializedContent.current = true; - } else if (cleanedValue === "

" && hasInitializedContent.current) { - hasInitializedContent.current = false; - } - } - }, [value, editor]); -}; diff --git a/packages/editor/core/src/ui/index.tsx b/packages/editor/core/src/ui/index.tsx index 79505baa9..6b0a22555 100644 --- a/packages/editor/core/src/ui/index.tsx +++ b/packages/editor/core/src/ui/index.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import { Extension } from "@tiptap/react"; import { getEditorClassNames } from "../lib/utils"; import { EditorProps } from "@tiptap/pm/view"; -import { useEditor } from "./hooks/useEditor"; +import { useEditor } from "./hooks/use-editor"; import { EditorContainer } from "../ui/components/editor-container"; import { EditorContentWrapper } from "../ui/components/editor-content"; import { diff --git a/packages/editor/extensions/src/extensions/drag-drop.tsx b/packages/editor/extensions/src/extensions/drag-drop.tsx index 33e7dcc34..5bf35cb2b 100644 --- a/packages/editor/extensions/src/extensions/drag-drop.tsx +++ b/packages/editor/extensions/src/extensions/drag-drop.tsx @@ -5,7 +5,7 @@ import { PluginKey, NodeSelection, Plugin } from "@tiptap/pm/state"; import { __serializeForClipboard, EditorView } from "@tiptap/pm/view"; function createDragHandleElement(): HTMLElement { - let dragHandleElement = document.createElement("div"); + const dragHandleElement = document.createElement("div"); dragHandleElement.draggable = true; dragHandleElement.dataset.dragHandle = ""; dragHandleElement.classList.add("drag-handle"); diff --git a/packages/editor/rich-text-editor/src/ui/index.tsx b/packages/editor/rich-text-editor/src/ui/index.tsx index 8757edf13..b3b261c3b 100644 --- a/packages/editor/rich-text-editor/src/ui/index.tsx +++ b/packages/editor/rich-text-editor/src/ui/index.tsx @@ -24,6 +24,7 @@ export type IRichTextEditor = { noBorder?: boolean; borderOnFocus?: boolean; cancelUploadImage?: () => any; + text_html?: string; customClassName?: string; editorContentCustomClassNames?: string; onChange?: (json: any, html: string) => void; @@ -48,6 +49,7 @@ interface EditorHandle { const RichTextEditor = ({ onChange, + text_html, dragDropEnabled, debouncedUpdatesEnabled, setIsSubmitting, @@ -76,6 +78,7 @@ const RichTextEditor = ({ deleteFile, restoreFile, forwardedRef, + text_html, extensions: RichTextEditorExtensions( uploadFile, setIsSubmitting, diff --git a/space/styles/editor.css b/space/styles/editor.css index 9f5623874..1a31712c6 100644 --- a/space/styles/editor.css +++ b/space/styles/editor.css @@ -6,6 +6,12 @@ height: 0; } +/* block quotes */ +.ProseMirror blockquote p::before, +.ProseMirror blockquote p::after { + display: none; +} + .ProseMirror .is-empty::before { content: attr(data-placeholder); float: left; @@ -246,16 +252,6 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { padding: 0; } -.ProseMirror:not(.dragging) .ProseMirror-selectednode:not(img):not(pre) { - outline: none !important; - border-radius: 0.2rem; - background-color: rgb(var(--color-background-90)); - border: 1px solid #5abbf7; - padding: 4px 2px 4px 2px; - transition: background-color 0.2s; - box-shadow: none; -} - .drag-handle { position: fixed; opacity: 1; @@ -320,3 +316,8 @@ div[data-type="horizontalRule"] { border-bottom: 1px solid rgb(var(--color-text-100)); } } + +/* image resizer */ +.moveable-control-box { + z-index: 10 !important; +} diff --git a/web/components/inbox/main-content.tsx b/web/components/inbox/main-content.tsx index 193f59263..e3b9aaca5 100644 --- a/web/components/inbox/main-content.tsx +++ b/web/components/inbox/main-content.tsx @@ -246,6 +246,7 @@ export const InboxMainContent: React.FC = observer(() => { issue={{ name: issueDetails.name, description_html: issueDetails.description_html, + id: issueDetails.id, }} handleFormSubmit={submitChanges} isAllowed={isAllowed || user?.id === issueDetails.created_by} diff --git a/web/components/issues/description-form.tsx b/web/components/issues/description-form.tsx index ef26e22d8..c61382a40 100644 --- a/web/components/issues/description-form.tsx +++ b/web/components/issues/description-form.tsx @@ -21,6 +21,7 @@ export interface IssueDetailsProps { issue: { name: string; description_html: string; + id: string; project_id?: string; }; workspaceSlug: string; @@ -55,12 +56,14 @@ export const IssueDescriptionForm: FC = (props) => { }); const [localTitleValue, setLocalTitleValue] = useState(""); - const issueTitleCurrentValue = watch("name"); + const [localIssueDescription, setLocalIssueDescription] = useState(""); + useEffect(() => { - if (localTitleValue === "" && issueTitleCurrentValue !== "") { - setLocalTitleValue(issueTitleCurrentValue); + if (issue.id) { + setLocalIssueDescription(issue.description_html); + setLocalTitleValue(issue.name); } - }, [issueTitleCurrentValue, localTitleValue]); + }, [issue.id]); const handleDescriptionFormSubmit = useCallback( async (formData: Partial) => { @@ -150,7 +153,8 @@ export const IssueDescriptionForm: FC = (props) => { uploadFile={fileService.getUploadFileFunction(workspaceSlug)} deleteFile={fileService.deleteImage} restoreFile={fileService.restoreImage} - value={value} + value={localIssueDescription} + text_html={localIssueDescription} setShouldShowAlert={setShowAlert} setIsSubmitting={setIsSubmitting} dragDropEnabled diff --git a/web/components/issues/issue-peek-overview/issue-detail.tsx b/web/components/issues/issue-peek-overview/issue-detail.tsx index e582ee3b2..2ceb6be77 100644 --- a/web/components/issues/issue-peek-overview/issue-detail.tsx +++ b/web/components/issues/issue-peek-overview/issue-detail.tsx @@ -78,13 +78,15 @@ export const PeekOverviewIssueDetails: FC = (props) = [issue, issueUpdate] ); - const [localTitleValue, setLocalTitleValue] = useState(issue.name); - const issueTitleCurrentValue = watch("name"); + const [localTitleValue, setLocalTitleValue] = useState(""); + const [localIssueDescription, setLocalIssueDescription] = useState(""); + useEffect(() => { - if (localTitleValue === "" && issueTitleCurrentValue !== "") { - setLocalTitleValue(issueTitleCurrentValue); + if (issue.id) { + setLocalIssueDescription(issue.description_html); + setLocalTitleValue(issue.name); } - }, [issueTitleCurrentValue, localTitleValue]); + }, [issue.id]); useEffect(() => { setLocalTitleValue(issue.name); @@ -170,7 +172,8 @@ export const PeekOverviewIssueDetails: FC = (props) = uploadFile={fileService.getUploadFileFunction(workspaceSlug)} deleteFile={fileService.deleteImage} restoreFile={fileService.restoreImage} - value={value} + value={localIssueDescription} + text_html={localIssueDescription} setShouldShowAlert={setShowAlert} setIsSubmitting={setIsSubmitting} dragDropEnabled diff --git a/web/styles/editor.css b/web/styles/editor.css index aebbf4850..149896c52 100644 --- a/web/styles/editor.css +++ b/web/styles/editor.css @@ -23,8 +23,6 @@ /* Custom image styles */ .ProseMirror img { - min-width: 100px; - min-height: 100px; transition: filter 0.1s ease-in-out; &:hover { @@ -254,15 +252,15 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p { padding: 0; } -.ProseMirror:not(.dragging) .ProseMirror-selectednode:not(img):not(pre) { - outline: none !important; - border-radius: 0.2rem; - background-color: rgb(var(--color-background-90)); - border: 1px solid #5abbf7; - padding: 4px 2px 4px 2px; - transition: background-color 0.2s; - box-shadow: none; -} +/* .ProseMirror:not(.dragging) .ProseMirror-selectednode:not(.node-image):not(pre) { */ +/* outline: none !important; */ +/* border-radius: 0.2rem; */ +/* background-color: rgb(var(--color-background-90)); */ +/* border: 1px solid #5abbf7; */ +/* padding: 4px 2px 4px 2px; */ +/* transition: background-color 0.2s; */ +/* box-shadow: none; */ +/* } */ .drag-handle { position: fixed; @@ -328,3 +326,8 @@ div[data-type="horizontalRule"] { border-bottom: 1px solid rgb(var(--color-text-100)); } } + +/* image resizer */ +.moveable-control-box { + z-index: 10 !important; +}