Skip to content

Commit

Permalink
fix: 修复单元格内的换行在编辑模式下的视觉错误 (#264)
Browse files Browse the repository at this point in the history
* feat: change table line break

* feat: add semi-selective line break deletion

* feat: add fragment handle

* Create angry-llamas-repair.md

* feat: add boundary handling and null checks
  • Loading branch information
HaibaraAiAPTX authored Oct 18, 2024
1 parent 5806829 commit c18aa53
Show file tree
Hide file tree
Showing 8 changed files with 375 additions and 13 deletions.
7 changes: 7 additions & 0 deletions .changeset/angry-llamas-repair.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
"@wangeditor-next/core": patch
"@wangeditor-next/editor": patch
"@wangeditor-next/table-module": patch
---

fix: 修复单元格内的换行在编辑模式下的视觉错误
2 changes: 1 addition & 1 deletion packages/core/src/editor/dom-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -713,7 +713,7 @@ export const DomEditor = {
// 未设置 maxLength ,则返回 number 最大值
if (typeof maxLength !== 'number' || maxLength <= 0) { return Infinity }

const editorText = editor.getText().replace(/\r|\n|(\r\n)/g, '') // 去掉换行
const editorText = editor.getText().replace(/\r|\n|(\r\n)|(\n\r)/g, '') // 去掉换行
const curLength = editorText.length
const leftLength = maxLength - curLength

Expand Down
22 changes: 16 additions & 6 deletions packages/core/src/editor/plugins/with-event-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@
* @author wangfupeng
*/

import { Editor, Node, Transforms, Range } from 'slate'
import { DomEditor } from '../dom-editor'
import { IDomEditor } from '../..'
import {
Editor, Node, Range, Transforms,
} from 'slate'

import { isDOMText, getPlainText } from '../../utils/dom'
import { IDomEditor } from '../..'
import { getPlainText, isDOMText } from '../../utils/dom'
import { DomEditor } from '../dom-editor'

export const withEventData = <T extends Editor>(editor: T) => {
const e = editor as T & IDomEditor
Expand Down Expand Up @@ -49,6 +51,7 @@ export const withEventData = <T extends Editor>(editor: T) => {
const [voidNode] = endVoid
const r = domRange.cloneRange()
const domNode = DomEditor.toDOMNode(e, voidNode)

r.setEndAfter(domNode)
contents = r.cloneContents()
}
Expand All @@ -65,6 +68,7 @@ export const withEventData = <T extends Editor>(editor: T) => {
// show up elsewhere when pasted.
Array.from(contents.querySelectorAll('[data-slate-zero-width]')).forEach(zw => {
const isNewline = zw.getAttribute('data-slate-zero-width') === 'n'

zw.textContent = isNewline ? '\n' : ''
})

Expand All @@ -75,6 +79,7 @@ export const withEventData = <T extends Editor>(editor: T) => {
const span = attach.ownerDocument.createElement('span')
// COMPAT: In Chrome and Safari, if we don't add the `white-space` style
// then leading and trailing spaces will be ignored. (2017/09/21)

span.style.whiteSpace = 'pre'
span.appendChild(attach)
contents.appendChild(span)
Expand All @@ -84,11 +89,13 @@ export const withEventData = <T extends Editor>(editor: T) => {
const fragment = e.getFragment()
const string = JSON.stringify(fragment)
const encoded = window.btoa(encodeURIComponent(string))

attach.setAttribute('data-slate-fragment', encoded)
data.setData('application/x-slate-fragment', encoded)

// Add the content to a <div> so that we can get its inner HTML.
const div = contents.ownerDocument.createElement('div')

div.appendChild(contents)
div.setAttribute('hidden', 'true')
contents.ownerDocument.body.appendChild(div)
Expand All @@ -102,9 +109,11 @@ export const withEventData = <T extends Editor>(editor: T) => {
e.insertData = (data: DataTransfer) => {
const fragment = data.getData('application/x-slate-fragment')
// 只有从编辑器中内复制的内容,才会获取 fragment,从其他地方粘贴到编辑器中,不会获取 fragment

if (fragment) {
const decoded = decodeURIComponent(window.atob(fragment))
const parsed = JSON.parse(decoded) as Node[]

e.insertFragment(parsed)
return
}
Expand All @@ -119,8 +128,9 @@ export const withEventData = <T extends Editor>(editor: T) => {
}

const leftLength = DomEditor.getLeftLengthOfMaxLength(e)

if (text) {
const lines = text.split(/\r\n|\r|\n/)
const lines = text.split(/\n\r|\r\n|\r|\n/)
let split = false

for (const line of lines) {
Expand All @@ -132,7 +142,7 @@ export const withEventData = <T extends Editor>(editor: T) => {
e.insertText(line)
split = true
}
return

}
}

Expand Down
12 changes: 8 additions & 4 deletions packages/core/src/to-html/text2html.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,16 @@
*/

import { Text } from 'slate'
import { IDomEditor } from '../editor/interface'

import { DomEditor } from '../editor/dom-editor'
import { STYLE_TO_HTML_FN_LIST } from './index'
import { IDomEditor } from '../editor/interface'
import { replaceHtmlSpecialSymbols } from '../utils/util'
import { STYLE_TO_HTML_FN_LIST } from './index'

function textToHtml(textNode: Text, editor: IDomEditor): string {
const { text } = textNode
if (text == null) throw new Error(`Current node is not slate Text ${JSON.stringify(textNode)}`)

if (text == null) { throw new Error(`Current node is not slate Text ${JSON.stringify(textNode)}`) }
let textHtml = text

// 替换 html 特殊字符
Expand All @@ -21,8 +23,9 @@ function textToHtml(textNode: Text, editor: IDomEditor): string {
const parents = DomEditor.getParentsNodes(editor, textNode)
const hasPre = parents.some(p => DomEditor.getNodeType(p) === 'pre') // 上级节点中,是否存在 <pre>
// 在 <pre> 标签不替换,其他都替换

if (!hasPre) {
textHtml = textHtml.replace(/\r\n|\r|\n/g, '<br>')
textHtml = textHtml.replace(/\n\r|\r\n|\r|\n/g, '<br>')
}

// 在 <pre> 内部,&nbsp; 替换为空格
Expand All @@ -33,6 +36,7 @@ function textToHtml(textNode: Text, editor: IDomEditor): string {
// 处理空字符串
if (textHtml === '') {
const parentNode = DomEditor.getParentNode(null, textNode)

if (parentNode && parentNode.children.length === 0) {
// textNode 是唯一的子节点,则改为 <br>
textHtml = '<br>'
Expand Down
2 changes: 1 addition & 1 deletion packages/editor/examples/default-mode.html
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,4 @@
</script>
</body>

</html>
</html>
1 change: 1 addition & 0 deletions packages/editor/examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ <h1>wangEditor examples</h1>
<li><a href="./batch-destroy.html">Batch destroy, test memory leak 批量销毁,测试内存泄漏</a></li>
<li><a href="./content-to-html.html">Content to html</a></li>
<li><a href="./new-menu.html">New menu 新注册菜单</a></li>
<li><a href="./table-line-break.html">表格换行符测试</a></li>
</ul>
</body>
</html>
212 changes: 212 additions & 0 deletions packages/editor/examples/table-line-break.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>simple mode</title>
<link href="https://cdn.bootcdn.net/ajax/libs/normalize/8.0.1/normalize.min.css" rel="stylesheet">
<link href="./css/view.css" rel="stylesheet">
<link href="./css/editor.css" rel="stylesheet">
<link href="../dist/css/style.css" rel="stylesheet">
</head>

<body>
<p>表格换行符测试</p>

<!-- 编辑器 -->
<div>
<div id="editor-toolbar" class="editor-toolbar"></div>
<div id="editor-text-area" class="editor-text-area"></div>
</div>

<div>
selection: <span id="selection-text"></span>
</div>

<div style="margin-top: 20px;">
<textarea id="text-content" readonly style="width: 100%; height: 500px;"></textarea>
</div>

<script src="js/init-content.js"></script>
<script src="../dist/index.js"></script>
<script>
const E = window.wangEditor

const content = [
{
"type": "paragraph",
"children": [
{
"text": ""
}
]
},
{
"type": "table",
"width": "auto",
"children": [
{
"type": "table-row",
"children": [
{
"type": "table-cell",
"children": [
{
"text": "多行文本\n\r"
},
{
"text": "多行文本",
"bgColor": "rgb(225, 60, 57)"
},
{
"text": "\n\r多行文本",
"bgColor": "rgb(251, 233, 230)"
},
{
"text": "\n\r"
},
{
"text": "多行文本\n\r多行文本",
"bgColor": "rgb(255, 77, 79)"
},
],
"isHeader": true
}
]
}
],
"columnWidths": [
276
],
"scrollWidth": 276,
"height": 127,
"isHoverCellBorder": true,
"resizingIndex": 0,
"isResizing": false
},
{
"type": "paragraph",
"children": [
{
"text": ""
}
],
"isHoverCellBorder": false,
"resizingIndex": -1,
"isResizing": false
}
]

// const content = [
// {
// "type": "paragraph",
// "children": [
// {
// "text": ""
// }
// ]
// },
// {
// "type": "table",
// "width": "auto",
// "children": [
// {
// "type": "table-row",
// "children": [
// {
// "type": "table-cell",
// "children": [
// {
// "type": "paragraph",
// "children": [
// {
// "text": "多行文本"
// }
// ]
// },
// {
// "type": "paragraph",
// "children": [
// {
// "text": "多行文本",
// "bgColor": "rgb(225, 60, 57)"
// }
// ]
// },
// {
// "type": "paragraph",
// "children": [
// {
// "text": "多行文本",
// "bgColor": "rgb(251, 233, 230)"
// }
// ]
// },
// {
// "type": "paragraph",
// "children": [
// {
// "text": "多行文本",
// "bgColor": "rgb(255, 77, 79)"
// }
// ]
// },
// ],
// "isHeader": true
// }
// ]
// }
// ],
// "columnWidths": [
// 276
// ],
// "scrollWidth": 276,
// "height": 127,
// "isHoverCellBorder": true,
// "resizingIndex": 0,
// "isResizing": false
// },
// {
// "type": "paragraph",
// "children": [
// {
// "text": ""
// }
// ],
// "isHoverCellBorder": false,
// "resizingIndex": -1,
// "isResizing": false
// }
// ]

// editor 配置
const editorConfig = {}
editorConfig.placeholder = '请输入内容123...'
editorConfig.onChange = (editor) => {
const contentStr = JSON.stringify(editor.children, null, 2)
document.getElementById('text-content').innerHTML = contentStr
console.log(editor.getHtml())
const selection = editor.selection
document.getElementById('selection-text').innerHTML = JSON.stringify(selection, null, 2)
console.log(selection)
}

const editor = E.createEditor({
selector: '#editor-text-area',
config: editorConfig,
content,
mode: 'simple'
})

const toolbar = E.createToolbar({
editor,
selector: '#editor-toolbar',
config: {},
mode: 'simple'
})
</script>
</body>

</html>
Loading

0 comments on commit c18aa53

Please sign in to comment.