diff --git a/packages/basic-modules/src/constants/icon-svg.ts b/packages/basic-modules/src/constants/icon-svg.ts index a228db90d..7fef44389 100644 --- a/packages/basic-modules/src/constants/icon-svg.ts +++ b/packages/basic-modules/src/constants/icon-svg.ts @@ -154,3 +154,7 @@ export const CHECK_BOX_SVG = // 回车 export const ENTER_SVG = '' + +// 格式刷 +export const FORMAT_PAINTER = + '' diff --git a/packages/basic-modules/src/index.ts b/packages/basic-modules/src/index.ts index 32d518b61..4988f543b 100644 --- a/packages/basic-modules/src/index.ts +++ b/packages/basic-modules/src/index.ts @@ -26,6 +26,7 @@ import wangEditorDividerModule from './modules/divider' import wangEditorCodeBlockModule from './modules/code-block' import wangEditorFullScreenModule from './modules/full-screen' import wangEditorCommonModule from './modules/common' +import wangEditorFormatPainterModule from './modules/format-painter' export default [ // text style @@ -54,6 +55,7 @@ export default [ wangEditorTodoModule, // command + wangEditorFormatPainterModule, wangEditorUndoRedoModule, wangEditorFullScreenModule, wangEditorCommonModule, diff --git a/packages/basic-modules/src/locale/en.ts b/packages/basic-modules/src/locale/en.ts index 311a0fd98..f2f795a0d 100644 --- a/packages/basic-modules/src/locale/en.ts +++ b/packages/basic-modules/src/locale/en.ts @@ -97,4 +97,7 @@ export default { todo: { todo: 'Todo', }, + formatPainter: { + title: 'Format Painter', + }, } diff --git a/packages/basic-modules/src/locale/zh-CN.ts b/packages/basic-modules/src/locale/zh-CN.ts index 4e860c987..6349197d8 100644 --- a/packages/basic-modules/src/locale/zh-CN.ts +++ b/packages/basic-modules/src/locale/zh-CN.ts @@ -97,4 +97,7 @@ export default { todo: { todo: '待办', }, + formatPainter: { + title: '格式刷', + }, } diff --git a/packages/basic-modules/src/modules/format-painter/helper.ts b/packages/basic-modules/src/modules/format-painter/helper.ts new file mode 100644 index 000000000..fe12933f7 --- /dev/null +++ b/packages/basic-modules/src/modules/format-painter/helper.ts @@ -0,0 +1,13 @@ +import { IDomEditor } from '@wangeditor-next/core' +import { SlateEditor } from '@wangeditor-next/editor' + +/** 清空所有标记(文本样式) */ +export function clearAllMarks(editor: IDomEditor) { + const marks = SlateEditor.marks(editor) + + if (marks) { + Object.keys(marks).forEach(mark => { + editor.removeMark(mark) + }) + } +} diff --git a/packages/basic-modules/src/modules/format-painter/index.ts b/packages/basic-modules/src/modules/format-painter/index.ts new file mode 100644 index 000000000..54a52ed34 --- /dev/null +++ b/packages/basic-modules/src/modules/format-painter/index.ts @@ -0,0 +1,15 @@ +/** + * @description format painter + * @author CodePencil + */ + +import { IModuleConf } from '@wangeditor-next/core' +import { formatPainterConf } from './menu/index' +import withFormatPainter from './plugin' + +const formatPainter: Partial = { + menus: [formatPainterConf], + editorPlugin: withFormatPainter, +} + +export default formatPainter diff --git a/packages/basic-modules/src/modules/format-painter/menu/FormatPainter.ts b/packages/basic-modules/src/modules/format-painter/menu/FormatPainter.ts new file mode 100644 index 000000000..9723dd3a1 --- /dev/null +++ b/packages/basic-modules/src/modules/format-painter/menu/FormatPainter.ts @@ -0,0 +1,67 @@ +/** + * @description Format Painter + * @author CodePencil + */ + +import { IButtonMenu, IDomEditor, t } from '@wangeditor-next/core' +import { SlateEditor } from '@wangeditor-next/editor' +import { FORMAT_PAINTER } from '../../../constants/icon-svg' +import { Text } from 'slate' +import { clearAllMarks } from '../helper' + +interface FormatPaintAttributes { + isSelect: boolean + formatStyle: Omit | null +} + +class FormatPainter implements IButtonMenu { + title = t('formatPainter.title') + iconSvg = FORMAT_PAINTER + tag = 'button' + static attrs: FormatPaintAttributes = { + isSelect: false, + formatStyle: null, + } + + getValue(editor: IDomEditor): string | boolean { + return '' + } + + isActive(editor: IDomEditor): boolean { + return FormatPainter.attrs.isSelect + } + + isDisabled(editor: IDomEditor): boolean { + return false + } + + setFormatHtml(editor: IDomEditor) { + if (!editor.getSelectionText().length) return + if (FormatPainter.attrs.formatStyle) { + clearAllMarks(editor) + for (const [key, value] of Object.entries(FormatPainter.attrs.formatStyle)) { + editor.addMark(key, value) + } + } + FormatPainter.attrs.formatStyle = {} + FormatPainter.attrs.isSelect = false + } + + exec(editor: IDomEditor, value: string | boolean) { + // 如果已经选中了格式刷则取消选中,反之保存已经选中文本的样式 + if (FormatPainter.attrs.isSelect) { + FormatPainter.attrs.isSelect = false + } else { + // 判断是否选中文本 + if (editor.getSelectionText().length) { + FormatPainter.attrs.formatStyle = SlateEditor.marks(editor) + FormatPainter.attrs.isSelect = true + } + } + + editor.blur() + editor.focus() + } +} + +export default FormatPainter diff --git a/packages/basic-modules/src/modules/format-painter/menu/index.ts b/packages/basic-modules/src/modules/format-painter/menu/index.ts new file mode 100644 index 000000000..c18644f6c --- /dev/null +++ b/packages/basic-modules/src/modules/format-painter/menu/index.ts @@ -0,0 +1,13 @@ +/** + * @description menu entry + * @author CodePencil + */ + +import FormatPainter from './FormatPainter' + +export const formatPainterConf = { + key: 'formatPainter', + factory() { + return new FormatPainter() + }, +} diff --git a/packages/basic-modules/src/modules/format-painter/plugin.ts b/packages/basic-modules/src/modules/format-painter/plugin.ts new file mode 100644 index 000000000..229f13a70 --- /dev/null +++ b/packages/basic-modules/src/modules/format-painter/plugin.ts @@ -0,0 +1,34 @@ +/** + * @description editor 插件,重写 editor API + * @author CodePencil + */ + +import { IDomEditor } from '@wangeditor-next/core' +import FormatPainter from './menu/FormatPainter' + +function withFormatPainter(editor: T): T { + const formatPainter = new FormatPainter() + + const { onChange } = editor + const newEditor = editor + + const handleMouseUp = () => { + formatPainter.setFormatHtml(newEditor) + document.removeEventListener('mouseup', handleMouseUp) + } + + newEditor.onChange = () => { + onChange() + + if (FormatPainter.attrs.isSelect) { + // 避免重复绑定 mouseup 事件 + document.removeEventListener('mouseup', handleMouseUp) + document.addEventListener('mouseup', handleMouseUp) + } + } + + // 返回 editor ,重要! + return newEditor +} + +export default withFormatPainter diff --git a/packages/editor/src/init-default-config/config/hoverbar.ts b/packages/editor/src/init-default-config/config/hoverbar.ts index 718318e57..ff9c77d76 100644 --- a/packages/editor/src/init-default-config/config/hoverbar.ts +++ b/packages/editor/src/init-default-config/config/hoverbar.ts @@ -55,6 +55,7 @@ export function genDefaultHoverbarKeys() { // 也可以自定义 match 来匹配元素,此时 key 就随意了 text: { menuKeys: [ + 'formatPainter', 'headerSelect', 'insertLink', 'bulletedList', diff --git a/packages/editor/src/init-default-config/config/toolbar.ts b/packages/editor/src/init-default-config/config/toolbar.ts index 27a1d7a1a..bac3479b4 100644 --- a/packages/editor/src/init-default-config/config/toolbar.ts +++ b/packages/editor/src/init-default-config/config/toolbar.ts @@ -82,6 +82,7 @@ export function genDefaultToolbarKeys() { 'undo', 'redo', '|', + 'formatPainter', 'fullScreen', ] }