From 665e2018aaae38aacd6ec9d32980fba245f12ddd Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Tue, 21 Mar 2023 21:34:48 +0800 Subject: [PATCH] feat:table cell vertical align --- .../assets/css/contextmenu/contextmenu.css | 16 +++++++++ .../assets/images/vertical-align-bottom.svg | 1 + .../assets/images/vertical-align-middle.svg | 1 + .../assets/images/vertical-align-top.svg | 1 + src/editor/assets/images/vertical-align.svg | 1 + src/editor/core/command/Command.ts | 8 ++++- src/editor/core/command/CommandAdapt.ts | 25 ++++++++++++++ .../core/contextmenu/menus/tableMenus.ts | 34 +++++++++++++++++++ src/editor/core/i18n/lang/en.json | 6 +++- src/editor/core/i18n/lang/zh-CN.json | 6 +++- src/editor/core/position/Position.ts | 23 +++++++++++-- src/editor/dataset/enum/VerticalAlign.ts | 5 +++ src/editor/index.ts | 2 ++ src/editor/interface/table/Td.ts | 2 ++ src/editor/utils/element.ts | 7 +++- 15 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 src/editor/assets/images/vertical-align-bottom.svg create mode 100644 src/editor/assets/images/vertical-align-middle.svg create mode 100644 src/editor/assets/images/vertical-align-top.svg create mode 100644 src/editor/assets/images/vertical-align.svg create mode 100644 src/editor/dataset/enum/VerticalAlign.ts diff --git a/src/editor/assets/css/contextmenu/contextmenu.css b/src/editor/assets/css/contextmenu/contextmenu.css index 346004ff2..8ad16bcce 100644 --- a/src/editor/assets/css/contextmenu/contextmenu.css +++ b/src/editor/assets/css/contextmenu/contextmenu.css @@ -129,4 +129,20 @@ .ce-contextmenu-merge-cancel-cell { background-image: url(../../../assets/images/merge-cancel-cell.svg); +} + +.ce-contextmenu-vertical-align { + background-image: url(../../../assets/images/vertical-align.svg); +} + +.ce-contextmenu-vertical-align-top { + background-image: url(../../../assets/images/vertical-align-top.svg); +} + +.ce-contextmenu-vertical-align-middle { + background-image: url(../../../assets/images/vertical-align-middle.svg); +} + +.ce-contextmenu-vertical-align-bottom { + background-image: url(../../../assets/images/vertical-align-bottom.svg); } \ No newline at end of file diff --git a/src/editor/assets/images/vertical-align-bottom.svg b/src/editor/assets/images/vertical-align-bottom.svg new file mode 100644 index 000000000..b4b8f0145 --- /dev/null +++ b/src/editor/assets/images/vertical-align-bottom.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/editor/assets/images/vertical-align-middle.svg b/src/editor/assets/images/vertical-align-middle.svg new file mode 100644 index 000000000..9fc9f0b15 --- /dev/null +++ b/src/editor/assets/images/vertical-align-middle.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/editor/assets/images/vertical-align-top.svg b/src/editor/assets/images/vertical-align-top.svg new file mode 100644 index 000000000..447ed0630 --- /dev/null +++ b/src/editor/assets/images/vertical-align-top.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/editor/assets/images/vertical-align.svg b/src/editor/assets/images/vertical-align.svg new file mode 100644 index 000000000..b41d2b21f --- /dev/null +++ b/src/editor/assets/images/vertical-align.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/editor/core/command/Command.ts b/src/editor/core/command/Command.ts index 062afb896..09ab13555 100644 --- a/src/editor/core/command/Command.ts +++ b/src/editor/core/command/Command.ts @@ -1,4 +1,4 @@ -import { IElement, ImageDisplay, INavigateInfo } from '../..' +import { IElement, ImageDisplay, INavigateInfo, VerticalAlign } from '../..' import { EditorMode, PageMode, PaperDirection } from '../../dataset/enum/Editor' import { RowFlex } from '../../dataset/enum/Row' import { IDrawImagePayload, IPainterOptions } from '../../interface/Draw' @@ -47,6 +47,7 @@ export class Command { private static deleteTable: CommandAdapt['deleteTable'] private static mergeTableCell: CommandAdapt['mergeTableCell'] private static cancelMergeTableCell: CommandAdapt['cancelMergeTableCell'] + private static tableTdVerticalAlign: CommandAdapt['tableTdVerticalAlign'] private static image: CommandAdapt['image'] private static hyperlink: CommandAdapt['hyperlink'] private static deleteHyperlink: CommandAdapt['deleteHyperlink'] @@ -120,6 +121,7 @@ export class Command { Command.deleteTable = adapt.deleteTable.bind(adapt) Command.mergeTableCell = adapt.mergeTableCell.bind(adapt) Command.cancelMergeTableCell = adapt.cancelMergeTableCell.bind(adapt) + Command.tableTdVerticalAlign = adapt.tableTdVerticalAlign.bind(adapt) Command.image = adapt.image.bind(adapt) Command.hyperlink = adapt.hyperlink.bind(adapt) Command.deleteHyperlink = adapt.deleteHyperlink.bind(adapt) @@ -311,6 +313,10 @@ export class Command { return Command.cancelMergeTableCell() } + public executeTableTdVerticalAlign(payload: VerticalAlign) { + return Command.tableTdVerticalAlign(payload) + } + public executeHyperlink(payload: IElement) { return Command.hyperlink(payload) } diff --git a/src/editor/core/command/CommandAdapt.ts b/src/editor/core/command/CommandAdapt.ts index 2b32b2784..663b5f06f 100644 --- a/src/editor/core/command/CommandAdapt.ts +++ b/src/editor/core/command/CommandAdapt.ts @@ -6,6 +6,7 @@ import { EditorContext, EditorMode, PageMode, PaperDirection } from '../../datas import { ElementType } from '../../dataset/enum/Element' import { ElementStyleKey } from '../../dataset/enum/ElementStyle' import { RowFlex } from '../../dataset/enum/Row' +import { VerticalAlign } from '../../dataset/enum/VerticalAlign' import { IDrawImagePayload, IPainterOptions } from '../../interface/Draw' import { IEditorOption, IEditorResult } from '../../interface/Editor' import { IElement, IElementStyle } from '../../interface/Element' @@ -997,6 +998,30 @@ export class CommandAdapt { this.tableTool.render(element, position[index!]) } + public tableTdVerticalAlign(payload: VerticalAlign) { + const isReadonly = this.draw.isReadonly() + if (isReadonly) return + const positionContext = this.position.getPositionContext() + if (!positionContext.isTable) return + const { index, trIndex, tdIndex } = positionContext + const originalElementList = this.draw.getOriginalElementList() + const element = originalElementList[index!] + const curTd = element?.trList?.[trIndex!]?.tdList?.[tdIndex!] + if ( + !curTd + || curTd.verticalAlign === payload + || (!curTd.verticalAlign && payload === VerticalAlign.TOP) + ) { + return + } + // 重设垂直对齐方式 + curTd.verticalAlign = payload + const { endIndex } = this.range.getRange() + this.draw.render({ + curIndex: endIndex + }) + } + public hyperlink(payload: IElement) { const isReadonly = this.draw.isReadonly() if (isReadonly) return diff --git a/src/editor/core/contextmenu/menus/tableMenus.ts b/src/editor/core/contextmenu/menus/tableMenus.ts index 1974f526d..0aa15f84c 100644 --- a/src/editor/core/contextmenu/menus/tableMenus.ts +++ b/src/editor/core/contextmenu/menus/tableMenus.ts @@ -1,3 +1,4 @@ +import { VerticalAlign } from '../../../dataset/enum/VerticalAlign' import { IRegisterContextMenu } from '../../../interface/contextmenu/ContextMenu' import { Command } from '../../command/Command' @@ -5,6 +6,39 @@ export const tableMenus: IRegisterContextMenu[] = [ { isDivider: true }, + { + i18nPath: 'contextmenu.table.verticalAlign', + icon: 'vertical-align', + when: (payload) => { + return !payload.isReadonly && payload.isInTable + }, + childMenus: [ + { + i18nPath: 'contextmenu.table.verticalAlignTop', + icon: 'vertical-align-top', + when: () => true, + callback: (command: Command) => { + command.executeTableTdVerticalAlign(VerticalAlign.TOP) + } + }, + { + i18nPath: 'contextmenu.table.verticalAlignMiddle', + icon: 'vertical-align-middle', + when: () => true, + callback: (command: Command) => { + command.executeTableTdVerticalAlign(VerticalAlign.MIDDLE) + } + }, + { + i18nPath: 'contextmenu.table.verticalAlignBottom', + icon: 'vertical-align-bottom', + when: () => true, + callback: (command: Command) => { + command.executeTableTdVerticalAlign(VerticalAlign.BOTTOM) + } + } + ] + }, { i18nPath: 'contextmenu.table.insertRowCol', icon: 'insert-row-col', diff --git a/src/editor/core/i18n/lang/en.json b/src/editor/core/i18n/lang/en.json index 165b2c5f1..c7c978522 100644 --- a/src/editor/core/i18n/lang/en.json +++ b/src/editor/core/i18n/lang/en.json @@ -35,7 +35,11 @@ "deleteCol": "Delete 1 col", "deleteTable": "Delete table", "mergeCell": "Merge cell", - "mergeCancelCell": "Cancel merge cell" + "mergeCancelCell": "Cancel merge cell", + "verticalAlign": "Vertical align", + "verticalAlignTop": "top", + "verticalAlignMiddle": "middle", + "verticalAlignBottom": "bottom" } }, "datePicker": { diff --git a/src/editor/core/i18n/lang/zh-CN.json b/src/editor/core/i18n/lang/zh-CN.json index d2e7b6b58..67be634c5 100644 --- a/src/editor/core/i18n/lang/zh-CN.json +++ b/src/editor/core/i18n/lang/zh-CN.json @@ -35,7 +35,11 @@ "deleteCol": "删除1列", "deleteTable": "删除整个表格", "mergeCell": "合并单元格", - "mergeCancelCell": "取消合并" + "mergeCancelCell": "取消合并", + "verticalAlign": "垂直对齐", + "verticalAlignTop": "顶端对齐", + "verticalAlignMiddle": "垂直居中", + "verticalAlignBottom": "底端对齐" } }, "datePicker": { diff --git a/src/editor/core/position/Position.ts b/src/editor/core/position/Position.ts index b4b9f0ba4..16e830b66 100644 --- a/src/editor/core/position/Position.ts +++ b/src/editor/core/position/Position.ts @@ -1,4 +1,4 @@ -import { ElementType, RowFlex } from '../..' +import { ElementType, RowFlex, VerticalAlign } from '../..' import { ZERO } from '../../dataset/constant/Common' import { ControlComponent, ImageDisplay } from '../../dataset/enum/Control' import { IComputePageRowPositionPayload, IComputePageRowPositionResult } from '../../interface/Position' @@ -112,15 +112,34 @@ export class Position { for (let d = 0; d < tr.tdList!.length; d++) { const td = tr.tdList[d] td.positionList = [] + const rowList = td.rowList! const drawRowResult = this.computePageRowPosition({ positionList: td.positionList, - rowList: td.rowList!, + rowList, pageNo, startIndex: 0, startX: (td.x! + tdPadding) * scale + tablePreX, startY: td.y! * scale + tablePreY, innerWidth: (td.width! - tdGap) * scale }) + // 垂直对齐方式 + if ( + td.verticalAlign === VerticalAlign.MIDDLE + || td.verticalAlign == VerticalAlign.BOTTOM + ) { + const rowsHeight = rowList.reduce((pre, cur) => pre + cur.height, 0) + const blankHeight = td.height! - tdGap - rowsHeight + const offsetHeight = td.verticalAlign === VerticalAlign.MIDDLE ? blankHeight / 2 : blankHeight + if (Math.floor(offsetHeight) > 0) { + td.positionList.forEach(tdPosition => { + const { coordinate: { leftTop, leftBottom, rightBottom, rightTop } } = tdPosition + leftTop[1] += offsetHeight + leftBottom[1] += offsetHeight + rightBottom[1] += offsetHeight + rightTop[1] += offsetHeight + }) + } + } x = drawRowResult.x y = drawRowResult.y } diff --git a/src/editor/dataset/enum/VerticalAlign.ts b/src/editor/dataset/enum/VerticalAlign.ts new file mode 100644 index 000000000..8793426c0 --- /dev/null +++ b/src/editor/dataset/enum/VerticalAlign.ts @@ -0,0 +1,5 @@ +export enum VerticalAlign { + TOP = 'top', + MIDDLE = 'middle', + BOTTOM = 'bottom' +} \ No newline at end of file diff --git a/src/editor/index.ts b/src/editor/index.ts index 81c35860d..e8515ab03 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -33,6 +33,7 @@ import { ICursorOption } from './interface/Cursor' import { defaultCursorOption } from './dataset/constant/Cursor' import { IPageNumber } from './interface/PageNumber' import { defaultPageNumberOption } from './dataset/constant/PageNumber' +import { VerticalAlign } from './dataset/enum/VerticalAlign' export default class Editor { @@ -161,6 +162,7 @@ export default class Editor { export { Editor, RowFlex, + VerticalAlign, EditorZone, EditorMode, ElementType, diff --git a/src/editor/interface/table/Td.ts b/src/editor/interface/table/Td.ts index 57f3d907e..c411920e0 100644 --- a/src/editor/interface/table/Td.ts +++ b/src/editor/interface/table/Td.ts @@ -1,3 +1,4 @@ +import { VerticalAlign } from '../../dataset/enum/VerticalAlign' import { IElement, IElementPosition } from '../Element' import { IRow } from '../Row' @@ -17,4 +18,5 @@ export interface ITd { colIndex?: number; rowList?: IRow[]; positionList?: IElementPosition[]; + verticalAlign?: VerticalAlign; } \ No newline at end of file diff --git a/src/editor/utils/element.ts b/src/editor/utils/element.ts index 095300df4..1907959c8 100644 --- a/src/editor/utils/element.ts +++ b/src/editor/utils/element.ts @@ -6,6 +6,7 @@ import { ZERO } from '../dataset/constant/Common' import { defaultControlOption } from '../dataset/constant/Control' import { EDITOR_ELEMENT_ZIP_ATTR } from '../dataset/constant/Element' import { ControlComponent, ControlType } from '../dataset/enum/Control' +import { ITd } from '../interface/table/Td' interface IFormatElementListOption { isHandleFirstElement?: boolean; @@ -308,11 +309,15 @@ export function zipElementList(payload: IElement[]): IElement[] { delete tr.id for (let d = 0; d < tr.tdList.length; d++) { const td = tr.tdList[d] - tr.tdList[d] = { + const zipTd: ITd = { colspan: td.colspan, rowspan: td.rowspan, value: zipElementList(td.value) } + if (td.verticalAlign) { + zipTd.verticalAlign = td.verticalAlign + } + tr.tdList[d] = zipTd } } }