diff --git a/src/components/index.ts b/src/components/index.ts index c5214c8..621b388 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -76,6 +76,7 @@ import { Diagrams } from "./menus/toolbar/tools/Diagrams.ts"; import { ToggleToc } from "./menus/toolbar/page/ToggleToc.ts"; import { BackgroundColor } from "./menus/toolbar/page/BackgroundColor.ts"; +import { Watermark } from "./menus/toolbar/page/Watermark.ts"; // 注册组件 defineCustomElement('uai-editor-header', Header); @@ -153,3 +154,4 @@ defineCustomElement('uai-editor-tools-menu-diagrams', Diagrams); defineCustomElement('uai-editor-page-menu-toggle-toc', ToggleToc); defineCustomElement('uai-editor-page-menu-background-color', BackgroundColor); +defineCustomElement('uai-editor-page-menu-watermark', Watermark); diff --git a/src/components/menus/toolbar/Classic.ts b/src/components/menus/toolbar/Classic.ts index 411835e..95347d1 100644 --- a/src/components/menus/toolbar/Classic.ts +++ b/src/components/menus/toolbar/Classic.ts @@ -69,6 +69,7 @@ import { Diagrams } from "./tools/Diagrams.ts"; import { ToggleToc } from "./page/ToggleToc.ts"; import { BackgroundColor } from "./page/BackgroundColor.ts"; +import { Watermark } from "./page/Watermark.ts"; /** * 传统菜单栏 @@ -164,6 +165,7 @@ export class Classic extends HTMLElement implements UAIEditorEventListener { // 页面菜单 pageMenuToggleToc!: ToggleToc; pageMenuBackgroundColor!: BackgroundColor; + pageMenuWatermark!: Watermark; constructor(defaultToolbarMenus: Record[]) { super(); @@ -408,6 +410,9 @@ export class Classic extends HTMLElement implements UAIEditorEventListener { this.pageMenuBackgroundColor = new BackgroundColor({ menuType: "popup", enable: true, header: "classic", hideText: false }); this.eventComponents.push(this.pageMenuBackgroundColor); + + this.pageMenuWatermark = new Watermark({ menuType: "popup", enable: true, header: "classic", hideText: false }); + this.eventComponents.push(this.pageMenuWatermark); } /** * 创建基础菜单 @@ -579,5 +584,6 @@ export class Classic extends HTMLElement implements UAIEditorEventListener { group2.classList.add("uai-classic-virtual-group"); this.classicMenuPageGroup.appendChild(group2); group2.appendChild(this.pageMenuBackgroundColor); + group2.appendChild(this.pageMenuWatermark); } } \ No newline at end of file diff --git a/src/components/menus/toolbar/Ribbon.ts b/src/components/menus/toolbar/Ribbon.ts index 0dfff40..860e764 100644 --- a/src/components/menus/toolbar/Ribbon.ts +++ b/src/components/menus/toolbar/Ribbon.ts @@ -71,6 +71,7 @@ import { Diagrams } from "./tools/Diagrams.ts"; import { ToggleToc } from "./page/ToggleToc.ts"; import { BackgroundColor } from "./page/BackgroundColor.ts"; +import { Watermark } from "./page/Watermark.ts"; /** * 经典菜单栏 @@ -167,6 +168,7 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener { // 页面菜单 pageMenuToggleToc!: ToggleToc; pageMenuBackgroundColor!: BackgroundColor; + pageMenuWatermark!: Watermark; constructor(defaultToolbarMenus: Record[]) { super(); @@ -431,6 +433,9 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener { this.pageMenuBackgroundColor = new BackgroundColor({ menuType: "popup", enable: true, huge: true, hideText: false }); this.eventComponents.push(this.pageMenuBackgroundColor); + + this.pageMenuWatermark = new Watermark({ menuType: "popup", enable: true, huge: true, hideText: false }); + this.eventComponents.push(this.pageMenuWatermark); } /** @@ -684,5 +689,6 @@ export class Ribbon extends HTMLElement implements UAIEditorEventListener { group2.classList.add("uai-ribbon-virtual-group"); this.ribbonMenuPageGroup.appendChild(group2); group2.appendChild(this.pageMenuBackgroundColor); + group2.appendChild(this.pageMenuWatermark); } } \ No newline at end of file diff --git a/src/components/menus/toolbar/page/Watermark.ts b/src/components/menus/toolbar/page/Watermark.ts new file mode 100644 index 0000000..bb8b912 --- /dev/null +++ b/src/components/menus/toolbar/page/Watermark.ts @@ -0,0 +1,170 @@ +// Copyright (c) 2024-present AI-Labs + +// @ ts-nocheck +import { MenuButton, MenuButtonOptions } from "../../MenuButton.ts"; +import icon from "../../../../assets/icons/watermark.svg"; +import { t } from "i18next"; +import { UAIEditorEventListener, UAIEditorOptions } from "../../../../core/UAIEditor.ts"; +import { EditorEvents } from "@tiptap/core"; +import tippy, { Instance, Props } from "tippy.js"; +import domtoimage from 'dom-to-image-more'; + +/** + * 页面菜单:设置水印 + */ +export class Watermark extends HTMLElement implements UAIEditorEventListener { + // 按钮选项 + menuButtonOptions: MenuButtonOptions = { + menuType: "popup", + enable: true, + icon: icon, + hideText: true, + text: t('page.watermark.text'), + tooltip: t('page.watermark.text'), + } + + // 功能按钮 + menuButton: MenuButton; + // 水印样式提示框 + tippyInstance!: Instance; + + 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); + } + + /** + * 创建水印设置提示框内容 + * @param event + * @param options + * @returns + */ + createContainer(event: EditorEvents["create"], options: UAIEditorOptions) { + // 找到页面容器 + const content = document.querySelector(".uai-zoomable-content") as HTMLElement; + const container = document.createElement("div"); + container.classList.add("uai-watermark-container"); + const type = document.createElement("div"); + type.classList.add("uai-watermark-type"); + container.appendChild(type); + + // 指定水印样式 + const watermarkers = ['compact', 'spacious']; + + // 添加可选水印样式 + watermarkers.forEach(watermarker => { + // 水印选项 + const item = document.createElement("div"); + item.classList.add("item"); + item.classList.add(watermarker); + + // 水印背景图例 + const bg = document.createElement("div"); + bg.classList.add("bg"); + item.appendChild(bg); + + // 水印说明文本 + const span = document.createElement("span"); + span.innerHTML = t(`page.watermark.${watermarker}`); + item.appendChild(span); + + // 添加鼠标点击事件,文档添加水印 + item.addEventListener("click", async () => { + const { toBlob } = domtoimage; + const watermark = document.createElement('div'); + watermark.innerText = "有爱文档"; + watermark.style.color = 'rgba(100, 100, 100, 0.3)'; + watermark.style.fontSize = '20px'; + watermark.style.fontFamily = 'Arial'; + watermark.style.justifyContent = 'center'; + watermark.style.alignItems = 'center'; + watermark.style.transform = 'rotate(-30deg)'; + + const blob = await toBlob(watermark, { scale: 1 }); + const url = URL.createObjectURL(blob); + + content.style.backgroundImage = `url('${url}')` + content.style.backgroundRepeat = `repeat` + }) + type.appendChild(item); + }) + + // 清除水印按钮 + const clearButton = document.createElement("div"); + clearButton.classList.add("uai-clear-button"); + clearButton.innerHTML = t('page.watermark.clear'); + // 清除水印按钮添加点击事件,点击清除水印 + clearButton.addEventListener("click", () => { + content.style.backgroundImage = ''; + }); + + // 创建一个观察器实例并传入回调函数 + const observer = new MutationObserver((mutations) => { + for (const mutation of mutations) { + if (mutation.type === 'attributes' && mutation.attributeName === 'style') { + if (content.style.backgroundImage === '') { + container.removeChild(clearButton); + } else { + container.appendChild(clearButton); + } + } + } + }); + + // 配置观察器选项 + const config = { attributes: true, attributeFilter: ['style'] }; + + // 开始观察目标节点 + observer.observe(content, config); + + return container; + } +} +