forked from github/plane
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:
parent
ac18906604
commit
e7ac7e1da8
@ -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";
|
||||||
|
@ -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();
|
||||||
|
@ -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;
|
||||||
|
|
@ -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";
|
||||||
|
|
@ -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]);
|
|
||||||
};
|
|
@ -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 {
|
||||||
|
@ -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");
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
@ -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}
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user