mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
[WEB-638] feat: add tabIndex prop support to navigate using Tab key in all the editors (#3902)
* feat: added tab index support to navigate using Tab key in all the editors * chore: changed the name of Table of Contents in Pages * chore: file formatting --------- Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
parent
899771a678
commit
4b30339a59
@ -1,17 +1,28 @@
|
|||||||
import { Editor, EditorContent } from "@tiptap/react";
|
import { Editor, EditorContent } from "@tiptap/react";
|
||||||
import { ReactNode } from "react";
|
import { FC, ReactNode } from "react";
|
||||||
import { ImageResizer } from "src/ui/extensions/image/image-resize";
|
import { ImageResizer } from "src/ui/extensions/image/image-resize";
|
||||||
|
|
||||||
interface EditorContentProps {
|
interface EditorContentProps {
|
||||||
editor: Editor | null;
|
editor: Editor | null;
|
||||||
editorContentCustomClassNames: string | undefined;
|
editorContentCustomClassNames: string | undefined;
|
||||||
children?: ReactNode;
|
children?: ReactNode;
|
||||||
|
tabIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const EditorContentWrapper = ({ editor, editorContentCustomClassNames = "", children }: EditorContentProps) => (
|
export const EditorContentWrapper: FC<EditorContentProps> = (props) => {
|
||||||
<div className={`contentEditor ${editorContentCustomClassNames}`}>
|
const { editor, editorContentCustomClassNames = "", tabIndex, children } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`contentEditor ${editorContentCustomClassNames}`}
|
||||||
|
tabIndex={tabIndex}
|
||||||
|
onFocus={() => {
|
||||||
|
editor?.chain().focus(undefined, { scrollIntoView: false }).run();
|
||||||
|
}}
|
||||||
|
>
|
||||||
<EditorContent editor={editor} />
|
<EditorContent editor={editor} />
|
||||||
{editor?.isActive("image") && editor?.isEditable && <ImageResizer editor={editor} />}
|
{editor?.isActive("image") && editor?.isEditable && <ImageResizer editor={editor} />}
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
};
|
||||||
|
@ -19,7 +19,7 @@ export const ContentBrowser = (props: ContentBrowserProps) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col overflow-hidden">
|
<div className="flex h-full flex-col overflow-hidden">
|
||||||
<h2 className="font-medium">Table of Contents</h2>
|
<h2 className="font-medium">Outline</h2>
|
||||||
<div className="h-full overflow-y-auto">
|
<div className="h-full overflow-y-auto">
|
||||||
{markings.length !== 0 ? (
|
{markings.length !== 0 ? (
|
||||||
markings.map((marking) =>
|
markings.map((marking) =>
|
||||||
|
@ -29,11 +29,13 @@ type IPageRenderer = {
|
|||||||
editorContentCustomClassNames?: string;
|
editorContentCustomClassNames?: string;
|
||||||
hideDragHandle?: () => void;
|
hideDragHandle?: () => void;
|
||||||
readonly: boolean;
|
readonly: boolean;
|
||||||
|
tabIndex?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PageRenderer = (props: IPageRenderer) => {
|
export const PageRenderer = (props: IPageRenderer) => {
|
||||||
const {
|
const {
|
||||||
documentDetails,
|
documentDetails,
|
||||||
|
tabIndex,
|
||||||
editor,
|
editor,
|
||||||
editorClassNames,
|
editorClassNames,
|
||||||
editorContentCustomClassNames,
|
editorContentCustomClassNames,
|
||||||
@ -169,7 +171,11 @@ export const PageRenderer = (props: IPageRenderer) => {
|
|||||||
)}
|
)}
|
||||||
<div className="flex relative h-full w-full flex-col pr-5 editor-renderer" onMouseOver={handleLinkHover}>
|
<div className="flex relative h-full w-full flex-col pr-5 editor-renderer" onMouseOver={handleLinkHover}>
|
||||||
<EditorContainer hideDragHandle={hideDragHandle} editor={editor} editorClassNames={editorClassNames}>
|
<EditorContainer hideDragHandle={hideDragHandle} editor={editor} editorClassNames={editorClassNames}>
|
||||||
<EditorContentWrapper editor={editor} editorContentCustomClassNames={editorContentCustomClassNames} />
|
<EditorContentWrapper
|
||||||
|
tabIndex={tabIndex}
|
||||||
|
editor={editor}
|
||||||
|
editorContentCustomClassNames={editorContentCustomClassNames}
|
||||||
|
/>
|
||||||
</EditorContainer>
|
</EditorContainer>
|
||||||
</div>
|
</div>
|
||||||
{isOpen && linkViewProps && coordinates && (
|
{isOpen && linkViewProps && coordinates && (
|
||||||
|
@ -47,6 +47,8 @@ interface IDocumentEditor {
|
|||||||
duplicationConfig?: IDuplicationConfig;
|
duplicationConfig?: IDuplicationConfig;
|
||||||
pageLockConfig?: IPageLockConfig;
|
pageLockConfig?: IPageLockConfig;
|
||||||
pageArchiveConfig?: IPageArchiveConfig;
|
pageArchiveConfig?: IPageArchiveConfig;
|
||||||
|
|
||||||
|
tabIndex?: number;
|
||||||
}
|
}
|
||||||
interface DocumentEditorProps extends IDocumentEditor {
|
interface DocumentEditorProps extends IDocumentEditor {
|
||||||
forwardedRef?: React.Ref<EditorHandle>;
|
forwardedRef?: React.Ref<EditorHandle>;
|
||||||
@ -79,6 +81,7 @@ const DocumentEditor = ({
|
|||||||
cancelUploadImage,
|
cancelUploadImage,
|
||||||
onActionCompleteHandler,
|
onActionCompleteHandler,
|
||||||
rerenderOnPropsChange,
|
rerenderOnPropsChange,
|
||||||
|
tabIndex,
|
||||||
}: IDocumentEditor) => {
|
}: IDocumentEditor) => {
|
||||||
const { markings, updateMarkings } = useEditorMarkings();
|
const { markings, updateMarkings } = useEditorMarkings();
|
||||||
const [sidePeekVisible, setSidePeekVisible] = useState(true);
|
const [sidePeekVisible, setSidePeekVisible] = useState(true);
|
||||||
@ -160,6 +163,7 @@ const DocumentEditor = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="h-full w-full md:w-[calc(100%-14rem)] lg:w-[calc(100%-18rem-18rem)] page-renderer">
|
<div className="h-full w-full md:w-[calc(100%-14rem)] lg:w-[calc(100%-18rem-18rem)] page-renderer">
|
||||||
<PageRenderer
|
<PageRenderer
|
||||||
|
tabIndex={tabIndex}
|
||||||
onActionCompleteHandler={onActionCompleteHandler}
|
onActionCompleteHandler={onActionCompleteHandler}
|
||||||
hideDragHandle={hideDragHandleOnMouseLeave}
|
hideDragHandle={hideDragHandleOnMouseLeave}
|
||||||
readonly={false}
|
readonly={false}
|
||||||
|
@ -28,6 +28,7 @@ interface IDocumentReadOnlyEditor {
|
|||||||
message: string;
|
message: string;
|
||||||
type: "success" | "error" | "warning" | "info";
|
type: "success" | "error" | "warning" | "info";
|
||||||
}) => void;
|
}) => void;
|
||||||
|
tabIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DocumentReadOnlyEditorProps extends IDocumentReadOnlyEditor {
|
interface DocumentReadOnlyEditorProps extends IDocumentReadOnlyEditor {
|
||||||
@ -51,6 +52,7 @@ const DocumentReadOnlyEditor = ({
|
|||||||
pageArchiveConfig,
|
pageArchiveConfig,
|
||||||
rerenderOnPropsChange,
|
rerenderOnPropsChange,
|
||||||
onActionCompleteHandler,
|
onActionCompleteHandler,
|
||||||
|
tabIndex,
|
||||||
}: DocumentReadOnlyEditorProps) => {
|
}: DocumentReadOnlyEditorProps) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [sidePeekVisible, setSidePeekVisible] = useState(true);
|
const [sidePeekVisible, setSidePeekVisible] = useState(true);
|
||||||
@ -108,6 +110,7 @@ const DocumentReadOnlyEditor = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className="h-full w-[calc(100%-14rem)] lg:w-[calc(100%-18rem-18rem)] page-renderer">
|
<div className="h-full w-[calc(100%-14rem)] lg:w-[calc(100%-18rem-18rem)] page-renderer">
|
||||||
<PageRenderer
|
<PageRenderer
|
||||||
|
tabIndex={tabIndex}
|
||||||
onActionCompleteHandler={onActionCompleteHandler}
|
onActionCompleteHandler={onActionCompleteHandler}
|
||||||
updatePageTitle={() => Promise.resolve()}
|
updatePageTitle={() => Promise.resolve()}
|
||||||
readonly={true}
|
readonly={true}
|
||||||
|
@ -42,6 +42,7 @@ interface ILiteTextEditor {
|
|||||||
mentionHighlights?: string[];
|
mentionHighlights?: string[];
|
||||||
mentionSuggestions?: IMentionSuggestion[];
|
mentionSuggestions?: IMentionSuggestion[];
|
||||||
submitButton?: React.ReactNode;
|
submitButton?: React.ReactNode;
|
||||||
|
tabIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface LiteTextEditorProps extends ILiteTextEditor {
|
interface LiteTextEditorProps extends ILiteTextEditor {
|
||||||
@ -74,6 +75,7 @@ const LiteTextEditor = (props: LiteTextEditorProps) => {
|
|||||||
mentionHighlights,
|
mentionHighlights,
|
||||||
mentionSuggestions,
|
mentionSuggestions,
|
||||||
submitButton,
|
submitButton,
|
||||||
|
tabIndex,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const editor = useEditor({
|
const editor = useEditor({
|
||||||
@ -103,7 +105,11 @@ const LiteTextEditor = (props: LiteTextEditorProps) => {
|
|||||||
return (
|
return (
|
||||||
<EditorContainer editor={editor} editorClassNames={editorClassNames}>
|
<EditorContainer editor={editor} editorClassNames={editorClassNames}>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<EditorContentWrapper editor={editor} editorContentCustomClassNames={editorContentCustomClassNames} />
|
<EditorContentWrapper
|
||||||
|
tabIndex={tabIndex}
|
||||||
|
editor={editor}
|
||||||
|
editorContentCustomClassNames={editorContentCustomClassNames}
|
||||||
|
/>
|
||||||
<div className="mt-4 w-full">
|
<div className="mt-4 w-full">
|
||||||
<FixedMenu
|
<FixedMenu
|
||||||
editor={editor}
|
editor={editor}
|
||||||
|
@ -8,6 +8,7 @@ interface ICoreReadOnlyEditor {
|
|||||||
borderOnFocus?: boolean;
|
borderOnFocus?: boolean;
|
||||||
customClassName?: string;
|
customClassName?: string;
|
||||||
mentionHighlights: string[];
|
mentionHighlights: string[];
|
||||||
|
tabIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EditorCoreProps extends ICoreReadOnlyEditor {
|
interface EditorCoreProps extends ICoreReadOnlyEditor {
|
||||||
@ -27,6 +28,7 @@ const LiteReadOnlyEditor = ({
|
|||||||
value,
|
value,
|
||||||
forwardedRef,
|
forwardedRef,
|
||||||
mentionHighlights,
|
mentionHighlights,
|
||||||
|
tabIndex,
|
||||||
}: EditorCoreProps) => {
|
}: EditorCoreProps) => {
|
||||||
const editor = useReadOnlyEditor({
|
const editor = useReadOnlyEditor({
|
||||||
value,
|
value,
|
||||||
@ -45,7 +47,11 @@ const LiteReadOnlyEditor = ({
|
|||||||
return (
|
return (
|
||||||
<EditorContainer editor={editor} editorClassNames={editorClassNames}>
|
<EditorContainer editor={editor} editorClassNames={editorClassNames}>
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<EditorContentWrapper editor={editor} editorContentCustomClassNames={editorContentCustomClassNames} />
|
<EditorContentWrapper
|
||||||
|
tabIndex={tabIndex}
|
||||||
|
editor={editor}
|
||||||
|
editorContentCustomClassNames={editorContentCustomClassNames}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</EditorContainer>
|
</EditorContainer>
|
||||||
);
|
);
|
||||||
|
@ -36,6 +36,7 @@ export type IRichTextEditor = {
|
|||||||
debouncedUpdatesEnabled?: boolean;
|
debouncedUpdatesEnabled?: boolean;
|
||||||
mentionHighlights?: string[];
|
mentionHighlights?: string[];
|
||||||
mentionSuggestions?: IMentionSuggestion[];
|
mentionSuggestions?: IMentionSuggestion[];
|
||||||
|
tabIndex?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export interface RichTextEditorProps extends IRichTextEditor {
|
export interface RichTextEditorProps extends IRichTextEditor {
|
||||||
@ -68,6 +69,7 @@ const RichTextEditor = ({
|
|||||||
mentionHighlights,
|
mentionHighlights,
|
||||||
rerenderOnPropsChange,
|
rerenderOnPropsChange,
|
||||||
mentionSuggestions,
|
mentionSuggestions,
|
||||||
|
tabIndex,
|
||||||
}: RichTextEditorProps) => {
|
}: RichTextEditorProps) => {
|
||||||
const [hideDragHandleOnMouseLeave, setHideDragHandleOnMouseLeave] = React.useState<() => void>(() => {});
|
const [hideDragHandleOnMouseLeave, setHideDragHandleOnMouseLeave] = React.useState<() => void>(() => {});
|
||||||
|
|
||||||
@ -110,7 +112,11 @@ const RichTextEditor = ({
|
|||||||
<EditorContainer hideDragHandle={hideDragHandleOnMouseLeave} editor={editor} editorClassNames={editorClassNames}>
|
<EditorContainer hideDragHandle={hideDragHandleOnMouseLeave} editor={editor} editorClassNames={editorClassNames}>
|
||||||
{editor && <EditorBubbleMenu editor={editor} />}
|
{editor && <EditorBubbleMenu editor={editor} />}
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<EditorContentWrapper editor={editor} editorContentCustomClassNames={editorContentCustomClassNames} />
|
<EditorContentWrapper
|
||||||
|
tabIndex={tabIndex}
|
||||||
|
editor={editor}
|
||||||
|
editorContentCustomClassNames={editorContentCustomClassNames}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</EditorContainer>
|
</EditorContainer>
|
||||||
);
|
);
|
||||||
|
@ -9,6 +9,7 @@ interface IRichTextReadOnlyEditor {
|
|||||||
borderOnFocus?: boolean;
|
borderOnFocus?: boolean;
|
||||||
customClassName?: string;
|
customClassName?: string;
|
||||||
mentionHighlights?: string[];
|
mentionHighlights?: string[];
|
||||||
|
tabIndex?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface RichTextReadOnlyEditorProps extends IRichTextReadOnlyEditor {
|
interface RichTextReadOnlyEditorProps extends IRichTextReadOnlyEditor {
|
||||||
|
@ -467,7 +467,7 @@ export const IssueFormRoot: FC<IssueFormProps> = observer((props) => {
|
|||||||
}}
|
}}
|
||||||
mentionHighlights={mentionHighlights}
|
mentionHighlights={mentionHighlights}
|
||||||
mentionSuggestions={mentionSuggestions}
|
mentionSuggestions={mentionSuggestions}
|
||||||
// tabIndex={2}
|
tabIndex={getTabIndex("description_html")}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
Loading…
Reference in New Issue
Block a user