UNPKG

wangeditor

Version:

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

293 lines (268 loc) 10.4 kB
/** * @description 段落行高 LineHeight * @author lichunlin * */ import DropListMenu from '../menu-constructors/DropListMenu' import $, { DomElement } from '../../utils/dom-core' import Editor from '../../editor/index' import { MenuActive } from '../menu-constructors/Menu' import lineHeightList from './lineHeightList' import { UA } from '../../utils/util' class LineHeight extends DropListMenu implements MenuActive { constructor(editor: Editor) { const $elem = $( `<div class="w-e-menu" data-title="行高"> <i class="w-e-icon-row-height"></i> </div>` ) let lineHeightMenu = new lineHeightList(editor, editor.config.lineHeights) const DropListMenu = { width: 100, title: '设置行高', type: 'list', // droplist 以列表形式展示 list: lineHeightMenu.getItemList(), clickHandler: (value: string) => { //保存焦点 editor.selection.saveRange() this.command(value) }, } super($elem, editor, DropListMenu) } /** * 执行命令 * @param value value */ public command(value: string): void { let selection = window.getSelection ? window.getSelection() : document.getSelection() //允许设置dom const allowArray: string[] = ['P'] const editor = this.editor let st: string = '' //恢复焦点 editor.selection.restoreSelection() const $selectionElem = $(editor.selection.getSelectionContainerElem()) if (!$selectionElem?.length) return const $selectionAll = $(editor.selection.getSelectionContainerElem()) // let dom:HTMLElement= $selectionElem.elems[0] let dom: HTMLElement = $(editor.selection.getSelectionStartElem()).elems[0] //获取元素的style let style: string | null = '' let styleList: string[] = [] //点击默认的时候删除line-height属性 并重新设置 style let styleStr: string = '' //选中多行操作 if ($selectionElem && editor.$textElem.equal($selectionElem)) { let isIE = UA.isIE() //获取range 开头结束的dom在 祖父元素的下标 let indexStore: Array<number> = [] let arrayDom_a: Array<HTMLElement> = [] let arrayDom_b: Array<HTMLElement> = [] //获取range 开头结束的dom const StartElem: DomElement = $(editor.selection.getSelectionStartElem()) const EndElem: DomElement = $(editor.selection.getSelectionEndElem()) const childList: NodeListOf<ChildNode> | undefined = editor.selection.getRange() ?.commonAncestorContainer.childNodes arrayDom_a.push(this.getDom(StartElem.elems[0])) childList?.forEach((item, index) => { if (item === this.getDom(StartElem.elems[0])) { indexStore.push(index) } if (item === this.getDom(EndElem.elems[0])) { indexStore.push(index) } }) //遍历 获取头尾之间的dom元素 let i = 0 let d: HTMLElement arrayDom_b.push(this.getDom(StartElem.elems[0])) while (arrayDom_a[i] !== this.getDom(EndElem.elems[0])) { d = $(arrayDom_a[i].nextElementSibling).elems[0] if (allowArray.indexOf($(d).getNodeName()) !== -1) { arrayDom_b.push(d) arrayDom_a.push(d) } else { arrayDom_a.push(d) } i++ } //设置段落选取 全选 if ($(arrayDom_a[0]).getNodeName() !== 'P') { i = 0 //遍历集合得到第一个p标签的下标 for (var k = 0; k < arrayDom_a.length; k++) { if ($(arrayDom_a[k]).getNodeName() === 'P') { i = k break } } //i===0 说明选区中没有p段落 if (i === 0) { return } let _i = 0 while (_i !== i) { arrayDom_a.shift() _i++ } } //设置替换的选区 this.setRange(arrayDom_a[0], arrayDom_a[arrayDom_a.length - 1]) //生成innerHtml html字符串 arrayDom_a.forEach(item => { style = item.getAttribute('style') styleList = style ? style.split(';') : [] styleStr = this.styleProcessing(styleList) if ($(item).getNodeName() === 'P') { //判断是否 点击默认 if (value) { styleStr += value ? `line-height:${value};` : '' } } if (!isIE) { st += `<${$(item).getNodeName().toLowerCase()} style="${styleStr}">${ item.innerHTML }</${$(item).getNodeName().toLowerCase()}>` } else { $(item).css('line-height', value) } }) if (st) { this.action(st, editor) } //恢复已选择的选区 dom = $selectionAll.elems[0] this.setRange(dom.children[indexStore[0]], dom.children[indexStore[1]]) return } //遍历dom 获取祖父元素 直到contenteditable属性的div标签 dom = this.getDom(dom) //校验允许lineheight设置标签 if (allowArray.indexOf($(dom).getNodeName()) === -1) { return } style = dom.getAttribute('style') styleList = style ? style.split(';') : [] //全选 dom下所有的内容 selection?.selectAllChildren(dom) //保存range editor.selection.saveRange() //判断是否存在value 默认 移除line-height if (!value) { if (style) { styleStr = this.styleProcessing(styleList) //避免没有其它属性 只留下 ‘style’ 减少代码 if (styleStr === '') { st = `<${$(dom).getNodeName().toLowerCase()}>${dom.innerHTML}</${$(dom) .getNodeName() .toLowerCase()}>` } else { st = `<${$(dom).getNodeName().toLowerCase()} style="${styleStr}">${ dom.innerHTML }</${$(dom).getNodeName().toLowerCase()}>` } this.action(st, editor) } return } if (style) { //存在style 检索其它style属性 styleStr = this.styleProcessing(styleList) + `line-height:${value};` } else { styleStr = `line-height:${value};` } st = `<${$(dom).getNodeName().toLowerCase()} style="${styleStr}">${dom.innerHTML}</${$(dom) .getNodeName() .toLowerCase()}>` //防止BLOCKQUOTE叠加 or IE下导致P嵌套出现误删 if ($(dom).getNodeName() === 'BLOCKQUOTE' || UA.isIE()) { $(dom).css('line-height', value) } else { this.action(st, editor) } } /** * 遍历dom 获取祖父元素 直到contenteditable属性的div标签 * */ public getDom(dom: HTMLElement): HTMLElement { let DOM: HTMLElement = $(dom).elems[0] if (!DOM.parentNode) { return DOM } function getParentNode($node: HTMLElement, editor: Editor): HTMLElement { const $parent = $($node.parentNode) if (editor.$textElem.equal($parent)) { return $node } else { return getParentNode($parent.elems[0], editor) } } DOM = getParentNode(DOM, this.editor) return DOM } /** * 执行 document.execCommand * */ public action(html_str: string, editor: Editor): void { editor.cmd.do('insertHTML', html_str) } /** * style 处理 */ public styleProcessing(styleList: Array<string>): string { let styleStr = '' styleList.forEach(item => { item !== '' && item.indexOf('line-height') === -1 ? (styleStr = styleStr + item + ';') : '' }) return styleStr } /** * 段落全选 比如:避免11变成111 */ public setRange(startDom: Node, endDom: Node): void { const editor = this.editor let selection = window.getSelection ? window.getSelection() : document.getSelection() //清除所有的选区 selection?.removeAllRanges() const range = document.createRange() let star = startDom let end = endDom range.setStart(star, 0) range.setEnd(end, 1) selection?.addRange(range) //保存设置好的选区 editor.selection.saveRange() //清除所有的选区 selection?.removeAllRanges() //恢复选区 editor.selection.restoreSelection() } /** * 尝试修改菜单激活状态 */ public tryChangeActive(): void { const editor = this.editor const $selectionElem = editor.selection.getSelectionContainerElem() if ($selectionElem && editor.$textElem.equal($selectionElem)) { //避免选中多行设置 return } let dom: DomElement | HTMLElement = $(editor.selection.getSelectionStartElem()) // 有些情况下 dom 可能为空,比如编辑器初始化 if (dom.length === 0) return dom = this.getDom(dom.elems[0]) let style: string | null = dom.getAttribute('style') ? dom.getAttribute('style') : '' //判断当前标签是否具有line-height属性 if (style && style.indexOf('line-height') !== -1) { this.active() } else { this.unActive() } } } export default LineHeight