@leafer-in/editor 1.0.0-rc.9 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/Editor.ts CHANGED
@@ -1,7 +1,8 @@
1
- import { IGroupInputData, IUI, IEventListenerId, IPointData, ILeafList, IEditSize, IGroup } from '@leafer-ui/interface'
2
- import { Group, Rect, DragEvent, RotateEvent, DataHelper, MathHelper, LeafList, Matrix, RenderEvent, KeyEvent } from '@leafer-ui/core'
1
+ import { IGroupInputData, IUI, IEventListenerId, IPointData, ILeafList, IEditSize, IGroup, IObject, IAlign } from '@leafer-ui/interface'
2
+ import { Group, Rect, DataHelper, MathHelper, LeafList, Matrix, RenderEvent, LeafHelper } from '@leafer-ui/draw'
3
+ import { DragEvent, RotateEvent, KeyEvent, ZoomEvent } from '@leafer-ui/core'
3
4
 
4
- import { IEditBox, IEditPoint, IEditor, IEditorConfig, IEditTool, IEditorScaleEvent } from '@leafer-in/interface'
5
+ import { IEditBox, IEditPoint, IEditor, IEditorConfig, IEditTool, IEditorScaleEvent, IInnerEditor } from '@leafer-in/interface'
5
6
 
6
7
  import { EditorMoveEvent } from './event/EditorMoveEvent'
7
8
  import { EditorScaleEvent } from './event/EditorScaleEvent'
@@ -10,44 +11,70 @@ import { EditorSkewEvent } from './event/EditorSkewEvent'
10
11
 
11
12
  import { EditSelect } from './display/EditSelect'
12
13
  import { EditBox } from './display/EditBox'
14
+ import { EditMask } from './display/EditMask'
13
15
 
14
16
  import { config } from './config'
15
- import { getEditTool } from './tool'
16
17
 
17
18
  import { onTarget, onHover } from './editor/target'
18
19
  import { targetAttr } from './decorator/data'
19
20
  import { EditorHelper } from './helper/EditorHelper'
20
21
  import { EditDataHelper } from './helper/EditDataHelper'
22
+ import { simulate } from './editor/simulate'
21
23
  import { updateCursor } from './editor/cursor'
24
+ import { EditToolCreator } from './tool/EditToolCreator'
25
+ import { InnerEditorEvent } from './event/InnerEditorEvent'
26
+ import { EditorGroupEvent } from './event/EditorGroupEvent'
22
27
 
23
28
 
24
29
  export class Editor extends Group implements IEditor {
25
30
 
26
31
  public config = config
27
32
 
33
+ public get mergeConfig(): IEditorConfig {
34
+ const { element, config } = this
35
+ return this.single && element.editConfig ? { ...config, ...element.editConfig } : config // 实时合并,后期可优化
36
+ }
37
+
28
38
  @targetAttr(onHover)
29
- public hoverTarget: IUI
39
+ public hoverTarget?: IUI
30
40
 
31
41
  @targetAttr(onTarget)
32
- public target: IUI | IUI[]
42
+ public target?: IUI | IUI[]
43
+
44
+ // 列表
33
45
 
34
- public leafList: ILeafList = new LeafList() // from target
35
46
  public get list(): IUI[] { return this.leafList.list as IUI[] }
47
+ public leafList: ILeafList = new LeafList() // from target
48
+ public openedGroupList: ILeafList = new LeafList()
49
+
50
+ // 状态
51
+
52
+ public get editing(): boolean { return !!this.list.length }
53
+ public innerEditing: boolean
54
+ public get groupOpening(): boolean { return !!this.openedGroupList.length }
36
55
 
37
- public get hasTarget(): boolean { return !!this.list.length }
38
56
  public get multiple(): boolean { return this.list.length > 1 }
39
57
  public get single(): boolean { return this.list.length === 1 }
40
58
 
59
+ public get dragging(): boolean { return this.editBox.dragging }
60
+
61
+ // 组件
62
+
41
63
  public get element() { return this.multiple ? this.simulateTarget : this.list[0] as IUI }
42
64
  public simulateTarget: IUI = new Rect({ visible: false })
43
65
 
44
66
  public editBox: IEditBox = new EditBox(this)
45
67
  public get buttons() { return this.editBox.buttons }
46
68
 
47
- public editTool: IEditTool
69
+ public editTool?: IEditTool
70
+
71
+ public innerEditor?: IInnerEditor
72
+ public editToolList: IObject = {}
73
+
48
74
  public selector: EditSelect = new EditSelect(this)
75
+ public editMask: EditMask = new EditMask(this)
49
76
 
50
- public get dragging(): boolean { return this.editBox.dragging }
77
+ public dragStartPoint: IPointData
51
78
 
52
79
  public targetEventIds: IEventListenerId[] = []
53
80
 
@@ -55,7 +82,17 @@ export class Editor extends Group implements IEditor {
55
82
  constructor(userConfig?: IEditorConfig, data?: IGroupInputData) {
56
83
  super(data)
57
84
  if (userConfig) this.config = DataHelper.default(userConfig, this.config)
58
- this.addMany(this.selector, this.editBox)
85
+ this.addMany(this.editMask, this.selector, this.editBox)
86
+ }
87
+
88
+ // select
89
+
90
+ public select(target: IUI | IUI[]): void {
91
+ this.target = target
92
+ }
93
+
94
+ public cancel(): void {
95
+ this.target = null
59
96
  }
60
97
 
61
98
  // item
@@ -79,64 +116,94 @@ export class Editor extends Group implements IEditor {
79
116
  // update
80
117
 
81
118
  public update(): void {
82
- if (this.target) {
83
- this.editTool.update(this)
119
+ if (this.editing) {
120
+ if (this.innerEditing) this.innerEditor.update()
121
+ this.editTool.update()
84
122
  this.selector.update()
85
123
  }
86
124
  }
87
125
 
126
+ public updateEditBox(): void {
127
+ if (this.multiple) simulate(this)
128
+ this.update()
129
+ }
130
+
88
131
  public updateEditTool(): void {
89
- this.editTool = getEditTool(this.list)
132
+ const tool = this.editTool
133
+ if (tool) {
134
+ this.editBox.unload()
135
+ tool.unload()
136
+ this.editTool = null
137
+ }
138
+
139
+ if (this.editing) {
140
+ const tag = this.single ? this.list[0].editOuter as string : 'EditTool'
141
+ this.editTool = this.editToolList[tag] = this.editToolList[tag] || EditToolCreator.get(tag, this)
142
+ this.editBox.load()
143
+ this.editTool.load()
144
+ }
90
145
  }
91
146
 
147
+
92
148
  // get
93
149
 
94
- public getEditSize(ui: IUI): IEditSize {
95
- let { editSize } = this.config
96
- return editSize === 'auto' ? ui.editSize : editSize
150
+ public getEditSize(_ui: IUI): IEditSize {
151
+ return this.mergeConfig.editSize
97
152
  }
98
153
 
99
154
  // operate
100
155
 
101
156
  public onMove(e: DragEvent): void {
102
- const move = e.getLocalMove(this.element)
157
+ const total = { x: e.totalX, y: e.totalY }
103
158
 
104
159
  if (e.shiftKey) {
105
- if (Math.abs(move.x) > Math.abs(move.y)) move.y = 0
106
- else move.x = 0
160
+ if (Math.abs(total.x) > Math.abs(total.y)) total.y = 0
161
+ else total.x = 0
107
162
  }
108
163
 
109
- this.move(move.x, move.y)
164
+ this.move(DragEvent.getValidMove(this.element, this.dragStartPoint, total))
110
165
  }
111
166
 
112
- public onScale(e: DragEvent): void {
167
+ public onScale(e: DragEvent | ZoomEvent): void {
113
168
  const { element } = this
114
- const { direction } = e.current as IEditPoint
115
169
 
116
- let { around, lockRatio } = this.config
117
- if (e.shiftKey) lockRatio = true
170
+ if (e instanceof ZoomEvent) {
171
+ if (this.mergeConfig.resizeable === 'zoom') {
172
+ e.stop()
173
+ this.scaleOf(element.getInnerPoint(e), e.scale, e.scale)
174
+ }
175
+ } else {
118
176
 
119
- const data = EditDataHelper.getScaleData(element.boxBounds, direction, e.getInnerMove(element), lockRatio, EditDataHelper.getAround(around, e.altKey))
177
+ const { direction } = e.current as IEditPoint
178
+ let { around, lockRatio } = this.mergeConfig
179
+ if (e.shiftKey || element.lockRatio) lockRatio = true
180
+
181
+ const data = EditDataHelper.getScaleData(element.boxBounds, direction, e.getInnerMove(element), lockRatio, EditDataHelper.getAround(around, e.altKey))
182
+
183
+ if (this.editTool.onScaleWithDrag) {
184
+ data.drag = e
185
+ this.scaleWithDrag(data)
186
+ } else {
187
+ this.scaleOf(data.origin, data.scaleX, data.scaleY)
188
+ }
120
189
 
121
- if (this.editTool.onScaleWithDrag) {
122
- data.drag = e
123
- this.scaleWithDrag(data)
124
- } else {
125
- this.scaleOf(data.origin, data.scaleX, data.scaleY)
126
190
  }
127
191
 
128
192
  }
129
193
 
130
194
  public onRotate(e: DragEvent | RotateEvent): void {
131
- const { skewable, around, rotateGap } = this.config
195
+ const { skewable, around, rotateGap } = this.mergeConfig
132
196
  const { direction, name } = e.current as IEditPoint
133
197
  if (skewable && name === 'resize-line') return this.onSkew(e as DragEvent)
134
198
 
135
- const { element, editBox } = this
199
+ const { element } = this
136
200
  let origin: IPointData, rotation: number
137
201
 
138
202
  if (e instanceof RotateEvent) {
139
- rotation = e.rotation, origin = element.getInnerPoint(e)
203
+ if (this.mergeConfig.rotateable === 'rotate') {
204
+ e.stop()
205
+ rotation = e.rotation, origin = element.getInnerPoint(e)
206
+ } else return
140
207
  } else {
141
208
  const last = { x: e.x - e.moveX, y: e.y - e.moveY }
142
209
  const data = EditDataHelper.getRotateData(element.boxBounds, direction, e.getInner(element), element.getInnerPoint(last), e.shiftKey ? null : (around || 'center'))
@@ -147,15 +214,15 @@ export class Editor extends Group implements IEditor {
147
214
  rotation = MathHelper.getGapRotation(rotation, rotateGap, element.rotation)
148
215
  if (!rotation) return
149
216
 
150
- if (editBox.flippedOne) rotation = -rotation
217
+ if (element.scaleX * element.scaleY < 0) rotation = -rotation // flippedOne
151
218
 
152
- this.rotateOf(origin, rotation)
219
+ this.rotateOf(origin, MathHelper.float(rotation, 2))
153
220
  }
154
221
 
155
222
 
156
223
  public onSkew(e: DragEvent): void {
157
224
  const { element } = this
158
- const { around } = this.config
225
+ const { around } = this.mergeConfig
159
226
 
160
227
  const { origin, skewX, skewY } = EditDataHelper.getSkewData(element.boxBounds, (e.current as IEditPoint).direction, e.getInnerMove(element), EditDataHelper.getAround(around, e.altKey))
161
228
  if (!skewX && !skewY) return
@@ -166,9 +233,11 @@ export class Editor extends Group implements IEditor {
166
233
 
167
234
  // transform
168
235
 
169
- public move(x: number, y: number): void {
236
+ public move(x: number | IPointData, y = 0): void {
237
+ if (!this.mergeConfig.moveable || this.element.locked) return
238
+
170
239
  const { element } = this
171
- const world = element.getWorldPointByLocal({ x, y }, null, true)
240
+ const world = element.getWorldPointByLocal(typeof x === 'object' ? { ...x } : { x, y }, null, true)
172
241
  const event = new EditorMoveEvent(EditorMoveEvent.MOVE, { target: element, editor: this, moveX: world.x, moveY: world.y })
173
242
 
174
243
  this.editTool.onMove(event)
@@ -178,6 +247,8 @@ export class Editor extends Group implements IEditor {
178
247
  }
179
248
 
180
249
  public scaleWithDrag(data: IEditorScaleEvent): void {
250
+ if (!this.mergeConfig.resizeable || this.element.locked) return
251
+
181
252
  const { element } = this
182
253
  const worldOrigin = element.getWorldPoint(data.origin)
183
254
  const event = new EditorScaleEvent(EditorScaleEvent.SCALE, { ...data, target: element, editor: this, worldOrigin })
@@ -186,16 +257,19 @@ export class Editor extends Group implements IEditor {
186
257
  this.emitEvent(event)
187
258
  }
188
259
 
189
- public scaleOf(origin: IPointData, scaleX: number, scaleY = scaleX, _resize?: boolean): void {
260
+
261
+ public scaleOf(origin: IPointData | IAlign, scaleX: number, scaleY = scaleX, _resize?: boolean): void {
262
+ if (!this.mergeConfig.resizeable || this.element.locked) return
263
+
190
264
  const { element } = this
191
- const worldOrigin = element.getWorldPoint(origin)
265
+ const worldOrigin = element.getWorldPoint(LeafHelper.getInnerOrigin(element, origin))
192
266
 
193
267
  let transform: Matrix
194
268
 
195
269
  if (this.multiple) {
196
- const childMatrix = { ...element.localTransform }
270
+ const oldMatrix = new Matrix(element.worldTransform)
197
271
  element.scaleOf(origin, scaleX, scaleY)
198
- transform = new Matrix(element.localTransform).divide(childMatrix)
272
+ transform = new Matrix(element.worldTransform).divide(oldMatrix) // world change transform
199
273
  }
200
274
 
201
275
  const event = new EditorScaleEvent(EditorScaleEvent.SCALE, { target: element, editor: this, worldOrigin, scaleX, scaleY, transform })
@@ -204,28 +278,39 @@ export class Editor extends Group implements IEditor {
204
278
  this.emitEvent(event)
205
279
  }
206
280
 
207
- public rotateOf(origin: IPointData, rotation: number): void {
281
+ public rotateOf(origin: IPointData | IAlign, rotation: number): void {
282
+ if (!this.mergeConfig.rotateable || this.element.locked) return
283
+
208
284
  const { element } = this
209
- const worldOrigin = element.getWorldPoint(origin)
285
+ const worldOrigin = element.getWorldPoint(LeafHelper.getInnerOrigin(element, origin))
210
286
 
211
- const event = new EditorRotateEvent(EditorRotateEvent.ROTATE, { target: element, editor: this, worldOrigin, rotation })
287
+
288
+ let transform: Matrix
289
+
290
+ if (this.multiple) {
291
+ const oldMatrix = new Matrix(element.worldTransform)
292
+ element.rotateOf(origin, rotation)
293
+ transform = new Matrix(element.worldTransform).divide(oldMatrix) // world change transform
294
+ }
295
+
296
+ const event = new EditorRotateEvent(EditorRotateEvent.ROTATE, { target: element, editor: this, worldOrigin, rotation, transform })
212
297
 
213
298
  this.editTool.onRotate(event)
214
299
  this.emitEvent(event)
215
-
216
- if (this.multiple) element.rotateOf(origin, rotation)
217
300
  }
218
301
 
219
- public skewOf(origin: IPointData, skewX: number, skewY = 0, _resize?: boolean): void {
302
+ public skewOf(origin: IPointData | IAlign, skewX: number, skewY = 0, _resize?: boolean): void {
303
+ if (!this.mergeConfig.skewable || this.element.locked) return
304
+
220
305
  const { element } = this
221
- const worldOrigin = element.getWorldPoint(origin)
306
+ const worldOrigin = element.getWorldPoint(LeafHelper.getInnerOrigin(element, origin))
222
307
 
223
308
  let transform: Matrix
224
309
 
225
310
  if (this.multiple) {
226
- const childMatrix = { ...element.localTransform }
311
+ const oldMatrix = new Matrix(element.worldTransform)
227
312
  element.skewOf(origin, skewX, skewY)
228
- transform = new Matrix(element.localTransform).divide(childMatrix)
313
+ transform = new Matrix(element.worldTransform).divide(oldMatrix) // world change transform
229
314
  }
230
315
 
231
316
  const event = new EditorSkewEvent(EditorSkewEvent.SKEW, {
@@ -238,17 +323,104 @@ export class Editor extends Group implements IEditor {
238
323
 
239
324
  // group
240
325
 
241
- public group(group?: IGroup): IGroup {
242
- if (this.multiple) this.target = EditorHelper.group(this.list, this.element, group)
326
+ public group(userGroup?: IGroup | IGroupInputData): IGroup {
327
+ if (this.multiple) {
328
+ this.target = EditorHelper.group(this.list, this.element, userGroup)
329
+ this.emitGroupEvent(EditorGroupEvent.GROUP, this.target as IGroup)
330
+ }
243
331
  return this.target as IGroup
244
332
  }
245
333
 
246
-
247
334
  public ungroup(): IUI[] {
248
- if (this.list.length) this.target = EditorHelper.ungroup(this.list)
335
+ const { list } = this
336
+ if (list.length) {
337
+ list.forEach(item => item.isBranch && this.emitGroupEvent(EditorGroupEvent.BEFORE_UNGROUP, item as IGroup))
338
+ this.target = EditorHelper.ungroup(list)
339
+ list.forEach(item => item.isBranch && this.emitGroupEvent(EditorGroupEvent.UNGROUP, item as IGroup))
340
+ }
249
341
  return this.list
250
342
  }
251
343
 
344
+ public openGroup(group: IGroup): void {
345
+ this.openedGroupList.add(group)
346
+ group.hitChildren = true
347
+ this.emitGroupEvent(EditorGroupEvent.OPEN, group)
348
+ }
349
+
350
+ public closeGroup(group: IGroup): void {
351
+ this.openedGroupList.remove(group)
352
+ group.hitChildren = false
353
+ this.emitGroupEvent(EditorGroupEvent.CLOSE, group)
354
+ }
355
+
356
+ public checkOpenedGroups(): void {
357
+ const opened = this.openedGroupList
358
+ if (opened.length) {
359
+ let { list } = opened
360
+ if (this.editing) list = [], opened.forEach(item => this.list.every(leaf => !LeafHelper.hasParent(leaf, item)) && list.push(item))
361
+ list.forEach(item => this.closeGroup(item as IGroup))
362
+ }
363
+ if (this.editing && !this.selector.dragging) this.checkDeepSelect()
364
+ }
365
+
366
+ public checkDeepSelect(): void {
367
+ let parent: IGroup, { list } = this
368
+ for (let i = 0; i < list.length; i++) {
369
+ parent = list[i].parent
370
+ while (parent && !parent.hitChildren) {
371
+ this.openGroup(parent)
372
+ parent = parent.parent
373
+ }
374
+ }
375
+ }
376
+
377
+ public emitGroupEvent(type: string, group: IGroup): void {
378
+ const event = new EditorGroupEvent(type, { editTarget: group })
379
+ this.emitEvent(event)
380
+ group.emitEvent(event)
381
+ }
382
+
383
+ // inner
384
+
385
+ public openInnerEditor(target?: IUI): void {
386
+ if (target) this.target = target
387
+ if (this.single) {
388
+ const editTarget = this.element
389
+ const tag = editTarget.editInner
390
+ if (tag && EditToolCreator.list[tag]) {
391
+ this.editTool.unload()
392
+ this.innerEditing = true
393
+ this.innerEditor = this.editToolList[tag] || EditToolCreator.get(tag, this)
394
+ this.innerEditor.editTarget = editTarget
395
+
396
+ this.emitInnerEvent(InnerEditorEvent.BEFORE_OPEN)
397
+ this.innerEditor.load()
398
+ this.emitInnerEvent(InnerEditorEvent.OPEN)
399
+ }
400
+ }
401
+ }
402
+
403
+ public closeInnerEditor(): void {
404
+ if (this.innerEditing) {
405
+ this.innerEditing = false
406
+
407
+ this.emitInnerEvent(InnerEditorEvent.BEFORE_CLOSE)
408
+ this.innerEditor.unload()
409
+ this.emitInnerEvent(InnerEditorEvent.CLOSE)
410
+
411
+ this.editTool.load()
412
+ this.innerEditor = null
413
+ }
414
+ }
415
+
416
+ public emitInnerEvent(type: string): void {
417
+ const { innerEditor } = this
418
+ const { editTarget } = innerEditor
419
+ const event = new InnerEditorEvent(type, { editTarget, innerEditor })
420
+ this.emitEvent(event)
421
+ editTarget.emitEvent(event)
422
+ }
423
+
252
424
  // lock
253
425
 
254
426
  public lock(): void {
@@ -301,7 +473,9 @@ export class Editor extends Group implements IEditor {
301
473
  public destroy(): void {
302
474
  if (!this.destroyed) {
303
475
  this.simulateTarget.destroy()
304
- this.target = this.hoverTarget = this.simulateTarget = null
476
+ Object.values(this.editToolList).forEach(item => item.destroy())
477
+ this.editToolList = {}
478
+ this.target = this.hoverTarget = this.simulateTarget = this.editTool = this.innerEditor = null
305
479
  super.destroy()
306
480
  }
307
481
  }
package/src/config.ts CHANGED
@@ -1,13 +1,16 @@
1
1
  import { IEditorConfig } from '@leafer-in/interface'
2
+ import { resizeSVG, rotateSVG, skewSVG } from './svg'
3
+
2
4
 
3
5
  export const config: IEditorConfig = {
4
- editSize: 'auto',
6
+ editSize: 'size',
7
+ keyEvent: true,
5
8
 
6
9
  stroke: '#836DFF',
7
10
  strokeWidth: 2,
8
11
 
9
12
  pointFill: '#FFFFFF',
10
- pointSize: 8,
13
+ pointSize: 10,
11
14
  pointRadius: 16,
12
15
 
13
16
  rotateGap: 45,
@@ -15,14 +18,20 @@ export const config: IEditorConfig = {
15
18
  buttonsDirection: 'bottom',
16
19
  buttonsMargin: 12,
17
20
 
21
+ hideOnSmall: true,
22
+
18
23
  moveCursor: 'move',
19
- resizeCursor: ['nwse-resize', 'ns-resize', 'nesw-resize', 'ew-resize', 'nwse-resize', 'ns-resize', 'nesw-resize', 'ew-resize'],
20
- rotateCursor: ['ne-resize', 'e-resize', 'se-resize', 's-resize', 'sw-resize', 'w-resize', 'nw-resize', 'n-resize'],
24
+ resizeCursor: { url: resizeSVG, x: 12, y: 12 },
25
+ rotateCursor: { url: rotateSVG, x: 12, y: 12 },
26
+ skewCursor: { url: skewSVG, x: 12, y: 12 },
21
27
 
22
28
  selector: true,
23
29
  hover: true,
30
+ select: 'press',
31
+ openInner: 'double',
24
32
  boxSelect: true,
25
33
 
34
+ moveable: true,
26
35
  resizeable: true,
27
36
  rotateable: true,
28
37
  skewable: true
@@ -1,5 +1,5 @@
1
1
  import { IFunction, ILeaf, IObject } from '@leafer-ui/interface'
2
- import { defineKey } from '@leafer-ui/core'
2
+ import { defineKey } from '@leafer-ui/draw'
3
3
 
4
4
 
5
5
  export function targetAttr(fn: IFunction) {