UNPKG

wangeditor

Version:

wangEditor - 轻量级 web 富文本编辑器,配置方便,使用简单,开源免费

219 lines (190 loc) 7.43 kB
/** * @description Menus 菜单栏 入口文件 * @author wangfupeng */ import Editor from '../editor/index' import Menu from './menu-constructors/Menu' import MenuConstructorList, { MenuListType } from './menu-list' import $, { DomElement } from './../utils/dom-core' // import { MenuActive } from './menu-constructors/Menu' class Menus { public editor: Editor public menuList: Menu[] public constructorList: MenuListType constructor(editor: Editor) { this.editor = editor this.menuList = [] this.constructorList = MenuConstructorList // 所有菜单构造函数的列表 } /** * 自定义添加菜单 * @param key 菜单 key ,和 editor.config.menus 对应 * @param Menu 菜单构造函数 */ public extend(key: string, Menu: any) { if (!Menu || typeof Menu !== 'function') return this.constructorList[key] = Menu } // 初始化菜单 public init(): void { // 从用户配置的 menus 入手,看需要初始化哪些菜单 const config = this.editor.config // 排除exclude包含的菜单 let excludeMenus: string[] | any = config.excludeMenus if (Array.isArray(excludeMenus) === false) excludeMenus = [] config.menus = config.menus.filter(key => excludeMenus.includes(key) === false) // 排除自扩展中exclude包含的菜单 let CustomMenuKeysList: string[] = Object.keys(Editor.globalCustomMenuConstructorList) CustomMenuKeysList = CustomMenuKeysList.filter(key => excludeMenus.includes(key)) CustomMenuKeysList.forEach((key: string) => { delete Editor.globalCustomMenuConstructorList[key] }) config.menus.forEach(menuKey => { const MenuConstructor = this.constructorList[menuKey] // 暂用 any ,后面再替换 this._initMenuList(menuKey, MenuConstructor) }) // 全局注册 for (let [menuKey, menuFun] of Object.entries(Editor.globalCustomMenuConstructorList)) { const MenuConstructor = menuFun // 暂用 any ,后面再替换 this._initMenuList(menuKey, MenuConstructor) } // 渲染 DOM this._addToToolbar() if (config.showMenuTooltips) { // 添加菜单栏tooltips this._bindMenuTooltips() } } /** * 创建 menu 实例,并放到 menuList 中 * @param menuKey 菜单 key ,和 editor.config.menus 对应 * @param MenuConstructor 菜单构造函数 */ private _initMenuList(menuKey: String, MenuConstructor: any): void { if (MenuConstructor == null || typeof MenuConstructor !== 'function') { // 必须是 class return } if (this.menuList.some(menu => menu.key === menuKey)) { console.warn('菜单名称重复:' + menuKey) } else { const m = new MenuConstructor(this.editor) m.key = menuKey this.menuList.push(m) } } // 绑定菜单栏tooltips private _bindMenuTooltips(): void { const editor = this.editor const $toolbarElem = editor.$toolbarElem const config = editor.config // 若isTooltipShowTop为true则伪元素为下三角,反之为上三角 const menuTooltipPosition = config.menuTooltipPosition const $tooltipEl = $( `<div class="w-e-menu-tooltip w-e-menu-tooltip-${menuTooltipPosition}"> <div class="w-e-menu-tooltip-item-wrapper"> <div></div> </div> </div>` ) $tooltipEl.css('visibility', 'hidden') $toolbarElem.append($tooltipEl) // 设置 z-index $tooltipEl.css('z-index', editor.zIndex.get('tooltip')) let showTimeoutId: number = 0 // 定时器,延时200ms显示tooltips // 清空计时器 function clearShowTimeoutId() { if (showTimeoutId) { clearTimeout(showTimeoutId) } } // 隐藏tooltip function hide() { clearShowTimeoutId() $tooltipEl.css('visibility', 'hidden') } // 事件监听 $toolbarElem .on('mouseover', (e: MouseEvent) => { const target = e.target as HTMLElement const $target = $(target) let title: string | undefined let $menuEl: DomElement | undefined if ($target.isContain($toolbarElem)) { hide() return } if ($target.parentUntil('.w-e-droplist') != null) { // 处于droplist中时隐藏 hide() } else { if ($target.attr('data-title')) { title = $target.attr('data-title') $menuEl = $target } else { const $parent = $target.parentUntil('.w-e-menu') if ($parent != null) { title = $parent.attr('data-title') $menuEl = $parent } } } if (title && $menuEl) { clearShowTimeoutId() const targetOffset = $menuEl.getOffsetData() $tooltipEl.text(editor.i18next.t('menus.title.' + title)) const tooltipOffset = $tooltipEl.getOffsetData() const left = targetOffset.left + targetOffset.width / 2 - tooltipOffset.width / 2 $tooltipEl.css('left', `${left}px`) // 2. 高度设置 if (menuTooltipPosition === 'up') { $tooltipEl.css('top', `${targetOffset.top - tooltipOffset.height - 8}px`) } else if (menuTooltipPosition === 'down') { $tooltipEl.css('top', `${targetOffset.top + targetOffset.height + 8}px`) } showTimeoutId = window.setTimeout(() => { $tooltipEl.css('visibility', 'visible') }, 200) } else { hide() } }) .on('mouseleave', () => { hide() }) } // 添加到菜单栏 private _addToToolbar(): void { const editor = this.editor const $toolbarElem = editor.$toolbarElem // 遍历添加到 DOM this.menuList.forEach(menu => { const $elem = menu.$elem if ($elem) { $toolbarElem.append($elem) } }) } /** * 获取菜单对象 * @param 菜单名称 小写 * @return Menus 菜单对象 */ public menuFind(key: string): Menu { const menuList = this.menuList for (let i = 0, l = menuList.length; i < l; i++) { if (menuList[i].key === key) return menuList[i] } return menuList[0] } /** * @description 修改菜单激活状态 */ public changeActive(): void { this.menuList.forEach(menu => { setTimeout((menu as any).tryChangeActive.bind(menu), 100) // 暂用 any ,后面再替换 }) } } export default Menus