forked from github/plane
fix: slash command scroll posiiton
This commit is contained in:
parent
bcc1131ec1
commit
78bb3085f3
@ -32,10 +32,9 @@ type FormData = {
|
||||
task: string;
|
||||
};
|
||||
|
||||
const TiptapEditor = React.forwardRef<
|
||||
ITiptapRichTextEditor,
|
||||
ITiptapRichTextEditor
|
||||
>((props, ref) => <Tiptap {...props} forwardedRef={ref} />);
|
||||
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>(
|
||||
(props, ref) => <Tiptap {...props} forwardedRef={ref} />
|
||||
);
|
||||
|
||||
TiptapEditor.displayName = "TiptapEditor";
|
||||
|
||||
@ -141,11 +140,12 @@ export const GptAssistantModal: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`absolute ${inset} z-20 w-full space-y-4 rounded-[10px] border border-custom-border-200 bg-custom-background-100 p-4 shadow ${isOpen ? "block" : "hidden"
|
||||
}`}
|
||||
className={`absolute ${inset} z-20 w-full space-y-4 rounded-[10px] border border-custom-border-200 bg-custom-background-100 p-4 shadow ${
|
||||
isOpen ? "block" : "hidden"
|
||||
}`}
|
||||
>
|
||||
{((content && content !== "") || (htmlContent && htmlContent !== "<p></p>")) && (
|
||||
<div className="remirror-section text-sm">
|
||||
<div id="tiptap-container" className="remirror-section text-sm">
|
||||
Content:
|
||||
<TiptapEditor
|
||||
value={htmlContent ?? `<p>${content}</p>`}
|
||||
@ -179,10 +179,11 @@ export const GptAssistantModal: React.FC<Props> = ({
|
||||
type="text"
|
||||
name="task"
|
||||
register={register}
|
||||
placeholder={`${content && content !== ""
|
||||
? "Tell AI what action to perform on this content..."
|
||||
: "Ask AI anything..."
|
||||
}`}
|
||||
placeholder={`${
|
||||
content && content !== ""
|
||||
? "Tell AI what action to perform on this content..."
|
||||
: "Ask AI anything..."
|
||||
}`}
|
||||
autoComplete="off"
|
||||
/>
|
||||
<div className={`flex gap-2 ${response === "" ? "justify-end" : "justify-between"}`}>
|
||||
@ -218,8 +219,8 @@ export const GptAssistantModal: React.FC<Props> = ({
|
||||
{isSubmitting
|
||||
? "Generating response..."
|
||||
: response === ""
|
||||
? "Generate response"
|
||||
: "Generate again"}
|
||||
? "Generate response"
|
||||
: "Generate again"}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -18,10 +18,9 @@ import type { ICurrentUserResponse, IIssueComment } from "types";
|
||||
import { PROJECT_ISSUES_ACTIVITY } from "constants/fetch-keys";
|
||||
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
|
||||
|
||||
const TiptapEditor = React.forwardRef<
|
||||
ITiptapRichTextEditor,
|
||||
ITiptapRichTextEditor
|
||||
>((props, ref) => <Tiptap {...props} forwardedRef={ref} />);
|
||||
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>(
|
||||
(props, ref) => <Tiptap {...props} forwardedRef={ref} />
|
||||
);
|
||||
|
||||
TiptapEditor.displayName = "TiptapEditor";
|
||||
|
||||
@ -88,15 +87,17 @@ export const AddComment: React.FC<Props> = ({ issueId, user, disabled = false })
|
||||
return (
|
||||
<div>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="issue-comments-section">
|
||||
<div id="tiptap-container" className="issue-comments-section">
|
||||
<Controller
|
||||
name="comment_html"
|
||||
control={control}
|
||||
render={({ field: { value, onChange } }) =>
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<TiptapEditor
|
||||
ref={editorRef}
|
||||
value={
|
||||
!value || value === "" || (typeof value === "object" && Object.keys(value).length === 0)
|
||||
!value ||
|
||||
value === "" ||
|
||||
(typeof value === "object" && Object.keys(value).length === 0)
|
||||
? watch("comment_html")
|
||||
: value
|
||||
}
|
||||
@ -107,7 +108,7 @@ export const AddComment: React.FC<Props> = ({ issueId, user, disabled = false })
|
||||
setValue("comment_json", comment_json);
|
||||
}}
|
||||
/>
|
||||
}
|
||||
)}
|
||||
/>
|
||||
|
||||
<SecondaryButton type="submit" disabled={isSubmitting || disabled} className="mt-2">
|
||||
|
@ -15,10 +15,9 @@ import { timeAgo } from "helpers/date-time.helper";
|
||||
import type { IIssueComment } from "types";
|
||||
import Tiptap, { ITiptapRichTextEditor } from "components/tiptap";
|
||||
|
||||
const TiptapEditor = React.forwardRef<
|
||||
ITiptapRichTextEditor,
|
||||
ITiptapRichTextEditor
|
||||
>((props, ref) => <Tiptap {...props} forwardedRef={ref} />);
|
||||
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>(
|
||||
(props, ref) => <Tiptap {...props} forwardedRef={ref} />
|
||||
);
|
||||
|
||||
TiptapEditor.displayName = "TiptapEditor";
|
||||
|
||||
@ -51,7 +50,7 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
|
||||
setIsEditing(false);
|
||||
|
||||
onSubmit(formData);
|
||||
console.log("watching", formData.comment_html)
|
||||
console.log("watching", formData.comment_html);
|
||||
|
||||
editorRef.current?.setEditorValue(formData.comment_html);
|
||||
showEditorRef.current?.setEditorValue(formData.comment_html);
|
||||
@ -103,16 +102,18 @@ export const CommentCard: React.FC<Props> = ({ comment, onSubmit, handleCommentD
|
||||
className={`flex-col gap-2 ${isEditing ? "flex" : "hidden"}`}
|
||||
onSubmit={handleSubmit(onEnter)}
|
||||
>
|
||||
<TiptapEditor
|
||||
ref={editorRef}
|
||||
value={watch("comment_html")}
|
||||
debouncedUpdatesEnabled={false}
|
||||
customClassName="min-h-[50px] p-3"
|
||||
onChange={(comment_json: Object, comment_html: string) => {
|
||||
setValue("comment_json", comment_json);
|
||||
setValue("comment_html", comment_html);
|
||||
}}
|
||||
/>
|
||||
<div id="tiptap-container">
|
||||
<TiptapEditor
|
||||
ref={editorRef}
|
||||
value={watch("comment_html")}
|
||||
debouncedUpdatesEnabled={false}
|
||||
customClassName="min-h-[50px] p-3"
|
||||
onChange={(comment_json: Object, comment_html: string) => {
|
||||
setValue("comment_json", comment_json);
|
||||
setValue("comment_html", comment_html);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-1 self-end">
|
||||
<button
|
||||
type="submit"
|
||||
|
@ -110,7 +110,7 @@ export const IssueDescriptionForm: FC<IssueDetailsProps> = ({
|
||||
)}
|
||||
</div>
|
||||
<span>{errors.name ? errors.name.message : null}</span>
|
||||
<div className="relative">
|
||||
<div id="tiptap-container" className="relative">
|
||||
<Controller
|
||||
name="description_html"
|
||||
control={control}
|
||||
|
@ -329,13 +329,14 @@ export const IssueForm: FC<IssueFormProps> = ({
|
||||
</div>
|
||||
)}
|
||||
{(fieldsToShow.includes("all") || fieldsToShow.includes("description")) && (
|
||||
<div className="relative">
|
||||
<div id="tiptap-container" className="relative">
|
||||
<div className="flex justify-end">
|
||||
{issueName && issueName !== "" && (
|
||||
<button
|
||||
type="button"
|
||||
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-90 ${iAmFeelingLucky ? "cursor-wait" : ""
|
||||
}`}
|
||||
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-90 ${
|
||||
iAmFeelingLucky ? "cursor-wait" : ""
|
||||
}`}
|
||||
onClick={handleAutoGenerateDescription}
|
||||
disabled={iAmFeelingLucky}
|
||||
>
|
||||
@ -367,7 +368,9 @@ export const IssueForm: FC<IssueFormProps> = ({
|
||||
<Tiptap
|
||||
debouncedUpdatesEnabled={false}
|
||||
value={
|
||||
!value || value === "" || (typeof value === "object" && Object.keys(value).length === 0)
|
||||
!value ||
|
||||
value === "" ||
|
||||
(typeof value === "object" && Object.keys(value).length === 0)
|
||||
? watch("description_html")
|
||||
: value
|
||||
}
|
||||
@ -545,7 +548,7 @@ export const IssueForm: FC<IssueFormProps> = ({
|
||||
onClick={() => setCreateMore((prevData) => !prevData)}
|
||||
>
|
||||
<span className="text-xs">Create more</span>
|
||||
<ToggleSwitch value={createMore} onChange={() => { }} size="md" />
|
||||
<ToggleSwitch value={createMore} onChange={() => {}} size="md" />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<SecondaryButton onClick={handleClose}>Discard</SecondaryButton>
|
||||
@ -555,8 +558,8 @@ export const IssueForm: FC<IssueFormProps> = ({
|
||||
? "Updating Issue..."
|
||||
: "Update Issue"
|
||||
: isSubmitting
|
||||
? "Adding Issue..."
|
||||
: "Add Issue"}
|
||||
? "Adding Issue..."
|
||||
: "Add Issue"}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -39,10 +39,9 @@ const defaultValues = {
|
||||
description_html: null,
|
||||
};
|
||||
|
||||
const TiptapEditor = React.forwardRef<
|
||||
ITiptapRichTextEditor,
|
||||
ITiptapRichTextEditor
|
||||
>((props, ref) => <Tiptap {...props} forwardedRef={ref} />);
|
||||
const TiptapEditor = React.forwardRef<ITiptapRichTextEditor, ITiptapRichTextEditor>(
|
||||
(props, ref) => <Tiptap {...props} forwardedRef={ref} />
|
||||
);
|
||||
|
||||
TiptapEditor.displayName = "TiptapEditor";
|
||||
|
||||
@ -232,9 +231,9 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
||||
description:
|
||||
!data.description || data.description === ""
|
||||
? {
|
||||
type: "doc",
|
||||
content: [{ type: "paragraph" }],
|
||||
}
|
||||
type: "doc",
|
||||
content: [{ type: "paragraph" }],
|
||||
}
|
||||
: data.description,
|
||||
description_html: data.description_html ?? "<p></p>",
|
||||
});
|
||||
@ -285,7 +284,10 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
||||
maxLength={255}
|
||||
/>
|
||||
</div>
|
||||
<div className="page-block-section relative -mt-2 text-custom-text-200">
|
||||
<div
|
||||
id="tiptap-container"
|
||||
className="page-block-section relative -mt-2 text-custom-text-200"
|
||||
>
|
||||
<Controller
|
||||
name="description_html"
|
||||
control={control}
|
||||
@ -317,8 +319,8 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
||||
value && value !== "" && Object.keys(value).length > 0
|
||||
? value
|
||||
: watch("description_html") && watch("description_html") !== ""
|
||||
? watch("description_html")
|
||||
: { type: "doc", content: [{ type: "paragraph" }] }
|
||||
? watch("description_html")
|
||||
: { type: "doc", content: [{ type: "paragraph" }] }
|
||||
}
|
||||
debouncedUpdatesEnabled={false}
|
||||
customClassName="text-sm"
|
||||
@ -335,8 +337,9 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
||||
<div className="m-2 mt-6 flex">
|
||||
<button
|
||||
type="button"
|
||||
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-80 ${iAmFeelingLucky ? "cursor-wait bg-custom-background-90" : ""
|
||||
}`}
|
||||
className={`flex items-center gap-1 rounded px-1.5 py-1 text-xs hover:bg-custom-background-80 ${
|
||||
iAmFeelingLucky ? "cursor-wait bg-custom-background-90" : ""
|
||||
}`}
|
||||
onClick={handleAutoGenerateDescription}
|
||||
disabled={iAmFeelingLucky}
|
||||
>
|
||||
@ -368,8 +371,8 @@ export const CreateUpdateBlockInline: React.FC<Props> = ({
|
||||
? "Updating..."
|
||||
: "Update block"
|
||||
: isSubmitting
|
||||
? "Adding..."
|
||||
: "Add block"}
|
||||
? "Adding..."
|
||||
: "Add block"}
|
||||
</PrimaryButton>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1,14 +1,14 @@
|
||||
// @ts-nocheck
|
||||
import { useEditor, EditorContent, Editor } from '@tiptap/react';
|
||||
import { useDebouncedCallback } from 'use-debounce';
|
||||
import { EditorBubbleMenu } from './bubble-menu';
|
||||
import { TiptapExtensions } from './extensions';
|
||||
import { TiptapEditorProps } from './props';
|
||||
import { useEditor, EditorContent, Editor } from "@tiptap/react";
|
||||
import { useDebouncedCallback } from "use-debounce";
|
||||
import { EditorBubbleMenu } from "./bubble-menu";
|
||||
import { TiptapExtensions } from "./extensions";
|
||||
import { TiptapEditorProps } from "./props";
|
||||
import { Node } from "@tiptap/pm/model";
|
||||
import { Editor as CoreEditor } from "@tiptap/core";
|
||||
import { useCallback, useImperativeHandle, useRef } from 'react';
|
||||
import { EditorState } from '@tiptap/pm/state';
|
||||
import fileService from 'services/file.service';
|
||||
import { useCallback, useImperativeHandle, useRef } from "react";
|
||||
import { EditorState } from "@tiptap/pm/state";
|
||||
import fileService from "services/file.service";
|
||||
|
||||
export interface ITiptapRichTextEditor {
|
||||
value: string;
|
||||
@ -34,7 +34,7 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
|
||||
value,
|
||||
noBorder,
|
||||
borderOnFocus,
|
||||
customClassName
|
||||
customClassName,
|
||||
} = props;
|
||||
|
||||
const editor = useEditor({
|
||||
@ -45,43 +45,40 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
|
||||
onUpdate: async ({ editor }) => {
|
||||
// for instant feedback loop
|
||||
setIsSubmitting?.(true);
|
||||
checkForNodeDeletions(editor)
|
||||
checkForNodeDeletions(editor);
|
||||
if (debouncedUpdatesEnabled) {
|
||||
debouncedUpdates({ onChange, editor });
|
||||
} else {
|
||||
onChange?.(editor.getJSON(), editor.getHTML());
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const editorRef: React.MutableRefObject<Editor | null> = useRef(null)
|
||||
const editorRef: React.MutableRefObject<Editor | null> = useRef(null);
|
||||
|
||||
useImperativeHandle(forwardedRef, () => ({
|
||||
clearEditor: () => {
|
||||
console.log('clearContent')
|
||||
console.log(editorRef)
|
||||
editorRef.current?.commands.clearContent()
|
||||
console.log("clearContent");
|
||||
console.log(editorRef);
|
||||
editorRef.current?.commands.clearContent();
|
||||
},
|
||||
setEditorValue: (content: string) => {
|
||||
console.log(editorRef, forwardedRef, content)
|
||||
editorRef.current?.commands.setContent(content)
|
||||
}
|
||||
}))
|
||||
console.log(editorRef, forwardedRef, content);
|
||||
editorRef.current?.commands.setContent(content);
|
||||
},
|
||||
}));
|
||||
|
||||
const previousState = useRef<EditorState>();
|
||||
|
||||
const onNodeDeleted = useCallback(
|
||||
async (node: Node) => {
|
||||
if (node.type.name === 'image') {
|
||||
const assetUrlWithWorkspaceId = new URL(node.attrs.src).pathname.substring(1);
|
||||
const resStatus = await fileService.deleteImage(assetUrlWithWorkspaceId);
|
||||
if (resStatus === 204) {
|
||||
console.log("file deleted successfully");
|
||||
}
|
||||
const onNodeDeleted = useCallback(async (node: Node) => {
|
||||
if (node.type.name === "image") {
|
||||
const assetUrlWithWorkspaceId = new URL(node.attrs.src).pathname.substring(1);
|
||||
const resStatus = await fileService.deleteImage(assetUrlWithWorkspaceId);
|
||||
if (resStatus === 204) {
|
||||
console.log("file deleted successfully");
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
}
|
||||
}, []);
|
||||
|
||||
const checkForNodeDeletions = useCallback(
|
||||
(editor: CoreEditor) => {
|
||||
@ -107,7 +104,7 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
|
||||
}
|
||||
}
|
||||
},
|
||||
[onNodeDeleted],
|
||||
[onNodeDeleted]
|
||||
);
|
||||
|
||||
const debouncedUpdates = useDebouncedCallback(async ({ onChange, editor }) => {
|
||||
@ -119,19 +116,19 @@ const Tiptap = (props: ITiptapRichTextEditor) => {
|
||||
}, 1000);
|
||||
|
||||
const editorClassNames = `mt-2 p-3 relative focus:outline-none rounded-md focus:border-custom-border-200
|
||||
${noBorder ? '' : 'border border-custom-border-200'
|
||||
} ${borderOnFocus ? 'focus:border border-custom-border-200' : 'focus:border-0'
|
||||
} ${customClassName}`;
|
||||
${noBorder ? "" : "border border-custom-border-200"} ${
|
||||
borderOnFocus ? "focus:border border-custom-border-200" : "focus:border-0"
|
||||
} ${customClassName}`;
|
||||
|
||||
if (!editor) return null
|
||||
editorRef.current = editor
|
||||
if (!editor) return null;
|
||||
editorRef.current = editor;
|
||||
|
||||
return (
|
||||
<div
|
||||
onClick={() => {
|
||||
editor?.chain().focus().run();
|
||||
}}
|
||||
className={`tiptap-editor-container relative ${editorClassNames}`}
|
||||
className={`tiptap-editor-container cursor-text relative ${editorClassNames}`}
|
||||
>
|
||||
{editor && <EditorBubbleMenu editor={editor} />}
|
||||
<div className={`${editorContentCustomClassNames}`}>
|
||||
|
@ -1,11 +1,4 @@
|
||||
import React, {
|
||||
useState,
|
||||
useEffect,
|
||||
useCallback,
|
||||
ReactNode,
|
||||
useRef,
|
||||
useLayoutEffect,
|
||||
} from "react";
|
||||
import React, { useState, useEffect, useCallback, ReactNode, useRef, useLayoutEffect } from "react";
|
||||
import { Editor, Range, Extension } from "@tiptap/core";
|
||||
import Suggestion from "@tiptap/suggestion";
|
||||
import { ReactRenderer } from "@tiptap/react";
|
||||
@ -42,15 +35,7 @@ const Command = Extension.create({
|
||||
return {
|
||||
suggestion: {
|
||||
char: "/",
|
||||
command: ({
|
||||
editor,
|
||||
range,
|
||||
props,
|
||||
}: {
|
||||
editor: Editor;
|
||||
range: Range;
|
||||
props: any;
|
||||
}) => {
|
||||
command: ({ editor, range, props }: { editor: Editor; range: Range; props: any }) => {
|
||||
props.command({ editor, range });
|
||||
},
|
||||
},
|
||||
@ -74,12 +59,7 @@ const getSuggestionItems = ({ query }: { query: string }) =>
|
||||
searchTerms: ["p", "paragraph"],
|
||||
icon: <Text size={18} />,
|
||||
command: ({ editor, range }: CommandProps) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.toggleNode("paragraph", "paragraph")
|
||||
.run();
|
||||
editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").run();
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -88,12 +68,7 @@ const getSuggestionItems = ({ query }: { query: string }) =>
|
||||
searchTerms: ["title", "big", "large"],
|
||||
icon: <Heading1 size={18} />,
|
||||
command: ({ editor, range }: CommandProps) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.setNode("heading", { level: 1 })
|
||||
.run();
|
||||
editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run();
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -102,12 +77,7 @@ const getSuggestionItems = ({ query }: { query: string }) =>
|
||||
searchTerms: ["subtitle", "medium"],
|
||||
icon: <Heading2 size={18} />,
|
||||
command: ({ editor, range }: CommandProps) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.setNode("heading", { level: 2 })
|
||||
.run();
|
||||
editor.chain().focus().deleteRange(range).setNode("heading", { level: 2 }).run();
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -116,12 +86,7 @@ const getSuggestionItems = ({ query }: { query: string }) =>
|
||||
searchTerms: ["subtitle", "small"],
|
||||
icon: <Heading3 size={18} />,
|
||||
command: ({ editor, range }: CommandProps) => {
|
||||
editor
|
||||
.chain()
|
||||
.focus()
|
||||
.deleteRange(range)
|
||||
.setNode("heading", { level: 3 })
|
||||
.run();
|
||||
editor.chain().focus().deleteRange(range).setNode("heading", { level: 3 }).run();
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -148,7 +113,7 @@ const getSuggestionItems = ({ query }: { query: string }) =>
|
||||
searchTerms: ["line", "divider", "horizontal", "rule", "separate"],
|
||||
icon: <MinusSquare size={18} />,
|
||||
command: ({ editor, range }: CommandProps) => {
|
||||
editor.chain().focus().deleteRange(range).setHorizontalRule().run()
|
||||
editor.chain().focus().deleteRange(range).setHorizontalRule().run();
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -209,12 +174,11 @@ const getSuggestionItems = ({ query }: { query: string }) =>
|
||||
return (
|
||||
item.title.toLowerCase().includes(search) ||
|
||||
item.description.toLowerCase().includes(search) ||
|
||||
(item.searchTerms &&
|
||||
item.searchTerms.some((term: string) => term.includes(search)))
|
||||
(item.searchTerms && item.searchTerms.some((term: string) => term.includes(search)))
|
||||
);
|
||||
}
|
||||
return true;
|
||||
});;
|
||||
});
|
||||
|
||||
export const updateScrollView = (container: HTMLElement, item: HTMLElement) => {
|
||||
const containerHeight = container.offsetHeight;
|
||||
@ -250,7 +214,7 @@ const CommandList = ({
|
||||
command(item);
|
||||
}
|
||||
},
|
||||
[command, items],
|
||||
[command, items]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@ -297,12 +261,13 @@ const CommandList = ({
|
||||
<div
|
||||
id="slash-command"
|
||||
ref={commandListContainer}
|
||||
className="z-50 h-auto max-h-[330px] w-72 overflow-y-auto rounded-md border border-custom-border-200 bg-custom-background-100 px-1 py-2 shadow-md transition-all"
|
||||
className="z-20 h-auto max-h-[330px] w-72 overflow-y-auto rounded-md border border-custom-border-200 bg-custom-background-100 px-1 py-2 shadow-md transition-all"
|
||||
>
|
||||
{items.map((item: CommandItemProps, index: number) =>
|
||||
{items.map((item: CommandItemProps, index: number) => (
|
||||
<button
|
||||
className={`flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm text-custom-text-90 hover:text-custom-text-100 ${index === selectedIndex ? "bg-gray-800 text-custom-text-90" : ""
|
||||
}`}
|
||||
className={`flex w-full items-center space-x-2 rounded-md px-2 py-1 text-left text-sm text-custom-text-90 hover:text-custom-text-100 ${
|
||||
index === selectedIndex ? "bg-gray-800 text-custom-text-90" : ""
|
||||
}`}
|
||||
key={index}
|
||||
onClick={() => selectItem(index)}
|
||||
>
|
||||
@ -311,7 +276,7 @@ const CommandList = ({
|
||||
<p className="text-xs text-stone-500">{item.description}</p>
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
))}
|
||||
</div>
|
||||
) : null;
|
||||
};
|
||||
@ -320,6 +285,8 @@ const renderItems = () => {
|
||||
let component: ReactRenderer | null = null;
|
||||
let popup: any | null = null;
|
||||
|
||||
const container = document.querySelector("#tiptap-container") as HTMLElement;
|
||||
|
||||
return {
|
||||
onStart: (props: { editor: Editor; clientRect: DOMRect }) => {
|
||||
component = new ReactRenderer(CommandList, {
|
||||
@ -330,7 +297,7 @@ const renderItems = () => {
|
||||
// @ts-ignore
|
||||
popup = tippy("body", {
|
||||
getReferenceClientRect: props.clientRect,
|
||||
appendTo: () => document.body,
|
||||
appendTo: () => container,
|
||||
content: component.element,
|
||||
showOnCreate: true,
|
||||
interactive: true,
|
||||
|
@ -629,17 +629,19 @@ const SinglePage: NextPage = () => {
|
||||
ref={provided.innerRef}
|
||||
{...provided.droppableProps}
|
||||
>
|
||||
{pageBlocks.map((block, index) => (
|
||||
<SinglePageBlock
|
||||
key={block.id}
|
||||
block={block}
|
||||
projectDetails={projectDetails}
|
||||
showBlockDetails={showBlock}
|
||||
index={index}
|
||||
user={user}
|
||||
/>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
<>
|
||||
{pageBlocks.map((block, index) => (
|
||||
<SinglePageBlock
|
||||
key={block.id}
|
||||
block={block}
|
||||
projectDetails={projectDetails}
|
||||
showBlockDetails={showBlock}
|
||||
index={index}
|
||||
user={user}
|
||||
/>
|
||||
))}
|
||||
{provided.placeholder}
|
||||
</>
|
||||
</div>
|
||||
)}
|
||||
</StrictModeDroppable>
|
||||
|
@ -32,18 +32,18 @@
|
||||
|
||||
/* Custom TODO list checkboxes – shoutout to this awesome tutorial: https://moderncss.dev/pure-css-custom-checkbox-style/ */
|
||||
|
||||
ul[data-type="taskList"] li>label {
|
||||
ul[data-type="taskList"] li > label {
|
||||
margin-right: 0.2rem;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
ul[data-type="taskList"] li>label {
|
||||
ul[data-type="taskList"] li > label {
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
ul[data-type="taskList"] li>label input[type="checkbox"] {
|
||||
ul[data-type="taskList"] li > label input[type="checkbox"] {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background-color: rgb(var(--color-background-100));
|
||||
@ -81,7 +81,7 @@ ul[data-type="taskList"] li>label input[type="checkbox"] {
|
||||
}
|
||||
}
|
||||
|
||||
ul[data-type="taskList"] li[data-checked="true"]>div>p {
|
||||
ul[data-type="taskList"] li[data-checked="true"] > div > p {
|
||||
color: rgb(var(--color-text-200));
|
||||
text-decoration: line-through;
|
||||
text-decoration-thickness: 2px;
|
||||
|
Loading…
Reference in New Issue
Block a user