diff --git a/src/editor/assets/css/control/select.css b/src/editor/assets/css/control/select.css new file mode 100644 index 0000000..010c9ad --- /dev/null +++ b/src/editor/assets/css/control/select.css @@ -0,0 +1,44 @@ +.select-control-popup { + max-width: 160px; + min-width: 69px; + max-height: 225px; + position: absolute; + z-index: 1; + border: 1px solid #e4e7ed; + border-radius: 4px; + background-color: #fff; + box-shadow: 0 2px 12px 0 rgba(0, 0, 0, .1); + box-sizing: border-box; + margin: 5px 0; + overflow-y: auto; +} + +.select-control-popup ul { + list-style: none; + padding: 3px 0; + margin: 0; + box-sizing: border-box; +} + +.select-control-popup ul li { + font-size: 13px; + padding: 0 20px; + position: relative; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + color: #666; + height: 36px; + line-height: 36px; + box-sizing: border-box; + cursor: pointer; +} + +.select-control-popup ul li:hover { + background-color: #EEF2FD; +} + +.select-control-popup ul li.active { + color: var(--COLOR-HOVER, #5175f4); + font-weight: 700; +} \ No newline at end of file diff --git a/src/editor/assets/css/index.css b/src/editor/assets/css/index.css index d976bd2..2b3c201 100644 --- a/src/editor/assets/css/index.css +++ b/src/editor/assets/css/index.css @@ -1,3 +1,5 @@ +@import './control/select.css'; + .inputarea { width: 0; height: 12px; diff --git a/src/editor/core/draw/control/Control.ts b/src/editor/core/draw/control/Control.ts index f0748ec..1113a21 100644 --- a/src/editor/core/draw/control/Control.ts +++ b/src/editor/core/draw/control/Control.ts @@ -1,9 +1,10 @@ import { ControlComponent, ControlType } from '../../../dataset/enum/Control' import { ElementType } from '../../../dataset/enum/Element' import { IControlInitOption, IControlInstance, IControlOption } from '../../../interface/Control' -import { IElement } from '../../../interface/Element' +import { IElement, IElementPosition } from '../../../interface/Element' import { RangeManager } from '../../range/RangeManager' import { Draw } from '../Draw' +import { SelectControl } from './select/SelectControl' import { TextControl } from './text/TextControl' interface IMoveCursorResult { @@ -40,14 +41,34 @@ export class Control { return false } + public getContainer(): HTMLDivElement { + return this.draw.getContainer() + } + public getElementList(): IElement[] { return this.draw.getElementList() } + public getPosition(): IElementPosition | null { + const positionList = this.draw.getPosition().getPositionList() + const { endIndex } = this.range.getRange() + return positionList[endIndex] || null + } + + public getPreY(): number { + const height = this.draw.getHeight() + const pageGap = this.draw.getPageGap() + return this.draw.getPageNo() * (height + pageGap) + } + public getRange() { return this.range.getRange() } + public shrinkBoundary() { + this.range.shrinkBoundary() + } + public getActiveControl(): IControlInstance | null { return this.activeControl } @@ -58,23 +79,42 @@ export class Control { const element = elementList[range.startIndex] // 判断控件是否已经激活 if (this.activeControl) { + // 列举控件唤醒下拉弹窗 + if (this.activeControl instanceof SelectControl) { + this.activeControl.awake() + } const controlElement = this.activeControl.getElement() if (element.controlId === controlElement.controlId) return } // 销毁旧激活控件 this.destroyControl() // 激活控件 - if (element.control!.type === ControlType.TEXT) { + const control = element.control! + if (control.type === ControlType.TEXT) { this.activeControl = new TextControl(element, this) + } else if (control.type === ControlType.SELECT) { + const selectControl = new SelectControl(element, this) + this.activeControl = selectControl + selectControl.awake() } } public destroyControl() { if (this.activeControl) { + if (this.activeControl instanceof SelectControl) { + this.activeControl.destroy() + } this.activeControl = null } } + public repaintControl(curIndex: number) { + this.range.setRange(curIndex, curIndex) + this.draw.render({ + curIndex + }) + } + public moveCursor(position: IControlInitOption): IMoveCursorResult { const { index, trIndex, tdIndex, tdValueIndex } = position let elementList = this.draw.getOriginalElementList() @@ -169,97 +209,12 @@ export class Control { } nextIndex++ } - if (!~leftIndex || !~rightIndex) return -1 + if (!~leftIndex || !~rightIndex) return startIndex // 删除元素 elementList.splice(leftIndex + 1, rightIndex - leftIndex) return leftIndex } - public shrinkBoundary() { - const elementList = this.getElementList() - const range = this.getRange() - const { startIndex, endIndex } = range - const startElement = elementList[startIndex] - const endElement = elementList[endIndex] - if (startIndex === endIndex) { - if (startElement.controlComponent === ControlComponent.PLACEHOLDER) { - // 找到第一个placeholder字符 - let index = startIndex - 1 - while (index > 0) { - const preElement = elementList[index] - if ( - preElement.controlId !== startElement.controlId || - preElement.controlComponent === ControlComponent.PREFIX - ) { - console.log(index) - range.startIndex = index - range.endIndex = index - break - } - index-- - } - } - } else { - // 首、尾为占位符时,收缩到最后一个前缀字符后 - 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(startIndex: number) { const elementList = this.getElementList() const startElement = elementList[startIndex] diff --git a/src/editor/core/draw/control/select/SelectControl.ts b/src/editor/core/draw/control/select/SelectControl.ts index e3608fa..9677995 100644 --- a/src/editor/core/draw/control/select/SelectControl.ts +++ b/src/editor/core/draw/control/select/SelectControl.ts @@ -1,3 +1,254 @@ -export class SelectControl { +import { EDITOR_COMPONENT } from '../../../../dataset/constant/Editor' +import { ControlComponent } from '../../../../dataset/enum/Control' +import { EditorComponent } from '../../../../dataset/enum/Editor' +import { KeyMap } from '../../../../dataset/enum/Keymap' +import { IControlInstance } from '../../../../interface/Control' +import { IElement } from '../../../../interface/Element' +import { Control } from '../Control' + +export class SelectControl implements IControlInstance { + + private element: IElement + private control: Control + private isPopup: boolean + private selectDom: HTMLDivElement | null + + constructor(element: IElement, control: Control) { + this.element = element + this.control = control + this.isPopup = false + this.selectDom = null + } + + public getElement(): IElement { + return this.element + } + + public getCode(): string | null { + return this.element.control?.code || null + } + + 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(): number { + const range = this.control.getRange() + return range.endIndex + } + + public keydown(evt: KeyboardEvent): number { + const elementList = this.control.getElementList() + const range = this.control.getRange() + // 收缩边界到Value内 + this.control.shrinkBoundary() + const { startIndex, endIndex } = range + const startElement = elementList[startIndex] + const endElement = elementList[endIndex] + // backspace + if (evt.key === KeyMap.Backspace) { + // 清空选项 + if (startIndex !== endIndex) { + return this.clearSelect() + } else { + if ( + startElement.controlComponent === ControlComponent.PREFIX || + endElement.controlComponent === ControlComponent.POSTFIX || + startElement.controlComponent === ControlComponent.PLACEHOLDER + ) { + // 前缀、后缀、占位符 + return this.control.removeControl(startIndex) + } else { + // 清空选项 + return this.clearSelect() + } + } + } else if (evt.key === KeyMap.Delete) { + // 移除选区元素 + if (startIndex !== endIndex) { + // 清空选项 + return this.clearSelect() + } else { + const endNextElement = elementList[endIndex + 1] + if (startElement.controlComponent === ControlComponent.PREFIX || + endNextElement.controlComponent === ControlComponent.POSTFIX || + startElement.controlComponent === ControlComponent.PLACEHOLDER + ) { + // 前缀、后缀、占位符 + return this.control.removeControl(startIndex) + } else { + // 清空选项 + return this.clearSelect() + } + } + } + return endIndex + } + + public cut(): number { + this.control.shrinkBoundary() + const { startIndex, endIndex } = this.control.getRange() + if (startIndex === endIndex) { + return startIndex + } + // 清空选项 + return this.clearSelect() + } + + public clearSelect(): number { + const elementList = this.control.getElementList() + const { startIndex } = this.control.getRange() + 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 || + preElement.controlComponent === ControlComponent.PREFIX + ) { + leftIndex = preIndex + break + } + preIndex-- + } + // 向右查找 + let nextIndex = startIndex + 1 + while (nextIndex < elementList.length) { + const nextElement = elementList[nextIndex] + if ( + nextElement.controlId !== startElement.controlId || + nextElement.controlComponent === ControlComponent.POSTFIX + ) { + rightIndex = nextIndex - 1 + break + } + nextIndex++ + } + if (!~leftIndex || !~rightIndex) return -1 + // 删除元素 + elementList.splice(leftIndex + 1, rightIndex - leftIndex) + // 增加占位符 + this.control.addPlaceholder(preIndex) + this.element.control!.code = null + return preIndex + } + + public setSelect(code: string) { + const control = this.element.control! + const valueSets = control.valueSets + if (!Array.isArray(valueSets) || !valueSets.length) return + // 转换code + const valueSet = valueSets.find(v => v.code === code) + if (!valueSet) return + // 清空选项 + const startIndex = this.clearSelect() + this.control.removePlaceholder(startIndex) + // 插入 + const elementList = this.control.getElementList() + const startElement = elementList[startIndex] + const start = startIndex + 1 + const data = valueSet.value.split('') + for (let i = 0; i < data.length; i++) { + elementList.splice(start + i, 0, { + ...startElement, + value: data[i], + controlComponent: ControlComponent.VALUE + }) + } + // render + const newIndex = start + data.length - 1 + this.control.repaintControl(newIndex) + // 设置状态 + this.element.control!.code = code + this.destroy() + } + + private _createSelectPopupDom() { + const control = this.element.control! + const valueSets = control.valueSets + if (!Array.isArray(valueSets) || !valueSets.length) return + const position = this.control.getPosition() + if (!position) return + // dom树: