feat:add table context menus

pr675
黄云飞 4 years ago
parent 1da03664db
commit 6c20ade9c8

@ -223,7 +223,7 @@
}
.contextmenu-content .contextmenu-item {
width: 180px;
min-width: 140px;
padding: 0 32px 0 16px;
height: 30px;
display: flex;
@ -248,6 +248,7 @@
flex: 1;
text-align: right;
line-height: 30px;
margin-left: 20px;
}
.contextmenu-content .contextmenu-item i {
@ -269,4 +270,40 @@
.contextmenu-print {
background-image: url(../../assets/images/print.svg);
}
.contextmenu-insert-row-col {
background-image: url(../../assets/images/insert-row-col.svg);
}
.contextmenu-insert-top-row {
background-image: url(../../assets/images/insert-top-row.svg);
}
.contextmenu-insert-bottom-row {
background-image: url(../../assets/images/insert-bottom-row.svg);
}
.contextmenu-insert-left-col {
background-image: url(../../assets/images/insert-left-col.svg);
}
.contextmenu-insert-right-col {
background-image: url(../../assets/images/insert-right-col.svg);
}
.contextmenu-delete-row-col {
background-image: url(../../assets/images/delete-row-col.svg);
}
.contextmenu-delete-row {
background-image: url(../../assets/images/delete-row.svg);
}
.contextmenu-delete-col {
background-image: url(../../assets/images/delete-col.svg);
}
.contextmenu-delete-table {
background-image: url(../../assets/images/delete-table.svg);
}

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path stroke="#929AA8" d="M5.5 7.5v-6h4v6z"/><path fill="#3D4757" d="M13 15V3h1v12zM1 15V3h1v12zm4 0v-1h1v1zm1-1v-1h1v1zm1-1v-1h1v1zm-1-1v-1h1v1zm-1-1v-1h1v1zm3 1v-1h1v1zm0 2v-1h1v1zm1-3v-1h1v1zm0 4v-1h1v1z"/></g></svg>

After

Width:  |  Height:  |  Size: 317 B

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path stroke="#929AA8" d="M8.5 6.5h6v2h-6z"/><path fill="#3D4757" d="M2 12h11v1H2zM2 2h11v1H2zm.63 3L7 9.35l-.635.65L2 5.63z"/><path fill="#3D4757" d="M2 9.363L6.355 5 7 5.707 2.695 10z"/></g></svg>

After

Width:  |  Height:  |  Size: 296 B

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path stroke="#929AA8" d="M8.5 5.5h6v4h-6z"/><path fill="#3D4757" d="M1 13h12v1H1zM1 1h12v1H1zm0 4h1v1H1zm1 1h1v1H2zm1 1h1v1H3zm1-1h1v1H4zm1-1h1v1H5zM4 8h1v1H4zM2 8h1v1H2zm3 1h1v1H5zM1 9h1v1H1z"/></g></svg>

After

Width:  |  Height:  |  Size: 304 B

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757" fill-rule="evenodd"><path d="M14 13h-1v-3H2v3H1v-3a1 1 0 011-1h11a1 1 0 011 1v3z" fill-rule="nonzero"/><path d="M5.625 2L10 6.375 9.375 7 5 2.625z"/><path d="M5 6.375L9.375 2l.625.625L5.625 7z"/></g></svg>

After

Width:  |  Height:  |  Size: 286 B

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#3D4757" d="M7 11h1V8H7zm.5 3L5 11h5z"/><rect stroke="#3D4757" x="1.5" y="2.5" width="12" height="3" rx="1"/></g></svg>

After

Width:  |  Height:  |  Size: 229 B

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#3D4757" d="M11 7v1h3V7zm-3 .5L11 5v5z"/><rect stroke="#3D4757" transform="rotate(90 4 7.5)" x="-2" y="6" width="12" height="3" rx="1"/></g></svg>

After

Width:  |  Height:  |  Size: 256 B

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#3D4757" d="M5 8V7H2v1zm3-.5L5 10V5z"/><rect stroke="#3D4757" transform="rotate(90 12 7.5)" x="6" y="6" width="12" height="3" rx="1"/></g></svg>

After

Width:  |  Height:  |  Size: 254 B

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path stroke="#3D4757" d="M8.5 5.5h6v4h-6z"/><path fill="#3D4757" d="M4 7v1h2V7zm-3 .5L4 5v5zM1 1h12v1H1zm0 12h12v1H1z"/></g></svg>

After

Width:  |  Height:  |  Size: 229 B

@ -0,0 +1 @@
<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#3D4757" d="M8 5H7v3h1zm-.5-3L10 5H5z"/><rect stroke="#3D4757" x="1.5" y="10.5" width="12" height="3" rx="1"/></g></svg>

After

Width:  |  Height:  |  Size: 230 B

@ -26,6 +26,13 @@ export class Command {
private static right: Function
private static rowMargin: Function
private static insertTable: Function
private static insertTableTopRow: Function
private static insertTableBottomRow: Function
private static insertTableLeftCol: Function
private static insertTableRightCol: Function
private static deleteTableRow: Function
private static deleteTableCol: Function
private static deleteTable: Function
private static image: Function
private static search: Function
private static print: Function
@ -56,6 +63,13 @@ export class Command {
Command.right = adapt.rowFlex.bind(adapt)
Command.rowMargin = adapt.rowMargin.bind(adapt)
Command.insertTable = adapt.insertTable.bind(adapt)
Command.insertTableTopRow = adapt.insertTableTopRow.bind(adapt)
Command.insertTableBottomRow = adapt.insertTableBottomRow.bind(adapt)
Command.insertTableLeftCol = adapt.insertTableLeftCol.bind(adapt)
Command.insertTableRightCol = adapt.insertTableRightCol.bind(adapt)
Command.deleteTableRow = adapt.deleteTableRow.bind(adapt)
Command.deleteTableCol = adapt.deleteTableCol.bind(adapt)
Command.deleteTable = adapt.deleteTable.bind(adapt)
Command.image = adapt.image.bind(adapt)
Command.search = adapt.search.bind(adapt)
Command.print = adapt.print.bind(adapt)
@ -156,6 +170,34 @@ export class Command {
return Command.insertTable(row, col)
}
public executeInsertTableTopRow() {
return Command.insertTableTopRow()
}
public executeInsertTableBottomRow() {
return Command.insertTableBottomRow()
}
public executeInsertTableLeftCol() {
return Command.insertTableLeftCol()
}
public executeInsertTableRightCol() {
return Command.insertTableRightCol()
}
public executDeleteTableRow() {
return Command.deleteTableRow()
}
public executDeleteTableCol() {
return Command.deleteTableCol()
}
public executDeleteTable() {
return Command.deleteTable()
}
public executeImage(payload: IDrawImagePayload) {
return Command.image(payload)
}

@ -15,6 +15,7 @@ import { getUUID } from "../../utils"
import { formatElementList } from "../../utils/element"
import { printImageBase64 } from "../../utils/print"
import { Draw } from "../draw/Draw"
import { TableTool } from "../draw/particle/table/TableTool"
import { CanvasEvent } from "../event/CanvasEvent"
import { HistoryManager } from "../history/HistoryManager"
import { Position } from "../position/Position"
@ -22,11 +23,14 @@ import { RangeManager } from "../range/RangeManager"
export class CommandAdapt {
private readonly defaultWidth: number = 40
private draw: Draw
private range: RangeManager
private position: Position
private historyManager: HistoryManager
private canvasEvent: CanvasEvent
private tableTool: TableTool
private options: Required<IEditorOption>
constructor(draw: Draw) {
@ -35,6 +39,7 @@ export class CommandAdapt {
this.position = draw.getPosition()
this.historyManager = draw.getHistoryManager()
this.canvasEvent = draw.getCanvasEvent()
this.tableTool = draw.getTableTool()
this.options = draw.getOptions()
}
@ -291,6 +296,364 @@ export class CommandAdapt {
this.draw.render({ curIndex, isSetCursor: false })
}
public insertTableTopRow() {
const positionContext = this.position.getPositionContext()
if (!positionContext.isTable) return
const { index, trIndex, tableId } = positionContext
const originalElementList = this.draw.getOriginalElementList()
const element = originalElementList[index!]
const curTrList = element.trList!
const curTr = curTrList[trIndex!]
// 之前跨行的增加跨行数
if (curTr.tdList.length < element.colgroup!.length) {
const curTrNo = curTr.tdList[0].rowIndex!
for (let t = 0; t < trIndex!; t++) {
const tr = curTrList[t]
for (let d = 0; d < tr.tdList.length; d++) {
const td = tr.tdList[d]
if (td.rowspan > 1 && td.rowIndex! + td.rowspan >= curTrNo + 1) {
td.rowspan += 1
}
}
}
}
// 增加当前行
const newTrId = getUUID()
const newTr: ITr = {
height: curTr.height,
id: newTrId,
tdList: []
}
for (let t = 0; t < curTr.tdList.length; t++) {
const curTd = curTr.tdList[t]
const newTdId = getUUID()
newTr.tdList.push({
id: newTdId,
rowspan: 1,
colspan: curTd.colspan,
value: [{
value: ZERO,
size: 16,
tableId,
trId: newTrId,
tdId: newTdId
}]
})
}
curTrList.splice(trIndex!, 0, newTr)
// 重新设置上下文
this.position.setPositionContext({
isTable: true,
index,
trIndex,
tdIndex: 0,
tdId: newTr.tdList[0].id,
trId: newTr.id,
tableId
})
this.range.setRange(0, 0)
// 重新渲染
this.draw.render({ curIndex: 0 })
const position = this.position.getOriginalPositionList()
this.tableTool.render(element, position[index!])
}
public insertTableBottomRow() {
const positionContext = this.position.getPositionContext()
if (!positionContext.isTable) return
const { index, trIndex, tableId } = positionContext
const originalElementList = this.draw.getOriginalElementList()
const element = originalElementList[index!]
const curTrList = element.trList!
const curTr = curTrList[trIndex!]
const anchorTr = curTrList.length - 1 === trIndex
? curTr
: curTrList[trIndex! + 1]
// 之前/当前行跨行的增加跨行数
if (anchorTr.tdList.length < element.colgroup!.length) {
const curTrNo = anchorTr.tdList[0].rowIndex!
for (let t = 0; t < trIndex! + 1; t++) {
const tr = curTrList[t]
for (let d = 0; d < tr.tdList.length; d++) {
const td = tr.tdList[d]
if (td.rowspan > 1 && td.rowIndex! + td.rowspan >= curTrNo + 1) {
td.rowspan += 1
}
}
}
}
// 增加当前行
const newTrId = getUUID()
const newTr: ITr = {
height: anchorTr.height,
id: newTrId,
tdList: []
}
for (let t = 0; t < anchorTr.tdList.length; t++) {
const curTd = anchorTr.tdList[t]
const newTdId = getUUID()
newTr.tdList.push({
id: newTdId,
rowspan: 1,
colspan: curTd.colspan,
value: [{
value: ZERO,
size: 16,
tableId,
trId: newTrId,
tdId: newTdId
}]
})
}
curTrList.splice(trIndex! + 1, 0, newTr)
// 重新设置上下文
this.position.setPositionContext({
isTable: true,
index,
trIndex: trIndex! + 1,
tdIndex: 0,
tdId: newTr.tdList[0].id,
trId: newTr.id,
tableId
})
this.range.setRange(0, 0)
// 重新渲染
this.draw.render({ curIndex: 0 })
const position = this.position.getOriginalPositionList()
this.tableTool.render(element, position[index!])
}
public insertTableLeftCol() {
const positionContext = this.position.getPositionContext()
if (!positionContext.isTable) return
const { index, tdIndex, tableId } = positionContext
const originalElementList = this.draw.getOriginalElementList()
const element = originalElementList[index!]
const curTrList = element.trList!
const curTdIndex = tdIndex!
// 增加列
for (let t = 0; t < curTrList.length; t++) {
const tr = curTrList[t]
const tdId = getUUID()
tr.tdList.splice(curTdIndex, 0, {
id: tdId,
rowspan: 1,
colspan: 1,
value: [{
value: ZERO,
size: 16,
tableId,
trId: tr.id,
tdId
}]
})
}
// 重新计算宽度
const colgroup = element.colgroup!
colgroup.splice(curTdIndex, 0, {
width: this.defaultWidth
})
const colgroupWidth = colgroup.reduce((pre, cur) => pre + cur.width, 0)
const width = this.draw.getOriginalInnerWidth()
if (colgroupWidth > width) {
const adjustWidth = (colgroupWidth - width) / colgroup.length
for (let g = 0; g < colgroup.length; g++) {
const group = colgroup[g]
group.width -= adjustWidth
}
}
// 重新设置上下文
this.position.setPositionContext({
isTable: true,
index,
trIndex: 0,
tdIndex: curTdIndex,
tdId: curTrList[0].tdList[curTdIndex].id,
trId: curTrList[0].id,
tableId
})
this.range.setRange(0, 0)
// 重新渲染
this.draw.render({ curIndex: 0 })
const position = this.position.getOriginalPositionList()
this.tableTool.render(element, position[index!])
}
public insertTableRightCol() {
const positionContext = this.position.getPositionContext()
if (!positionContext.isTable) return
const { index, tdIndex, tableId } = positionContext
const originalElementList = this.draw.getOriginalElementList()
const element = originalElementList[index!]
const curTrList = element.trList!
const curTdIndex = tdIndex! + 1
// 增加列
for (let t = 0; t < curTrList.length; t++) {
const tr = curTrList[t]
const tdId = getUUID()
tr.tdList.splice(curTdIndex, 0, {
id: tdId,
rowspan: 1,
colspan: 1,
value: [{
value: ZERO,
size: 16,
tableId,
trId: tr.id,
tdId
}]
})
}
// 重新计算宽度
const colgroup = element.colgroup!
colgroup.splice(curTdIndex, 0, {
width: this.defaultWidth
})
const colgroupWidth = colgroup.reduce((pre, cur) => pre + cur.width, 0)
const width = this.draw.getOriginalInnerWidth()
if (colgroupWidth > width) {
const adjustWidth = (colgroupWidth - width) / colgroup.length
for (let g = 0; g < colgroup.length; g++) {
const group = colgroup[g]
group.width -= adjustWidth
}
}
// 重新设置上下文
this.position.setPositionContext({
isTable: true,
index,
trIndex: 0,
tdIndex: curTdIndex,
tdId: curTrList[0].tdList[curTdIndex].id,
trId: curTrList[0].id,
tableId
})
this.range.setRange(0, 0)
// 重新渲染
this.draw.render({ curIndex: 0 })
const position = this.position.getOriginalPositionList()
this.tableTool.render(element, position[index!])
}
public deleteTableRow() {
const positionContext = this.position.getPositionContext()
if (!positionContext.isTable) return
const { index, trIndex } = positionContext
const originalElementList = this.draw.getOriginalElementList()
const element = originalElementList[index!]
const curTrList = element.trList!
const curTr = curTrList[trIndex!]
// 如果是最后一行,直接删除整个表格
if (curTrList.length <= 1) {
this.deleteTable()
return
}
// 补跨行
for (let d = 0; d < curTr.tdList.length; d++) {
const td = curTr.tdList[d]
if (td.rowspan > 1) {
let start = trIndex! + 1
while (start < trIndex! + td.rowspan) {
const tdId = getUUID()
const tr = curTrList[start]
tr.tdList.splice(d, 0, {
id: tdId,
rowspan: 1,
colspan: 1,
value: [{
value: ZERO,
size: 16,
tableId: element.id,
trId: tr.id,
tdId
}]
})
start += 1
}
}
}
// 删除当前行
curTrList.splice(trIndex!, 1)
// 重新设置上下文
this.position.setPositionContext({
isTable: false
})
this.range.setRange(0, 0)
// 重新渲染
this.draw.render({ isSetCursor: false })
this.tableTool.dispose()
}
public deleteTableCol() {
const positionContext = this.position.getPositionContext()
if (!positionContext.isTable) return
const { index, tdIndex, trIndex } = positionContext
const originalElementList = this.draw.getOriginalElementList()
const element = originalElementList[index!]
const curTrList = element.trList!
const curTd = curTrList[trIndex!].tdList[tdIndex!]
const curColIndex = curTd.colIndex!
// 如果是最后一列,直接删除整个表格
const moreTdTr = curTrList.find(tr => tr.tdList.length > 1)
if (!moreTdTr) {
this.deleteTable()
return
}
// 跨列处理
for (let t = 0; t < curTrList.length; t++) {
const tr = curTrList[t]
for (let d = 0; d < tr.tdList.length; d++) {
const td = tr.tdList[d]
if (td.colspan > 1) {
const tdColIndex = td.colIndex!
// 交叉减去一列
if (tdColIndex <= curColIndex && tdColIndex + td.colspan - 1 >= curColIndex) {
td.colspan -= 1
}
}
}
}
// 删除当前列
for (let t = 0; t < curTrList.length; t++) {
const tr = curTrList[t]
let start = -1
for (let d = 0; d < tr.tdList.length; d++) {
const td = tr.tdList[d]
if (td.colIndex === curColIndex) {
start = d
}
}
if (~start) {
tr.tdList.splice(start, 1)
}
}
element.colgroup?.splice(curColIndex, 1)
// 重新设置上下文
this.position.setPositionContext({
isTable: false
})
this.range.setRange(0, 0)
// 重新渲染
this.draw.render({ isSetCursor: false })
this.tableTool.dispose()
}
public deleteTable() {
const positionContext = this.position.getPositionContext()
if (positionContext.isTable) {
const originalElementList = this.draw.getOriginalElementList()
originalElementList.splice(positionContext.index!, 1)
const curIndex = positionContext.index! - 1
this.position.setPositionContext({
isTable: false,
index: curIndex
})
this.range.setRange(curIndex, curIndex)
this.draw.render({ curIndex })
this.tableTool.dispose()
}
}
public image(payload: IDrawImagePayload) {
const { startIndex, endIndex } = this.range.getRange()
if (startIndex === 0 && endIndex === 0) return

@ -0,0 +1,78 @@
import { IRegisterContextMenu } from "../../../interface/contextmenu/ContextMenu"
import { Command } from "../../command/Command"
export const tableMenus: IRegisterContextMenu[] = [
{
isDivider: true
},
{
name: '插入行列',
icon: 'insert-row-col',
when: (payload) => {
return payload.isInTable
},
childMenus: [
{
name: '上方插入1行',
icon: 'insert-top-row',
when: () => true,
callback: (command: Command) => {
command.executeInsertTableTopRow()
}
}, {
name: '下方插入1行',
icon: 'insert-bottom-row',
when: () => true,
callback: (command: Command) => {
command.executeInsertTableBottomRow()
}
}, {
name: '左侧插入1列',
icon: 'insert-left-col',
when: () => true,
callback: (command: Command) => {
command.executeInsertTableLeftCol()
}
}, {
name: '右侧插入1列',
icon: 'insert-right-col',
when: () => true,
callback: (command: Command) => {
command.executeInsertTableRightCol()
}
}
]
},
{
name: '删除行列',
icon: 'delete-row-col',
when: (payload) => {
return payload.isInTable
},
childMenus: [
{
name: '删除1行',
icon: 'delete-row',
when: () => true,
callback: (command: Command) => {
command.executDeleteTableRow()
}
},
{
name: '删除一列',
icon: 'delete-col',
when: () => true,
callback: (command: Command) => {
command.executDeleteTableCol()
}
}, {
name: '删除整个表格',
icon: 'delete-table',
when: () => true,
callback: (command: Command) => {
command.executDeleteTable()
}
}
]
}
]

@ -123,6 +123,11 @@ export class Draw {
return width - margins[1] - margins[3]
}
public getOriginalInnerWidth(): number {
const { width, margins } = this.options
return width - margins[1] - margins[3]
}
public getMargins(): number[] {
return this.options.margins.map(m => m * this.options.scale)
}

@ -69,7 +69,7 @@ export class TableParticle {
let colIndex = 0
const preTd = tr.tdList[d - 1]
if (preTd) {
colIndex = preTd.colIndex! + (offsetXIndex || 1)
colIndex = preTd.colIndex! + offsetXIndex + 1
if (preTd.colspan > 1) {
colIndex += preTd.colspan - 1
}

@ -11,6 +11,7 @@ import { formatElementList } from './utils/element'
import { Register } from './core/register/Register'
import { globalMenus } from './core/contextmenu/menus/GlobalMenus'
import { ContextMenu } from './core/contextmenu/ContextMenu'
import { tableMenus } from './core/contextmenu/menus/tableMenus'
import { IRegisterContextMenu } from './interface/contextmenu/ContextMenu'
export default class Editor {
@ -64,6 +65,7 @@ export default class Editor {
contextMenu
})
this.register.contextMenuList(globalMenus)
this.register.contextMenuList(tableMenus)
}
}

Loading…
Cancel
Save