From 2de1ba7b62cc04307abeaec78b61516db41a71aa Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Sun, 24 Mar 2024 16:37:10 +0800 Subject: [PATCH] feat: move between table cells using up and down keys #465 --- src/editor/core/draw/Draw.ts | 2 +- .../core/event/handlers/keydown/left.ts | 8 +- .../core/event/handlers/keydown/right.ts | 8 +- .../core/event/handlers/keydown/updown.ts | 317 +++++++++++++++--- 4 files changed, 277 insertions(+), 58 deletions(-) diff --git a/src/editor/core/draw/Draw.ts b/src/editor/core/draw/Draw.ts index edfc5a3..525fa3a 100644 --- a/src/editor/core/draw/Draw.ts +++ b/src/editor/core/draw/Draw.ts @@ -741,7 +741,7 @@ export class Draw { } public getRowCount(): number { - return this.rowList.length + return this.getRowList().length } public async getDataURL(payload: IGetImageOption = {}): Promise { diff --git a/src/editor/core/event/handlers/keydown/left.ts b/src/editor/core/event/handlers/keydown/left.ts index 54ffb08..f1eec34 100644 --- a/src/editor/core/event/handlers/keydown/left.ts +++ b/src/editor/core/event/handlers/keydown/left.ts @@ -82,7 +82,7 @@ export function left(evt: KeyboardEvent, host: CanvasEvent) { if (startIndex === 0) { const originalElementList = draw.getOriginalElementList() const trList = originalElementList[positionContext.index!].trList! - for (let r = 0; r < trList.length; r++) { + outer: for (let r = 0; r < trList.length; r++) { const tr = trList[r] if (tr.id !== element.trId) continue const tdList = tr.tdList @@ -112,15 +112,15 @@ export function left(evt: KeyboardEvent, host: CanvasEvent) { index: positionContext.index, trIndex: preTrIndex, tdIndex: preTdIndex, - tdId: preTr.id, - trId: preTd.id, + tdId: preTd.id, + trId: preTr.id, tableId: element.id }) anchorStartIndex = preTd.value.length - 1 anchorEndIndex = anchorStartIndex draw.getTableTool().render() } - break + break outer } } } diff --git a/src/editor/core/event/handlers/keydown/right.ts b/src/editor/core/event/handlers/keydown/right.ts index 201bee3..0517014 100644 --- a/src/editor/core/event/handlers/keydown/right.ts +++ b/src/editor/core/event/handlers/keydown/right.ts @@ -82,7 +82,7 @@ export function right(evt: KeyboardEvent, host: CanvasEvent) { if (!nextElement) { const originalElementList = draw.getOriginalElementList() const trList = originalElementList[positionContext.index!].trList! - for (let r = 0; r < trList.length; r++) { + outer: for (let r = 0; r < trList.length; r++) { const tr = trList[r] if (tr.id !== element.trId) continue const tdList = tr.tdList @@ -113,15 +113,15 @@ export function right(evt: KeyboardEvent, host: CanvasEvent) { index: positionContext.index, trIndex: nextTrIndex, tdIndex: nextTdIndex, - tdId: preTr.id, - trId: preTd.id, + tdId: preTd.id, + trId: preTr.id, tableId: element.id }) anchorStartIndex = 0 anchorEndIndex = anchorStartIndex draw.getTableTool().render() } - break + break outer } } } diff --git a/src/editor/core/event/handlers/keydown/updown.ts b/src/editor/core/event/handlers/keydown/updown.ts index 222bd36..f4564de 100644 --- a/src/editor/core/event/handlers/keydown/updown.ts +++ b/src/editor/core/event/handlers/keydown/updown.ts @@ -1,44 +1,20 @@ +import { ElementType } from '../../../../dataset/enum/Element' import { KeyMap } from '../../../../dataset/enum/KeyMap' import { MoveDirection } from '../../../../dataset/enum/Observer' import { IElementPosition } from '../../../../interface/Element' import { CanvasEvent } from '../../CanvasEvent' -export function updown(evt: KeyboardEvent, host: CanvasEvent) { - const draw = host.getDraw() - const isReadonly = draw.isReadonly() - if (isReadonly) return - const position = draw.getPosition() - const cursorPosition = position.getCursorPosition() - if (!cursorPosition) return - const rangeManager = draw.getRange() - const { startIndex, endIndex } = rangeManager.getRange() - const positionList = position.getPositionList() - let anchorPosition: IElementPosition = cursorPosition - // 扩大选区时,判断移动光标点 - if (evt.shiftKey) { - if (startIndex === cursorPosition.index) { - anchorPosition = positionList[endIndex] - } else { - anchorPosition = positionList[startIndex] - } - } - const { - index, - rowNo, - rowIndex, - coordinate: { - leftTop: [curLeftX], - rightTop: [curRightX] - } - } = anchorPosition - // 向上时在首行、向下时在尾行则忽略 - const isUp = evt.key === KeyMap.Up - if ( - (isUp && rowIndex === 0) || - (!isUp && rowIndex === draw.getRowCount() - 1) - ) { - return - } +interface IGetNextPositionIndexPayload { + positionList: IElementPosition[] + index: number + rowNo: number + isUp: boolean + cursorX: number +} +// 根据当前位置索引查找上下行最接近的索引位置 +function getNextPositionIndex(payload: IGetNextPositionIndexPayload) { + const { positionList, index, isUp, rowNo, cursorX } = payload + let nextIndex = 0 // 查找下一行位置列表 const probablePosition: IElementPosition[] = [] if (isUp) { @@ -65,7 +41,6 @@ export function updown(evt: KeyboardEvent, host: CanvasEvent) { } } // 查找下一行位置:第一个存在交叉宽度的元素位置 - let nextIndex = 0 for (let p = 0; p < probablePosition.length; p++) { const nextPosition = probablePosition[p] const { @@ -77,29 +52,273 @@ export function updown(evt: KeyboardEvent, host: CanvasEvent) { if (p === probablePosition.length - 1) { nextIndex = nextPosition.index } - if (curRightX <= nextLeftX || curLeftX >= nextRightX) continue + if (cursorX < nextLeftX || cursorX > nextRightX) continue nextIndex = nextPosition.index break } - if (!nextIndex) return - // shift则缩放选区 - let anchorStartIndex = nextIndex - let anchorEndIndex = nextIndex - if (evt.shiftKey) { - if (startIndex !== endIndex) { - if (startIndex === cursorPosition.index) { - anchorStartIndex = startIndex + return nextIndex +} + +export function updown(evt: KeyboardEvent, host: CanvasEvent) { + const draw = host.getDraw() + const isReadonly = draw.isReadonly() + if (isReadonly) return + const position = draw.getPosition() + const cursorPosition = position.getCursorPosition() + if (!cursorPosition) return + const rangeManager = draw.getRange() + const { startIndex, endIndex } = rangeManager.getRange() + let positionList = position.getPositionList() + const isUp = evt.key === KeyMap.Up + // 新的光标开始结束位置 + let anchorStartIndex = -1 + let anchorEndIndex = -1 + // 单元格之间跳转及跳出表格逻辑 + const positionContext = position.getPositionContext() + if ( + !evt.shiftKey && + positionContext.isTable && + ((isUp && cursorPosition.rowIndex === 0) || + (!isUp && cursorPosition.rowIndex === draw.getRowCount() - 1)) + ) { + const { index, trIndex, tdIndex, tableId } = positionContext + if (isUp) { + // 向上移动-第一行则移出到表格外,否则上一行相同列位置 + if (trIndex === 0) { + position.setPositionContext({ + isTable: false + }) + anchorStartIndex = index! - 1 + anchorEndIndex = anchorStartIndex + draw.getTableTool().dispose() } else { - anchorEndIndex = endIndex + // 查找上一行相同列索引位置信息 + let preTrIndex = -1 + let preTdIndex = -1 + const originalElementList = draw.getOriginalElementList() + const trList = originalElementList[index!].trList! + // 当前单元格所在列实际索引 + const curTdColIndex = trList[trIndex!].tdList[tdIndex!].colIndex! + outer: for (let r = trIndex! - 1; r >= 0; r--) { + const tr = trList[r] + const tdList = tr.tdList! + for (let d = 0; d < tdList.length; d++) { + const td = tdList[d] + if ( + td.colIndex === curTdColIndex || + (td.colIndex! + td.colspan - 1 >= curTdColIndex && + td.colIndex! <= curTdColIndex) + ) { + preTrIndex = r + preTdIndex = d + break outer + } + } + } + if (!~preTrIndex || !~preTdIndex) return + const preTr = trList[preTrIndex] + const preTd = preTr.tdList[preTdIndex] + position.setPositionContext({ + isTable: true, + index, + trIndex: preTrIndex, + tdIndex: preTdIndex, + tdId: preTr.id, + trId: preTd.id, + tableId + }) + anchorStartIndex = preTd.value.length - 1 + anchorEndIndex = anchorStartIndex + draw.getTableTool().render() } } else { + // 向下移动-最后一行则移出表格外,否则下一行相同列位置 + const originalElementList = draw.getOriginalElementList() + const trList = originalElementList[index!].trList! + if (trIndex === trList.length - 1) { + position.setPositionContext({ + isTable: false + }) + anchorStartIndex = index! + anchorEndIndex = anchorStartIndex + draw.getTableTool().dispose() + } else { + // 查找下一行相同列索引位置信息 + let nexTrIndex = -1 + let nextTdIndex = -1 + // 当前单元格所在列实际索引 + const curTdColIndex = trList[trIndex!].tdList[tdIndex!].colIndex! + outer: for (let r = trIndex! + 1; r < trList.length; r++) { + const tr = trList[r] + const tdList = tr.tdList! + for (let d = 0; d < tdList.length; d++) { + const td = tdList[d] + if ( + td.colIndex === curTdColIndex || + (td.colIndex! + td.colspan - 1 >= curTdColIndex && + td.colIndex! <= curTdColIndex) + ) { + nexTrIndex = r + nextTdIndex = d + break outer + } + } + } + if (!~nexTrIndex || !~nextTdIndex) return + const nextTr = trList[nexTrIndex] + const nextTd = nextTr.tdList[nextTdIndex] + position.setPositionContext({ + isTable: true, + index, + trIndex: nexTrIndex, + tdIndex: nextTdIndex, + tdId: nextTr.id, + trId: nextTd.id, + tableId + }) + anchorStartIndex = nextTd.value.length - 1 + anchorEndIndex = anchorStartIndex + draw.getTableTool().render() + } + } + } else { + // 普通元素及跳进表格逻辑 + let anchorPosition: IElementPosition = cursorPosition + // 扩大选区时,判断移动光标点 + if (evt.shiftKey) { + if (startIndex === cursorPosition.index) { + anchorPosition = positionList[endIndex] + } else { + anchorPosition = positionList[startIndex] + } + } + const { + index, + rowNo, + rowIndex, + coordinate: { + rightTop: [curRightX] + } + } = anchorPosition + // 向上时在首行、向下时在尾行则忽略 + if ( + (isUp && rowIndex === 0) || + (!isUp && rowIndex === draw.getRowCount() - 1) + ) { + return + } + // 查找下一行位置列表 + const nextIndex = getNextPositionIndex({ + positionList, + index, + rowNo, + isUp, + cursorX: curRightX + }) + if (!nextIndex) return + // shift则缩放选区 + anchorStartIndex = nextIndex + anchorEndIndex = nextIndex + if (evt.shiftKey) { + if (startIndex !== endIndex) { + if (startIndex === cursorPosition.index) { + anchorStartIndex = startIndex + } else { + anchorEndIndex = endIndex + } + } else { + if (isUp) { + anchorEndIndex = endIndex + } else { + anchorStartIndex = startIndex + } + } + } + // 如果下一行是表格则进入单元格内 + const elementList = draw.getElementList() + const nextElement = elementList[nextIndex] + if (nextElement.type === ElementType.TABLE) { + const { scale } = draw.getOptions() + const margins = draw.getMargins() + const trList = nextElement.trList! + // 查找进入的单元格及元素位置 + let trIndex = -1 + let tdIndex = -1 + let tdPositionIndex = -1 if (isUp) { - anchorEndIndex = endIndex + outer: for (let r = trList.length - 1; r >= 0; r--) { + const tr = trList[r] + const tdList = tr.tdList! + for (let d = 0; d < tdList.length; d++) { + const td = tdList[d] + const tdX = td.x! * scale + margins[3] + const tdWidth = td.width! * scale + if (curRightX >= tdX && curRightX <= tdX + tdWidth) { + const tdPositionList = td.positionList! + const lastPosition = tdPositionList[tdPositionList.length - 1] + const nextPositionIndex = + getNextPositionIndex({ + positionList: tdPositionList, + index: lastPosition.index + 1, // 虚拟起始位置+1(从左往右找) + rowNo: lastPosition.rowNo - 1, // 虚拟起始行号-1(从下往上找) + isUp, + cursorX: curRightX + }) || lastPosition.index + trIndex = r + tdIndex = d + tdPositionIndex = nextPositionIndex + break outer + } + } + } } else { - anchorStartIndex = startIndex + outer: for (let r = 0; r < trList.length; r++) { + const tr = trList[r] + const tdList = tr.tdList! + for (let d = 0; d < tdList.length; d++) { + const td = tdList[d] + const tdX = td.x! * scale + margins[3] + const tdWidth = td.width! * scale + if (curRightX >= tdX && curRightX <= tdX + tdWidth) { + const tdPositionList = td.positionList! + const nextPositionIndex = + getNextPositionIndex({ + positionList: tdPositionList, + index: -1, // 虚拟起始位置-1(从右往左找) + rowNo: -1, // 虚拟起始行号-1(从上往下找) + isUp, + cursorX: curRightX + }) || 0 + trIndex = r + tdIndex = d + tdPositionIndex = nextPositionIndex + break outer + } + } + } + } + // 设置上下文 + if (~trIndex && ~tdIndex && ~tdPositionIndex) { + const nextTr = trList[trIndex] + const nextTd = nextTr.tdList[tdIndex] + position.setPositionContext({ + isTable: true, + index: nextIndex, + trIndex: trIndex, + tdIndex: tdIndex, + tdId: nextTd.id, + trId: nextTr.id, + tableId: nextElement.id + }) + anchorStartIndex = tdPositionIndex + anchorEndIndex = anchorStartIndex + positionList = position.getPositionList() + draw.getTableTool().render() } } } + // 执行跳转 + if (!~anchorStartIndex || !~anchorEndIndex) return if (anchorStartIndex > anchorEndIndex) { // prettier-ignore [anchorStartIndex, anchorEndIndex] = [anchorEndIndex, anchorStartIndex]