wangeditor
Version:
wangEditor - 轻量级 web 富文本编辑器,配置方便,使用简单,开源免费
293 lines (268 loc) • 10.4 kB
text/typescript
/**
* @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