Skip to content

Commit

Permalink
fix: 修复选中段落设置行高,最后一行的内容会被重复添加的问题
Browse files Browse the repository at this point in the history
  • Loading branch information
Gavin-yh committed Dec 20, 2021
1 parent 38b4b22 commit 46e49a9
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 150 deletions.
201 changes: 57 additions & 144 deletions src/menus/lineHeight/index.ts
Expand Up @@ -9,7 +9,6 @@ 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) {
Expand Down Expand Up @@ -39,163 +38,81 @@ class LineHeight extends DropListMenu implements MenuActive {
* @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 $containerElem = $(editor.selection.getSelectionContainerElem())

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 (!$containerElem.elems.length) return

//选中多行操作
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> = []
if ($containerElem && editor.$textElem.equal($containerElem)) {
// 标识是否可以设置行高的样式
let setStyleLock: boolean = false

//获取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++
}
const selectionStartElem: HTMLElement = $(editor.selection.getSelectionStartElem())
.elems[0]
const SelectionEndElem: HTMLElement = $(editor.selection.getSelectionEndElem()).elems[0]

//设置段落选取 全选
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++
// 获取选区中,在contenteditable下的直接父元素
const StartElemWrap: HTMLElement = this.getDom(selectionStartElem)
const EndElemWrap: HTMLElement = this.getDom(SelectionEndElem)

const containerElemChildren = $containerElem.elems[0].children

for (let i = 0; i < containerElemChildren.length; i++) {
const item: HTMLElement = containerElemChildren[i] as HTMLElement

// 目前只支持p 段落标签设置行高
if ($(item).getNodeName() !== 'P') {
continue
}
}
//设置替换的选区
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 (item === StartElemWrap) {
setStyleLock = true
}

if (!isIE) {
st += `<${$(item).getNodeName().toLowerCase()} style="${styleStr}">${
item.innerHTML
}</${$(item).getNodeName().toLowerCase()}>`
} else {
// 证明在区间节点里
if (setStyleLock) {
$(item).css('line-height', value)
}
})

if (st) {
this.action(st, editor)
if (item === EndElemWrap) {
setStyleLock = false

// 当设置完选择的EndElemWrap时,就可以退出
return
}
}
}

//恢复已选择的选区
dom = $selectionAll.elems[0]
this.setRange(dom.children[indexStore[0]], dom.children[indexStore[1]])
//重新设置选区
editor.selection.createRangeByElems(selectionStartElem, SelectionEndElem)

return
}

//遍历dom 获取祖父元素 直到contenteditable属性的div标签
dom = this.getDom(dom)
// 单行操作
// 选中区间的dom元素
const selectElem = $containerElem.elems[0]

//校验允许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)
}
// 获取选区中,在contenteditable下的直接父元素
const selectElemWrapdom = this.getDom(selectElem)

// 目前只支持p 段落标签设置行高
if ($(selectElemWrapdom).getNodeName() !== 'P') {
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)
}
$(selectElemWrapdom).css('line-height', value)

//重新设置选区
editor.selection.createRangeByElems(selectElemWrapdom, selectElemWrapdom)

return
}

/**
Expand All @@ -220,16 +137,10 @@ class LineHeight extends DropListMenu implements MenuActive {
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 = ''
Expand All @@ -243,6 +154,8 @@ class LineHeight extends DropListMenu implements MenuActive {

/**
* 段落全选 比如:避免11变成111
*
* 废弃的方法
*/
public setRange(startDom: Node, endDom: Node): void {
const editor = this.editor
Expand Down
20 changes: 14 additions & 6 deletions test/unit/menus/lineHeight.test.ts
Expand Up @@ -32,8 +32,13 @@ describe('LineHeight menu', () => {
mockCmdFn(document)
const cmdVal = '2'
lineHeightMenu.command(cmdVal)
// 此处触发 editor.cmd.do('insertHTML', xx),可以被 jest 成功执行,具体参考 mockCmdFn 的描述
expect(editor.$textElem.elems[0]).toContainHTML('<p style="line-height:2;"><br></p>')

// 空状态下,设置行高
expect(
editor.$textElem.elems[0].innerHTML.indexOf(
'<p data-we-empty-p="" style="line-height:2;"><br></p>'
)
).toBeGreaterThanOrEqual(0)
})

test('lineHeight 菜单:选择多行增加行高', () => {
Expand Down Expand Up @@ -99,7 +104,8 @@ describe('LineHeight menu', () => {

const cmdVal = '2'
lineHeightMenu.command(cmdVal)
// 此处触发 editor.cmd.do('insertHTML', xx),可以被 jest 成功执行,具体参考 mockCmdFn 的描述

// 设置多行时,只针对p, 段落标签其设置
expect(
editor.$textElem.elems[0].innerHTML.indexOf(
'<div>345</div><div>234<span>123</span></div>'
Expand All @@ -110,15 +116,16 @@ describe('LineHeight menu', () => {
test('lineHeight 菜单:增加行高, 如果不传value值,则设为默认行高,并且不设置 line-height 样式', () => {
mockCmdFn(document)

editor.txt.html('<p style="color:red;">123</p>')
editor.txt.html('<p style="color:red">123</p>')

const [startNode] = Array.from(editor.$textElem.elems[0].childNodes)
lineHeightMenu.setRange(startNode, startNode)

lineHeightMenu.command('')
// 此处触发 editor.cmd.do('insertHTML', xx),可以被 jest 成功执行,具体参考 mockCmdFn 的描述

expect(
editor.$textElem.elems[0].innerHTML.indexOf('<p style="color:red;">123</p>')
editor.$textElem.elems[0].innerHTML.indexOf('<p style="color:red">123</p>')
).toBeGreaterThanOrEqual(0)
})

Expand All @@ -132,9 +139,10 @@ describe('LineHeight menu', () => {

lineHeightMenu.command('2')
// 此处触发 editor.cmd.do('insertHTML', xx),可以被 jest 成功执行,具体参考 mockCmdFn 的描述

expect(
editor.$textElem.elems[0].innerHTML.indexOf(
'<p style="color:red;line-height:2;">123</p>'
'<p style="color:red; line-height:2;">123</p>'
)
).toBeGreaterThanOrEqual(0)
})
Expand Down

0 comments on commit 46e49a9

Please sign in to comment.