fix: Image Resizing and PR (#2996)

* added image min width and height programatically

* fixed editor initialization for peek view and inbox issues

* fixed ts issues with issue id in inbox
This commit is contained in:
M. Palanikannan 2023-12-06 12:16:34 +05:30 committed by sriram veeraghanta
parent 91cb15c2e3
commit 1bddaf75b2
13 changed files with 81 additions and 63 deletions

View File

@ -15,8 +15,8 @@ export { EditorContainer } from "./ui/components/editor-container";
export { EditorContentWrapper } from "./ui/components/editor-content"; export { EditorContentWrapper } from "./ui/components/editor-content";
// hooks // hooks
export { useEditor } from "./ui/hooks/useEditor"; export { useEditor } from "./ui/hooks/use-editor";
export { useReadOnlyEditor } from "./ui/hooks/useReadOnlyEditor"; export { useReadOnlyEditor } from "./ui/hooks/use-read-only-editor";
// helper items // helper items
export * from "./ui/menus/menu-items"; export * from "./ui/menus/menu-items";

View File

@ -1,4 +1,5 @@
import { Editor } from "@tiptap/react"; import { Editor } from "@tiptap/react";
import { useState } from "react";
import Moveable from "react-moveable"; import Moveable from "react-moveable";
export const ImageResizer = ({ editor }: { editor: Editor }) => { export const ImageResizer = ({ editor }: { editor: Editor }) => {
@ -17,6 +18,8 @@ export const ImageResizer = ({ editor }: { editor: Editor }) => {
} }
}; };
const [aspectRatio, setAspectRatio] = useState(1);
return ( return (
<> <>
<Moveable <Moveable
@ -28,9 +31,29 @@ export const ImageResizer = ({ editor }: { editor: Editor }) => {
keepRatio keepRatio
resizable resizable
throttleResize={0} 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) => { onResize={({ target, width, height, delta }: any) => {
delta[0] && (target!.style.width = `${width}px`); if (delta[0]) {
delta[1] && (target!.style.height = `${height}px`); 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={() => { onResizeEnd={() => {
updateMediaSize(); updateMediaSize();

View File

@ -4,7 +4,6 @@ import { CoreEditorProps } from "../props";
import { CoreEditorExtensions } from "../extensions"; import { CoreEditorExtensions } from "../extensions";
import { EditorProps } from "@tiptap/pm/view"; import { EditorProps } from "@tiptap/pm/view";
import { getTrimmedHTML } from "../../lib/utils"; import { getTrimmedHTML } from "../../lib/utils";
import { useInitializedContent } from "./useInitializedContent";
import { import {
DeleteImage, DeleteImage,
IMentionSuggestion, IMentionSuggestion,
@ -15,6 +14,7 @@ import {
interface CustomEditorProps { interface CustomEditorProps {
uploadFile: UploadImage; uploadFile: UploadImage;
restoreFile: RestoreImage; restoreFile: RestoreImage;
text_html?: string;
deleteFile: DeleteImage; deleteFile: DeleteImage;
cancelUploadImage?: () => any; cancelUploadImage?: () => any;
setIsSubmitting?: ( setIsSubmitting?: (
@ -38,6 +38,7 @@ export const useEditor = ({
cancelUploadImage, cancelUploadImage,
editorProps = {}, editorProps = {},
value, value,
text_html,
extensions = [], extensions = [],
onStart, onStart,
onChange, onChange,
@ -78,11 +79,9 @@ export const useEditor = ({
onChange?.(editor.getJSON(), getTrimmedHTML(editor.getHTML())); onChange?.(editor.getJSON(), getTrimmedHTML(editor.getHTML()));
}, },
}, },
[], [text_html],
); );
useInitializedContent(editor, value);
const editorRef: MutableRefObject<Editor | null> = useRef(null); const editorRef: MutableRefObject<Editor | null> = useRef(null);
editorRef.current = editor; editorRef.current = editor;

View File

@ -5,8 +5,8 @@ import {
MutableRefObject, MutableRefObject,
useEffect, useEffect,
} from "react"; } from "react";
import { CoreReadOnlyEditorExtensions } from "../../ui/read-only/extensions"; import { CoreReadOnlyEditorExtensions } from "../read-only/extensions";
import { CoreReadOnlyEditorProps } from "../../ui/read-only/props"; import { CoreReadOnlyEditorProps } from "../read-only/props";
import { EditorProps } from "@tiptap/pm/view"; import { EditorProps } from "@tiptap/pm/view";
import { IMentionSuggestion } from "@plane/editor-types"; import { IMentionSuggestion } from "@plane/editor-types";

View File

@ -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 : "<p></p>";
if (cleanedValue !== "<p></p>" && !hasInitializedContent.current) {
editor.commands.setContent(cleanedValue);
hasInitializedContent.current = true;
} else if (cleanedValue === "<p></p>" && hasInitializedContent.current) {
hasInitializedContent.current = false;
}
}
}, [value, editor]);
};

View File

@ -3,7 +3,7 @@ import * as React from "react";
import { Extension } from "@tiptap/react"; import { Extension } from "@tiptap/react";
import { getEditorClassNames } from "../lib/utils"; import { getEditorClassNames } from "../lib/utils";
import { EditorProps } from "@tiptap/pm/view"; 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 { EditorContainer } from "../ui/components/editor-container";
import { EditorContentWrapper } from "../ui/components/editor-content"; import { EditorContentWrapper } from "../ui/components/editor-content";
import { import {

View File

@ -5,7 +5,7 @@ import { PluginKey, NodeSelection, Plugin } from "@tiptap/pm/state";
import { __serializeForClipboard, EditorView } from "@tiptap/pm/view"; import { __serializeForClipboard, EditorView } from "@tiptap/pm/view";
function createDragHandleElement(): HTMLElement { function createDragHandleElement(): HTMLElement {
let dragHandleElement = document.createElement("div"); const dragHandleElement = document.createElement("div");
dragHandleElement.draggable = true; dragHandleElement.draggable = true;
dragHandleElement.dataset.dragHandle = ""; dragHandleElement.dataset.dragHandle = "";
dragHandleElement.classList.add("drag-handle"); dragHandleElement.classList.add("drag-handle");

View File

@ -24,6 +24,7 @@ export type IRichTextEditor = {
noBorder?: boolean; noBorder?: boolean;
borderOnFocus?: boolean; borderOnFocus?: boolean;
cancelUploadImage?: () => any; cancelUploadImage?: () => any;
text_html?: string;
customClassName?: string; customClassName?: string;
editorContentCustomClassNames?: string; editorContentCustomClassNames?: string;
onChange?: (json: any, html: string) => void; onChange?: (json: any, html: string) => void;
@ -48,6 +49,7 @@ interface EditorHandle {
const RichTextEditor = ({ const RichTextEditor = ({
onChange, onChange,
text_html,
dragDropEnabled, dragDropEnabled,
debouncedUpdatesEnabled, debouncedUpdatesEnabled,
setIsSubmitting, setIsSubmitting,
@ -76,6 +78,7 @@ const RichTextEditor = ({
deleteFile, deleteFile,
restoreFile, restoreFile,
forwardedRef, forwardedRef,
text_html,
extensions: RichTextEditorExtensions( extensions: RichTextEditorExtensions(
uploadFile, uploadFile,
setIsSubmitting, setIsSubmitting,

View File

@ -6,6 +6,12 @@
height: 0; height: 0;
} }
/* block quotes */
.ProseMirror blockquote p::before,
.ProseMirror blockquote p::after {
display: none;
}
.ProseMirror .is-empty::before { .ProseMirror .is-empty::before {
content: attr(data-placeholder); content: attr(data-placeholder);
float: left; float: left;
@ -246,16 +252,6 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
padding: 0; 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 { .drag-handle {
position: fixed; position: fixed;
opacity: 1; opacity: 1;
@ -320,3 +316,8 @@ div[data-type="horizontalRule"] {
border-bottom: 1px solid rgb(var(--color-text-100)); border-bottom: 1px solid rgb(var(--color-text-100));
} }
} }
/* image resizer */
.moveable-control-box {
z-index: 10 !important;
}

View File

@ -246,6 +246,7 @@ export const InboxMainContent: React.FC = observer(() => {
issue={{ issue={{
name: issueDetails.name, name: issueDetails.name,
description_html: issueDetails.description_html, description_html: issueDetails.description_html,
id: issueDetails.id,
}} }}
handleFormSubmit={submitChanges} handleFormSubmit={submitChanges}
isAllowed={isAllowed || user?.id === issueDetails.created_by} isAllowed={isAllowed || user?.id === issueDetails.created_by}

View File

@ -21,6 +21,7 @@ export interface IssueDetailsProps {
issue: { issue: {
name: string; name: string;
description_html: string; description_html: string;
id: string;
project_id?: string; project_id?: string;
}; };
workspaceSlug: string; workspaceSlug: string;
@ -55,12 +56,14 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = (props) => {
}); });
const [localTitleValue, setLocalTitleValue] = useState(""); const [localTitleValue, setLocalTitleValue] = useState("");
const issueTitleCurrentValue = watch("name"); const [localIssueDescription, setLocalIssueDescription] = useState("");
useEffect(() => { useEffect(() => {
if (localTitleValue === "" && issueTitleCurrentValue !== "") { if (issue.id) {
setLocalTitleValue(issueTitleCurrentValue); setLocalIssueDescription(issue.description_html);
setLocalTitleValue(issue.name);
} }
}, [issueTitleCurrentValue, localTitleValue]); }, [issue.id]);
const handleDescriptionFormSubmit = useCallback( const handleDescriptionFormSubmit = useCallback(
async (formData: Partial<IIssue>) => { async (formData: Partial<IIssue>) => {
@ -150,7 +153,8 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = (props) => {
uploadFile={fileService.getUploadFileFunction(workspaceSlug)} uploadFile={fileService.getUploadFileFunction(workspaceSlug)}
deleteFile={fileService.deleteImage} deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage} restoreFile={fileService.restoreImage}
value={value} value={localIssueDescription}
text_html={localIssueDescription}
setShouldShowAlert={setShowAlert} setShouldShowAlert={setShowAlert}
setIsSubmitting={setIsSubmitting} setIsSubmitting={setIsSubmitting}
dragDropEnabled dragDropEnabled

View File

@ -78,13 +78,15 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
[issue, issueUpdate] [issue, issueUpdate]
); );
const [localTitleValue, setLocalTitleValue] = useState(issue.name); const [localTitleValue, setLocalTitleValue] = useState("");
const issueTitleCurrentValue = watch("name"); const [localIssueDescription, setLocalIssueDescription] = useState("");
useEffect(() => { useEffect(() => {
if (localTitleValue === "" && issueTitleCurrentValue !== "") { if (issue.id) {
setLocalTitleValue(issueTitleCurrentValue); setLocalIssueDescription(issue.description_html);
setLocalTitleValue(issue.name);
} }
}, [issueTitleCurrentValue, localTitleValue]); }, [issue.id]);
useEffect(() => { useEffect(() => {
setLocalTitleValue(issue.name); setLocalTitleValue(issue.name);
@ -170,7 +172,8 @@ export const PeekOverviewIssueDetails: FC<IPeekOverviewIssueDetails> = (props) =
uploadFile={fileService.getUploadFileFunction(workspaceSlug)} uploadFile={fileService.getUploadFileFunction(workspaceSlug)}
deleteFile={fileService.deleteImage} deleteFile={fileService.deleteImage}
restoreFile={fileService.restoreImage} restoreFile={fileService.restoreImage}
value={value} value={localIssueDescription}
text_html={localIssueDescription}
setShouldShowAlert={setShowAlert} setShouldShowAlert={setShowAlert}
setIsSubmitting={setIsSubmitting} setIsSubmitting={setIsSubmitting}
dragDropEnabled dragDropEnabled

View File

@ -23,8 +23,6 @@
/* Custom image styles */ /* Custom image styles */
.ProseMirror img { .ProseMirror img {
min-width: 100px;
min-height: 100px;
transition: filter 0.1s ease-in-out; transition: filter 0.1s ease-in-out;
&:hover { &:hover {
@ -254,15 +252,15 @@ ul[data-type="taskList"] li[data-checked="true"] > div > p {
padding: 0; padding: 0;
} }
.ProseMirror:not(.dragging) .ProseMirror-selectednode:not(img):not(pre) { /* .ProseMirror:not(.dragging) .ProseMirror-selectednode:not(.node-image):not(pre) { */
outline: none !important; /* outline: none !important; */
border-radius: 0.2rem; /* border-radius: 0.2rem; */
background-color: rgb(var(--color-background-90)); /* background-color: rgb(var(--color-background-90)); */
border: 1px solid #5abbf7; /* border: 1px solid #5abbf7; */
padding: 4px 2px 4px 2px; /* padding: 4px 2px 4px 2px; */
transition: background-color 0.2s; /* transition: background-color 0.2s; */
box-shadow: none; /* box-shadow: none; */
} /* } */
.drag-handle { .drag-handle {
position: fixed; position: fixed;
@ -328,3 +326,8 @@ div[data-type="horizontalRule"] {
border-bottom: 1px solid rgb(var(--color-text-100)); border-bottom: 1px solid rgb(var(--color-text-100));
} }
} }
/* image resizer */
.moveable-control-box {
z-index: 10 !important;
}