feat: align and delete in table tool bar

This commit is contained in:
Jocs 2018-02-01 21:39:46 +08:00
parent 9f5f1ed1d9
commit 2e73062cb4
15 changed files with 215 additions and 24 deletions

View File

@ -7,6 +7,7 @@
- [ ] 在通过 Aganippe 打开文件时,无法通过右键选择 Aganippe。严重 bug
- [ ] 在通过 Aganippe 打开文件时,通过右键选择软件,但是打开无内容。(严重 bug
- [ ] export html: (3) keyframe 和 font-face 以及 bar-top 的样式都可以删除。(4) 打包后的应用 axios 获取样式有问题。(5) 输出的 html 中 a 标签无法点击
- [ ] table: 1. table 前面不能够点击出现光标。2. 处理 table 内容选中后的backspace enter 等。
**菜单**
@ -151,11 +152,11 @@ _ 底线
**表格功能**
* [ ] 输入`|xxx|xxx|`回车或其他失去 active 的操作生成2 * 2 表格。如果是回车p 1 1自动获取光标。
* [x] 输入`|xxx|xxx|`回车或其他失去 active 的操作生成2 * 2 表格。如果是回车p 1 1自动获取光标。
block 类型包括 table、thead、tr、th、tbody、td
* [ ] 处理表格内部的 enter、cmd + enter、backspace 键。
* [x] 处理表格内部的 enter、cmd + enter、backspace 键。
enter 光标跳转到下一行第一个cell。如果已经是最后一行光标跳转到下一的段落。

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="128px" height="120.47px" viewBox="0 0 1088 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#515151" d="M1088 124.032C1088 157.184 1061.12 184.064 1027.968 184.064L126.72 184.064C93.568 184.064 66.688 157.184 66.688 124.032L66.688 124.032C66.688 90.88 93.568 64 126.72 64L1027.968 64C1061.12 64 1088 90.88 1088 124.032L1088 124.032 1088 124.032ZM1088 892.032C1088 925.184 1061.12 952.064 1027.968 952.064L126.72 952.064C93.568 952.064 66.688 925.184 66.688 892.032L66.688 892.032C66.688 858.88 93.568 832 126.72 832L1027.968 832C1061.12 832 1088 858.88 1088 892.032L1088 892.032 1088 892.032ZM1088 508.032C1088 541.184 1061.12 568.064 1027.968 568.064L126.72 568.064C93.568 568.064 66.688 541.184 66.688 508.032L66.688 508.032C66.688 474.88 93.568 448 126.72 448L1027.968 448C1061.12 448 1088 474.88 1088 508.032L1088 508.032 1088 508.032ZM896 700.032C896 733.184 869.12 760.064 835.968 760.064L318.72 760.064C285.568 760.064 258.688 733.184 258.688 700.032L258.688 700.032C258.688 666.88 285.568 640 318.72 640L835.968 640C869.12 640 896 666.88 896 700.032L896 700.032 896 700.032ZM896 316.032C896 349.184 869.12 376.064 835.968 376.064L318.72 376.064C285.568 376.064 258.688 349.184 258.688 316.032L258.688 316.032C258.688 282.88 285.568 256 318.72 256L835.968 256C869.12 256 896 282.88 896 316.032L896 316.032 896 316.032Z" /></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="128px" height="128.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#515151" d="M1023.934464 768.016384l0 73.138176q0 14.856192-10.856448 25.71264t-25.71264 10.856448l-950.796288 0q-14.856192 0-25.71264-10.856448t-10.856448-25.71264l0-73.138176q0-14.856192 10.856448-25.71264t25.71264-10.856448l950.796288 0q14.856192 0 25.71264 10.856448t10.856448 25.71264zm-219.414528-219.414528l0 73.138176q0 14.856192-10.856448 25.71264t-25.71264 10.856448l-731.38176 0q-14.856192 0-25.71264-10.856448t-10.856448-25.71264l0-73.138176q0-14.856192 10.856448-25.71264t25.71264-10.856448l731.38176 0q14.856192 0 25.71264 10.856448t10.856448 25.71264zm146.276352-219.414528l0 73.138176q0 14.856192-10.856448 25.71264t-25.71264 10.856448l-877.658112 0q-14.856192 0-25.71264-10.856448t-10.856448-25.71264l0-73.138176q0-14.856192 10.856448-25.71264t25.71264-10.856448l877.658112 0q14.856192 0 25.71264 10.856448t10.856448 25.71264zm-219.414528-219.414528l0 73.138176q0 14.856192-10.856448 25.71264t-25.71264 10.856448l-658.243584 0q-14.856192 0-25.71264-10.856448t-10.856448-25.71264l0-73.138176q0-14.856192 10.856448-25.71264t25.71264-10.856448l658.243584 0q14.856192 0 25.71264 10.856448t10.856448 25.71264z" /></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="128px" height="128.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#515151" d="M1024 768l0 73.142857q0 14.857143-10.857143 25.714286t-25.714286 10.857143l-950.857143 0q-14.857143 0-25.714286-10.857143t-10.857143-25.714286l0-73.142857q0-14.857143 10.857143-25.714286t25.714286-10.857143l950.857143 0q14.857143 0 25.714286 10.857143t10.857143 25.714286zm0-219.428571l0 73.142857q0 14.857143-10.857143 25.714286t-25.714286 10.857143l-731.428571 0q-14.857143 0-25.714286-10.857143t-10.857143-25.714286l0-73.142857q0-14.857143 10.857143-25.714286t25.714286-10.857143l731.428571 0q14.857143 0 25.714286 10.857143t10.857143 25.714286zm0-219.428571l0 73.142857q0 14.857143-10.857143 25.714286t-25.714286 10.857143l-877.714286 0q-14.857143 0-25.714286-10.857143t-10.857143-25.714286l0-73.142857q0-14.857143 10.857143-25.714286t25.714286-10.857143l877.714286 0q14.857143 0 25.714286 10.857143t10.857143 25.714286zm0-219.428571l0 73.142857q0 14.857143-10.857143 25.714286t-25.714286 10.857143l-658.285714 0q-14.857143 0-25.714286-10.857143t-10.857143-25.714286l0-73.142857q0-14.857143 10.857143-25.714286t25.714286-10.857143l658.285714 0q14.857143 0 25.714286 10.857143t10.857143 25.714286z" /></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="128px" height="128.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#515151" d="M704 128c-8.384-49.536-50.56-64-92.8-64L407.872 64C365.632 64 328.448 78.464 320 128l0 64 384 0L704 128zM204.8 412.416l0 473.216C204.8 934.144 228.288 960 281.6 960l460.8 0c53.376 0 76.8-25.856 76.8-74.368L819.2 412.416c53.952 0 76.8-29.76 76.8-78.208C896 285.696 872.512 256 819.2 256L550.4 256 204.8 256C151.488 256 128 285.696 128 334.208 128 382.656 151.488 412.416 204.8 412.416zM576 387.968C576 386.496 576.576 385.408 576.704 384l62.656 0C639.488 385.408 640 386.496 640 387.968l0 414.464c0 39.424-64 39.424-64 0L576 387.968zM384 387.968C384 386.496 384.448 385.408 384.64 384l59.136 0c0.128 1.408 0.704 2.496 0.704 3.968l0 414.464c0 39.424-60.48 39.424-60.48 0L384 387.968z" /></svg>

After

Width:  |  Height:  |  Size: 973 B

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="128px" height="128.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#515151" d="M512 0C229.29408 0 0 229.248 0 512c0 282.76736 229.26336 512 512 512 282.76736 0 512-229.18144 512-512C1024 229.29408 794.76736 0 512 0z m209.90976 654.09024a47.5904 47.5904 0 0 1 14.08512 33.90976 47.95392 47.95392 0 0 1-47.99488 47.99488 47.75936 47.75936 0 0 1-33.90976-14.08512L512 579.89632l-141.99296 142.05952a47.9488 47.9488 0 0 1-34.00704 14.08512c-26.45504 0-47.95904-21.45792-47.95904-48.01536A47.68768 47.68768 0 0 1 302.08 654.15168L444.15488 512 302.1056 369.95584a47.88224 47.88224 0 0 1-14.03904-33.95584 47.99488 47.99488 0 0 1 81.92-33.95584l141.99296 142.07488L654.07488 302.08a47.92832 47.92832 0 0 1 33.90976-14.03904c26.48576 0 47.99488 21.45792 47.99488 47.99488a47.75424 47.75424 0 0 1-14.08512 33.95584l-141.99808 141.99296 142.01344 142.1056z m0 0" /></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg class="icon" width="128px" height="128.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path fill="#515151" d="M329.142857 786.285714v-109.714285q0-8-5.142857-13.142858t-13.142857-5.142857H128q-8 0-13.142857 5.142857t-5.142857 13.142858v109.714285q0 8 5.142857 13.142857t13.142857 5.142858h182.857143q8 0 13.142857-5.142858t5.142857-13.142857z m0-219.428571V457.142857q0-8-5.142857-13.142857t-13.142857-5.142857H128q-8 0-13.142857 5.142857t-5.142857 13.142857v109.714286q0 8 5.142857 13.142857t13.142857 5.142857h182.857143q8 0 13.142857-5.142857t5.142857-13.142857z m292.571429 219.428571v-109.714285q0-8-5.142857-13.142858t-13.142858-5.142857H420.571429q-8 0-13.142858 5.142857t-5.142857 13.142858v109.714285q0 8 5.142857 13.142857t13.142858 5.142858h182.857142q8 0 13.142858-5.142858t5.142857-13.142857zM329.142857 347.428571V237.714286q0-8-5.142857-13.142857t-13.142857-5.142858H128q-8 0-13.142857 5.142858t-5.142857 13.142857v109.714285q0 8 5.142857 13.142858t13.142857 5.142857h182.857143q8 0 13.142857-5.142857t5.142857-13.142858z m292.571429 219.428572V457.142857q0-8-5.142857-13.142857t-13.142858-5.142857H420.571429q-8 0-13.142858 5.142857t-5.142857 13.142857v109.714286q0 8 5.142857 13.142857t13.142858 5.142857h182.857142q8 0 13.142858-5.142857t5.142857-13.142857z m292.571428 219.428571v-109.714285q0-8-5.142857-13.142858t-13.142857-5.142857h-182.857143q-8 0-13.142857 5.142857t-5.142857 13.142858v109.714285q0 8 5.142857 13.142857t13.142857 5.142858h182.857143q8 0 13.142857-5.142858t5.142857-13.142857z m-292.571428-438.857143V237.714286q0-8-5.142857-13.142857t-13.142858-5.142858H420.571429q-8 0-13.142858 5.142858t-5.142857 13.142857v109.714285q0 8 5.142857 13.142858t13.142858 5.142857h182.857142q8 0 13.142858-5.142857t5.142857-13.142858z m292.571428 219.428572V457.142857q0-8-5.142857-13.142857t-13.142857-5.142857h-182.857143q-8 0-13.142857 5.142857t-5.142857 13.142857v109.714286q0 8 5.142857 13.142857t13.142857 5.142857h182.857143q8 0 13.142857-5.142857t5.142857-13.142857z m0-219.428572V237.714286q0-8-5.142857-13.142857t-13.142857-5.142858h-182.857143q-8 0-13.142857 5.142858t-5.142857 13.142857v109.714285q0 8 5.142857 13.142858t13.142857 5.142857h182.857143q8 0 13.142857-5.142857t5.142857-13.142858z m73.142857-182.857142v621.714285q0 37.714286-26.857142 64.571429t-64.571429 26.857143H128q-37.714286 0-64.571429-26.857143T36.571429 786.285714V164.571429q0-37.714286 26.857142-64.571429t64.571429-26.857143h768q37.714286 0 64.571429 26.857143t26.857142 64.571429z" /></svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -77,7 +77,8 @@ export const CLASS_OR_ID = genUpper2LowerKeyHash([
'AG_BULLET_LIST_ITEM',
'AG_TASK_LIST',
'AG_TASK_LIST_ITEM',
'AG_TASK_LIST_ITEM_CHECKBOX'
'AG_TASK_LIST_ITEM_CHECKBOX',
'AG_TABLE_TOOL_BAR'
])
export const codeMirrorConfig = {

View File

@ -15,11 +15,15 @@ const enterCtrl = ContentState => {
this.insertAfter(container, parent)
}
ContentState.prototype.createRow = function (columns) {
ContentState.prototype.createRow = function (row) {
const trBlock = this.createBlock('tr')
const len = row.children.length
let i
for (i = 0; i < columns; i++) {
for (i = 0; i < len; i++) {
const tdBlock = this.createBlock('td')
const preChild = row.children[i]
tdBlock.column = i
tdBlock.align = preChild.align
this.appendChild(trBlock, tdBlock)
}
return trBlock
@ -110,13 +114,14 @@ const enterCtrl = ContentState => {
if (!nextSibling) {
const rowContainer = this.getBlock(row.parent)
const table = this.getBlock(rowContainer.parent)
const figure = this.getBlock(table.parent)
if (rowContainer.type === 'thead') {
nextSibling = table.children[1]
} else if (table.nextSibling) {
nextSibling = this.getBlock(table.nextSibling)
} else if (figure.nextSibling) {
nextSibling = this.getBlock(figure.nextSibling)
} else {
nextSibling = this.createBlock('p')
this.insertAfter(nextSibling, table)
this.insertAfter(nextSibling, figure)
}
}
return this.firstInDescendant(nextSibling)
@ -127,7 +132,7 @@ const enterCtrl = ContentState => {
const rowContainer = this.getBlock(row.parent)
if (event.metaKey) {
const nextRow = this.createRow(row.children.length)
const nextRow = this.createRow(row)
if (rowContainer.type === 'thead') {
const tBody = this.getBlock(rowContainer.nextSibling)
this.insertBefore(nextRow, tBody.children[0])

View File

@ -76,9 +76,9 @@ class ContentState {
render () {
const { blocks, cursor } = this
const activeBlockKey = this.getActiveBlockKey()
const activeBlocks = this.getActiveBlocks()
this.stateRender.render(blocks, cursor, activeBlockKey)
this.stateRender.render(blocks, cursor, activeBlocks)
this.setCursor()
this.pre2CodeMirror()
console.log('render')
@ -237,13 +237,15 @@ class ContentState {
remove(this.blocks, block)
}
getActiveBlockKey () {
let block = this.getBlock(this.cursor.key)
if (!block) return null
while (block.parent) {
getActiveBlocks () {
let result = []
let block = this.getBlock(this.cursor.start.key)
if (block) result.push(block)
while (block && block.parent) {
block = this.getBlock(block.parent)
result.push(block)
}
return block.key
return result
}
getCursorBlock () {

View File

@ -1,9 +1,14 @@
import { isLengthEven } from '../utils'
import TableIcon from '../assets/icons/table.svg'
import LeftIcon from '../assets/icons/align-left.svg'
import CenterIcon from '../assets/icons/align-center.svg'
import RightIcon from '../assets/icons/align-right.svg'
import DeleteIcon from '../assets/icons/delete.svg'
const TABLE_BLOCK_REG = /^\|.*?(\\*)\|.*?(\\*)\|/
const tableBlockCtrl = ContentState => {
ContentState.prototype.createTable = function (block) {
ContentState.prototype.initTable = function (block) {
const { text } = block
const rowHeader = []
const len = text.length
@ -22,6 +27,7 @@ const tableBlockCtrl = ContentState => {
}
}
const colLen = rowHeader.length
const table = this.createBlock('table')
const tHead = this.createBlock('thead')
const headRow = this.createBlock('tr')
const tBody = this.createBlock('tbody')
@ -31,23 +37,102 @@ const tableBlockCtrl = ContentState => {
for (i = 0; i < colLen; i++) {
const headCell = this.createBlock('th', rowHeader[i])
const bodyCell = this.createBlock('td')
headCell.column = i
headCell.align = ''
bodyCell.column = i
bodyCell.align = ''
this.appendChild(headRow, headCell)
this.appendChild(bodyRow, bodyCell)
if (i === 0) result = bodyCell
}
block.type = 'table'
this.appendChild(table, tHead)
this.appendChild(table, tBody)
const toolBar = this.createBlock('div')
toolBar.editable = false
const ul = this.createBlock('ul')
const tools = [{
label: 'table',
icon: TableIcon
}, {
label: 'left',
icon: LeftIcon
}, {
label: 'center',
icon: CenterIcon
}, {
label: 'right',
icon: RightIcon
}, {
label: 'delete',
icon: DeleteIcon
}]
tools.forEach(tool => {
const toolBlock = this.createBlock('li')
const imgBlock = this.createBlock('img')
imgBlock.src = tool.icon
toolBlock.label = tool.label
this.appendChild(toolBlock, imgBlock)
this.appendChild(ul, toolBlock)
})
this.appendChild(toolBar, ul)
block.type = 'figure'
block.text = ''
block.children = []
this.appendChild(block, tHead)
this.appendChild(block, tBody)
this.appendChild(block, toolBar)
this.appendChild(block, table)
return result
}
ContentState.prototype.tableToolBarClick = function (type) {
const { start: { key } } = this.cursor
const block = this.getBlock(key)
if (!(/td|th/.test(block.type))) throw new Error('table is not active')
const { column, align } = block
const getTable = td => {
const row = this.getBlock(block.parent)
const rowContainer = this.getBlock(row.parent)
return this.getBlock(rowContainer.parent)
}
const table = getTable(block)
switch (type) {
case 'left':
case 'center':
case 'right': {
const newAlign = align === type ? '' : type
table.children.forEach(rowContainer => {
rowContainer.children.forEach(row => {
row.children[column].align = newAlign
})
})
this.render()
break
}
case 'delete': {
const figure = this.getBlock(table.parent)
figure.children = []
figure.type = 'p'
figure.text = ''
const key = figure.key
const offset = 0
this.cursor = {
start: { key, offset },
end: { key, offset }
}
this.render()
break
}
}
}
ContentState.prototype.tableBlockUpdate = function (block) {
const { type, text } = block
if (type !== 'li' && type !== 'p') return false
const match = TABLE_BLOCK_REG.exec(text)
return (match && isLengthEven(match[1]) && isLengthEven(match[2])) ? this.createTable(block) : false
return (match && isLengthEven(match[1]) && isLengthEven(match[2])) ? this.initTable(block) : false
}
}

View File

@ -29,7 +29,7 @@ const updateCtrl = ContentState => {
}
ContentState.prototype.checkInlineUpdate = function (block) {
if (/th|td/.test(block.type)) return false
if (/th|td|figure/.test(block.type)) return false
const { text } = block
const parent = this.getParent(block)
const [match, bullet, tasklist, order, header, blockquote, hr] = text.match(INLINE_UPDATE_REG) || []

View File

@ -21,10 +21,58 @@ h6.ag-active::before {
font-weight: 100;
}
figure {
padding: 0;
margin: 0;
margin-top: 18px;
position: relative;
}
.ag-table-tool-bar {
user-select: none;
position: absolute;
top: -20px;
left: 0;
display: none;
}
.ag-table-tool-bar ul {
height: 18px;
list-style: none;
margin: 0;
padding: 0;
display: flex;
}
.ag-table-tool-bar ul li {
box-sizing: border-box;
display: flex;
width: 18px;
height: 18px;
padding: 2px;
margin-right: 3px;
cursor: pointer;
border-radius: 3px;
}
.ag-table-tool-bar ul li img {
width: 14px;
height: 14px;
}
.ag-table-tool-bar ul li.active {
background: lightblue;
}
.ag-table-tool-bar ul li:hover {
background: #bbb;
}
figure.ag-active .ag-table-tool-bar {
display: block;
}
table {
width: 100%;
border-collapse: collapse;
border: 1px solid #ebeef5;
margin-top: 0;
}
a {

View File

@ -58,6 +58,7 @@ class Aganippe {
this.dispatchEnter()
this.dispatchUpdateState()
this.dispatchCopyCut()
this.dispatchTableToolBar()
}
/**
@ -269,9 +270,26 @@ class Aganippe {
eventCenter.attachDOMEvent(container, 'keydown', handler)
}
dispatchTableToolBar () {
const { container, eventCenter } = this
const handler = event => {
const target = event.target
const parent = target.parentNode
if (parent && parent.hasAttribute('data-label')) {
const type = parent.getAttribute('data-label')
this.contentState.tableToolBarClick(type)
}
}
eventCenter.attachDOMEvent(container, 'click', handler)
}
dispatchUpdateState () {
const { container, eventCenter } = this
const changeHandler = event => {
// const target = event.target
// const style = getComputedStyle(target)
// if (event.type === 'click' && !style.contenteditable) return
if (!this._isEditChinese || event.type === 'input') {
this.contentState.updateState(event)
}

View File

@ -49,12 +49,12 @@ class StateRender {
* [render]: 2 steps:
* render vdom
*/
render (blocks, cursor, activeBlockKey) {
render (blocks, cursor, activeBlocks) {
const selector = `${LOWERCASE_TAGS.div}#${CLASS_OR_ID['AG_EDITOR_ID']}`
const renderBlock = block => {
const type = block.type === 'hr' ? 'p' : block.type
const isActive = block.key === activeBlockKey || block.key === cursor.start.key
const isActive = activeBlocks.some(b => b.key === block.key) || block.key === cursor.start.key
let blockSelector = isActive
? `${type}#${block.key}.${CLASS_OR_ID['AG_PARAGRAPH']}.${CLASS_OR_ID['AG_ACTIVE']}`
@ -66,6 +66,10 @@ class StateRender {
}
if (block.children.length) {
if (/div/.test(block.type) && !block.editable) {
blockSelector += `.${CLASS_OR_ID['AG_TABLE_TOOL_BAR']}`
Object.assign(data.attrs, { contenteditable: 'false' })
}
if (/ul|ol/.test(block.type) && block.listType) {
switch (block.listType) {
case 'order':
@ -81,6 +85,15 @@ class StateRender {
break
}
}
if (block.type === 'li' && block.label) {
const { label } = block
const { align } = activeBlocks[0]
if (align && block.label === align) {
blockSelector += '.active'
}
Object.assign(data.dataset, { label })
}
if (block.type === 'li' && block.listItemType) {
switch (block.listItemType) {
case 'order':
@ -109,6 +122,18 @@ class StateRender {
}, [])
: [ h(LOWERCASE_TAGS.br) ]
if (/th|td/.test(block.type)) {
const { align } = block
if (align) {
Object.assign(data.attrs, { style: `text-align:${align}` })
}
}
if (/img/.test(block.type)) {
const { src } = block
Object.assign(data.attrs, { src })
children = ''
}
if (/^h\d$/.test(block.type)) {
Object.assign(data.dataset, { head: block.type })
}