parent
f19077b2ce
commit
3e183aefa5
@ -0,0 +1,198 @@
|
||||
import { EditorZone } from '../../../dataset/enum/Editor'
|
||||
import { ElementType } from '../../../dataset/enum/Element'
|
||||
import { DeepRequired } from '../../../interface/Common'
|
||||
import { IEditorOption } from '../../../interface/Editor'
|
||||
import { IElement, IElementFillRect } from '../../../interface/Element'
|
||||
import { IPositionContext } from '../../../interface/Position'
|
||||
import { IRange } from '../../../interface/Range'
|
||||
import { getUUID } from '../../../utils'
|
||||
import { RangeManager } from '../../range/RangeManager'
|
||||
import { Draw } from '../Draw'
|
||||
|
||||
export class Group {
|
||||
private draw: Draw
|
||||
private options: DeepRequired<IEditorOption>
|
||||
private range: RangeManager
|
||||
private fillRectMap: Map<string, IElementFillRect>
|
||||
|
||||
constructor(draw: Draw) {
|
||||
this.draw = draw
|
||||
this.options = draw.getOptions()
|
||||
this.range = draw.getRange()
|
||||
this.fillRectMap = new Map()
|
||||
}
|
||||
|
||||
public setGroup(): string | null {
|
||||
if (
|
||||
this.draw.isReadonly() ||
|
||||
this.draw.getZone().getZone() !== EditorZone.MAIN
|
||||
) {
|
||||
return null
|
||||
}
|
||||
const selection = this.range.getSelection()
|
||||
if (!selection) return null
|
||||
const groupId = getUUID()
|
||||
selection.forEach(el => {
|
||||
if (!Array.isArray(el.groupIds)) {
|
||||
el.groupIds = []
|
||||
}
|
||||
el.groupIds.push(groupId)
|
||||
})
|
||||
this.draw.render({
|
||||
isSetCursor: false,
|
||||
isCompute: false
|
||||
})
|
||||
return groupId
|
||||
}
|
||||
|
||||
public getElementListByGroupId(
|
||||
elementList: IElement[],
|
||||
groupId: string
|
||||
): IElement[] {
|
||||
const groupElementList: IElement[] = []
|
||||
for (let e = 0; e < elementList.length; e++) {
|
||||
const element = elementList[e]
|
||||
if (element.type === ElementType.TABLE) {
|
||||
const trList = element.trList!
|
||||
for (let r = 0; r < trList.length; r++) {
|
||||
const tr = trList[r]
|
||||
for (let d = 0; d < tr.tdList.length; d++) {
|
||||
const td = tr.tdList[d]
|
||||
const tdGroupElementList = this.getElementListByGroupId(
|
||||
td.value,
|
||||
groupId
|
||||
)
|
||||
if (tdGroupElementList.length) {
|
||||
groupElementList.push(...tdGroupElementList)
|
||||
return groupElementList
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (element?.groupIds?.includes(groupId)) {
|
||||
groupElementList.push(element)
|
||||
const nextElement = elementList[e + 1]
|
||||
if (!nextElement?.groupIds?.includes(groupId)) break
|
||||
}
|
||||
}
|
||||
return groupElementList
|
||||
}
|
||||
|
||||
public deleteGroup(groupId: string) {
|
||||
if (this.draw.isReadonly()) return
|
||||
// 仅主体内容可以成组
|
||||
const elementList = this.draw.getOriginalMainElementList()
|
||||
const groupElementList = this.getElementListByGroupId(elementList, groupId)
|
||||
if (!groupElementList.length) return
|
||||
for (let e = 0; e < groupElementList.length; e++) {
|
||||
const element = groupElementList[e]
|
||||
const groupIds = element.groupIds!
|
||||
const groupIndex = groupIds.findIndex(id => id === groupId)
|
||||
groupIds.splice(groupIndex, 1)
|
||||
// 不包含成组时删除字段,减少存储及内存占用
|
||||
if (!groupIds.length) {
|
||||
delete element.groupIds
|
||||
}
|
||||
}
|
||||
this.draw.render({
|
||||
isSetCursor: false,
|
||||
isCompute: false
|
||||
})
|
||||
}
|
||||
|
||||
public getContextByGroupId(
|
||||
elementList: IElement[],
|
||||
groupId: string
|
||||
): (IRange & IPositionContext) | null {
|
||||
for (let e = 0; e < elementList.length; e++) {
|
||||
const element = elementList[e]
|
||||
if (element.type === ElementType.TABLE) {
|
||||
const trList = element.trList!
|
||||
for (let r = 0; r < trList.length; r++) {
|
||||
const tr = trList[r]
|
||||
for (let d = 0; d < tr.tdList.length; d++) {
|
||||
const td = tr.tdList[d]
|
||||
const range = this.getContextByGroupId(td.value, groupId)
|
||||
if (range) {
|
||||
return {
|
||||
...range,
|
||||
isTable: true,
|
||||
index: e,
|
||||
trIndex: r,
|
||||
tdIndex: d,
|
||||
tdId: td.id,
|
||||
trId: tr.id,
|
||||
tableId: element.tableId
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const nextElement = elementList[e + 1]
|
||||
if (
|
||||
element.groupIds?.includes(groupId) &&
|
||||
!nextElement?.groupIds?.includes(groupId)
|
||||
) {
|
||||
return {
|
||||
isTable: false,
|
||||
startIndex: e,
|
||||
endIndex: e
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
public clearFillInfo() {
|
||||
this.fillRectMap.clear()
|
||||
}
|
||||
|
||||
public recordFillInfo(
|
||||
element: IElement,
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number
|
||||
) {
|
||||
const groupIds = element.groupIds
|
||||
if (!groupIds) return
|
||||
for (const groupId of groupIds) {
|
||||
const fillRect = this.fillRectMap.get(groupId)
|
||||
if (!fillRect) {
|
||||
this.fillRectMap.set(groupId, {
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height
|
||||
})
|
||||
} else {
|
||||
fillRect.width += width
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public render(ctx: CanvasRenderingContext2D) {
|
||||
if (!this.fillRectMap.size) return
|
||||
// 当前激活组信息
|
||||
const range = this.range.getRange()
|
||||
const elementList = this.draw.getElementList()
|
||||
const anchorGroupIds = elementList[range.endIndex]?.groupIds
|
||||
const {
|
||||
group: { backgroundColor, opacity, activeOpacity, activeBackgroundColor }
|
||||
} = this.options
|
||||
ctx.save()
|
||||
this.fillRectMap.forEach((fillRect, groupId) => {
|
||||
const { x, y, width, height } = fillRect
|
||||
if (anchorGroupIds?.includes(groupId)) {
|
||||
ctx.globalAlpha = activeOpacity
|
||||
ctx.fillStyle = activeBackgroundColor
|
||||
} else {
|
||||
ctx.globalAlpha = opacity
|
||||
ctx.fillStyle = backgroundColor
|
||||
}
|
||||
ctx.fillRect(x, y, width, height)
|
||||
})
|
||||
ctx.restore()
|
||||
this.clearFillInfo()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
import { IElement } from '../../../interface/Element'
|
||||
|
||||
enum ElementType {
|
||||
TABLE = 'table'
|
||||
}
|
||||
|
||||
function getGroupIds(elementList: IElement[]): string[] {
|
||||
const groupIds: string[] = []
|
||||
for (const element of elementList) {
|
||||
if (element.type === ElementType.TABLE) {
|
||||
const trList = element.trList!
|
||||
for (let r = 0; r < trList.length; r++) {
|
||||
const tr = trList[r]
|
||||
for (let d = 0; d < tr.tdList.length; d++) {
|
||||
const td = tr.tdList[d]
|
||||
groupIds.push(...getGroupIds(td.value))
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!element.groupIds) continue
|
||||
for (const groupId of element.groupIds) {
|
||||
if (!groupIds.includes(groupId)) {
|
||||
groupIds.push(groupId)
|
||||
}
|
||||
}
|
||||
}
|
||||
return groupIds
|
||||
}
|
||||
|
||||
onmessage = evt => {
|
||||
const elementList = <IElement[]>evt.data
|
||||
const groupIds = getGroupIds(elementList)
|
||||
postMessage(groupIds)
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
import { IGroup } from '../../interface/Group'
|
||||
|
||||
export const defaultGroupOption: Readonly<Required<IGroup>> = {
|
||||
opacity: 0.1,
|
||||
backgroundColor: '#E99D00',
|
||||
activeOpacity: 0.5,
|
||||
activeBackgroundColor: '#E99D00',
|
||||
disabled: false
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
export interface IGroup {
|
||||
opacity?: number
|
||||
backgroundColor?: string
|
||||
activeOpacity?: number
|
||||
activeBackgroundColor?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
Loading…
Reference in new issue