[WIKI-578] refactor: editor types structure #7536

This commit is contained in:
Aaryan Khandelwal 2025-08-04 18:01:51 +05:30 committed by GitHub
parent fa150c2b47
commit 7ead606798
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
29 changed files with 103 additions and 111 deletions

View file

@ -30,6 +30,7 @@ const DocumentEditor = (props: IDocumentEditorProps) => {
flaggedExtensions,
forwardedRef,
id,
isTouchDevice,
handleEditorReady,
mentionHandler,
onChange,
@ -96,6 +97,7 @@ const DocumentEditor = (props: IDocumentEditorProps) => {
editor={editor}
editorContainerClassName={cn(editorContainerClassName, "document-editor")}
id={id}
isTouchDevice={!!isTouchDevice}
/>
);
};

View file

@ -1,14 +1,14 @@
import { Editor, EditorContent } from "@tiptap/react";
import { type Editor, EditorContent } from "@tiptap/react";
import { FC, ReactNode } from "react";
interface EditorContentProps {
type Props = {
children?: ReactNode;
editor: Editor | null;
id: string;
tabIndex?: number;
}
};
export const EditorContentWrapper: FC<EditorContentProps> = (props) => {
export const EditorContentWrapper: FC<Props> = (props) => {
const { editor, children, tabIndex, id } = props;
return (

View file

@ -4,12 +4,12 @@ import { FC, useCallback, useEffect, useRef, useState } from "react";
// components
import { LinkView, LinkViewProps } from "@/components/links";
interface LinkViewContainerProps {
type Props = {
editor: Editor;
containerRef: React.RefObject<HTMLDivElement>;
}
};
export const LinkViewContainer: FC<LinkViewContainerProps> = ({ editor, containerRef }) => {
export const LinkViewContainer: FC<Props> = ({ editor, containerRef }) => {
const [linkViewProps, setLinkViewProps] = useState<LinkViewProps>();
const [isOpen, setIsOpen] = useState(false);
const [virtualElement, setVirtualElement] = useState<Element | null>(null);

View file

@ -6,13 +6,13 @@ import { LinkViewProps, LinkViews } from "@/components/links";
// helpers
import { isValidHttpUrl } from "@/helpers/common";
interface InputViewProps {
type InputViewProps = {
label: string;
value: string;
placeholder: string;
onChange: (value: string) => void;
autoFocus?: boolean;
}
};
const InputView = ({ label, value, placeholder, onChange, autoFocus }: InputViewProps) => (
<div className="flex flex-col gap-1">
@ -28,10 +28,10 @@ const InputView = ({ label, value, placeholder, onChange, autoFocus }: InputView
</div>
);
interface LinkEditViewProps {
type LinkEditViewProps = {
viewProps: LinkViewProps;
switchView: (view: LinkViews) => void;
}
};
export const LinkEditView = ({ viewProps }: LinkEditViewProps) => {
const { editor, from, to, url: initialUrl, text: initialText, closeLinkView } = viewProps;

View file

@ -5,7 +5,7 @@ import { LinkEditView, LinkPreview } from "@/components/links";
export type LinkViews = "LinkPreview" | "LinkEditView";
export interface LinkViewProps {
export type LinkViewProps = {
view?: LinkViews;
editor: Editor;
from: number;
@ -13,7 +13,7 @@ export interface LinkViewProps {
url: string;
text?: string;
closeLinkView: () => void;
}
};
export const LinkView = (props: LinkViewProps & { style: CSSProperties }) => {
const [currentView, setCurrentView] = useState<LinkViews>(props.view ?? "LinkPreview");

View file

@ -1,15 +1,15 @@
import { Editor } from "@tiptap/react";
import type { Editor } from "@tiptap/react";
import { Copy, LucideIcon, Trash2 } from "lucide-react";
import { useCallback, useEffect, useRef } from "react";
import tippy, { Instance } from "tippy.js";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
interface BlockMenuProps {
type Props = {
editor: Editor;
}
};
export const BlockMenu = (props: BlockMenuProps) => {
export const BlockMenu = (props: Props) => {
const { editor } = props;
const menuRef = useRef<HTMLDivElement>(null);
const popup = useRef<Instance | null>(null);

View file

@ -29,7 +29,7 @@ import { TextAlignmentSelector } from "./alignment-selector";
type EditorBubbleMenuProps = Omit<BubbleMenuProps, "children">;
export interface EditorStateType {
export type EditorStateType = {
code: boolean;
bold: boolean;
italic: boolean;
@ -47,7 +47,7 @@ export interface EditorStateType {
backgroundColor: string;
}
| undefined;
}
};
export const EditorBubbleMenu: FC<EditorBubbleMenuProps> = (props: { editor: Editor }) => {
const menuRef = useRef<HTMLDivElement>(null);

View file

@ -2,9 +2,9 @@ import { Mark, markInputRule, markPasteRule, mergeAttributes } from "@tiptap/cor
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
export interface CodeOptions {
HTMLAttributes: Record<string, any>;
}
type InlineCodeOptions = {
HTMLAttributes: Record<string, unknown>;
};
declare module "@tiptap/core" {
interface Commands<ReturnType> {
@ -28,7 +28,7 @@ declare module "@tiptap/core" {
export const inputRegex = /(?:^|\s)((?:`)((?:[^`]+))(?:`))$/;
const pasteRegex = /(?:^|\s)((?:`)((?:[^`]+))(?:`))/g;
export const CustomCodeInlineExtension = Mark.create<CodeOptions>({
export const CustomCodeInlineExtension = Mark.create<InlineCodeOptions>({
name: CORE_EXTENSIONS.CODE_INLINE,
addOptions() {

View file

@ -3,10 +3,10 @@
import { CodeBlockOptions, CodeBlock } from "./code-block";
import { LowlightPlugin } from "./lowlight-plugin";
export interface CodeBlockLowlightOptions extends CodeBlockOptions {
type CodeBlockLowlightOptions = CodeBlockOptions & {
lowlight: any;
defaultLanguage: string | null | undefined;
}
};
export const CodeBlockLowlight = CodeBlock.extend<CodeBlockLowlightOptions>({
addOptions() {

View file

@ -1,6 +1,6 @@
"use client";
import { Node as ProseMirrorNode } from "@tiptap/pm/model";
import type { Node as ProseMirrorNode } from "@tiptap/pm/model";
import { NodeViewWrapper, NodeViewContent } from "@tiptap/react";
import ts from "highlight.js/lib/languages/typescript";
import { common, createLowlight } from "lowlight";
@ -15,11 +15,11 @@ import { cn } from "@plane/utils";
const lowlight = createLowlight(common);
lowlight.register("ts", ts);
interface CodeBlockComponentProps {
type Props = {
node: ProseMirrorNode;
}
};
export const CodeBlockComponent: React.FC<CodeBlockComponentProps> = ({ node }) => {
export const CodeBlockComponent: React.FC<Props> = ({ node }) => {
const [copied, setCopied] = useState(false);
const copyToClipboard = async (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {

View file

@ -3,7 +3,7 @@ import { Plugin, PluginKey } from "@tiptap/pm/state";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
export interface CodeBlockOptions {
export type CodeBlockOptions = {
/**
* Adds a prefix to language classes that are applied to code tags.
* Defaults to `'language-'`.
@ -22,8 +22,8 @@ export interface CodeBlockOptions {
/**
* Custom HTML attributes that should be added to the rendered HTML tag.
*/
HTMLAttributes: Record<string, any>;
}
HTMLAttributes: Record<string, unknown>;
};
declare module "@tiptap/core" {
interface Commands<ReturnType> {

View file

@ -10,12 +10,12 @@ import { autolink } from "./helpers/autolink";
import { clickHandler } from "./helpers/clickHandler";
import { pasteHandler } from "./helpers/pasteHandler";
export interface LinkProtocolOptions {
type LinkProtocolOptions = {
scheme: string;
optionalSlashes?: boolean;
}
};
export interface LinkOptions {
type LinkOptions = {
/**
* If enabled, it adds links as you type.
*/
@ -40,14 +40,14 @@ export interface LinkOptions {
/**
* A list of HTML attributes to be rendered.
*/
HTMLAttributes: Record<string, any>;
HTMLAttributes: Record<string, unknown>;
/**
* A validation function that modifies link verification for the auto linker.
* @param url - The url to be validated.
* @returns - True if the url is valid, false otherwise.
*/
validate?: (url: string) => boolean;
}
};
declare module "@tiptap/core" {
interface Commands<ReturnType> {

View file

@ -1,28 +1,17 @@
import { computePosition, flip, shift } from "@floating-ui/dom";
import { Editor, posToDOMRect } from "@tiptap/react";
import { type Editor, posToDOMRect } from "@tiptap/react";
import { SuggestionKeyDownProps } from "@tiptap/suggestion";
import { forwardRef, useCallback, useEffect, useImperativeHandle, useRef, useState } from "react";
// plane imports
import { cn } from "@plane/utils";
export interface EmojiItem {
export type EmojiItem = {
name: string;
emoji: string;
shortcodes: string[];
tags: string[];
fallbackImage?: string;
}
export interface EmojiListProps {
items: EmojiItem[];
command: (item: { name: string }) => void;
editor: Editor;
query: string;
}
export interface EmojiListRef {
onKeyDown: (props: SuggestionKeyDownProps) => boolean;
}
};
const updatePosition = (editor: Editor, element: HTMLElement) => {
const virtualElement = {
@ -43,7 +32,18 @@ const updatePosition = (editor: Editor, element: HTMLElement) => {
});
};
export const EmojiList = forwardRef<EmojiListRef, EmojiListProps>((props, ref) => {
export type EmojiListRef = {
onKeyDown: (props: SuggestionKeyDownProps) => boolean;
};
type Props = {
items: EmojiItem[];
command: (item: { name: string }) => void;
editor: Editor;
query: string;
};
export const EmojiList = forwardRef<EmojiListRef, Props>((props, ref) => {
const { items, command, editor, query } = props;
const [selectedIndex, setSelectedIndex] = useState<number>(0);
const [isVisible, setIsVisible] = useState(false);

View file

@ -65,11 +65,11 @@ export type EmojiItem = {
/**
* Store some custom data
*/
[key: string]: any;
[key: string]: unknown;
};
export type EmojiOptions = {
HTMLAttributes: Record<string, any>;
HTMLAttributes: Record<string, unknown>;
emojis: EmojiItem[];
enableEmoticons: boolean;
forceFallbackImages: boolean;

View file

@ -2,13 +2,8 @@ import { Extension } from "@tiptap/core";
import { Plugin, PluginKey } from "@tiptap/pm/state";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
export interface IMarking {
type: "heading";
level: number;
text: string;
sequence: number;
}
// types
import type { IMarking } from "@/types";
export type HeadingExtensionStorage = {
headings: IMarking[];

View file

@ -3,9 +3,9 @@ import { NodeSelection, TextSelection } from "@tiptap/pm/state";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
export interface HorizontalRuleOptions {
HTMLAttributes: Record<string, any>;
}
type HorizontalRuleOptions = {
HTMLAttributes: Record<string, unknown>;
};
declare module "@tiptap/core" {
interface Commands<ReturnType> {

View file

@ -9,9 +9,9 @@ import { TableCellSelectionOutlinePlugin } from "./plugins/selection-outline/plu
import { DEFAULT_COLUMN_WIDTH } from "./table";
import { isCellSelection } from "./table/utilities/helpers";
export interface TableCellOptions {
HTMLAttributes: Record<string, any>;
}
type TableCellOptions = {
HTMLAttributes: Record<string, unknown>;
};
export const TableCell = Node.create<TableCellOptions>({
name: CORE_EXTENSIONS.TABLE_CELL,

View file

@ -4,9 +4,9 @@ import { CORE_EXTENSIONS } from "@/constants/extension";
// local imports
import { DEFAULT_COLUMN_WIDTH } from "./table";
export interface TableHeaderOptions {
HTMLAttributes: Record<string, any>;
}
type TableHeaderOptions = {
HTMLAttributes: Record<string, unknown>;
};
export const TableHeader = Node.create<TableHeaderOptions>({
name: CORE_EXTENSIONS.TABLE_HEADER,

View file

@ -2,9 +2,9 @@ import { mergeAttributes, Node } from "@tiptap/core";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
export interface TableRowOptions {
HTMLAttributes: Record<string, any>;
}
type TableRowOptions = {
HTMLAttributes: Record<string, unknown>;
};
export const TableRow = Node.create<TableRowOptions>({
name: CORE_EXTENSIONS.TABLE_ROW,

View file

@ -32,14 +32,14 @@ import { insertLineAboveTableAction } from "./utilities/insert-line-above-table-
import { insertLineBelowTableAction } from "./utilities/insert-line-below-table-action";
import { DEFAULT_COLUMN_WIDTH } from ".";
export interface TableOptions {
HTMLAttributes: Record<string, any>;
type TableOptions = {
HTMLAttributes: Record<string, unknown>;
resizable: boolean;
handleWidth: number;
cellMinWidth: number;
lastColumnResizable: boolean;
allowTableNodeSelection: boolean;
}
};
declare module "@tiptap/core" {
interface Commands<ReturnType> {

View file

@ -3,7 +3,7 @@ import { Fragment, Node as ProsemirrorNode, NodeType } from "@tiptap/pm/model";
export function createCell(
cellType: NodeType,
cellContent?: Fragment | ProsemirrorNode | Array<ProsemirrorNode>,
attrs?: Record<string, any>
attrs?: Record<string, unknown>
): ProsemirrorNode | null | undefined {
if (cellContent) {
return cellType.createChecked(attrs, cellContent);

View file

@ -1,4 +1,4 @@
import { Editor, findParentNodeClosestToPos, KeyboardShortcutCommand } from "@tiptap/core";
import { type Editor, findParentNodeClosestToPos, type KeyboardShortcutCommand } from "@tiptap/core";
import type { Node as ProseMirrorNode } from "@tiptap/pm/model";
import { CellSelection, TableMap } from "@tiptap/pm/tables";
// constants
@ -6,18 +6,18 @@ import { CORE_EXTENSIONS } from "@/constants/extension";
// extensions
import { isCellEmpty, isCellSelection } from "@/extensions/table/table/utilities/helpers";
interface CellCoord {
type CellCoord = {
row: number;
col: number;
}
};
interface TableInfo {
type TableInfo = {
node: ProseMirrorNode;
pos: number;
map: TableMap;
totalColumns: number;
totalRows: number;
}
};
export const handleDeleteKeyOnTable: KeyboardShortcutCommand = (props) => {
const { editor } = props;

View file

@ -1,6 +1,8 @@
import type { Node as ProseMirrorNode } from "@tiptap/pm/model";
import type { Selection } from "@tiptap/pm/state";
import { CellSelection } from "@tiptap/pm/tables";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
/**
* @description Check if the selection is a cell selection
@ -22,7 +24,7 @@ export const isCellEmpty = (cell: ProseMirrorNode | null): boolean => {
// Check if cell has any non-empty content
let hasContent = false;
cell.content.forEach((node) => {
if (node.type.name === "paragraph") {
if (node.type.name === CORE_EXTENSIONS.PARAGRAPH) {
if (node.content.size > 0) {
hasContent = true;
}

View file

@ -1,6 +1,6 @@
import { textInputRule } from "@tiptap/core";
export interface TypographyOptions {
export type TypographyOptions = {
emDash: false | string;
ellipsis: false | string;
leftArrow: false | string;
@ -20,7 +20,7 @@ export interface TypographyOptions {
oneQuarter: false | string;
threeQuarters: false | string;
impliesArrowRight: false | string;
}
};
export const emDash = (override?: string) =>
textInputRule({

View file

@ -30,13 +30,13 @@ declare module "@tiptap/core" {
}
}
export interface UtilityExtensionStorage {
export type UtilityExtensionStorage = {
assetsList: TEditorAsset[];
assetsUploadStatus: TFileHandler["assetsUploadStatus"];
uploadInProgress: boolean;
activeDropbarExtensions: TActiveDropbarExtensions[];
isTouchDevice: boolean;
}
};
type Props = Pick<IEditorProps, "disabledExtensions"> & {
fileHandler: TFileHandler;

View file

@ -1,4 +1,4 @@
import { ReactNodeViewRenderer, NodeViewWrapper } from "@tiptap/react";
import { ReactNodeViewRenderer, NodeViewWrapper, type NodeViewProps } from "@tiptap/react";
// local imports
import { WorkItemEmbedExtensionConfig } from "./extension-config";
@ -17,7 +17,7 @@ type Props = {
export const WorkItemEmbedExtension = (props: Props) =>
WorkItemEmbedExtensionConfig.extend({
addNodeView() {
return ReactNodeViewRenderer((issueProps: any) => (
return ReactNodeViewRenderer((issueProps: NodeViewProps) => (
<NodeViewWrapper>
{props.widgetCallback({
issueId: issueProps.node.attrs.entity_identifier,

View file

@ -5,13 +5,13 @@ import { cn } from "@plane/utils";
// constants
import { CORE_EXTENSIONS } from "@/constants/extension";
interface EditorClassNames {
type EditorClassNameArgs = {
noBorder?: boolean;
borderOnFocus?: boolean;
containerClassName?: string;
}
};
export const getEditorClassNames = ({ noBorder, borderOnFocus, containerClassName }: EditorClassNames) =>
export const getEditorClassNames = ({ noBorder, borderOnFocus, containerClassName }: EditorClassNameArgs) =>
cn(
"w-full max-w-full sm:rounded-lg focus:outline-none focus:border-0",
{

View file

@ -1,11 +1,6 @@
import { Editor } from "@tiptap/react";
export interface IMarking {
type: "heading";
level: number;
text: string;
sequence: number;
}
import type { Editor } from "@tiptap/react";
// types
import type { IMarking } from "@/types";
function findNthH1(editor: Editor, n: number, level: number): number {
let count = 0;

View file

@ -4,10 +4,9 @@ import type { Selection } from "@tiptap/pm/state";
import type { EditorProps, EditorView } from "@tiptap/pm/view";
// extension types
import type { TTextAlign } from "@/extensions";
// helpers
import type { IMarking } from "@/helpers/scroll-to-node";
// types
import type {
IMarking,
TAIHandler,
TDisplayConfig,
TDocumentEventEmitter,
@ -129,7 +128,7 @@ export type EditorRefApi = {
};
// editor props
export interface IEditorProps {
export type IEditorProps = {
autofocus?: boolean;
bubbleMenuEnabled?: boolean;
containerClassName?: string;
@ -155,7 +154,7 @@ export interface IEditorProps {
placeholder?: string | ((isFocused: boolean, value: string) => string);
tabIndex?: number;
value?: string | null;
}
};
export type ILiteTextEditorProps = IEditorProps;
@ -163,8 +162,7 @@ export type IRichTextEditorProps = IEditorProps & {
dragDropEnabled?: boolean;
};
export interface ICollaborativeDocumentEditorProps
extends Omit<IEditorProps, "initialValue" | "onEnterKeyPress" | "value"> {
export type ICollaborativeDocumentEditorProps = Omit<IEditorProps, "initialValue" | "onEnterKeyPress" | "value"> & {
aiHandler?: TAIHandler;
documentLoaderClassName?: string;
dragDropEnabled?: boolean;
@ -173,16 +171,16 @@ export interface ICollaborativeDocumentEditorProps
realtimeConfig: TRealtimeConfig;
serverHandler?: TServerHandler;
user: TUserDetails;
}
};
export interface IDocumentEditorProps extends Omit<IEditorProps, "initialValue" | "onEnterKeyPress" | "value"> {
export type IDocumentEditorProps = Omit<IEditorProps, "initialValue" | "onEnterKeyPress" | "value"> & {
aiHandler?: TAIHandler;
embedHandler: TEmbedConfig;
user?: TUserDetails;
value: Content;
}
};
export interface EditorEvents {
export type EditorEvents = {
beforeCreate: never;
create: never;
update: never;
@ -192,4 +190,4 @@ export interface EditorEvents {
blur: never;
destroy: never;
ready: { height: number };
}
};