fix: project states fixes (#2731)
* fix: project states fixes * fix: states fixes * fix: formating all files
This commit is contained in:
parent
bd1a850f35
commit
20fb79567f
156 changed files with 1585 additions and 1758 deletions
|
|
@ -10,24 +10,24 @@ The `@plane/rich-text-editor` package extends from the `editor-core` package, in
|
|||
|
||||
`RichTextEditor` & `RichTextEditorWithRef`
|
||||
|
||||
- **Read Only Editor Instances**: We have added a really light weight *Read Only* Editor instance for the Rich editor types (with and without Ref)
|
||||
- **Read Only Editor Instances**: We have added a really light weight _Read Only_ Editor instance for the Rich editor types (with and without Ref)
|
||||
`RichReadOnlyEditor` &`RichReadOnlyEditorWithRef`
|
||||
|
||||
## RichTextEditor
|
||||
|
||||
| Prop | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `uploadFile` | `(file: File) => Promise<string>` | A function that handles file upload. It takes a file as input and handles the process of uploading that file. |
|
||||
| `deleteFile` | `(assetUrlWithWorkspaceId: string) => Promise<any>` | A function that handles deleting an image. It takes the asset url from your bucket and handles the process of deleting that image. |
|
||||
| `value` | `html string` | The initial content of the editor. |
|
||||
| `debouncedUpdatesEnabled` | `boolean` | If set to true, the `onChange` event handler is debounced, meaning it will only be invoked after the specified delay (default 1500ms) once the user has stopped typing. |
|
||||
| `onChange` | `(json: any, html: string) => void` | This function is invoked whenever the content of the editor changes. It is passed the new content in both JSON and HTML formats. |
|
||||
| `setIsSubmitting` | `(isSubmitting: "submitting" \| "submitted" \| "saved") => void` | This function is called to update the submission status. |
|
||||
| `setShouldShowAlert` | `(showAlert: boolean) => void` | This function is used to show or hide an alert incase of content not being "saved". |
|
||||
| `noBorder` | `boolean` | If set to true, the editor will not have a border. |
|
||||
| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. |
|
||||
| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. |
|
||||
| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. |
|
||||
| Prop | Type | Description |
|
||||
| ------------------------------- | ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `uploadFile` | `(file: File) => Promise<string>` | A function that handles file upload. It takes a file as input and handles the process of uploading that file. |
|
||||
| `deleteFile` | `(assetUrlWithWorkspaceId: string) => Promise<any>` | A function that handles deleting an image. It takes the asset url from your bucket and handles the process of deleting that image. |
|
||||
| `value` | `html string` | The initial content of the editor. |
|
||||
| `debouncedUpdatesEnabled` | `boolean` | If set to true, the `onChange` event handler is debounced, meaning it will only be invoked after the specified delay (default 1500ms) once the user has stopped typing. |
|
||||
| `onChange` | `(json: any, html: string) => void` | This function is invoked whenever the content of the editor changes. It is passed the new content in both JSON and HTML formats. |
|
||||
| `setIsSubmitting` | `(isSubmitting: "submitting" \| "submitted" \| "saved") => void` | This function is called to update the submission status. |
|
||||
| `setShouldShowAlert` | `(showAlert: boolean) => void` | This function is used to show or hide an alert incase of content not being "saved". |
|
||||
| `noBorder` | `boolean` | If set to true, the editor will not have a border. |
|
||||
| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. |
|
||||
| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. |
|
||||
| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. |
|
||||
|
||||
### Usage
|
||||
|
||||
|
|
@ -57,43 +57,47 @@ The `@plane/rich-text-editor` package extends from the `editor-core` package, in
|
|||
2. Example of how to use the `RichTextEditorWithRef` component
|
||||
|
||||
```tsx
|
||||
const editorRef = useRef<any>(null);
|
||||
const editorRef = useRef<any>(null);
|
||||
|
||||
// can use it to set the editor's value
|
||||
editorRef.current?.setEditorValue(`${watch("description_html")}`);
|
||||
// can use it to set the editor's value
|
||||
editorRef.current?.setEditorValue(`${watch("description_html")}`);
|
||||
|
||||
// can use it to clear the editor
|
||||
editorRef?.current?.clearEditor();
|
||||
// can use it to clear the editor
|
||||
editorRef?.current?.clearEditor();
|
||||
|
||||
return (<RichTextEditorWithRef
|
||||
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
|
||||
deleteFile={fileService.deleteImage}
|
||||
ref={editorRef}
|
||||
debouncedUpdatesEnabled={false}
|
||||
value={value}
|
||||
customClassName="min-h-[150px]"
|
||||
onChange={(description: Object, description_html: string) => {
|
||||
onChange(description_html);
|
||||
// custom stuff you want to do
|
||||
} } />)
|
||||
return (
|
||||
<RichTextEditorWithRef
|
||||
uploadFile={fileService.getUploadFileFunction(workspaceSlug as string)}
|
||||
deleteFile={fileService.deleteImage}
|
||||
ref={editorRef}
|
||||
debouncedUpdatesEnabled={false}
|
||||
value={value}
|
||||
customClassName="min-h-[150px]"
|
||||
onChange={(description: Object, description_html: string) => {
|
||||
onChange(description_html);
|
||||
// custom stuff you want to do
|
||||
}}
|
||||
/>
|
||||
);
|
||||
```
|
||||
|
||||
## RichReadOnlyEditor
|
||||
|
||||
| Prop | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| `value` | `html string` | The initial content of the editor. |
|
||||
| `noBorder` | `boolean` | If set to true, the editor will not have a border. |
|
||||
| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. |
|
||||
| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. |
|
||||
| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. |
|
||||
| Prop | Type | Description |
|
||||
| ------------------------------- | ------------- | --------------------------------------------------------------------- |
|
||||
| `value` | `html string` | The initial content of the editor. |
|
||||
| `noBorder` | `boolean` | If set to true, the editor will not have a border. |
|
||||
| `borderOnFocus` | `boolean` | If set to true, the editor will show a border when it is focused. |
|
||||
| `customClassName` | `string` | This is a custom CSS class that can be applied to the editor. |
|
||||
| `editorContentCustomClassNames` | `string` | This is a custom CSS class that can be applied to the editor content. |
|
||||
|
||||
### Usage
|
||||
|
||||
Here is an example of how to use the `RichReadOnlyEditor` component
|
||||
|
||||
```tsx
|
||||
<RichReadOnlyEditor
|
||||
value={issueDetails.description_html}
|
||||
customClassName="p-3 min-h-[50px] shadow-sm" />
|
||||
<RichReadOnlyEditor
|
||||
value={issueDetails.description_html}
|
||||
customClassName="p-3 min-h-[50px] shadow-sm"
|
||||
/>
|
||||
```
|
||||
|
|
|
|||
|
|
@ -2,4 +2,4 @@ import "./styles/github-dark.css";
|
|||
|
||||
export { RichTextEditor, RichTextEditorWithRef } from "./ui";
|
||||
export { RichReadOnlyEditor, RichReadOnlyEditorWithRef } from "./ui/read-only";
|
||||
export type { IMentionSuggestion, IMentionHighlight } from "./ui"
|
||||
export type { IMentionSuggestion, IMentionHighlight } from "./ui";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import HorizontalRule from "@tiptap/extension-horizontal-rule";
|
||||
import Placeholder from "@tiptap/extension-placeholder";
|
||||
import CodeBlockLowlight from "@tiptap/extension-code-block-lowlight";
|
||||
import { common, createLowlight } from 'lowlight'
|
||||
import { common, createLowlight } from "lowlight";
|
||||
import { InputRule } from "@tiptap/core";
|
||||
|
||||
import ts from "highlight.js/lib/languages/typescript";
|
||||
|
|
@ -9,51 +9,53 @@ import ts from "highlight.js/lib/languages/typescript";
|
|||
import SlashCommand from "./slash-command";
|
||||
import { UploadImage } from "../";
|
||||
|
||||
const lowlight = createLowlight(common)
|
||||
const lowlight = createLowlight(common);
|
||||
lowlight.register("ts", ts);
|
||||
|
||||
export const RichTextEditorExtensions = (
|
||||
uploadFile: UploadImage,
|
||||
setIsSubmitting?: (isSubmitting: "submitting" | "submitted" | "saved") => void
|
||||
setIsSubmitting?: (
|
||||
isSubmitting: "submitting" | "submitted" | "saved",
|
||||
) => void,
|
||||
) => [
|
||||
HorizontalRule.extend({
|
||||
addInputRules() {
|
||||
return [
|
||||
new InputRule({
|
||||
find: /^(?:---|—-|___\s|\*\*\*\s)$/,
|
||||
handler: ({ state, range, commands }) => {
|
||||
commands.splitBlock();
|
||||
HorizontalRule.extend({
|
||||
addInputRules() {
|
||||
return [
|
||||
new InputRule({
|
||||
find: /^(?:---|—-|___\s|\*\*\*\s)$/,
|
||||
handler: ({ state, range, commands }) => {
|
||||
commands.splitBlock();
|
||||
|
||||
const attributes = {};
|
||||
const { tr } = state;
|
||||
const start = range.from;
|
||||
const end = range.to;
|
||||
// @ts-ignore
|
||||
tr.replaceWith(start - 1, end, this.type.create(attributes));
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
}).configure({
|
||||
HTMLAttributes: {
|
||||
class: "mb-6 border-t border-custom-border-300",
|
||||
},
|
||||
}),
|
||||
SlashCommand(uploadFile, setIsSubmitting),
|
||||
CodeBlockLowlight.configure({
|
||||
lowlight,
|
||||
}),
|
||||
Placeholder.configure({
|
||||
placeholder: ({ node }) => {
|
||||
if (node.type.name === "heading") {
|
||||
return `Heading ${node.attrs.level}`;
|
||||
}
|
||||
if (node.type.name === "image" || node.type.name === "table") {
|
||||
return "";
|
||||
}
|
||||
const attributes = {};
|
||||
const { tr } = state;
|
||||
const start = range.from;
|
||||
const end = range.to;
|
||||
// @ts-ignore
|
||||
tr.replaceWith(start - 1, end, this.type.create(attributes));
|
||||
},
|
||||
}),
|
||||
];
|
||||
},
|
||||
}).configure({
|
||||
HTMLAttributes: {
|
||||
class: "mb-6 border-t border-custom-border-300",
|
||||
},
|
||||
}),
|
||||
SlashCommand(uploadFile, setIsSubmitting),
|
||||
CodeBlockLowlight.configure({
|
||||
lowlight,
|
||||
}),
|
||||
Placeholder.configure({
|
||||
placeholder: ({ node }) => {
|
||||
if (node.type.name === "heading") {
|
||||
return `Heading ${node.attrs.level}`;
|
||||
}
|
||||
if (node.type.name === "image" || node.type.name === "table") {
|
||||
return "";
|
||||
}
|
||||
|
||||
return "Press '/' for commands...";
|
||||
},
|
||||
includeChildren: true,
|
||||
}),
|
||||
];
|
||||
return "Press '/' for commands...";
|
||||
},
|
||||
includeChildren: true,
|
||||
}),
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,7 +1,19 @@
|
|||
import { Editor } from "@tiptap/core";
|
||||
import { Check, Trash } from "lucide-react";
|
||||
import { Dispatch, FC, SetStateAction, useCallback, useEffect, useRef } from "react";
|
||||
import { cn, isValidHttpUrl, setLinkEditor, unsetLinkEditor, } from "@plane/editor-core";
|
||||
import {
|
||||
Dispatch,
|
||||
FC,
|
||||
SetStateAction,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
} from "react";
|
||||
import {
|
||||
cn,
|
||||
isValidHttpUrl,
|
||||
setLinkEditor,
|
||||
unsetLinkEditor,
|
||||
} from "@plane/editor-core";
|
||||
|
||||
interface LinkSelectorProps {
|
||||
editor: Editor;
|
||||
|
|
@ -9,7 +21,11 @@ interface LinkSelectorProps {
|
|||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
export const LinkSelector: FC<LinkSelectorProps> = ({ editor, isOpen, setIsOpen }) => {
|
||||
export const LinkSelector: FC<LinkSelectorProps> = ({
|
||||
editor,
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
}) => {
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const onLinkSubmit = useCallback(() => {
|
||||
|
|
@ -31,7 +47,7 @@ export const LinkSelector: FC<LinkSelectorProps> = ({ editor, isOpen, setIsOpen
|
|||
type="button"
|
||||
className={cn(
|
||||
"flex h-full items-center space-x-2 px-3 py-1.5 text-sm font-medium text-custom-text-300 hover:bg-custom-background-100 active:bg-custom-background-100",
|
||||
{ "bg-custom-background-100": isOpen }
|
||||
{ "bg-custom-background-100": isOpen },
|
||||
)}
|
||||
onClick={() => {
|
||||
setIsOpen(!isOpen);
|
||||
|
|
|
|||
|
|
@ -1,10 +1,16 @@
|
|||
import { BulletListItem, cn, CodeItem, HeadingOneItem, HeadingThreeItem, HeadingTwoItem, NumberedListItem, QuoteItem, TodoListItem } from "@plane/editor-core";
|
||||
import { Editor } from "@tiptap/react";
|
||||
import {
|
||||
Check,
|
||||
ChevronDown,
|
||||
TextIcon,
|
||||
} from "lucide-react";
|
||||
BulletListItem,
|
||||
cn,
|
||||
CodeItem,
|
||||
HeadingOneItem,
|
||||
HeadingThreeItem,
|
||||
HeadingTwoItem,
|
||||
NumberedListItem,
|
||||
QuoteItem,
|
||||
TodoListItem,
|
||||
} from "@plane/editor-core";
|
||||
import { Editor } from "@tiptap/react";
|
||||
import { Check, ChevronDown, TextIcon } from "lucide-react";
|
||||
import { Dispatch, FC, SetStateAction } from "react";
|
||||
|
||||
import { BubbleMenuItem } from ".";
|
||||
|
|
@ -15,12 +21,17 @@ interface NodeSelectorProps {
|
|||
setIsOpen: Dispatch<SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
export const NodeSelector: FC<NodeSelectorProps> = ({ editor, isOpen, setIsOpen }) => {
|
||||
export const NodeSelector: FC<NodeSelectorProps> = ({
|
||||
editor,
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
}) => {
|
||||
const items: BubbleMenuItem[] = [
|
||||
{
|
||||
name: "Text",
|
||||
icon: TextIcon,
|
||||
command: () => editor.chain().focus().toggleNode("paragraph", "paragraph").run(),
|
||||
command: () =>
|
||||
editor.chain().focus().toggleNode("paragraph", "paragraph").run(),
|
||||
isActive: () =>
|
||||
editor.isActive("paragraph") &&
|
||||
!editor.isActive("bulletList") &&
|
||||
|
|
@ -63,7 +74,10 @@ export const NodeSelector: FC<NodeSelectorProps> = ({ editor, isOpen, setIsOpen
|
|||
}}
|
||||
className={cn(
|
||||
"flex items-center justify-between rounded-sm px-2 py-1 text-sm text-custom-text-200 hover:bg-custom-primary-100/5 hover:text-custom-text-100",
|
||||
{ "bg-custom-primary-100/5 text-custom-text-100": activeItem.name === item.name }
|
||||
{
|
||||
"bg-custom-primary-100/5 text-custom-text-100":
|
||||
activeItem.name === item.name,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
"use client"
|
||||
import { EditorContainer, EditorContentWrapper, getEditorClassNames, useReadOnlyEditor } from '@plane/editor-core';
|
||||
import * as React from 'react';
|
||||
"use client";
|
||||
import {
|
||||
EditorContainer,
|
||||
EditorContentWrapper,
|
||||
getEditorClassNames,
|
||||
useReadOnlyEditor,
|
||||
} from "@plane/editor-core";
|
||||
import * as React from "react";
|
||||
|
||||
interface IRichTextReadOnlyEditor {
|
||||
value: string;
|
||||
|
|
@ -35,23 +40,31 @@ const RichReadOnlyEditor = ({
|
|||
mentionHighlights,
|
||||
});
|
||||
|
||||
const editorClassNames = getEditorClassNames({ noBorder, borderOnFocus, customClassName });
|
||||
const editorClassNames = getEditorClassNames({
|
||||
noBorder,
|
||||
borderOnFocus,
|
||||
customClassName,
|
||||
});
|
||||
|
||||
if (!editor) return null;
|
||||
|
||||
return (
|
||||
<EditorContainer editor={editor} editorClassNames={editorClassNames}>
|
||||
<div className="flex flex-col">
|
||||
<EditorContentWrapper editor={editor} editorContentCustomClassNames={editorContentCustomClassNames} />
|
||||
<EditorContentWrapper
|
||||
editor={editor}
|
||||
editorContentCustomClassNames={editorContentCustomClassNames}
|
||||
/>
|
||||
</div>
|
||||
</EditorContainer >
|
||||
</EditorContainer>
|
||||
);
|
||||
};
|
||||
|
||||
const RichReadOnlyEditorWithRef = React.forwardRef<EditorHandle, IRichTextReadOnlyEditor>((props, ref) => (
|
||||
<RichReadOnlyEditor {...props} forwardedRef={ref} />
|
||||
));
|
||||
const RichReadOnlyEditorWithRef = React.forwardRef<
|
||||
EditorHandle,
|
||||
IRichTextReadOnlyEditor
|
||||
>((props, ref) => <RichReadOnlyEditor {...props} forwardedRef={ref} />);
|
||||
|
||||
RichReadOnlyEditorWithRef.displayName = "RichReadOnlyEditorWithRef";
|
||||
|
||||
export { RichReadOnlyEditor , RichReadOnlyEditorWithRef };
|
||||
export { RichReadOnlyEditor, RichReadOnlyEditorWithRef };
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue