feat: add element group
This commit is contained in:
@@ -76,6 +76,9 @@ export class Command {
|
||||
public executeLocationCatalog: CommandAdapt['locationCatalog']
|
||||
public executeWordTool: CommandAdapt['wordTool']
|
||||
public executeSetHTML: CommandAdapt['setHTML']
|
||||
public executeSetGroup: CommandAdapt['setGroup']
|
||||
public executeDeleteGroup: CommandAdapt['deleteGroup']
|
||||
public executeLocationGroup: CommandAdapt['locationGroup']
|
||||
public getCatalog: CommandAdapt['getCatalog']
|
||||
public getImage: CommandAdapt['getImage']
|
||||
public getValue: CommandAdapt['getValue']
|
||||
@@ -89,6 +92,7 @@ export class Command {
|
||||
public getPaperMargin: CommandAdapt['getPaperMargin']
|
||||
public getSearchNavigateInfo: CommandAdapt['getSearchNavigateInfo']
|
||||
public getLocale: CommandAdapt['getLocale']
|
||||
public getGroupIds: CommandAdapt['getGroupIds']
|
||||
|
||||
constructor(adapt: CommandAdapt) {
|
||||
// 全局命令
|
||||
@@ -173,7 +177,9 @@ export class Command {
|
||||
this.executeLocationCatalog = adapt.locationCatalog.bind(adapt)
|
||||
this.executeWordTool = adapt.wordTool.bind(adapt)
|
||||
this.executeSetHTML = adapt.setHTML.bind(adapt)
|
||||
|
||||
this.executeSetGroup = adapt.setGroup.bind(adapt)
|
||||
this.executeDeleteGroup = adapt.deleteGroup.bind(adapt)
|
||||
this.executeLocationGroup = adapt.locationGroup.bind(adapt)
|
||||
// 获取
|
||||
this.getImage = adapt.getImage.bind(adapt)
|
||||
this.getValue = adapt.getValue.bind(adapt)
|
||||
@@ -188,5 +194,6 @@ export class Command {
|
||||
this.getPaperMargin = adapt.getPaperMargin.bind(adapt)
|
||||
this.getSearchNavigateInfo = adapt.getSearchNavigateInfo.bind(adapt)
|
||||
this.getLocale = adapt.getLocale.bind(adapt)
|
||||
this.getGroupIds = adapt.getGroupIds.bind(adapt)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,7 +321,10 @@ export class CommandAdapt {
|
||||
selection.forEach(el => {
|
||||
el.underline = !!~noUnderlineIndex
|
||||
})
|
||||
this.draw.render({ isSetCursor: false })
|
||||
this.draw.render({
|
||||
isSetCursor: false,
|
||||
isCompute: false
|
||||
})
|
||||
}
|
||||
|
||||
public strikeout() {
|
||||
@@ -333,7 +336,10 @@ export class CommandAdapt {
|
||||
selection.forEach(el => {
|
||||
el.strikeout = !!~noStrikeoutIndex
|
||||
})
|
||||
this.draw.render({ isSetCursor: false })
|
||||
this.draw.render({
|
||||
isSetCursor: false,
|
||||
isCompute: false
|
||||
})
|
||||
}
|
||||
|
||||
public superscript() {
|
||||
@@ -1922,4 +1928,45 @@ export class CommandAdapt {
|
||||
footer: getElementList(footer)
|
||||
})
|
||||
}
|
||||
|
||||
public setGroup(): string | null {
|
||||
const isReadonly = this.draw.isReadonly()
|
||||
if (isReadonly) return null
|
||||
return this.draw.getGroup().setGroup()
|
||||
}
|
||||
|
||||
public deleteGroup(groupId: string) {
|
||||
const isReadonly = this.draw.isReadonly()
|
||||
if (isReadonly) return
|
||||
this.draw.getGroup().deleteGroup(groupId)
|
||||
}
|
||||
|
||||
public getGroupIds(): Promise<string[]> {
|
||||
return this.draw.getWorkerManager().getGroupIds()
|
||||
}
|
||||
|
||||
public locationGroup(groupId: string) {
|
||||
const elementList = this.draw.getOriginalMainElementList()
|
||||
const context = this.draw
|
||||
.getGroup()
|
||||
.getContextByGroupId(elementList, groupId)
|
||||
if (!context) return
|
||||
const { isTable, index, trIndex, tdIndex, tdId, trId, tableId, endIndex } =
|
||||
context
|
||||
this.position.setPositionContext({
|
||||
isTable,
|
||||
index,
|
||||
trIndex,
|
||||
tdIndex,
|
||||
tdId,
|
||||
trId,
|
||||
tableId
|
||||
})
|
||||
this.range.setRange(endIndex, endIndex)
|
||||
this.draw.render({
|
||||
curIndex: endIndex,
|
||||
isCompute: false,
|
||||
isSubmitHistory: false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ import { Placeholder } from './frame/Placeholder'
|
||||
import { WORD_LIKE_REG } from '../../dataset/constant/Regular'
|
||||
import { EventBus } from '../event/eventbus/EventBus'
|
||||
import { EventBusMap } from '../../interface/EventBus'
|
||||
import { Group } from './interactive/Group'
|
||||
|
||||
export class Draw {
|
||||
private container: HTMLDivElement
|
||||
@@ -106,6 +107,7 @@ export class Draw {
|
||||
private margin: Margin
|
||||
private background: Background
|
||||
private search: Search
|
||||
private group: Group
|
||||
private underline: Underline
|
||||
private strikeout: Strikeout
|
||||
private highlight: Highlight
|
||||
@@ -175,6 +177,7 @@ export class Draw {
|
||||
this.margin = new Margin(this)
|
||||
this.background = new Background(this)
|
||||
this.search = new Search(this)
|
||||
this.group = new Group(this)
|
||||
this.underline = new Underline(this)
|
||||
this.strikeout = new Strikeout(this)
|
||||
this.highlight = new Highlight(this)
|
||||
@@ -451,6 +454,10 @@ export class Draw {
|
||||
return this.search
|
||||
}
|
||||
|
||||
public getGroup(): Group {
|
||||
return this.group
|
||||
}
|
||||
|
||||
public getHistoryManager(): HistoryManager {
|
||||
return this.historyManager
|
||||
}
|
||||
@@ -1443,8 +1450,13 @@ export class Draw {
|
||||
const { rowList, pageNo, elementList, positionList, startIndex, zone } =
|
||||
payload
|
||||
const isPrintMode = this.mode === EditorMode.PRINT
|
||||
const { scale, tdPadding, defaultBasicRowMarginHeight, defaultRowMargin } =
|
||||
this.options
|
||||
const {
|
||||
scale,
|
||||
tdPadding,
|
||||
defaultBasicRowMarginHeight,
|
||||
defaultRowMargin,
|
||||
group
|
||||
} = this.options
|
||||
const { isCrossRowCol, tableId } = this.range.getRange()
|
||||
let index = startIndex
|
||||
for (let i = 0; i < rowList.length; i++) {
|
||||
@@ -1610,6 +1622,10 @@ export class Draw {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 组信息记录
|
||||
if (!group.disabled && element.groupIds) {
|
||||
this.group.recordFillInfo(element, x, y, metrics.width, curRow.height)
|
||||
}
|
||||
index++
|
||||
// 绘制表格内元素
|
||||
if (element.type === ElementType.TABLE) {
|
||||
@@ -1641,6 +1657,8 @@ export class Draw {
|
||||
}
|
||||
// 绘制富文本及文字
|
||||
this._drawRichText(ctx)
|
||||
// 绘制批注样式
|
||||
this.group.render(ctx)
|
||||
// 绘制选区
|
||||
if (!isPrintMode) {
|
||||
if (rangeRecord.width && rangeRecord.height) {
|
||||
@@ -1767,7 +1785,8 @@ export class Draw {
|
||||
isSetCursor = true,
|
||||
isCompute = true,
|
||||
isLazy = true,
|
||||
isInit = false
|
||||
isInit = false,
|
||||
isSourceHistory = false
|
||||
} = payload || {}
|
||||
let { curIndex } = payload || {}
|
||||
const innerWidth = this.getInnerWidth()
|
||||
@@ -1861,7 +1880,11 @@ export class Draw {
|
||||
self.footer.setElementList(deepClone(oldFooterElementList))
|
||||
self.elementList = deepClone(oldElementList)
|
||||
self.range.setRange(startIndex, endIndex)
|
||||
self.render({ curIndex, isSubmitHistory: false })
|
||||
self.render({
|
||||
curIndex,
|
||||
isSubmitHistory: false,
|
||||
isSourceHistory: true
|
||||
})
|
||||
})
|
||||
}
|
||||
// 信息变动回调
|
||||
@@ -1882,7 +1905,7 @@ export class Draw {
|
||||
this.eventBus.emit('pageSizeChange', this.pageRowList.length)
|
||||
}
|
||||
// 文档内容改变
|
||||
if (isSubmitHistory && !isInit) {
|
||||
if ((isSubmitHistory || isSourceHistory) && !isInit) {
|
||||
if (this.listener.contentChange) {
|
||||
this.listener.contentChange()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,8 @@ export function input(data: string, host: CanvasEvent) {
|
||||
(copyElement.type === SUPERSCRIPT && nextElement?.type === SUPERSCRIPT)
|
||||
) {
|
||||
EDITOR_ELEMENT_COPY_ATTR.forEach(attr => {
|
||||
// 在分组外无需复制分组信息
|
||||
if (attr === 'groupIds' && !nextElement?.groupIds) return
|
||||
const value = copyElement[attr] as never
|
||||
if (value !== undefined) {
|
||||
newElement[attr] = value
|
||||
|
||||
@@ -339,6 +339,8 @@ export class RangeManager {
|
||||
const painter = !!this.draw.getPainterStyle()
|
||||
const undo = this.historyManager.isCanUndo()
|
||||
const redo = this.historyManager.isCanRedo()
|
||||
// 组信息
|
||||
const groupIds = curElement.groupIds || null
|
||||
const rangeStyle: IRangeStyle = {
|
||||
type,
|
||||
undo,
|
||||
@@ -357,7 +359,8 @@ export class RangeManager {
|
||||
dashArray,
|
||||
level,
|
||||
listType,
|
||||
listStyle
|
||||
listStyle,
|
||||
groupIds
|
||||
}
|
||||
if (rangeStyleChangeListener) {
|
||||
rangeStyleChangeListener(rangeStyle)
|
||||
@@ -396,7 +399,8 @@ export class RangeManager {
|
||||
dashArray: [],
|
||||
level: null,
|
||||
listType: null,
|
||||
listStyle: null
|
||||
listStyle: null,
|
||||
groupIds: null
|
||||
}
|
||||
if (rangeStyleChangeListener) {
|
||||
rangeStyleChangeListener(rangeStyle)
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import { Draw } from '../draw/Draw'
|
||||
import WordCountWorker from './works/wordCount?worker&inline'
|
||||
import CatalogWorker from './works/catalog?worker&inline'
|
||||
import GroupWorker from './works/group?worker&inline'
|
||||
import { ICatalog } from '../../interface/Catalog'
|
||||
|
||||
export class WorkerManager {
|
||||
private draw: Draw
|
||||
private wordCountWorker: Worker
|
||||
private catalogWorker: Worker
|
||||
private groupWorker: Worker
|
||||
|
||||
constructor(draw: Draw) {
|
||||
this.draw = draw
|
||||
this.wordCountWorker = new WordCountWorker()
|
||||
this.catalogWorker = new CatalogWorker()
|
||||
this.groupWorker = new GroupWorker()
|
||||
}
|
||||
|
||||
public getWordCount(): Promise<number> {
|
||||
@@ -43,4 +46,19 @@ export class WorkerManager {
|
||||
this.catalogWorker.postMessage(elementList)
|
||||
})
|
||||
}
|
||||
|
||||
public getGroupIds(): Promise<string[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.groupWorker.onmessage = evt => {
|
||||
resolve(evt.data)
|
||||
}
|
||||
|
||||
this.groupWorker.onerror = evt => {
|
||||
reject(evt)
|
||||
}
|
||||
|
||||
const elementList = this.draw.getOriginalMainElementList()
|
||||
this.groupWorker.postMessage(elementList)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -26,7 +26,8 @@ export const EDITOR_ELEMENT_COPY_ATTR: Array<keyof IElement> = [
|
||||
'url',
|
||||
'hyperlinkId',
|
||||
'dateId',
|
||||
'dateFormat'
|
||||
'dateFormat',
|
||||
'groupIds'
|
||||
]
|
||||
|
||||
export const EDITOR_ELEMENT_ZIP_ATTR: Array<keyof IElement> = [
|
||||
@@ -56,7 +57,8 @@ export const EDITOR_ELEMENT_ZIP_ATTR: Array<keyof IElement> = [
|
||||
'level',
|
||||
'listType',
|
||||
'listStyle',
|
||||
'listWrap'
|
||||
'listWrap',
|
||||
'groupIds'
|
||||
]
|
||||
|
||||
export const TABLE_CONTEXT_ATTR: Array<keyof IElement> = [
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -5,7 +5,8 @@ export enum EditorComponent {
|
||||
FOOTER = 'footer',
|
||||
CONTEXTMENU = 'contextmenu',
|
||||
POPUP = 'popup',
|
||||
CATALOG = 'catalog'
|
||||
CATALOG = 'catalog',
|
||||
COMMENT = 'comment'
|
||||
}
|
||||
|
||||
export enum EditorContext {
|
||||
|
||||
+8
-1
@@ -59,6 +59,8 @@ import { Plugin } from './core/plugin/Plugin'
|
||||
import { UsePlugin } from './interface/Plugin'
|
||||
import { EventBus } from './core/event/eventbus/EventBus'
|
||||
import { EventBusMap } from './interface/EventBus'
|
||||
import { IGroup } from './interface/Group'
|
||||
import { defaultGroupOption } from './dataset/constant/Group'
|
||||
|
||||
export default class Editor {
|
||||
public command: Command
|
||||
@@ -109,6 +111,10 @@ export default class Editor {
|
||||
...defaultPlaceholderOption,
|
||||
...options.placeholder
|
||||
}
|
||||
const groupOptions: Required<IGroup> = {
|
||||
...defaultGroupOption,
|
||||
...options.group
|
||||
}
|
||||
|
||||
const editorOptions: DeepRequired<IEditorOption> = {
|
||||
mode: EditorMode.EDIT,
|
||||
@@ -158,7 +164,8 @@ export default class Editor {
|
||||
checkbox: checkboxOptions,
|
||||
cursor: cursorOptions,
|
||||
title: titleOptions,
|
||||
placeholder: placeholderOptions
|
||||
placeholder: placeholderOptions,
|
||||
group: groupOptions
|
||||
}
|
||||
// 数据处理
|
||||
let headerElementList: IElement[] = []
|
||||
|
||||
@@ -9,6 +9,7 @@ export interface IDrawOption {
|
||||
isCompute?: boolean
|
||||
isLazy?: boolean
|
||||
isInit?: boolean
|
||||
isSourceHistory?: boolean
|
||||
}
|
||||
|
||||
export interface IDrawImagePayload {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ICheckboxOption } from './Checkbox'
|
||||
import { IControlOption } from './Control'
|
||||
import { ICursorOption } from './Cursor'
|
||||
import { IFooter } from './Footer'
|
||||
import { IGroup } from './Group'
|
||||
import { IHeader } from './Header'
|
||||
import { IMargin } from './Margin'
|
||||
import { IPageNumber } from './PageNumber'
|
||||
@@ -70,6 +71,7 @@ export interface IEditorOption {
|
||||
cursor?: ICursorOption
|
||||
title?: ITitleOption
|
||||
placeholder?: IPlaceholder
|
||||
group?: IGroup
|
||||
}
|
||||
|
||||
export interface IEditorResult {
|
||||
|
||||
@@ -32,6 +32,10 @@ export interface IElementStyle {
|
||||
letterSpacing?: number
|
||||
}
|
||||
|
||||
export interface IElementGroup {
|
||||
groupIds?: string[]
|
||||
}
|
||||
|
||||
export interface ITitleElement {
|
||||
valueList?: IElement[]
|
||||
level?: TitleLevel
|
||||
@@ -103,6 +107,7 @@ export interface IBlockElement {
|
||||
|
||||
export type IElement = IElementBasic &
|
||||
IElementStyle &
|
||||
IElementGroup &
|
||||
ITable &
|
||||
IHyperlinkElement &
|
||||
ISuperscriptSubscript &
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export interface IGroup {
|
||||
opacity?: number
|
||||
backgroundColor?: string
|
||||
activeOpacity?: number
|
||||
activeBackgroundColor?: string
|
||||
disabled?: boolean
|
||||
}
|
||||
@@ -29,6 +29,7 @@ export interface IRangeStyle {
|
||||
level: TitleLevel | null
|
||||
listType: ListType | null
|
||||
listStyle: ListStyle | null
|
||||
groupIds: string[] | null
|
||||
}
|
||||
|
||||
export type IRangeStyleChange = (payload: IRangeStyle) => void
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { cloneProperty, deepClone, getUUID, splitText } from '.'
|
||||
import { cloneProperty, deepClone, getUUID, isArrayEqual, splitText } from '.'
|
||||
import {
|
||||
ElementType,
|
||||
IEditorOption,
|
||||
@@ -375,7 +375,17 @@ export function isSameElementExceptValue(
|
||||
if (sourceKeys.length !== targetKeys.length) return false
|
||||
for (let s = 0; s < sourceKeys.length; s++) {
|
||||
const key = sourceKeys[s] as never
|
||||
// 值不需要校验
|
||||
if (key === 'value') continue
|
||||
// groupIds数组需特殊校验数组是否相等
|
||||
if (
|
||||
key === 'groupIds' &&
|
||||
Array.isArray(source[key]) &&
|
||||
Array.isArray(target[key]) &&
|
||||
isArrayEqual(source[key], target[key])
|
||||
) {
|
||||
continue
|
||||
}
|
||||
if (source[key] !== target[key]) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -249,3 +249,10 @@ export function findScrollContainer(element: HTMLElement) {
|
||||
}
|
||||
return document.documentElement
|
||||
}
|
||||
|
||||
export function isArrayEqual(arr1: unknown[], arr2: unknown[]): boolean {
|
||||
if (arr1.length !== arr2.length) {
|
||||
return false
|
||||
}
|
||||
return !arr1.some(item => !arr2.includes(item))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user