From 944052753f4ba71b7cf058fef7eb5046c5986f5d Mon Sep 17 00:00:00 2001 From: wux_labs Date: Wed, 1 Jan 2025 20:23:52 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=A1=A8=E6=A0=BC=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/index.ts | 18 ++ src/components/menus/toolbar/Classic.ts | 86 ++++++ src/components/menus/toolbar/Ribbon.ts | 94 +++++++ .../menus/toolbar/table/AddColumnAfter.ts | 73 +++++ .../menus/toolbar/table/AddColumnBefore.ts | 73 +++++ .../menus/toolbar/table/AddRowAfter.ts | 73 +++++ .../menus/toolbar/table/AddRowBefore.ts | 73 +++++ .../menus/toolbar/table/DeleteColumn.ts | 73 +++++ .../menus/toolbar/table/DeleteRow.ts | 73 +++++ .../menus/toolbar/table/DeleteTable.ts | 73 +++++ .../menus/toolbar/table/InsertTable.ts | 264 ++++++++++++++++++ src/core/UAIExtensions.ts | 12 + 12 files changed, 985 insertions(+) create mode 100644 src/components/menus/toolbar/table/AddColumnAfter.ts create mode 100644 src/components/menus/toolbar/table/AddColumnBefore.ts create mode 100644 src/components/menus/toolbar/table/AddRowAfter.ts create mode 100644 src/components/menus/toolbar/table/AddRowBefore.ts create mode 100644 src/components/menus/toolbar/table/DeleteColumn.ts create mode 100644 src/components/menus/toolbar/table/DeleteRow.ts create mode 100644 src/components/menus/toolbar/table/DeleteTable.ts create mode 100644 src/components/menus/toolbar/table/InsertTable.ts diff --git a/src/components/index.ts b/src/components/index.ts index cdbde3e..d7e609f 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -63,6 +63,15 @@ import { Symbol } from "./menus/toolbar/insert/Symbol.ts"; import { Math } from "./menus/toolbar/insert/Math.ts"; import { Toc } from "./menus/toolbar/insert/Toc.ts"; +import { InsertTable } from "./menus/toolbar/table/InsertTable.ts"; +import { AddColumnAfter } from "./menus/toolbar/table/AddColumnAfter.ts"; +import { AddColumnBefore } from "./menus/toolbar/table/AddColumnBefore.ts"; +import { AddRowAfter } from "./menus/toolbar/table/AddRowAfter.ts"; +import { AddRowBefore } from "./menus/toolbar/table/AddRowBefore.ts"; +import { DeleteColumn } from "./menus/toolbar/table/DeleteColumn.ts"; +import { DeleteRow } from "./menus/toolbar/table/DeleteRow.ts"; +import { DeleteTable } from "./menus/toolbar/table/DeleteTable.ts"; + import { ToggleToc } from "./menus/toolbar/page/ToggleToc.ts"; // 注册组件 @@ -128,4 +137,13 @@ defineCustomElement('uai-editor-insert-menu-symbol', Symbol); defineCustomElement('uai-editor-insert-menu-math', Math); defineCustomElement('uai-editor-insert-menu-toc', Toc); +defineCustomElement('uai-editor-table-menu-insert-table', InsertTable); +defineCustomElement('uai-editor-table-menu-add-column-after', AddColumnAfter); +defineCustomElement('uai-editor-table-menu-add-column-before', AddColumnBefore); +defineCustomElement('uai-editor-table-menu-delete-column', DeleteColumn); +defineCustomElement('uai-editor-table-menu-add-row-after', AddRowAfter); +defineCustomElement('uai-editor-table-menu-add-row-before', AddRowBefore); +defineCustomElement('uai-editor-table-menu-delete-row', DeleteRow); +defineCustomElement('uai-editor-table-menu-delete-table', DeleteTable); + defineCustomElement('uai-editor-page-menu-toggle-toc', ToggleToc); diff --git a/src/components/menus/toolbar/Classic.ts b/src/components/menus/toolbar/Classic.ts index fc0f98e..7ee409c 100644 --- a/src/components/menus/toolbar/Classic.ts +++ b/src/components/menus/toolbar/Classic.ts @@ -56,6 +56,15 @@ import { Symbol } from "./insert/Symbol.ts"; import { Math } from "./insert/Math.ts"; import { Toc } from "./insert/Toc.ts"; +import { InsertTable } from "./table/InsertTable.ts"; +import { AddColumnAfter } from "./table/AddColumnAfter.ts"; +import { AddColumnBefore } from "./table/AddColumnBefore.ts"; +import { DeleteColumn } from "./table/DeleteColumn.ts"; +import { AddRowAfter } from "./table/AddRowAfter.ts"; +import { AddRowBefore } from "./table/AddRowBefore.ts"; +import { DeleteRow } from "./table/DeleteRow.ts"; +import { DeleteTable } from "./table/DeleteTable.ts"; + import { ToggleToc } from "./page/ToggleToc.ts"; /** @@ -77,6 +86,10 @@ export class Classic extends HTMLElement implements UAIEditorEventListener { classicMenuInsertScrollable!: ScrollableDiv; classicMenuInsertGroup!: HTMLElement; + // 表格菜单容器 + classicMenuTableScrollable!: ScrollableDiv; + classicMenuTableGroup!: HTMLElement; + // 页面菜单容器 classicMenuPageScrollable!: ScrollableDiv; classicMenuPageGroup!: HTMLElement; @@ -128,6 +141,16 @@ export class Classic extends HTMLElement implements UAIEditorEventListener { insertMenuMath!: Math; insertMenuToc!: Toc; + // 表格菜单 + tableMenuInsertTable!: InsertTable; + tableMenuAddColumnAfter!: AddColumnAfter; + tableMenuAddColumnBefore!: AddColumnBefore; + tableMenuDeleteColumn!: DeleteColumn; + tableMenuAddRowAfter!: AddRowAfter; + tableMenuAddRowBefore!: AddRowBefore; + tableMenuDeleteRow!: DeleteRow; + tableMenuDeleteTable!: DeleteTable; + // 页面菜单 pageMenuToggleToc!: ToggleToc; @@ -176,6 +199,7 @@ export class Classic extends HTMLElement implements UAIEditorEventListener { const menu = selectMuenus.selectedOptions[0].value; this.classicMenuBaseScrollable.style.display = "none"; this.classicMenuInsertScrollable.style.display = "none"; + this.classicMenuTableScrollable.style.display = "none"; this.classicMenuPageScrollable.style.display = "none"; if (menu === "base") { this.classicMenuBaseScrollable.style.display = "flex"; @@ -183,6 +207,9 @@ export class Classic extends HTMLElement implements UAIEditorEventListener { if (menu === "insert") { this.classicMenuInsertScrollable.style.display = "flex"; } + if (menu === "table") { + this.classicMenuTableScrollable.style.display = "flex"; + } if (menu === "page") { this.classicMenuPageScrollable.style.display = "flex"; } @@ -190,6 +217,7 @@ export class Classic extends HTMLElement implements UAIEditorEventListener { // 创建分组菜单 this.createBaseMenu(event, options); this.createInsertMenu(event, options); + this.createTableMenu(event, options); this.createPageMenu(event, options); this.classicMenuBaseScrollable.style.display = "flex"; } @@ -332,6 +360,30 @@ export class Classic extends HTMLElement implements UAIEditorEventListener { this.insertMenuToc = new Toc({ menuType: "button", enable: true, header: "classic", hideText: false }); this.eventComponents.push(this.insertMenuToc); + this.tableMenuInsertTable = new InsertTable({ menuType: "popup", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuInsertTable); + + this.tableMenuAddColumnAfter = new AddColumnAfter({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuAddColumnAfter); + + this.tableMenuAddColumnBefore = new AddColumnBefore({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuAddColumnBefore); + + this.tableMenuDeleteColumn = new DeleteColumn({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuDeleteColumn); + + this.tableMenuAddRowAfter = new AddRowAfter({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuAddRowAfter); + + this.tableMenuAddRowBefore = new AddRowBefore({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuAddRowBefore); + + this.tableMenuDeleteRow = new DeleteRow({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuDeleteRow); + + this.tableMenuDeleteTable = new DeleteTable({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuDeleteTable); + this.pageMenuToggleToc = new ToggleToc({ menuType: "button", enable: true, header: "classic", hideText: false }); this.eventComponents.push(this.pageMenuToggleToc); } @@ -430,6 +482,40 @@ export class Classic extends HTMLElement implements UAIEditorEventListener { group3.appendChild(this.insertMenuToc); } + /** + * 创建表格菜单 + * @param event + * @param options + */ + createTableMenu(event: EditorEvents["create"], options: UAIEditorOptions) { + this.classicMenuTableGroup = document.createElement("div"); + this.classicMenuTableGroup.style.display = "flex"; + this.classicMenuTableScrollable = new ScrollableDiv(this.classicMenuTableGroup); + this.classicMenuTableScrollable.style.display = "none"; + this.classicMenu.appendChild(this.classicMenuTableScrollable); + this.classicMenuTableScrollable.onCreate(event, options); + + const group1 = document.createElement("div"); + group1.classList.add("uai-classic-virtual-group"); + this.classicMenuTableGroup.appendChild(group1); + group1.appendChild(this.tableMenuInsertTable); + + const group2 = document.createElement("div"); + group2.classList.add("uai-classic-virtual-group"); + this.classicMenuTableGroup.appendChild(group2); + group2.appendChild(this.tableMenuAddColumnBefore); + group2.appendChild(this.tableMenuAddColumnAfter); + group2.appendChild(this.tableMenuDeleteColumn); + group2.appendChild(this.tableMenuAddRowBefore); + group2.appendChild(this.tableMenuAddRowAfter); + group2.appendChild(this.tableMenuDeleteRow); + + const group3 = document.createElement("div"); + group3.classList.add("uai-classic-virtual-group"); + this.classicMenuTableGroup.appendChild(group3); + group3.appendChild(this.tableMenuDeleteTable); + } + /** * 创建页面菜单 * @param event diff --git a/src/components/menus/toolbar/Ribbon.ts b/src/components/menus/toolbar/Ribbon.ts index 95e48dd..4594b87 100644 --- a/src/components/menus/toolbar/Ribbon.ts +++ b/src/components/menus/toolbar/Ribbon.ts @@ -58,6 +58,15 @@ import { Symbol } from "./insert/Symbol.ts"; import { Math } from "./insert/Math.ts"; import { Toc } from "./insert/Toc.ts"; +import { InsertTable } from "./table/InsertTable.ts"; +import { AddColumnAfter } from "./table/AddColumnAfter.ts"; +import { AddColumnBefore } from "./table/AddColumnBefore.ts"; +import { DeleteColumn } from "./table/DeleteColumn.ts"; +import { AddRowAfter } from "./table/AddRowAfter.ts"; +import { AddRowBefore } from "./table/AddRowBefore.ts"; +import { DeleteRow } from "./table/DeleteRow.ts"; +import { DeleteTable } from "./table/DeleteTable.ts"; + import { ToggleToc } from "./page/ToggleToc.ts"; /** @@ -80,6 +89,10 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener { ribbonMenuInsertScrollable!: ScrollableDiv; ribbonMenuInsertGroup!: HTMLElement; + // 表格菜单容器 + ribbonMenuTableScrollable!: ScrollableDiv; + ribbonMenuTableGroup!: HTMLElement; + // 页面菜单容器 ribbonMenuPageScrollable!: ScrollableDiv; ribbonMenuPageGroup!: HTMLElement; @@ -131,6 +144,16 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener { insertMenuMath!: Math; insertMenuToc!: Toc; + // 表格菜单 + tableMenuInsertTable!: InsertTable; + tableMenuAddColumnAfter!: AddColumnAfter; + tableMenuAddColumnBefore!: AddColumnBefore; + tableMenuDeleteColumn!: DeleteColumn; + tableMenuAddRowAfter!: AddRowAfter; + tableMenuAddRowBefore!: AddRowBefore; + tableMenuDeleteRow!: DeleteRow; + tableMenuDeleteTable!: DeleteTable; + // 页面菜单 pageMenuToggleToc!: ToggleToc; @@ -172,6 +195,7 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener { tab.classList.add("active"); this.ribbonMenuBaseScrollable.style.display = "none"; this.ribbonMenuInsertScrollable.style.display = "none"; + this.ribbonMenuTableScrollable.style.display = "none"; this.ribbonMenuPageScrollable.style.display = "none"; if (menu.value === "base") { this.ribbonMenuBaseScrollable.style.display = "flex"; @@ -179,6 +203,9 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener { if (menu.value === "insert") { this.ribbonMenuInsertScrollable.style.display = "flex"; } + if (menu.value === "table") { + this.ribbonMenuTableScrollable.style.display = "flex"; + } if (menu.value === "page") { this.ribbonMenuPageScrollable.style.display = "flex"; } @@ -201,6 +228,7 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener { // 创建分组菜单 this.createBaseMenu(event, options); this.createInsertMenu(event, options); + this.createTableMenu(event, options); this.createPageMenu(event, options); this.ribbonMenuBaseScrollable.style.display = "flex"; } @@ -355,6 +383,30 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener { this.insertMenuToc = new Toc({ menuType: "button", enable: true, huge: true }); this.eventComponents.push(this.insertMenuToc); + this.tableMenuInsertTable = new InsertTable({ menuType: "popup", enable: true, huge: true, hideText: false }); + this.eventComponents.push(this.tableMenuInsertTable); + + this.tableMenuAddColumnAfter = new AddColumnAfter({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuAddColumnAfter); + + this.tableMenuAddColumnBefore = new AddColumnBefore({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuAddColumnBefore); + + this.tableMenuDeleteColumn = new DeleteColumn({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuDeleteColumn); + + this.tableMenuAddRowAfter = new AddRowAfter({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuAddRowAfter); + + this.tableMenuAddRowBefore = new AddRowBefore({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuAddRowBefore); + + this.tableMenuDeleteRow = new DeleteRow({ menuType: "button", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.tableMenuDeleteRow); + + this.tableMenuDeleteTable = new DeleteTable({ menuType: "button", enable: true, huge: true, hideText: false }); + this.eventComponents.push(this.tableMenuDeleteTable); + this.pageMenuToggleToc = new ToggleToc({ menuType: "button", enable: true, huge: true, hideText: false }); this.eventComponents.push(this.pageMenuToggleToc); } @@ -527,6 +579,48 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener { group3.appendChild(this.insertMenuToc); } + /** + * 创建表格菜单 + * @param event + * @param options + */ + createTableMenu(_event: EditorEvents["create"], _options: UAIEditorOptions) { + this.ribbonMenuTableGroup = document.createElement("div"); + this.ribbonMenuTableGroup.classList.add("uai-ribbon-container"); + this.ribbonMenuTableGroup.style.display = "flex"; + this.ribbonMenuTableScrollable = new ScrollableDiv(this.ribbonMenuTableGroup); + this.ribbonMenuTableScrollable.style.display = "none"; + this.ribbonScrollableContainer.appendChild(this.ribbonMenuTableScrollable); + + const group1 = document.createElement("div"); + group1.classList.add("uai-ribbon-virtual-group"); + this.ribbonMenuTableGroup.appendChild(group1); + group1.appendChild(this.tableMenuInsertTable); + + const group2 = document.createElement("div"); + group2.classList.add("uai-ribbon-virtual-group"); + this.ribbonMenuTableGroup.appendChild(group2); + + const group2row1 = document.createElement("div"); + group2row1.classList.add("uai-ribbon-virtual-group-row"); + group2.appendChild(group2row1); + group2row1.appendChild(this.tableMenuAddColumnBefore); + group2row1.appendChild(this.tableMenuAddColumnAfter); + group2row1.appendChild(this.tableMenuDeleteColumn); + + const group2row2 = document.createElement("div"); + group2row2.classList.add("uai-ribbon-virtual-group-row"); + group2.appendChild(group2row2); + group2row2.appendChild(this.tableMenuAddRowBefore); + group2row2.appendChild(this.tableMenuAddRowAfter); + group2row2.appendChild(this.tableMenuDeleteRow); + + const group3 = document.createElement("div"); + group3.classList.add("uai-ribbon-virtual-group"); + this.ribbonMenuTableGroup.appendChild(group3); + group3.appendChild(this.tableMenuDeleteTable); + } + /** * 创建页面菜单 * @param event diff --git a/src/components/menus/toolbar/table/AddColumnAfter.ts b/src/components/menus/toolbar/table/AddColumnAfter.ts new file mode 100644 index 0000000..e27aa66 --- /dev/null +++ b/src/components/menus/toolbar/table/AddColumnAfter.ts @@ -0,0 +1,73 @@ +// Copyright (c) 2024-present AI-Labs + +// @ ts-nocheck +import { MenuButton, MenuButtonOptions } from "../../MenuButton.ts"; +import icon from "../../../../assets/icons/table-add-column-after.svg"; +import { t } from "i18next"; +import { UAIEditorEventListener, UAIEditorOptions, InnerEditor } from "../../../../core/UAIEditor.ts"; +import { EditorEvents } from "@tiptap/core"; + +/** + * 表格菜单:后面添加列 + */ +export class AddColumnAfter extends HTMLElement implements UAIEditorEventListener { + // 按钮选项 + menuButtonOptions: MenuButtonOptions = { + menuType: "button", + enable: true, + icon: icon, + hideText: true, + text: t('table.addColumnAfter'), + tooltip: t('table.addColumnAfter'), + } + + // 功能按钮 + menuButton: MenuButton; + + constructor(options: MenuButtonOptions) { + super(); + + // 初始化功能按钮选项 + this.menuButtonOptions = { ...this.menuButtonOptions, ...options }; + + // 创建功能按钮 + this.menuButton = new MenuButton(this.menuButtonOptions); + } + + /** + * 定义创建方法 + * @param event + * @param options + */ + onCreate(event: EditorEvents["create"], options: UAIEditorOptions) { + this.menuButton.onCreate(event, options); + this.appendChild(this.menuButton); + this.onEditableChange(false); + + // 添加鼠标点击事件,在后面插入列 + this.addEventListener("click", () => { + if (this.menuButtonOptions.enable) { + event.editor.chain().focus().addColumnAfter().run(); + } + }) + } + + /** + * 定义Transaction监听方法 + * @param event + * @param options + */ + onTransaction(event: EditorEvents["transaction"], options: UAIEditorOptions) { + this.menuButton.onTransaction(event, options); + if (this.menuButton.menuButton) { + var disable = event.editor.can().addColumnAfter(); + this.onEditableChange(disable); + } + } + + onEditableChange(editable: boolean) { + this.menuButtonOptions.enable = editable; + this.menuButton.onEditableChange(editable); + } +} + diff --git a/src/components/menus/toolbar/table/AddColumnBefore.ts b/src/components/menus/toolbar/table/AddColumnBefore.ts new file mode 100644 index 0000000..bd2d2de --- /dev/null +++ b/src/components/menus/toolbar/table/AddColumnBefore.ts @@ -0,0 +1,73 @@ +// Copyright (c) 2024-present AI-Labs + +// @ ts-nocheck +import { MenuButton, MenuButtonOptions } from "../../MenuButton.ts"; +import icon from "../../../../assets/icons/table-add-column-after.svg"; +import { t } from "i18next"; +import { UAIEditorEventListener, UAIEditorOptions, InnerEditor } from "../../../../core/UAIEditor.ts"; +import { EditorEvents } from "@tiptap/core"; + +/** + * 表格菜单:前面添加列 + */ +export class AddColumnBefore extends HTMLElement implements UAIEditorEventListener { + // 按钮选项 + menuButtonOptions: MenuButtonOptions = { + menuType: "button", + enable: true, + icon: icon, + hideText: true, + text: t('table.addColumnBefore'), + tooltip: t('table.addColumnBefore'), + } + + // 功能按钮 + menuButton: MenuButton; + + constructor(options: MenuButtonOptions) { + super(); + + // 初始化功能按钮选项 + this.menuButtonOptions = { ...this.menuButtonOptions, ...options }; + + // 创建功能按钮 + this.menuButton = new MenuButton(this.menuButtonOptions); + } + + /** + * 定义创建方法 + * @param event + * @param options + */ + onCreate(event: EditorEvents["create"], options: UAIEditorOptions) { + this.menuButton.onCreate(event, options); + this.appendChild(this.menuButton); + this.onEditableChange(false); + + // 添加鼠标点击事件,在前面插入列 + this.addEventListener("click", () => { + if (this.menuButtonOptions.enable) { + event.editor.chain().focus().addColumnBefore().run(); + } + }) + } + + /** + * 定义Transaction监听方法 + * @param event + * @param options + */ + onTransaction(event: EditorEvents["transaction"], options: UAIEditorOptions) { + this.menuButton.onTransaction(event, options); + if (this.menuButton.menuButton) { + var disable = event.editor.can().addColumnBefore(); + this.onEditableChange(disable); + } + } + + onEditableChange(editable: boolean) { + this.menuButtonOptions.enable = editable; + this.menuButton.onEditableChange(editable); + } +} + diff --git a/src/components/menus/toolbar/table/AddRowAfter.ts b/src/components/menus/toolbar/table/AddRowAfter.ts new file mode 100644 index 0000000..f164973 --- /dev/null +++ b/src/components/menus/toolbar/table/AddRowAfter.ts @@ -0,0 +1,73 @@ +// Copyright (c) 2024-present AI-Labs + +// @ ts-nocheck +import { MenuButton, MenuButtonOptions } from "../../MenuButton.ts"; +import icon from "../../../../assets/icons/table-add-row-after.svg"; +import { t } from "i18next"; +import { UAIEditorEventListener, UAIEditorOptions, InnerEditor } from "../../../../core/UAIEditor.ts"; +import { EditorEvents } from "@tiptap/core"; + +/** + * 表格菜单:后面添加行 + */ +export class AddRowAfter extends HTMLElement implements UAIEditorEventListener { + // 按钮选项 + menuButtonOptions: MenuButtonOptions = { + menuType: "button", + enable: true, + icon: icon, + hideText: true, + text: t('table.addRowAfter'), + tooltip: t('table.addRowAfter'), + } + + // 功能按钮 + menuButton: MenuButton; + + constructor(options: MenuButtonOptions) { + super(); + + // 初始化功能按钮选项 + this.menuButtonOptions = { ...this.menuButtonOptions, ...options }; + + // 创建功能按钮 + this.menuButton = new MenuButton(this.menuButtonOptions); + } + + /** + * 定义创建方法 + * @param event + * @param options + */ + onCreate(event: EditorEvents["create"], options: UAIEditorOptions) { + this.menuButton.onCreate(event, options); + this.appendChild(this.menuButton); + this.onEditableChange(false); + + // 添加鼠标点击事件,在后面插入行 + this.addEventListener("click", () => { + if (this.menuButtonOptions.enable) { + event.editor.chain().focus().addRowAfter().run(); + } + }) + } + + /** + * 定义Transaction监听方法 + * @param event + * @param options + */ + onTransaction(event: EditorEvents["transaction"], options: UAIEditorOptions) { + this.menuButton.onTransaction(event, options); + if (this.menuButton.menuButton) { + var disable = event.editor.can().addRowAfter(); + this.onEditableChange(disable); + } + } + + onEditableChange(editable: boolean) { + this.menuButtonOptions.enable = editable; + this.menuButton.onEditableChange(editable); + } +} + diff --git a/src/components/menus/toolbar/table/AddRowBefore.ts b/src/components/menus/toolbar/table/AddRowBefore.ts new file mode 100644 index 0000000..bbf50fb --- /dev/null +++ b/src/components/menus/toolbar/table/AddRowBefore.ts @@ -0,0 +1,73 @@ +// Copyright (c) 2024-present AI-Labs + +// @ ts-nocheck +import { MenuButton, MenuButtonOptions } from "../../MenuButton.ts"; +import icon from "../../../../assets/icons/table-add-row-before.svg"; +import { t } from "i18next"; +import { UAIEditorEventListener, UAIEditorOptions, InnerEditor } from "../../../../core/UAIEditor.ts"; +import { EditorEvents } from "@tiptap/core"; + +/** + * 表格菜单:前面添加行 + */ +export class AddRowBefore extends HTMLElement implements UAIEditorEventListener { + // 按钮选项 + menuButtonOptions: MenuButtonOptions = { + menuType: "button", + enable: true, + icon: icon, + hideText: true, + text: t('table.addRowBefore'), + tooltip: t('table.addRowBefore'), + } + + // 功能按钮 + menuButton: MenuButton; + + constructor(options: MenuButtonOptions) { + super(); + + // 初始化功能按钮选项 + this.menuButtonOptions = { ...this.menuButtonOptions, ...options }; + + // 创建功能按钮 + this.menuButton = new MenuButton(this.menuButtonOptions); + } + + /** + * 定义创建方法 + * @param event + * @param options + */ + onCreate(event: EditorEvents["create"], options: UAIEditorOptions) { + this.menuButton.onCreate(event, options); + this.appendChild(this.menuButton); + this.onEditableChange(false); + + // 添加鼠标点击事件,在前面插入行 + this.addEventListener("click", () => { + if (this.menuButtonOptions.enable) { + event.editor.chain().focus().addRowBefore().run(); + } + }) + } + + /** + * 定义Transaction监听方法 + * @param event + * @param options + */ + onTransaction(event: EditorEvents["transaction"], options: UAIEditorOptions) { + this.menuButton.onTransaction(event, options); + if (this.menuButton.menuButton) { + var disable = event.editor.can().addRowBefore(); + this.onEditableChange(disable); + } + } + + onEditableChange(editable: boolean) { + this.menuButtonOptions.enable = editable; + this.menuButton.onEditableChange(editable); + } +} + diff --git a/src/components/menus/toolbar/table/DeleteColumn.ts b/src/components/menus/toolbar/table/DeleteColumn.ts new file mode 100644 index 0000000..5f73dfa --- /dev/null +++ b/src/components/menus/toolbar/table/DeleteColumn.ts @@ -0,0 +1,73 @@ +// Copyright (c) 2024-present AI-Labs + +// @ ts-nocheck +import { MenuButton, MenuButtonOptions } from "../../MenuButton.ts"; +import icon from "../../../../assets/icons/table-delete-column.svg"; +import { t } from "i18next"; +import { UAIEditorEventListener, UAIEditorOptions, InnerEditor } from "../../../../core/UAIEditor.ts"; +import { EditorEvents } from "@tiptap/core"; + +/** + * 表格菜单:删除列 + */ +export class DeleteColumn extends HTMLElement implements UAIEditorEventListener { + // 按钮选项 + menuButtonOptions: MenuButtonOptions = { + menuType: "button", + enable: true, + icon: icon, + hideText: true, + text: t('table.deleteColumn.text'), + tooltip: t('table.deleteColumn.text'), + } + + // 功能按钮 + menuButton: MenuButton; + + constructor(options: MenuButtonOptions) { + super(); + + // 初始化功能按钮选项 + this.menuButtonOptions = { ...this.menuButtonOptions, ...options }; + + // 创建功能按钮 + this.menuButton = new MenuButton(this.menuButtonOptions); + } + + /** + * 定义创建方法 + * @param event + * @param options + */ + onCreate(event: EditorEvents["create"], options: UAIEditorOptions) { + this.menuButton.onCreate(event, options); + this.appendChild(this.menuButton); + this.onEditableChange(false); + + // 添加鼠标点击事件,删除列 + this.addEventListener("click", () => { + if (this.menuButtonOptions.enable) { + event.editor.chain().focus().deleteColumn().run(); + } + }) + } + + /** + * 定义Transaction监听方法 + * @param event + * @param options + */ + onTransaction(event: EditorEvents["transaction"], options: UAIEditorOptions) { + this.menuButton.onTransaction(event, options); + if (this.menuButton.menuButton) { + var disable = event.editor.can().deleteColumn(); + this.onEditableChange(disable); + } + } + + onEditableChange(editable: boolean) { + this.menuButtonOptions.enable = editable; + this.menuButton.onEditableChange(editable); + } +} + diff --git a/src/components/menus/toolbar/table/DeleteRow.ts b/src/components/menus/toolbar/table/DeleteRow.ts new file mode 100644 index 0000000..a7da6f4 --- /dev/null +++ b/src/components/menus/toolbar/table/DeleteRow.ts @@ -0,0 +1,73 @@ +// Copyright (c) 2024-present AI-Labs + +// @ ts-nocheck +import { MenuButton, MenuButtonOptions } from "../../MenuButton.ts"; +import icon from "../../../../assets/icons/table-delete-row.svg"; +import { t } from "i18next"; +import { UAIEditorEventListener, UAIEditorOptions, InnerEditor } from "../../../../core/UAIEditor.ts"; +import { EditorEvents } from "@tiptap/core"; + +/** + * 表格菜单:删除行 + */ +export class DeleteRow extends HTMLElement implements UAIEditorEventListener { + // 按钮选项 + menuButtonOptions: MenuButtonOptions = { + menuType: "button", + enable: true, + icon: icon, + hideText: true, + text: t('table.deleteRow.text'), + tooltip: t('table.deleteRow.text'), + } + + // 功能按钮 + menuButton: MenuButton; + + constructor(options: MenuButtonOptions) { + super(); + + // 初始化功能按钮选项 + this.menuButtonOptions = { ...this.menuButtonOptions, ...options }; + + // 创建功能按钮 + this.menuButton = new MenuButton(this.menuButtonOptions); + } + + /** + * 定义创建方法 + * @param event + * @param options + */ + onCreate(event: EditorEvents["create"], options: UAIEditorOptions) { + this.menuButton.onCreate(event, options); + this.appendChild(this.menuButton); + this.onEditableChange(false); + + // 添加鼠标点击事件,删除行 + this.addEventListener("click", () => { + if (this.menuButtonOptions.enable) { + event.editor.chain().focus().deleteRow().run(); + } + }) + } + + /** + * 定义Transaction监听方法 + * @param event + * @param options + */ + onTransaction(event: EditorEvents["transaction"], options: UAIEditorOptions) { + this.menuButton.onTransaction(event, options); + if (this.menuButton.menuButton) { + var disable = event.editor.can().deleteRow(); + this.onEditableChange(disable); + } + } + + onEditableChange(editable: boolean) { + this.menuButtonOptions.enable = editable; + this.menuButton.onEditableChange(editable); + } +} + diff --git a/src/components/menus/toolbar/table/DeleteTable.ts b/src/components/menus/toolbar/table/DeleteTable.ts new file mode 100644 index 0000000..945f176 --- /dev/null +++ b/src/components/menus/toolbar/table/DeleteTable.ts @@ -0,0 +1,73 @@ +// Copyright (c) 2024-present AI-Labs + +// @ ts-nocheck +import { MenuButton, MenuButtonOptions } from "../../MenuButton.ts"; +import icon from "../../../../assets/icons/table-delete.svg"; +import { t } from "i18next"; +import { UAIEditorEventListener, UAIEditorOptions, InnerEditor } from "../../../../core/UAIEditor.ts"; +import { EditorEvents } from "@tiptap/core"; + +/** + * 表格菜单:删除表格 + */ +export class DeleteTable extends HTMLElement implements UAIEditorEventListener { + // 按钮选项 + menuButtonOptions: MenuButtonOptions = { + menuType: "button", + enable: true, + icon: icon, + hideText: true, + text: t('table.delete.text'), + tooltip: t('table.delete.text'), + } + + // 功能按钮 + menuButton: MenuButton; + + constructor(options: MenuButtonOptions) { + super(); + + // 初始化功能按钮选项 + this.menuButtonOptions = { ...this.menuButtonOptions, ...options }; + + // 创建功能按钮 + this.menuButton = new MenuButton(this.menuButtonOptions); + } + + /** + * 定义创建方法 + * @param event + * @param options + */ + onCreate(event: EditorEvents["create"], options: UAIEditorOptions) { + this.menuButton.onCreate(event, options); + this.appendChild(this.menuButton); + this.onEditableChange(false); + + // 添加鼠标点击事件,删除表格 + this.addEventListener("click", () => { + if (this.menuButtonOptions.enable) { + event.editor.chain().focus().deleteTable().run(); + } + }) + } + + /** + * 定义Transaction监听方法 + * @param event + * @param options + */ + onTransaction(event: EditorEvents["transaction"], options: UAIEditorOptions) { + this.menuButton.onTransaction(event, options); + if (this.menuButton.menuButton) { + var disable = event.editor.can().deleteTable(); + this.onEditableChange(disable); + } + } + + onEditableChange(editable: boolean) { + this.menuButtonOptions.enable = editable; + this.menuButton.onEditableChange(editable); + } +} + diff --git a/src/components/menus/toolbar/table/InsertTable.ts b/src/components/menus/toolbar/table/InsertTable.ts new file mode 100644 index 0000000..798fa70 --- /dev/null +++ b/src/components/menus/toolbar/table/InsertTable.ts @@ -0,0 +1,264 @@ +// Copyright (c) 2024-present AI-Labs + +// @ ts-nocheck +import { MenuButton, MenuButtonOptions } from "../../MenuButton.ts"; +import icon from "../../../../assets/icons/table.svg"; +import { t } from "i18next"; +import { UAIEditorEventListener, UAIEditorOptions } from "../../../../core/UAIEditor.ts"; +import { EditorEvents } from "@tiptap/core"; +import tippy, { Instance, Props } from "tippy.js"; + +/** + * 表格菜单:插入表格 + */ +export class InsertTable extends HTMLElement implements UAIEditorEventListener { + // 按钮选项 + menuButtonOptions: MenuButtonOptions = { + menuType: "popup", + enable: true, + icon: icon, + hideText: true, + text: t('table.insert.text'), + tooltip: t('table.insert.tip'), + } + + // 功能按钮 + menuButton: MenuButton; + tippyInstance!: Instance; + + // 表格大小设置 + selected = { rows: 0, cols: 0 } + + // 表格行数 + gridRows = 10; + // 表格列数 + gridCols = 10; + + // 手工输入行数 + rowsInput!: HTMLInputElement; + // 手工输入列数 + colsInput!: HTMLInputElement; + // 是否包含标题行 + withHeaderRow!: HTMLInputElement; + + constructor(options: MenuButtonOptions) { + super(); + + // 初始化功能按钮选项 + this.menuButtonOptions = { ...this.menuButtonOptions, ...options }; + + // 创建功能按钮 + this.menuButton = new MenuButton(this.menuButtonOptions); + } + + /** + * 定义创建方法 + * @param event + * @param options + */ + onCreate(event: EditorEvents["create"], options: UAIEditorOptions) { + this.menuButton.onCreate(event, options); + this.appendChild(this.menuButton); + + // 创建表格设置提示框 + if (this.menuButtonOptions.enable && this.menuButton.menuButtonArrow) { + this.tippyInstance = tippy(this.menuButton.menuButton, { + content: this.createContainer(event, options), + appendTo: 'parent', + placement: 'bottom', + trigger: 'click', + interactive: true, + arrow: false, + onShow: () => { + this.menuButton.tippyInstance?.disable(); + }, + onHidden: () => { + this.menuButton.tippyInstance?.enable(); + }, + }); + } + } + + /** + * 定义Transaction监听方法 + * @param event + * @param options + */ + onTransaction(event: EditorEvents["transaction"], options: UAIEditorOptions) { + this.menuButton.onTransaction(event, options); + } + + onEditableChange(editable: boolean) { + this.menuButtonOptions.enable = editable; + this.menuButton.onEditableChange(editable); + } + + /** + * 渲染选择的行、列 + */ + renderSelected() { + this.rowsInput.value = `${this.selected.rows}`; + this.colsInput.value = `${this.selected.cols}`; + // 为所有选中的行、列设置样式 + for (var i = 0; i < this.gridRows; i++) { + for (var j = 0; j < this.gridCols; j++) { + const cell = document.querySelector(`#uai-table-rows-${i}-cells-${j}`) + cell?.classList.remove("selected"); + if (this.isSelected(i, j)) { + cell?.classList.add("selected"); + } + } + } + } + + /** + * 判断指定的行、列是否选中 + * @param rows + * @param cols + * @returns + */ + isSelected(rows: number, cols: number) { + return ( + this.selected.rows && + this.selected.rows > rows && + this.selected.cols && + this.selected.cols > cols + ) + } + + /** + * 插入表格 + * @param event + * @param options + * @returns + */ + insertTable(event: EditorEvents["create"], options: UAIEditorOptions) { + const { rows, cols } = this.selected; + if (rows === 0 || rows > 1000 || cols === 0 || cols > 30) { + return + } + event.editor.chain().focus().insertTable({ rows, cols, withHeaderRow: this.withHeaderRow.checked }).scrollIntoView().run(); + this.tippyInstance.hide(); + } + + /** + * 创建表格设置容器 + * @param event + * @param options + * @returns + */ + createContainer(event: EditorEvents["create"], options: UAIEditorOptions) { + const container = document.createElement("div"); + container.classList.add("uai-table-container"); + const grid = document.createElement("div"); + grid.classList.add("uai-table-grid"); + + // 设置指定的行、列 + for (var i = 0; i < this.gridRows; i++) { + const row = document.createElement("div"); + row.classList.add("row"); + for (var j = 0; j < this.gridCols; j++) { + // 绘制单元格块 + const cell = document.createElement("div"); + cell.id = `uai-table-rows-${i}-cells-${j}`; + cell.setAttribute("rows", `${i}`); + cell.setAttribute("cols", `${j}`); + cell.classList.add("cell"); + // 设置鼠标事件,设置所选行、列数 + cell.addEventListener("mouseover", () => { + this.selected.rows = +cell.getAttribute("rows")! + 1; + this.selected.cols = +cell.getAttribute("cols")! + 1; + }) + // 设置鼠标点击事件,插入表格 + cell.addEventListener("click", () => { + this.insertTable(event, options); + }); + row.appendChild(cell); + } + grid.appendChild(row); + } + // 设置鼠标事件,渲染单元格块 + grid.addEventListener("mousemove", () => { + this.renderSelected(); + }); + // 设置鼠标事件,渲染单元格块 + grid.addEventListener("mouseleave", () => { + this.selected.rows = 0; + this.selected.cols = 0; + this.renderSelected(); + }); + container.appendChild(grid); + + // 创建自定义表格行列数设置区 + const custom = document.createElement("div"); + custom.classList.add("uai-table-custom"); + + const title = document.createElement("div"); + title.classList.add("uai-table-custom-title"); + title.innerHTML = t('table.insert.property'); + custom.appendChild(title); + + // 表格行数输入区 + const rowsDiv = document.createElement("div"); + rowsDiv.classList.add("uai-table-custom-input"); + rowsDiv.innerHTML = t('table.insert.rows'); + this.rowsInput = document.createElement("input"); + this.rowsInput.type = "number"; + this.rowsInput.min = "0"; + this.rowsInput.max = "100"; + this.rowsInput.placeholder = "请输入"; + this.rowsInput.addEventListener("input", () => { + this.selected.rows = +this.rowsInput.value; + this.renderSelected(); + }); + rowsDiv.appendChild(this.rowsInput); + custom.appendChild(rowsDiv); + + // 表格列数输入区 + const colsDiv = document.createElement("div"); + colsDiv.classList.add("uai-table-custom-input"); + colsDiv.innerHTML = t('table.insert.cols'); + this.colsInput = document.createElement("input"); + this.colsInput.type = "number"; + this.colsInput.min = "0"; + this.colsInput.max = "100"; + this.colsInput.placeholder = "请输入"; + this.colsInput.addEventListener("input", () => { + this.selected.cols = +this.colsInput.value; + this.renderSelected(); + }); + colsDiv.appendChild(this.colsInput); + custom.appendChild(colsDiv); + + this.rowsInput.value = `${this.selected.rows}`; + this.colsInput.value = `${this.selected.cols}`; + + // 设置插入的表格是否带有标题行 + const withHeaderDiv = document.createElement("div"); + withHeaderDiv.classList.add("uai-table-custom-header"); + this.withHeaderRow = document.createElement("input"); + this.withHeaderRow.type = "checkbox"; + this.withHeaderRow.id = "withHeaderRow"; + this.withHeaderRow.checked = true; + const withHeaderRowLabel = document.createElement('label'); + withHeaderRowLabel.htmlFor = 'withHeaderRow'; + withHeaderRowLabel.textContent = t('table.insert.withHeader'); + withHeaderDiv.appendChild(this.withHeaderRow); + withHeaderDiv.appendChild(withHeaderRowLabel); + custom.appendChild(withHeaderDiv); + + // 插入表格按钮 + const insertButton = document.createElement("div"); + insertButton.classList.add("uai-table-insert-button"); + insertButton.innerHTML = t('table.insert.create'); + // 添加鼠标点击事件,插入表格 + insertButton.addEventListener("click", () => { + this.insertTable(event, options); + }); + custom.appendChild(insertButton); + + container.appendChild(custom); + return container; + } +} + diff --git a/src/core/UAIExtensions.ts b/src/core/UAIExtensions.ts index 99982b9..7626278 100644 --- a/src/core/UAIExtensions.ts +++ b/src/core/UAIExtensions.ts @@ -11,6 +11,10 @@ import { Highlight } from "@tiptap/extension-highlight"; import { Link } from "@tiptap/extension-link"; import { Subscript } from "@tiptap/extension-subscript"; import { Superscript } from "@tiptap/extension-superscript"; +import { Table } from "@tiptap/extension-table"; +import { TableCell } from "@tiptap/extension-table-cell"; +import { TableHeader } from "@tiptap/extension-table-header"; +import { TableRow } from "@tiptap/extension-table-row"; import { TaskItem } from "@tiptap/extension-task-item"; import { TaskList } from "@tiptap/extension-task-list"; import { TextAlign } from "@tiptap/extension-text-align"; @@ -72,6 +76,14 @@ export const allExtensions = (uaiEditor: UAIEditor, _options: UAIEditorOptions): }), Subscript, Superscript, + Table.configure({ + resizable: true, + lastColumnResizable: true, + allowTableNodeSelection: true, + }), + TableCell, + TableHeader, + TableRow, TableOfContents.configure({ getIndex: getHierarchicalIndexes, onUpdate: (content) => {