From 3602093d590855e5674b6fb94094819aea334487 Mon Sep 17 00:00:00 2001 From: Hufe921 Date: Mon, 4 Apr 2022 17:01:55 +0800 Subject: [PATCH] feat:text control input data --- src/editor/core/draw/control/Control.ts | 106 +++++++++++++-- .../core/draw/control/text/TextControl.ts | 125 +++++++++++++++++- src/editor/core/event/CanvasEvent.ts | 52 ++++++-- src/editor/interface/Control.ts | 16 +++ src/editor/utils/element.ts | 53 ++++---- 5 files changed, 297 insertions(+), 55 deletions(-) diff --git a/src/editor/core/draw/control/Control.ts b/src/editor/core/draw/control/Control.ts index b3b597a..018da44 100644 --- a/src/editor/core/draw/control/Control.ts +++ b/src/editor/core/draw/control/Control.ts @@ -1,39 +1,96 @@ -import { ControlComponent } from '../../../dataset/enum/Control' +import { ControlComponent, ControlType } from '../../../dataset/enum/Control' import { ElementType } from '../../../dataset/enum/Element' +import { IControlInitOption, IControlInitResult, IControlInstance } from '../../../interface/Control' import { IElement } from '../../../interface/Element' -import { ICurrentPosition } from '../../../interface/Position' +import { RangeManager } from '../../range/RangeManager' import { Draw } from '../Draw' +import { TextControl } from './text/TextControl' +interface IMoveCursorResult { + newIndex: number; + newElement: IElement; +} export class Control { private draw: Draw + private range: RangeManager + private activeControl: IControlInstance | null constructor(draw: Draw) { this.draw = draw + this.range = draw.getRange() + this.activeControl = null + } + + public getElementList(): IElement[] { + return this.draw.getElementList() + } + + public getRange() { + return this.range.getRange() + } + + public getActiveControl(): IControlInstance | null { + return this.activeControl + } + + // 判断选区部分在控件边界外 + public isPartRangeInControlOutside(): boolean { + const { startIndex, endIndex } = this.getRange() + if (!~startIndex && !~endIndex) return false + const elementList = this.getElementList() + const startElement = elementList[startIndex] + const endElement = elementList[endIndex] + if ( + (startElement.type === ElementType.CONTROL || endElement.type === ElementType.CONTROL) + && startElement.controlId !== endElement.controlId + ) { + return true + } + return false + } + + public initControl(option: IControlInitOption): IControlInitResult { + // 调整光标位置 + const { newIndex, newElement } = this.moveCursor(option) + const control = newElement.control! + // 销毁激活控件 + this.destroyControl() + // 激活控件 + if (control.type === ControlType.TEXT) { + this.activeControl = new TextControl(this) + } + return { newIndex } } // 调整起始光标位置到控件合适的位置 - public moveCursorIndex(position: ICurrentPosition) { + public moveCursor(position: IControlInitOption): IMoveCursorResult { const { index, trIndex, tdIndex, tdValueIndex } = position let elementList = this.draw.getOriginalElementList() let element: IElement + const newIndex = position.isTable ? tdValueIndex! : index if (position.isTable) { elementList = elementList[index!].trList![trIndex!].tdList[tdIndex!].value element = elementList[tdValueIndex!] } else { element = elementList[index] } - if (element.type !== ElementType.CONTROL) return - // VALUE-无需移动 - if (element.controlComponent === ControlComponent.VALUE) return - // POSTFIX-移动到最后一个后缀字符后 - if (element.controlComponent === ControlComponent.POSTFIX) { + if (element.controlComponent === ControlComponent.VALUE) { + // VALUE-无需移动 + return { + newIndex, + newElement: element + } + } else if (element.controlComponent === ControlComponent.POSTFIX) { + // POSTFIX-移动到最后一个后缀字符后 let startIndex = index + 1 while (startIndex < elementList.length) { const nextElement = elementList[startIndex] if (nextElement.controlId !== element.controlId) { - position.index = startIndex - 1 - break + return { + newIndex: startIndex - 1, + newElement: elementList[startIndex - 1] + } } startIndex++ } @@ -46,8 +103,10 @@ export class Control { nextElement.controlId !== element.controlId || nextElement.controlComponent !== ControlComponent.PREFIX ) { - position.index = startIndex - 1 - break + return { + newIndex: startIndex - 1, + newElement: elementList[startIndex - 1] + } } startIndex++ } @@ -60,12 +119,31 @@ export class Control { preElement.controlId !== element.controlId || preElement.controlComponent === ControlComponent.PREFIX ) { - position.index = startIndex - break + return { + newIndex: startIndex, + newElement: elementList[startIndex] + } } startIndex-- } } + return { + newIndex, + newElement: element + } + } + + public destroyControl() { + if (this.activeControl) { + this.activeControl = null + } + } + + public setValue(data: IElement[]): number { + if (!this.activeControl) { + throw new Error('active control is null') + } + return this.activeControl.setValue(data) } } \ No newline at end of file diff --git a/src/editor/core/draw/control/text/TextControl.ts b/src/editor/core/draw/control/text/TextControl.ts index 11ae793..809e6a3 100644 --- a/src/editor/core/draw/control/text/TextControl.ts +++ b/src/editor/core/draw/control/text/TextControl.ts @@ -1,3 +1,126 @@ -export class TextControl { +import { ControlComponent } from '../../../../dataset/enum/Control' +import { IControlInstance } from '../../../../interface/Control' +import { IElement } from '../../../../interface/Element' +import { IRange } from '../../../../interface/Range' +import { Control } from '../Control' + +export class TextControl implements IControlInstance { + + private control: Control + + constructor(control: Control) { + this.control = control + } + + public shrinkBoundary(elementList: IElement[], range: IRange) { + const { startIndex, endIndex } = range + if (startIndex === endIndex) return + const startElement = elementList[startIndex] + const endElement = elementList[endIndex] + // 首、尾为占位符时,收缩到最后一个前缀字符后 + if ( + startElement.controlComponent === ControlComponent.PLACEHOLDER || + endElement.controlComponent === ControlComponent.PLACEHOLDER + ) { + let index = endIndex - 1 + while (index > 0) { + const preElement = elementList[index] + if ( + preElement.controlId !== endElement.controlId + || preElement.controlComponent === ControlComponent.PREFIX + ) { + range.startIndex = index + range.endIndex = index + return + } + index-- + } + } + // 向右查找到第一个Value + if (startElement.controlComponent === ControlComponent.PREFIX) { + let index = startIndex + 1 + while (index < elementList.length) { + const nextElement = elementList[index] + if ( + nextElement.controlId !== startElement.controlId + || nextElement.controlComponent === ControlComponent.VALUE + ) { + range.startIndex = index - 1 + break + } else if (nextElement.controlComponent === ControlComponent.PLACEHOLDER) { + range.startIndex = index - 1 + range.endIndex = index - 1 + return + } + index++ + } + } + // 向左查找到第一个Value + if (endElement.controlComponent !== ControlComponent.VALUE) { + let index = startIndex - 1 + while (index > 0) { + const preElement = elementList[index] + if ( + preElement.controlId !== startElement.controlId + || preElement.controlComponent === ControlComponent.VALUE + ) { + range.startIndex = index + break + } else if (preElement.controlComponent === ControlComponent.PLACEHOLDER) { + range.startIndex = index + range.endIndex = index + return + } + index-- + } + } + } + + public removePlaceholder(elementList: IElement[], range: IRange) { + const { startIndex } = range + const startElement = elementList[startIndex] + const nextElement = elementList[startIndex + 1] + if ( + startElement.controlComponent === ControlComponent.PLACEHOLDER || + nextElement.controlComponent === ControlComponent.PLACEHOLDER + ) { + let index = startIndex + while (index < elementList.length) { + const curElement = elementList[index] + if (curElement.controlId !== startElement.controlId) break + if (curElement.controlComponent === ControlComponent.PLACEHOLDER) { + elementList.splice(index, 1) + } else { + index++ + } + } + } + } + + public setValue(data: IElement[]): number { + const elementList = this.control.getElementList() + const range = this.control.getRange() + // 收缩边界到Value内 + this.shrinkBoundary(elementList, range) + const { startIndex, endIndex } = range + // 移除选区元素 + if (startIndex !== endIndex) { + elementList.splice(startIndex + 1, endIndex - startIndex) + } else { + // 移除空白占位符 + this.removePlaceholder(elementList, range) + } + // 插入 + const startElement = elementList[startIndex] + const start = range.startIndex + 1 + for (let i = 0; i < data.length; i++) { + elementList.splice(start + i, 0, { + ...startElement, + ...data[i], + controlComponent: ControlComponent.VALUE + }) + } + return start + data.length - 1 + } } \ No newline at end of file diff --git a/src/editor/core/event/CanvasEvent.ts b/src/editor/core/event/CanvasEvent.ts index 342d0ac..1a494f6 100644 --- a/src/editor/core/event/CanvasEvent.ts +++ b/src/editor/core/event/CanvasEvent.ts @@ -166,9 +166,27 @@ export class CanvasEvent { x: evt.offsetX, y: evt.offsetY }) - // 如果是控件-光标需移动到合适位置 + // 激活控件 if (positionResult.isControl) { - this.control.moveCursorIndex(positionResult) + const { + index, + isTable, + trIndex, + tdIndex, + tdValueIndex + } = positionResult + const { newIndex } = this.control.initControl({ + index, + isTable, + trIndex, + tdIndex, + tdValueIndex + }) + if (isTable) { + positionResult.tdValueIndex = newIndex + } else { + positionResult.index = newIndex + } } const { index, @@ -439,6 +457,10 @@ export class CanvasEvent { if (!this.cursor) return const cursorPosition = this.position.getCursorPosition() if (!data || !cursorPosition || this.isCompositing) return + if (this.control.isPartRangeInControlOutside()) { + // 忽略选区部分在控件的输入 + return + } const { TEXT, HYPERLINK, SUBSCRIPT, SUPERSCRIPT } = ElementType const text = data.replaceAll(`\n`, ZERO) const elementList = this.draw.getElementList() @@ -477,18 +499,24 @@ export class CanvasEvent { } return newElement }) - let start = 0 - if (isCollapsed) { - start = index + 1 + // 控件-移除placeholder + let curIndex: number + if (positionContext.isControl) { + curIndex = this.control.setValue(inputData) } else { - start = startIndex + 1 - elementList.splice(startIndex + 1, endIndex - startIndex) - } - // 禁止直接使用解构存在性能问题 - for (let i = 0; i < inputData.length; i++) { - elementList.splice(start + i, 0, inputData[i]) + let start = 0 + if (isCollapsed) { + start = index + 1 + } else { + start = startIndex + 1 + elementList.splice(startIndex + 1, endIndex - startIndex) + } + // 禁止直接使用解构存在性能问题 + for (let i = 0; i < inputData.length; i++) { + elementList.splice(start + i, 0, inputData[i]) + } + curIndex = (isCollapsed ? index : startIndex) + inputData.length } - const curIndex = (isCollapsed ? index : startIndex) + inputData.length this.range.setRange(curIndex, curIndex) this.draw.render({ curIndex }) } diff --git a/src/editor/interface/Control.ts b/src/editor/interface/Control.ts index 78f56a0..f0feb90 100644 --- a/src/editor/interface/Control.ts +++ b/src/editor/interface/Control.ts @@ -26,4 +26,20 @@ export interface IControlOption { bracketColor?: string; prefix?: string; postfix?: string; +} + +export interface IControlInitOption { + index: number; + isTable?: boolean; + trIndex?: number; + tdIndex?: number; + tdValueIndex?: number; +} + +export interface IControlInitResult { + newIndex: number; +} + +export interface IControlInstance { + setValue(data: IElement[]): number; } \ No newline at end of file diff --git a/src/editor/utils/element.ts b/src/editor/utils/element.ts index 8af8aa7..e1e89a1 100644 --- a/src/editor/utils/element.ts +++ b/src/editor/utils/element.ts @@ -1,6 +1,7 @@ import { deepClone, getUUID } from '.' import { ElementType, IEditorOption, IElement } from '..' import { ZERO } from '../dataset/constant/Common' +import { defaultControlOption } from '../dataset/constant/Control' import { EDITOR_ELEMENT_ZIP_ATTR } from '../dataset/constant/Element' import { ControlComponent } from '../dataset/enum/Control' @@ -80,20 +81,18 @@ export function formatElementList(elementList: IElement[], options: IFormatEleme thePrePostfixArgs.color = editorOptions.control.bracketColor } // 前缀 - if (prefix) { - const prefixStrList = prefix.split('') - for (let p = 0; p < prefixStrList.length; p++) { - const value = prefixStrList[p] - elementList.splice(i, 0, { - controlId, - value, - type: el.type, - control: el.control, - controlComponent: ControlComponent.PREFIX, - ...thePrePostfixArgs - }) - i++ - } + const prefixStrList = (prefix || defaultControlOption.prefix).split('') + for (let p = 0; p < prefixStrList.length; p++) { + const value = prefixStrList[p] + elementList.splice(i, 0, { + controlId, + value, + type: el.type, + control: el.control, + controlComponent: ControlComponent.PREFIX, + ...thePrePostfixArgs + }) + i++ } // 值 if (value && value.length) { @@ -133,20 +132,18 @@ export function formatElementList(elementList: IElement[], options: IFormatEleme } } // 后缀 - if (postfix) { - const postfixStrList = postfix.split('') - for (let p = 0; p < postfixStrList.length; p++) { - const value = postfixStrList[p] - elementList.splice(i, 0, { - controlId, - value, - type: el.type, - control: el.control, - controlComponent: ControlComponent.POSTFIX, - ...thePrePostfixArgs - }) - i++ - } + const postfixStrList = (postfix || defaultControlOption.postfix).split('') + for (let p = 0; p < postfixStrList.length; p++) { + const value = postfixStrList[p] + elementList.splice(i, 0, { + controlId, + value, + type: el.type, + control: el.control, + controlComponent: ControlComponent.POSTFIX, + ...thePrePostfixArgs + }) + i++ } i-- } else if ((!el.type || el.type === ElementType.TEXT) && el.value.length > 1) {