feat: add date control #601
parent
d54a701adb
commit
398919938f
@ -0,0 +1,361 @@
|
|||||||
|
import {
|
||||||
|
CONTROL_STYLE_ATTR,
|
||||||
|
EDITOR_ELEMENT_STYLE_ATTR,
|
||||||
|
TEXTLIKE_ELEMENT_TYPE
|
||||||
|
} from '../../../../dataset/constant/Element'
|
||||||
|
import { ControlComponent } from '../../../../dataset/enum/Control'
|
||||||
|
import { ElementType } from '../../../../dataset/enum/Element'
|
||||||
|
import { KeyMap } from '../../../../dataset/enum/KeyMap'
|
||||||
|
import {
|
||||||
|
IControlContext,
|
||||||
|
IControlInstance,
|
||||||
|
IControlRuleOption
|
||||||
|
} from '../../../../interface/Control'
|
||||||
|
import { IElement } from '../../../../interface/Element'
|
||||||
|
import { omitObject, pickObject } from '../../../../utils'
|
||||||
|
import { formatElementContext } from '../../../../utils/element'
|
||||||
|
import { Draw } from '../../Draw'
|
||||||
|
import { DatePicker } from '../../particle/date/DatePicker'
|
||||||
|
import { Control } from '../Control'
|
||||||
|
|
||||||
|
export class DateControl implements IControlInstance {
|
||||||
|
private draw: Draw
|
||||||
|
private element: IElement
|
||||||
|
private control: Control
|
||||||
|
private isPopup: boolean
|
||||||
|
private datePicker: DatePicker | null
|
||||||
|
|
||||||
|
constructor(element: IElement, control: Control) {
|
||||||
|
const draw = control.getDraw()
|
||||||
|
this.draw = draw
|
||||||
|
this.element = element
|
||||||
|
this.control = control
|
||||||
|
this.isPopup = false
|
||||||
|
this.datePicker = null
|
||||||
|
}
|
||||||
|
|
||||||
|
public setElement(element: IElement) {
|
||||||
|
this.element = element
|
||||||
|
}
|
||||||
|
|
||||||
|
public getElement(): IElement {
|
||||||
|
return this.element
|
||||||
|
}
|
||||||
|
|
||||||
|
public getIsPopup(): boolean {
|
||||||
|
return this.isPopup
|
||||||
|
}
|
||||||
|
|
||||||
|
public getValueRange(context: IControlContext = {}): [number, number] | null {
|
||||||
|
const elementList = context.elementList || this.control.getElementList()
|
||||||
|
const { startIndex } = context.range || this.control.getRange()
|
||||||
|
const startElement = elementList[startIndex]
|
||||||
|
// 向左查找
|
||||||
|
let preIndex = startIndex
|
||||||
|
while (preIndex > 0) {
|
||||||
|
const preElement = elementList[preIndex]
|
||||||
|
if (
|
||||||
|
preElement.controlId !== startElement.controlId ||
|
||||||
|
preElement.controlComponent === ControlComponent.PREFIX
|
||||||
|
) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
preIndex--
|
||||||
|
}
|
||||||
|
// 向右查找
|
||||||
|
let nextIndex = startIndex + 1
|
||||||
|
while (nextIndex < elementList.length) {
|
||||||
|
const nextElement = elementList[nextIndex]
|
||||||
|
if (
|
||||||
|
nextElement.controlId !== startElement.controlId ||
|
||||||
|
nextElement.controlComponent === ControlComponent.POSTFIX
|
||||||
|
) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
nextIndex++
|
||||||
|
}
|
||||||
|
if (preIndex === nextIndex) return null
|
||||||
|
return [preIndex, nextIndex - 1]
|
||||||
|
}
|
||||||
|
|
||||||
|
public getValue(context: IControlContext = {}): IElement[] {
|
||||||
|
const elementList = context.elementList || this.control.getElementList()
|
||||||
|
const range = this.getValueRange(context)
|
||||||
|
if (!range) return []
|
||||||
|
const data: IElement[] = []
|
||||||
|
const [startIndex, endIndex] = range
|
||||||
|
for (let i = startIndex; i <= endIndex; i++) {
|
||||||
|
const element = elementList[i]
|
||||||
|
if (element.controlComponent === ControlComponent.VALUE) {
|
||||||
|
data.push(element)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
public setValue(
|
||||||
|
data: IElement[],
|
||||||
|
context: IControlContext = {},
|
||||||
|
options: IControlRuleOption = {}
|
||||||
|
): number {
|
||||||
|
// 校验是否可以设置
|
||||||
|
if (!options.isIgnoreDisabledRule && this.control.getIsDisabledControl()) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
const elementList = context.elementList || this.control.getElementList()
|
||||||
|
const range = context.range || this.control.getRange()
|
||||||
|
// 收缩边界到Value内
|
||||||
|
this.control.shrinkBoundary(context)
|
||||||
|
const { startIndex, endIndex } = range
|
||||||
|
const draw = this.control.getDraw()
|
||||||
|
// 移除选区元素
|
||||||
|
if (startIndex !== endIndex) {
|
||||||
|
draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex)
|
||||||
|
} else {
|
||||||
|
// 移除空白占位符
|
||||||
|
this.control.removePlaceholder(startIndex, context)
|
||||||
|
}
|
||||||
|
// 非文本类元素或前缀过渡掉样式属性
|
||||||
|
const startElement = elementList[startIndex]
|
||||||
|
const anchorElement =
|
||||||
|
(startElement.type &&
|
||||||
|
!TEXTLIKE_ELEMENT_TYPE.includes(startElement.type)) ||
|
||||||
|
startElement.controlComponent === ControlComponent.PREFIX
|
||||||
|
? pickObject(startElement, [
|
||||||
|
'control',
|
||||||
|
'controlId',
|
||||||
|
...CONTROL_STYLE_ATTR
|
||||||
|
])
|
||||||
|
: omitObject(startElement, ['type'])
|
||||||
|
// 插入起始位置
|
||||||
|
const start = range.startIndex + 1
|
||||||
|
for (let i = 0; i < data.length; i++) {
|
||||||
|
const newElement: IElement = {
|
||||||
|
...anchorElement,
|
||||||
|
...data[i],
|
||||||
|
controlComponent: ControlComponent.VALUE
|
||||||
|
}
|
||||||
|
formatElementContext(elementList, [newElement], startIndex)
|
||||||
|
draw.spliceElementList(elementList, start + i, 0, newElement)
|
||||||
|
}
|
||||||
|
return start + data.length - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
public clearSelect(
|
||||||
|
context: IControlContext = {},
|
||||||
|
options: IControlRuleOption = {}
|
||||||
|
): number {
|
||||||
|
const { isIgnoreDisabledRule = false, isAddPlaceholder = true } = options
|
||||||
|
// 校验是否可以设置
|
||||||
|
if (!isIgnoreDisabledRule && this.control.getIsDisabledControl()) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
const range = this.getValueRange(context)
|
||||||
|
if (!range) return -1
|
||||||
|
const [leftIndex, rightIndex] = range
|
||||||
|
if (!~leftIndex || !~rightIndex) return -1
|
||||||
|
const elementList = context.elementList || this.control.getElementList()
|
||||||
|
// 删除元素
|
||||||
|
const draw = this.control.getDraw()
|
||||||
|
draw.spliceElementList(elementList, leftIndex + 1, rightIndex - leftIndex)
|
||||||
|
// 增加占位符
|
||||||
|
if (isAddPlaceholder) {
|
||||||
|
this.control.addPlaceholder(leftIndex, context)
|
||||||
|
}
|
||||||
|
return leftIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
public setSelect(
|
||||||
|
date: string,
|
||||||
|
context: IControlContext = {},
|
||||||
|
options: IControlRuleOption = {}
|
||||||
|
) {
|
||||||
|
// 校验是否可以设置
|
||||||
|
if (!options.isIgnoreDisabledRule && this.control.getIsDisabledControl()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const elementList = context.elementList || this.control.getElementList()
|
||||||
|
const range = context.range || this.control.getRange()
|
||||||
|
// 样式赋值元素-默认值的第一个字符样式,否则取默认样式
|
||||||
|
const valueElement = this.getValue(context)[0]
|
||||||
|
const styleElement = valueElement
|
||||||
|
? pickObject(valueElement, EDITOR_ELEMENT_STYLE_ATTR)
|
||||||
|
: pickObject(elementList[range.startIndex], CONTROL_STYLE_ATTR)
|
||||||
|
// 清空选项
|
||||||
|
const prefixIndex = this.clearSelect(context, {
|
||||||
|
isAddPlaceholder: false
|
||||||
|
})
|
||||||
|
if (!~prefixIndex) return
|
||||||
|
// 属性赋值元素-默认为前缀属性
|
||||||
|
const propertyElement = omitObject(
|
||||||
|
elementList[prefixIndex],
|
||||||
|
EDITOR_ELEMENT_STYLE_ATTR
|
||||||
|
)
|
||||||
|
const start = prefixIndex + 1
|
||||||
|
const draw = this.control.getDraw()
|
||||||
|
for (let i = 0; i < date.length; i++) {
|
||||||
|
const newElement: IElement = {
|
||||||
|
...styleElement,
|
||||||
|
...propertyElement,
|
||||||
|
type: ElementType.TEXT,
|
||||||
|
value: date[i],
|
||||||
|
controlComponent: ControlComponent.VALUE
|
||||||
|
}
|
||||||
|
formatElementContext(elementList, [newElement], prefixIndex)
|
||||||
|
draw.spliceElementList(elementList, start + i, 0, newElement)
|
||||||
|
}
|
||||||
|
// 重新渲染控件
|
||||||
|
if (!context.range) {
|
||||||
|
const newIndex = start + date.length - 1
|
||||||
|
this.control.repaintControl({
|
||||||
|
curIndex: newIndex
|
||||||
|
})
|
||||||
|
this.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public keydown(evt: KeyboardEvent): number | null {
|
||||||
|
if (this.control.getIsDisabledControl()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
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]
|
||||||
|
const draw = this.control.getDraw()
|
||||||
|
// backspace
|
||||||
|
if (evt.key === KeyMap.Backspace) {
|
||||||
|
// 移除选区元素
|
||||||
|
if (startIndex !== endIndex) {
|
||||||
|
draw.spliceElementList(
|
||||||
|
elementList,
|
||||||
|
startIndex + 1,
|
||||||
|
endIndex - startIndex
|
||||||
|
)
|
||||||
|
const value = this.getValue()
|
||||||
|
if (!value.length) {
|
||||||
|
this.control.addPlaceholder(startIndex)
|
||||||
|
}
|
||||||
|
return startIndex
|
||||||
|
} else {
|
||||||
|
if (
|
||||||
|
startElement.controlComponent === ControlComponent.PREFIX ||
|
||||||
|
endElement.controlComponent === ControlComponent.POSTFIX ||
|
||||||
|
startElement.controlComponent === ControlComponent.PLACEHOLDER
|
||||||
|
) {
|
||||||
|
// 前缀、后缀、占位符
|
||||||
|
return this.control.removeControl(startIndex)
|
||||||
|
} else {
|
||||||
|
// 文本
|
||||||
|
draw.spliceElementList(elementList, startIndex, 1)
|
||||||
|
const value = this.getValue()
|
||||||
|
if (!value.length) {
|
||||||
|
this.control.addPlaceholder(startIndex - 1)
|
||||||
|
}
|
||||||
|
return startIndex - 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (evt.key === KeyMap.Delete) {
|
||||||
|
// 移除选区元素
|
||||||
|
if (startIndex !== endIndex) {
|
||||||
|
draw.spliceElementList(
|
||||||
|
elementList,
|
||||||
|
startIndex + 1,
|
||||||
|
endIndex - startIndex
|
||||||
|
)
|
||||||
|
const value = this.getValue()
|
||||||
|
if (!value.length) {
|
||||||
|
this.control.addPlaceholder(startIndex)
|
||||||
|
}
|
||||||
|
return startIndex
|
||||||
|
} else {
|
||||||
|
const endNextElement = elementList[endIndex + 1]
|
||||||
|
if (
|
||||||
|
(startElement.controlComponent === ControlComponent.PREFIX &&
|
||||||
|
endNextElement.controlComponent === ControlComponent.PLACEHOLDER) ||
|
||||||
|
endNextElement.controlComponent === ControlComponent.POSTFIX ||
|
||||||
|
startElement.controlComponent === ControlComponent.PLACEHOLDER
|
||||||
|
) {
|
||||||
|
// 前缀、后缀、占位符
|
||||||
|
return this.control.removeControl(startIndex)
|
||||||
|
} else {
|
||||||
|
// 文本
|
||||||
|
draw.spliceElementList(elementList, startIndex + 1, 1)
|
||||||
|
const value = this.getValue()
|
||||||
|
if (!value.length) {
|
||||||
|
this.control.addPlaceholder(startIndex)
|
||||||
|
}
|
||||||
|
return startIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return endIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
public cut(): number {
|
||||||
|
if (this.control.getIsDisabledControl()) {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
this.control.shrinkBoundary()
|
||||||
|
const { startIndex, endIndex } = this.control.getRange()
|
||||||
|
if (startIndex === endIndex) {
|
||||||
|
return startIndex
|
||||||
|
}
|
||||||
|
const draw = this.control.getDraw()
|
||||||
|
const elementList = this.control.getElementList()
|
||||||
|
draw.spliceElementList(elementList, startIndex + 1, endIndex - startIndex)
|
||||||
|
const value = this.getValue()
|
||||||
|
if (!value.length) {
|
||||||
|
this.control.addPlaceholder(startIndex)
|
||||||
|
}
|
||||||
|
return startIndex
|
||||||
|
}
|
||||||
|
|
||||||
|
public awake() {
|
||||||
|
if (this.isPopup || this.control.getIsDisabledControl()) return
|
||||||
|
const position = this.control.getPosition()
|
||||||
|
if (!position) return
|
||||||
|
const elementList = this.draw.getElementList()
|
||||||
|
const { startIndex } = this.control.getRange()
|
||||||
|
if (elementList[startIndex + 1]?.controlId !== this.element.controlId) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 渲染日期控件
|
||||||
|
this.datePicker = new DatePicker(this.draw, {
|
||||||
|
onSubmit: this._setDate.bind(this)
|
||||||
|
})
|
||||||
|
const range = this.getValueRange()
|
||||||
|
const value = range
|
||||||
|
? elementList
|
||||||
|
.slice(range[0] + 1, range[1] + 1)
|
||||||
|
.map(el => el.value)
|
||||||
|
.join('')
|
||||||
|
: ''
|
||||||
|
const dateFormat = this.element.control?.dateFormat
|
||||||
|
this.datePicker.render({
|
||||||
|
value,
|
||||||
|
position,
|
||||||
|
dateFormat
|
||||||
|
})
|
||||||
|
// 弹窗状态
|
||||||
|
this.isPopup = true
|
||||||
|
}
|
||||||
|
|
||||||
|
public destroy() {
|
||||||
|
if (!this.isPopup) return
|
||||||
|
this.datePicker?.destroy()
|
||||||
|
this.isPopup = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private _setDate(date: string) {
|
||||||
|
if (!date) {
|
||||||
|
this.clearSelect()
|
||||||
|
} else {
|
||||||
|
this.setSelect(date)
|
||||||
|
}
|
||||||
|
this.destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in new issue