diff --git a/.changeset/spicy-foxes-add.md b/.changeset/spicy-foxes-add.md new file mode 100644 index 000000000..2ba421afc --- /dev/null +++ b/.changeset/spicy-foxes-add.md @@ -0,0 +1,7 @@ +--- +"@wangeditor-next/basic-modules": patch +"@wangeditor-next/core": patch +"@wangeditor-next/editor": patch +--- + +perf: added menu config ts type diff --git a/packages/basic-modules/src/modules/image/helper.ts b/packages/basic-modules/src/modules/image/helper.ts index 6b61a8b81..a44ccc226 100644 --- a/packages/basic-modules/src/modules/image/helper.ts +++ b/packages/basic-modules/src/modules/image/helper.ts @@ -155,5 +155,5 @@ export async function updateImageNode( const imageNode = DomEditor.getSelectedNodeByType(editor, 'image') const { onUpdatedImage } = editor.getMenuConfig('editImage') - if (onUpdatedImage) { onUpdatedImage(imageNode) } + if (onUpdatedImage) { onUpdatedImage(imageNode as ImageElement) } } diff --git a/packages/basic-modules/src/modules/link/menu/config.ts b/packages/basic-modules/src/modules/link/menu/config.ts index 7d843b2d5..60780eb0b 100644 --- a/packages/basic-modules/src/modules/link/menu/config.ts +++ b/packages/basic-modules/src/modules/link/menu/config.ts @@ -10,7 +10,7 @@ export function genLinkMenuConfig() { * @param text link text * @param url link url */ - checkLink(text: string, url: string): boolean | string | undefined { + checkLink(_text: string, _url: string): boolean | string | undefined { // 1. 返回 true ,说明检查通过 // 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串) // 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入 diff --git a/packages/core/src/config/index.ts b/packages/core/src/config/index.ts index a3324660f..9afbbaf4d 100644 --- a/packages/core/src/config/index.ts +++ b/packages/core/src/config/index.ts @@ -3,8 +3,9 @@ * @author wangfupeng */ -import forEach from 'lodash.foreach' import cloneDeep from 'lodash.clonedeep' +import forEach from 'lodash.foreach' + import { IEditorConfig, IMenuConfig, IToolbarConfig } from './interface' import { GLOBAL_MENU_CONF } from './register' @@ -13,10 +14,11 @@ import { GLOBAL_MENU_CONF } from './register' */ export function genEditorConfig(userConfig: Partial = {}): IEditorConfig { const defaultMenuConf = cloneDeep(GLOBAL_MENU_CONF) - const newMenuConf: IMenuConfig = {} + const newMenuConf: Partial = {} // 单独处理 menuConf const { MENU_CONF: userMenuConf = {} } = userConfig + forEach(defaultMenuConf, (menuConf, menuKey) => { // 生成新的 menu config newMenuConf[menuKey] = { diff --git a/packages/core/src/config/interface.ts b/packages/core/src/config/interface.ts index 3c7178982..f227a34ff 100644 --- a/packages/core/src/config/interface.ts +++ b/packages/core/src/config/interface.ts @@ -3,9 +3,13 @@ * @author wangfupeng */ -import { Range, NodeEntry, Node } from 'slate' +import { ImageElement } from 'packages/basic-modules/src/modules/image/custom-types' +import { VideoElement } from 'packages/video-module/src/module/custom-types' +import { Node, NodeEntry, Range } from 'slate' + import { IDomEditor } from '../editor/interface' import { IMenuGroup } from '../menus/interface' +import { IUploadConfig } from '../upload' interface IHoverbarConf { // key 即 element type @@ -18,18 +22,161 @@ interface IHoverbarConf { export type AlertType = 'success' | 'info' | 'warning' | 'error' export interface ISingleMenuConfig { - [key: string]: any + [key: string]: any; + iconSvg?: string; +} + +interface IColorConfig { + colors: string[]; +} + +interface IFontSizeItem { + name: string; + value: string; +} + +interface IFontSizeConfig { + fontSizeList: (string | IFontSizeItem)[]; +} + +interface IFontFamilyItem { + name: string; + value: string; +} + +interface IFontFamilyConfig { + fontFamilyList: (string | IFontFamilyItem)[]; +} + +interface ILineHeightConfig { + lineHeightList: string[]; +} + +interface IImageMenuBaseConfig { + checkImage?: (src: string, alt: string, url: string) => boolean | undefined | string; + parseImageSrc?: (src: string) => string; +} + +interface IInsertImageConfig extends IImageMenuBaseConfig { + onInsertedImage?: (imageNode: ImageElement | null) => void; +} + +interface IEditImageConfig extends IImageMenuBaseConfig { + onUpdatedImage?: (imageNode: ImageElement | null) => void; +} +interface IEmotionConfig { + emotions: string[]; +} + +interface IInsertTableConfig { + minWidth: number; + tableHeader: { + selected: boolean; + }; + tableFullWidth: { + selected: boolean; + } +} + +interface ILinkConfig { + checkLink: (text:string, url:string)=> string | boolean | undefined + parseLinkUrl: (url: string) => string +} + +interface IInsertVideoConfig { + onInsertedVideo: (videoNode: VideoElement) => NodeEntry | Range; + checkVideo: (src:string, poster:string)=> string | boolean | undefined + parseVideoSrc: (url: string) => string +} + +interface IUploadVideoConfig extends IUploadConfig { + // 视频专属配置 +} + +interface IUploadImageConfig extends IUploadConfig { + base64LimitSize?: number; +} + +interface ICodeLangConfig { + codeLangs: { text: string; value: string }[]; } export interface IMenuConfig { - [key: string]: ISingleMenuConfig + bold: ISingleMenuConfig; + underline: ISingleMenuConfig; + italic: ISingleMenuConfig; + through: ISingleMenuConfig; + code: ISingleMenuConfig; + sub: ISingleMenuConfig; + sup: ISingleMenuConfig; + clearStyle: ISingleMenuConfig; + color: IColorConfig; + bgColor: IColorConfig; + fontSize: IFontSizeConfig; + fontFamily: IFontFamilyConfig; + indent: ISingleMenuConfig; + delIndent: ISingleMenuConfig; + justifyLeft: ISingleMenuConfig; + justifyRight: ISingleMenuConfig; + justifyCenter: ISingleMenuConfig; + justifyJustify: ISingleMenuConfig; + lineHeight: ILineHeightConfig; + insertImage: IInsertImageConfig; + deleteImage: ISingleMenuConfig; + editImage: IEditImageConfig; + viewImageLink: ISingleMenuConfig; + imageWidth30: ISingleMenuConfig; + imageWidth50: ISingleMenuConfig; + imageWidth100: ISingleMenuConfig; + editorImageSizeMenu: ISingleMenuConfig; + divider: ISingleMenuConfig; + emotion: IEmotionConfig; + insertLink: ILinkConfig; + editLink: ILinkConfig; + unLink: ISingleMenuConfig; + viewLink: ISingleMenuConfig; + codeBlock: ISingleMenuConfig; + blockquote: ISingleMenuConfig; + headerSelect: ISingleMenuConfig; + header1: ISingleMenuConfig; + header2: ISingleMenuConfig; + header3: ISingleMenuConfig; + header4: ISingleMenuConfig; + header5: ISingleMenuConfig; + header6: ISingleMenuConfig; + todo: ISingleMenuConfig; + formatPainter: ISingleMenuConfig; + redo: ISingleMenuConfig; + undo: ISingleMenuConfig; + fullScreen: ISingleMenuConfig; + enter: ISingleMenuConfig; + bulletedList: ISingleMenuConfig; + numberedList: ISingleMenuConfig; + insertTable: ISingleMenuConfig; + deleteTable: ISingleMenuConfig; + insertTableRow: IInsertTableConfig; + deleteTableRow: ISingleMenuConfig; + insertTableCol: ISingleMenuConfig; + deleteTableCol: ISingleMenuConfig; + tableHeader: ISingleMenuConfig; + tableFullWidth: ISingleMenuConfig; + mergeTableCell: ISingleMenuConfig; + splitTableCell: ISingleMenuConfig; + setTableProperty: ISingleMenuConfig; + setTableCellProperty: ISingleMenuConfig; + insertVideo: IInsertVideoConfig; + uploadVideo: IUploadVideoConfig; + editVideoSize: ISingleMenuConfig; + editVideoSrc: ISingleMenuConfig; + uploadImage: IUploadImageConfig; + codeSelectLang: ICodeLangConfig; } /** * editor config */ export interface IEditorConfig { - //【注意】如增加 onXxx 回调函数时,要同步到 vue2/vue3 组件 + // 【注意】如增加 onXxx 回调函数时,要同步到 vue2/vue3 组件 customAlert: (info: string, type: AlertType) => void onCreated?: (editor: IDomEditor) => void @@ -54,7 +201,7 @@ export interface IEditorConfig { maxLength?: number // 各个 menu 的配置汇总,可以通过 key 获取单个 menu 的配置 - MENU_CONF?: IMenuConfig + MENU_CONF?: Partial // 悬浮菜单栏 menu hoverbarKeys?: IHoverbarConf diff --git a/packages/core/src/config/register.ts b/packages/core/src/config/register.ts index 25c20b0e8..14f02b2d4 100644 --- a/packages/core/src/config/register.ts +++ b/packages/core/src/config/register.ts @@ -6,7 +6,7 @@ import { IMenuConfig, ISingleMenuConfig } from '../config/interface' // 全局的菜单配置 -export const GLOBAL_MENU_CONF: IMenuConfig = {} +export const GLOBAL_MENU_CONF: Partial = {} /** * 注册全局菜单配置 @@ -14,6 +14,6 @@ export const GLOBAL_MENU_CONF: IMenuConfig = {} * @param config config */ export function registerGlobalMenuConf(key: string, config?: ISingleMenuConfig) { - if (config == null) return + if (config == null) { return } GLOBAL_MENU_CONF[key] = config } diff --git a/packages/core/src/editor/interface.ts b/packages/core/src/editor/interface.ts index e62edaad3..1bc956329 100644 --- a/packages/core/src/editor/interface.ts +++ b/packages/core/src/editor/interface.ts @@ -3,14 +3,21 @@ * @author wangfupeng */ -import { Editor, Location, Node, Ancestor, Element } from 'slate' import ee from 'event-emitter' -import { IEditorConfig, AlertType, ISingleMenuConfig } from '../config/interface' +import { + Ancestor, Editor, Element, Location, Node, +} from 'slate' + +import { + AlertType, IEditorConfig, IMenuConfig, ISingleMenuConfig, +} from '../config/interface' import { IPositionStyle } from '../menus/interface' import { DOMElement } from '../utils/dom' export type ElementWithId = Element & { id: string } +export type getMenuConfigReturnType = K extends keyof IMenuConfig ? IMenuConfig[K] : ISingleMenuConfig + /** * 扩展 slate Editor 接口 */ @@ -21,7 +28,7 @@ export interface IDomEditor extends Editor { // config getConfig: () => IEditorConfig - getMenuConfig: (menuKey: string) => ISingleMenuConfig + getMenuConfig: (menuKey: K) => getMenuConfigReturnType getAllMenuKeys: () => string[] alert: (info: string, type: AlertType) => void diff --git a/packages/core/src/editor/plugins/with-config.ts b/packages/core/src/editor/plugins/with-config.ts index c1901f1ba..d4baf2b6a 100644 --- a/packages/core/src/editor/plugins/with-config.ts +++ b/packages/core/src/editor/plugins/with-config.ts @@ -4,17 +4,20 @@ */ import { Editor } from 'slate' -import { IDomEditor } from '../..' -import { EDITOR_TO_CONFIG } from '../../utils/weak-maps' -import { IEditorConfig, AlertType, ISingleMenuConfig } from '../../config/interface' + +import { getMenuConfigReturnType, IDomEditor } from '../..' +import { AlertType, IEditorConfig } from '../../config/interface' import { MENU_ITEM_FACTORIES } from '../../menus/register' +import { EDITOR_TO_CONFIG } from '../../utils/weak-maps' export const withConfig = (editor: T) => { const e = editor as T & IDomEditor e.getAllMenuKeys = (): string[] => { const arr: string[] = [] - for (let key in MENU_ITEM_FACTORIES) { + + // eslint-disable-next-line guard-for-in + for (const key in MENU_ITEM_FACTORIES) { arr.push(key) } return arr @@ -23,20 +26,23 @@ export const withConfig = (editor: T) => { // 获取 editor 配置信息 e.getConfig = (): IEditorConfig => { const config = EDITOR_TO_CONFIG.get(e) - if (config == null) throw new Error('Can not get editor config') + + if (config == null) { throw new Error('Can not get editor config') } return config } // 获取 menu config - e.getMenuConfig = (menuKey: string): ISingleMenuConfig => { + e.getMenuConfig = (menuKey: string): getMenuConfigReturnType => { const { MENU_CONF = {} } = e.getConfig() + return MENU_CONF[menuKey] || {} } // alert e.alert = (info: string, type: AlertType = 'info') => { const { customAlert } = e.getConfig() - if (customAlert) customAlert(info, type) + + if (customAlert) { customAlert(info, type) } } return e diff --git a/packages/core/src/menus/interface.ts b/packages/core/src/menus/interface.ts index 9c8776289..fbc5c5d4c 100644 --- a/packages/core/src/menus/interface.ts +++ b/packages/core/src/menus/interface.ts @@ -4,6 +4,8 @@ */ import { Node } from 'slate' + +import { ISingleMenuConfig } from '../config/interface' import { IDomEditor } from '../editor/interface' import { DOMElement } from '../utils/dom' @@ -72,5 +74,5 @@ export type MenuFactoryType = () => IButtonMenu | ISelectMenu | IDropPanelMenu | export interface IRegisterMenuConf { key: string factory: MenuFactoryType - config?: { [key: string]: any } + config?: ISingleMenuConfig } diff --git a/packages/core/src/menus/register.ts b/packages/core/src/menus/register.ts index 423673009..32a289137 100644 --- a/packages/core/src/menus/register.ts +++ b/packages/core/src/menus/register.ts @@ -3,8 +3,9 @@ * @author wangfupeng */ -import { MenuFactoryType, IRegisterMenuConf } from './interface' +import { ISingleMenuConfig } from '../config/interface' import { registerGlobalMenuConf } from '../config/register' +import { IRegisterMenuConf, MenuFactoryType } from './interface' // menu item 的工厂函数 - 集合 export const MENU_ITEM_FACTORIES: { @@ -18,7 +19,7 @@ export const MENU_ITEM_FACTORIES: { */ export function registerMenu( registerMenuConf: IRegisterMenuConf, - customConfig?: { [key: string]: any } + customConfig?: ISingleMenuConfig, ) { const { key, factory, config } = registerMenuConf diff --git a/packages/editor/src/Boot.ts b/packages/editor/src/Boot.ts index ac5599e9c..8689a1823 100644 --- a/packages/editor/src/Boot.ts +++ b/packages/editor/src/Boot.ts @@ -5,37 +5,33 @@ import { IDomEditor, - // 配置 IEditorConfig, - IToolbarConfig, + // to html + IElemToHtmlConf, IModuleConf, - + IParseElemHtmlConf, + IPreParseHtmlConf, // 注册菜单 IRegisterMenuConf, - registerMenu, - // 渲染 modal -> view IRenderElemConf, - RenderStyleFnType, - registerStyleHandler, - registerRenderElemConf, - - // to html - IElemToHtmlConf, - styleToHtmlFnType, - registerStyleToHtmlHandler, - registerElemToHtmlConf, - - // parseHtml - PreParseHtmlFnType, - IPreParseHtmlConf, - registerPreParseHtmlConf, + IToolbarConfig, ParseStyleHtmlFnType, - IParseElemHtmlConf, + // parseHtml + registerElemToHtmlConf, + registerMenu, registerParseElemHtmlConf, registerParseStyleHtmlHandler, + registerPreParseHtmlConf, + registerRenderElemConf, + registerStyleHandler, + registerStyleToHtmlHandler, + RenderStyleFnType, + styleToHtmlFnType, } from '@wangeditor-next/core' +import { ISingleMenuConfig } from 'packages/core/src/config/interface' + import registerModule from './register-builtin-modules/register' type PluginType = (editor: T) => T @@ -47,13 +43,16 @@ class Boot { // editor 配置 static editorConfig: Partial = {} + static setEditorConfig(newConfig: Partial = {}) { this.editorConfig = { ...this.editorConfig, ...newConfig, } } + static simpleEditorConfig: Partial = {} + static setSimpleEditorConfig(newConfig: Partial = {}) { this.simpleEditorConfig = { ...this.simpleEditorConfig, @@ -61,15 +60,18 @@ class Boot { } } - //toolbar 配置 + // toolbar 配置 static toolbarConfig: Partial = {} + static setToolbarConfig(newConfig: Partial = {}) { this.toolbarConfig = { ...this.toolbarConfig, ...newConfig, } } + static simpleToolbarConfig: Partial = {} + static setSimpleToolbarConfig(newConfig: Partial = {}) { this.simpleToolbarConfig = { ...this.simpleToolbarConfig, @@ -79,13 +81,14 @@ class Boot { // 注册插件 static plugins: PluginType[] = [] + static registerPlugin(plugin: PluginType) { this.plugins.push(plugin) } // 注册 menu // TODO 可在注册时传入配置,在开发文档中说明 - static registerMenu(menuConf: IRegisterMenuConf, customConfig?: { [key: string]: any }) { + static registerMenu(menuConf: IRegisterMenuConf, customConfig?: ISingleMenuConfig) { registerMenu(menuConf, customConfig) }