feat:text control delete
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
import { ControlComponent, ControlType } from '../../../dataset/enum/Control'
|
import { ControlComponent, ControlType } from '../../../dataset/enum/Control'
|
||||||
import { ElementType } from '../../../dataset/enum/Element'
|
import { ElementType } from '../../../dataset/enum/Element'
|
||||||
import { IControlInitOption, IControlInitResult, IControlInstance } from '../../../interface/Control'
|
import { IControlInitOption, IControlInitResult, IControlInstance } from '../../../interface/Control'
|
||||||
|
import { IEditorOption } from '../../../interface/Editor'
|
||||||
import { IElement } from '../../../interface/Element'
|
import { IElement } from '../../../interface/Element'
|
||||||
import { RangeManager } from '../../range/RangeManager'
|
import { RangeManager } from '../../range/RangeManager'
|
||||||
import { Draw } from '../Draw'
|
import { Draw } from '../Draw'
|
||||||
@@ -14,14 +15,20 @@ export class Control {
|
|||||||
|
|
||||||
private draw: Draw
|
private draw: Draw
|
||||||
private range: RangeManager
|
private range: RangeManager
|
||||||
|
private options: Required<IEditorOption>
|
||||||
private activeControl: IControlInstance | null
|
private activeControl: IControlInstance | null
|
||||||
|
|
||||||
constructor(draw: Draw) {
|
constructor(draw: Draw) {
|
||||||
this.draw = draw
|
this.draw = draw
|
||||||
this.range = draw.getRange()
|
this.range = draw.getRange()
|
||||||
|
this.options = draw.getOptions()
|
||||||
this.activeControl = null
|
this.activeControl = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getOptions(): Required<IEditorOption> {
|
||||||
|
return this.options
|
||||||
|
}
|
||||||
|
|
||||||
public getElementList(): IElement[] {
|
public getElementList(): IElement[] {
|
||||||
return this.draw.getElementList()
|
return this.draw.getElementList()
|
||||||
}
|
}
|
||||||
@@ -146,4 +153,11 @@ export class Control {
|
|||||||
return this.activeControl.setValue(data)
|
return this.activeControl.setValue(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public keydown(evt: KeyboardEvent): number {
|
||||||
|
if (!this.activeControl) {
|
||||||
|
throw new Error('active control is null')
|
||||||
|
}
|
||||||
|
return this.activeControl.keydown(evt)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import { ControlComponent } from '../../../../dataset/enum/Control'
|
import { ControlComponent } from '../../../../dataset/enum/Control'
|
||||||
import { IControlInstance } from '../../../../interface/Control'
|
import { ElementType } from '../../../../dataset/enum/Element'
|
||||||
|
import { KeyMap } from '../../../../dataset/enum/Keymap'
|
||||||
|
import { IControlInstance, IControlOption } from '../../../../interface/Control'
|
||||||
import { IElement } from '../../../../interface/Element'
|
import { IElement } from '../../../../interface/Element'
|
||||||
import { IRange } from '../../../../interface/Range'
|
import { IRange } from '../../../../interface/Range'
|
||||||
import { Control } from '../Control'
|
import { Control } from '../Control'
|
||||||
@@ -7,9 +9,11 @@ import { Control } from '../Control'
|
|||||||
export class TextControl implements IControlInstance {
|
export class TextControl implements IControlInstance {
|
||||||
|
|
||||||
private control: Control
|
private control: Control
|
||||||
|
private options: IControlOption
|
||||||
|
|
||||||
constructor(control: Control) {
|
constructor(control: Control) {
|
||||||
this.control = control
|
this.control = control
|
||||||
|
this.options = control.getOptions().control
|
||||||
}
|
}
|
||||||
|
|
||||||
public shrinkBoundary(elementList: IElement[], range: IRange) {
|
public shrinkBoundary(elementList: IElement[], range: IRange) {
|
||||||
@@ -97,6 +101,94 @@ export class TextControl implements IControlInstance {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public removeControl(elementList: IElement[], range: IRange): number {
|
||||||
|
const { startIndex } = range
|
||||||
|
const startElement = elementList[startIndex]
|
||||||
|
let leftIndex = -1
|
||||||
|
let rightIndex = -1
|
||||||
|
// 向左查找
|
||||||
|
let preIndex = startIndex
|
||||||
|
while (preIndex > 0) {
|
||||||
|
const preElement = elementList[preIndex]
|
||||||
|
if (preElement.controlId !== startElement.controlId) {
|
||||||
|
leftIndex = preIndex
|
||||||
|
break
|
||||||
|
}
|
||||||
|
preIndex--
|
||||||
|
}
|
||||||
|
// 向右查找
|
||||||
|
let nextIndex = startIndex + 1
|
||||||
|
while (nextIndex < elementList.length) {
|
||||||
|
const nextElement = elementList[nextIndex]
|
||||||
|
if (nextElement.controlId !== startElement.controlId) {
|
||||||
|
rightIndex = nextIndex - 1
|
||||||
|
break
|
||||||
|
}
|
||||||
|
nextIndex++
|
||||||
|
}
|
||||||
|
if (!~leftIndex || !~rightIndex) return -1
|
||||||
|
// 删除元素
|
||||||
|
elementList.splice(leftIndex + 1, rightIndex - leftIndex)
|
||||||
|
// 清除实例
|
||||||
|
this.control.destroyControl()
|
||||||
|
return leftIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
public addPlaceholder(elementList: IElement[], startIndex: number) {
|
||||||
|
const startElement = elementList[startIndex]
|
||||||
|
const control = startElement.control!
|
||||||
|
const placeholderStrList = control.placeholder.split('')
|
||||||
|
for (let p = 0; p < placeholderStrList.length; p++) {
|
||||||
|
const value = placeholderStrList[p]
|
||||||
|
elementList.splice(startIndex + p + 1, 0, {
|
||||||
|
value,
|
||||||
|
controlId: startElement.controlId,
|
||||||
|
type: ElementType.CONTROL,
|
||||||
|
control: startElement.control,
|
||||||
|
controlComponent: ControlComponent.PLACEHOLDER,
|
||||||
|
color: this.options.placeholderColor
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public getValue(): IElement[] {
|
||||||
|
const elementList = this.control.getElementList()
|
||||||
|
const { startIndex } = this.control.getRange()
|
||||||
|
const startElement = elementList[startIndex]
|
||||||
|
const data: IElement[] = []
|
||||||
|
// 向左查找
|
||||||
|
let preIndex = startIndex
|
||||||
|
while (preIndex > 0) {
|
||||||
|
const preElement = elementList[preIndex]
|
||||||
|
if (
|
||||||
|
preElement.controlId !== startElement.controlId ||
|
||||||
|
preElement.controlComponent === ControlComponent.PREFIX
|
||||||
|
) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (preElement.controlComponent === ControlComponent.VALUE) {
|
||||||
|
data.unshift(preElement)
|
||||||
|
}
|
||||||
|
preIndex--
|
||||||
|
}
|
||||||
|
// 向右查找
|
||||||
|
let nextIndex = startIndex + 1
|
||||||
|
while (nextIndex < elementList.length) {
|
||||||
|
const nextElement = elementList[nextIndex]
|
||||||
|
if (
|
||||||
|
nextElement.controlId !== startElement.controlId ||
|
||||||
|
nextElement.controlComponent === ControlComponent.POSTFIX
|
||||||
|
) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if (nextElement.controlComponent === ControlComponent.VALUE) {
|
||||||
|
data.push(nextElement)
|
||||||
|
}
|
||||||
|
nextIndex++
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
public setValue(data: IElement[]): number {
|
public setValue(data: IElement[]): number {
|
||||||
const elementList = this.control.getElementList()
|
const elementList = this.control.getElementList()
|
||||||
const range = this.control.getRange()
|
const range = this.control.getRange()
|
||||||
@@ -123,4 +215,67 @@ export class TextControl implements IControlInstance {
|
|||||||
return start + data.length - 1
|
return start + data.length - 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public keydown(evt: KeyboardEvent): number {
|
||||||
|
const elementList = this.control.getElementList()
|
||||||
|
const range = this.control.getRange()
|
||||||
|
// 收缩边界到Value内
|
||||||
|
this.shrinkBoundary(elementList, range)
|
||||||
|
const { startIndex, endIndex } = range
|
||||||
|
const startElement = elementList[startIndex]
|
||||||
|
const endElement = elementList[endIndex]
|
||||||
|
// backspace
|
||||||
|
if (evt.key === KeyMap.Backspace) {
|
||||||
|
// 移除选区元素
|
||||||
|
if (startIndex !== endIndex) {
|
||||||
|
elementList.splice(startIndex + 1, endIndex - startIndex)
|
||||||
|
const value = this.getValue()
|
||||||
|
if (!value.length) {
|
||||||
|
this.addPlaceholder(elementList, startIndex)
|
||||||
|
}
|
||||||
|
return startIndex
|
||||||
|
} else {
|
||||||
|
if (startElement.controlComponent === ControlComponent.PREFIX) {
|
||||||
|
// 前缀
|
||||||
|
return this.removeControl(elementList, range)
|
||||||
|
} else if (endElement.controlComponent === ControlComponent.POSTFIX) {
|
||||||
|
// 后缀
|
||||||
|
return this.removeControl(elementList, range)
|
||||||
|
} else {
|
||||||
|
// 文本
|
||||||
|
elementList.splice(startIndex, 1)
|
||||||
|
const value = this.getValue()
|
||||||
|
if (!value.length) {
|
||||||
|
this.addPlaceholder(elementList, startIndex - 1)
|
||||||
|
}
|
||||||
|
return startIndex - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (evt.key === KeyMap.Delete) {
|
||||||
|
// 移除选区元素
|
||||||
|
if (startIndex !== endIndex) {
|
||||||
|
elementList.splice(startIndex + 1, endIndex - startIndex)
|
||||||
|
const value = this.getValue()
|
||||||
|
if (!value.length) {
|
||||||
|
this.addPlaceholder(elementList, startIndex)
|
||||||
|
}
|
||||||
|
return startIndex
|
||||||
|
} else {
|
||||||
|
const endNextElement = elementList[endIndex + 1]
|
||||||
|
if (endNextElement.controlComponent === ControlComponent.POSTFIX) {
|
||||||
|
// 后缀
|
||||||
|
return this.removeControl(elementList, range)
|
||||||
|
} else {
|
||||||
|
// 文本
|
||||||
|
elementList.splice(startIndex + 1, 1)
|
||||||
|
const value = this.getValue()
|
||||||
|
if (!value.length) {
|
||||||
|
this.addPlaceholder(elementList, startIndex)
|
||||||
|
}
|
||||||
|
return startIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -187,6 +187,8 @@ export class CanvasEvent {
|
|||||||
} else {
|
} else {
|
||||||
positionResult.index = newIndex
|
positionResult.index = newIndex
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
this.control.destroyControl()
|
||||||
}
|
}
|
||||||
const {
|
const {
|
||||||
index,
|
index,
|
||||||
@@ -273,8 +275,15 @@ export class CanvasEvent {
|
|||||||
const { index } = cursorPosition
|
const { index } = cursorPosition
|
||||||
const { startIndex, endIndex } = this.range.getRange()
|
const { startIndex, endIndex } = this.range.getRange()
|
||||||
const isCollapsed = startIndex === endIndex
|
const isCollapsed = startIndex === endIndex
|
||||||
|
// 当前激活控件
|
||||||
|
const isPartRangeInControlOutside = this.control.isPartRangeInControlOutside()
|
||||||
|
const activeControl = this.control.getActiveControl()
|
||||||
if (evt.key === KeyMap.Backspace) {
|
if (evt.key === KeyMap.Backspace) {
|
||||||
if (isReadonly) return
|
if (isReadonly || isPartRangeInControlOutside) return
|
||||||
|
let curIndex: number
|
||||||
|
if (activeControl) {
|
||||||
|
curIndex = this.control.keydown(evt)
|
||||||
|
} else {
|
||||||
// 判断是否允许删除
|
// 判断是否允许删除
|
||||||
if (isCollapsed && elementList[index].value === ZERO && index === 0) {
|
if (isCollapsed && elementList[index].value === ZERO && index === 0) {
|
||||||
evt.preventDefault()
|
evt.preventDefault()
|
||||||
@@ -285,17 +294,23 @@ export class CanvasEvent {
|
|||||||
} else {
|
} else {
|
||||||
elementList.splice(index, 1)
|
elementList.splice(index, 1)
|
||||||
}
|
}
|
||||||
const curIndex = isCollapsed ? index - 1 : startIndex
|
curIndex = isCollapsed ? index - 1 : startIndex
|
||||||
|
}
|
||||||
this.range.setRange(curIndex, curIndex)
|
this.range.setRange(curIndex, curIndex)
|
||||||
this.draw.render({ curIndex })
|
this.draw.render({ curIndex })
|
||||||
} else if (evt.key === KeyMap.Delete) {
|
} else if (evt.key === KeyMap.Delete) {
|
||||||
if (isReadonly) return
|
if (isReadonly || isPartRangeInControlOutside) return
|
||||||
|
let curIndex: number
|
||||||
|
if (activeControl) {
|
||||||
|
curIndex = this.control.keydown(evt)
|
||||||
|
} else {
|
||||||
if (!isCollapsed) {
|
if (!isCollapsed) {
|
||||||
elementList.splice(startIndex + 1, endIndex - startIndex)
|
elementList.splice(startIndex + 1, endIndex - startIndex)
|
||||||
} else {
|
} else {
|
||||||
elementList.splice(index + 1, 1)
|
elementList.splice(index + 1, 1)
|
||||||
}
|
}
|
||||||
const curIndex = isCollapsed ? index : startIndex
|
curIndex = isCollapsed ? index : startIndex
|
||||||
|
}
|
||||||
this.range.setRange(curIndex, curIndex)
|
this.range.setRange(curIndex, curIndex)
|
||||||
this.draw.render({ curIndex })
|
this.draw.render({ curIndex })
|
||||||
} else if (evt.key === KeyMap.Enter) {
|
} else if (evt.key === KeyMap.Enter) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { EDITOR_COMPONENT } from '../../dataset/constant/Editor'
|
|||||||
import { IEditorOption } from '../../interface/Editor'
|
import { IEditorOption } from '../../interface/Editor'
|
||||||
import { findParent } from '../../utils'
|
import { findParent } from '../../utils'
|
||||||
import { Cursor } from '../cursor/Cursor'
|
import { Cursor } from '../cursor/Cursor'
|
||||||
|
import { Control } from '../draw/control/Control'
|
||||||
import { Draw } from '../draw/Draw'
|
import { Draw } from '../draw/Draw'
|
||||||
import { HyperlinkParticle } from '../draw/particle/HyperlinkParticle'
|
import { HyperlinkParticle } from '../draw/particle/HyperlinkParticle'
|
||||||
import { ImageParticle } from '../draw/particle/ImageParticle'
|
import { ImageParticle } from '../draw/particle/ImageParticle'
|
||||||
@@ -20,6 +21,7 @@ export class GlobalEvent {
|
|||||||
private imageParticle: ImageParticle
|
private imageParticle: ImageParticle
|
||||||
private tableTool: TableTool
|
private tableTool: TableTool
|
||||||
private hyperlinkParticle: HyperlinkParticle
|
private hyperlinkParticle: HyperlinkParticle
|
||||||
|
private control: Control
|
||||||
|
|
||||||
constructor(draw: Draw, canvasEvent: CanvasEvent) {
|
constructor(draw: Draw, canvasEvent: CanvasEvent) {
|
||||||
this.draw = draw
|
this.draw = draw
|
||||||
@@ -31,6 +33,7 @@ export class GlobalEvent {
|
|||||||
this.imageParticle = draw.getImageParticle()
|
this.imageParticle = draw.getImageParticle()
|
||||||
this.tableTool = draw.getTableTool()
|
this.tableTool = draw.getTableTool()
|
||||||
this.hyperlinkParticle = draw.getHyperlinkParticle()
|
this.hyperlinkParticle = draw.getHyperlinkParticle()
|
||||||
|
this.control = draw.getControl()
|
||||||
}
|
}
|
||||||
|
|
||||||
public register() {
|
public register() {
|
||||||
@@ -74,6 +77,7 @@ export class GlobalEvent {
|
|||||||
this.imageParticle.clearResizer()
|
this.imageParticle.clearResizer()
|
||||||
this.tableTool.dispose()
|
this.tableTool.dispose()
|
||||||
this.hyperlinkParticle.clearHyperlinkPopup()
|
this.hyperlinkParticle.clearHyperlinkPopup()
|
||||||
|
this.control.destroyControl()
|
||||||
}
|
}
|
||||||
|
|
||||||
public setDragState() {
|
public setDragState() {
|
||||||
|
|||||||
@@ -41,5 +41,9 @@ export interface IControlInitResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IControlInstance {
|
export interface IControlInstance {
|
||||||
|
getValue(): IElement[];
|
||||||
|
|
||||||
setValue(data: IElement[]): number;
|
setValue(data: IElement[]): number;
|
||||||
|
|
||||||
|
keydown(evt: KeyboardEvent): number;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user