@leafer-in/editor 1.0.0-rc.3 → 1.0.0-rc.30

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.
Files changed (41) hide show
  1. package/dist/editor.cjs +1937 -0
  2. package/dist/editor.esm.js +1822 -422
  3. package/dist/editor.esm.min.js +1 -1
  4. package/dist/editor.js +1848 -428
  5. package/dist/editor.min.cjs +1 -0
  6. package/dist/editor.min.js +1 -1
  7. package/package.json +12 -9
  8. package/src/Editor.ts +396 -145
  9. package/src/config.ts +38 -0
  10. package/src/decorator/data.ts +16 -0
  11. package/src/display/EditBox.ts +342 -0
  12. package/src/display/EditMask.ts +37 -0
  13. package/src/display/EditPoint.ts +9 -0
  14. package/src/display/EditSelect.ts +255 -0
  15. package/src/display/SelectArea.ts +30 -0
  16. package/src/display/Stroker.ts +92 -0
  17. package/src/editor/cursor.ts +45 -0
  18. package/src/editor/simulate.ts +14 -0
  19. package/src/editor/target.ts +39 -0
  20. package/src/event/EditorEvent.ts +33 -0
  21. package/src/event/EditorGroupEvent.ts +23 -0
  22. package/src/event/EditorMoveEvent.ts +17 -0
  23. package/src/event/EditorRotateEvent.ts +4 -10
  24. package/src/event/EditorScaleEvent.ts +28 -0
  25. package/src/event/EditorSkewEvent.ts +18 -0
  26. package/src/event/InnerEditorEvent.ts +23 -0
  27. package/src/helper/EditDataHelper.ts +183 -0
  28. package/src/helper/EditSelectHelper.ts +34 -0
  29. package/src/helper/EditorHelper.ts +73 -0
  30. package/src/index.ts +50 -3
  31. package/src/svg.ts +54 -0
  32. package/src/tool/EditTool.ts +99 -0
  33. package/src/tool/EditToolCreator.ts +32 -0
  34. package/src/tool/InnerEditor.ts +68 -0
  35. package/src/tool/LineEditTool.ts +135 -0
  36. package/types/index.d.ts +293 -45
  37. package/src/cursor.ts +0 -57
  38. package/src/event/EditorResizeEvent.ts +0 -34
  39. package/src/resize.ts +0 -87
  40. package/src/tool/LineTool.ts +0 -88
  41. package/src/tool/RectTool.ts +0 -139
@@ -0,0 +1,92 @@
1
+ import { IUI, ILeaferCanvas, IRenderOptions, IRectInputData } from '@leafer-ui/interface'
2
+ import { Paint, UI, MatrixHelper } from '@leafer-ui/draw'
3
+
4
+ import { IStroker } from '@leafer-in/interface'
5
+
6
+ import { targetAttr } from '../decorator/data'
7
+
8
+
9
+ const matrix = MatrixHelper.get()
10
+ const { abs } = Math
11
+ const { copy, scale } = MatrixHelper
12
+
13
+ export class Stroker extends UI implements IStroker {
14
+
15
+ @targetAttr(onTarget)
16
+ public target: IUI | IUI[]
17
+
18
+ public list: IUI[] = []
19
+
20
+ constructor() {
21
+ super()
22
+ this.hittable = false
23
+ this.strokeAlign = 'center'
24
+ }
25
+
26
+ public setTarget(target: IUI | IUI[], style: IRectInputData): void {
27
+ this.set(style)
28
+ this.target = target
29
+ }
30
+
31
+ public __draw(canvas: ILeaferCanvas, options: IRenderOptions): void {
32
+ const { list } = this
33
+ if (list.length) {
34
+
35
+ let leaf: IUI
36
+ const { stroke, strokeWidth, fill } = this.__
37
+ const { bounds } = options
38
+
39
+ for (let i = 0; i < list.length; i++) {
40
+ leaf = list[i]
41
+ if (bounds && bounds.hit(leaf.__world, options.matrix)) {
42
+
43
+ const aScaleX = abs(leaf.__world.scaleX), aScaleY = abs(leaf.__world.scaleY)
44
+
45
+ if (aScaleX !== aScaleY) { // need no scale stroke, use rect path
46
+
47
+ copy(matrix, leaf.__world)
48
+ scale(matrix, 1 / aScaleX, 1 / aScaleY)
49
+
50
+ canvas.setWorld(matrix, options.matrix)
51
+ canvas.beginPath()
52
+ this.__.strokeWidth = strokeWidth
53
+
54
+ const { x, y, width, height } = leaf.__layout.boxBounds
55
+ canvas.rect(x * aScaleX, y * aScaleY, width * aScaleX, height * aScaleY)
56
+
57
+ } else {
58
+
59
+ canvas.setWorld(leaf.__world, options.matrix)
60
+ canvas.beginPath()
61
+
62
+ if (leaf.__.__useArrow) {
63
+ leaf.__drawPath(canvas)
64
+ } else {
65
+ leaf.__.__pathForRender ? leaf.__drawRenderPath(canvas) : leaf.__drawPathByBox(canvas)
66
+ }
67
+
68
+ this.__.strokeWidth = strokeWidth / abs(leaf.__world.scaleX)
69
+
70
+ }
71
+
72
+ if (stroke) typeof stroke === 'string' ? Paint.stroke(stroke, this, canvas) : Paint.strokes(stroke, this, canvas)
73
+ if (fill) typeof fill === 'string' ? Paint.fill(fill, this, canvas) : Paint.fills(fill, this, canvas)
74
+ }
75
+ }
76
+
77
+ this.__.strokeWidth = strokeWidth
78
+ }
79
+ }
80
+
81
+ public destroy(): void {
82
+ this.target = null
83
+ super.destroy()
84
+ }
85
+
86
+ }
87
+
88
+ function onTarget(stroker: Stroker): void {
89
+ const value = stroker.target
90
+ stroker.list = value ? (value instanceof Array ? value : [value]) : []
91
+ stroker.forceUpdate()
92
+ }
@@ -0,0 +1,45 @@
1
+ import { IObject, IUIEvent } from '@leafer-ui/interface'
2
+
3
+ import { IEditor } from '@leafer-in/interface'
4
+ import { MathHelper } from '@leafer-ui/draw'
5
+
6
+ import { EditDataHelper } from '../helper/EditDataHelper'
7
+
8
+
9
+ const cacheCursors: IObject = {}
10
+
11
+ export function updateCursor(editor: IEditor, e: IUIEvent): void {
12
+ const { editBox } = editor, point = editBox.enterPoint
13
+ if (!point || !editor.editing || !editBox.visible) return
14
+ if (point.name === 'circle') return // 独立旋转按钮
15
+
16
+ let { rotation } = editBox
17
+ const { resizeCursor, rotateCursor, skewCursor, resizeable, rotateable, skewable } = editor.mergeConfig
18
+ const { pointType } = point, { flippedX, flippedY } = editBox
19
+
20
+ let showResize = pointType === 'resize'
21
+ if (showResize && rotateable && (e.metaKey || e.ctrlKey || !resizeable)) showResize = false
22
+ const showSkew = skewable && !showResize && point.name === 'resize-line'
23
+
24
+ const cursor = showSkew ? skewCursor : (showResize ? resizeCursor : rotateCursor)
25
+ rotation += (EditDataHelper.getFlipDirection(point.direction, flippedX, flippedY) + 1) * 45
26
+ rotation = Math.round(MathHelper.formatRotation(rotation, true) / 2) * 2
27
+
28
+ const { url, x, y } = cursor
29
+ const key = url + rotation
30
+
31
+ if (cacheCursors[key]) {
32
+ point.cursor = cacheCursors[key]
33
+ } else {
34
+ cacheCursors[key] = point.cursor = { url: toDataURL(url, rotation), x, y }
35
+ }
36
+ }
37
+
38
+ export function updateMoveCursor(editor: IEditor): void {
39
+ editor.editBox.rect.cursor = editor.mergeConfig.moveCursor
40
+ }
41
+
42
+
43
+ function toDataURL(svg: string, rotation: number): string {
44
+ return '"data:image/svg+xml,' + encodeURIComponent(svg.replace('{{rotation}}', rotation.toString())) + '"'
45
+ }
@@ -0,0 +1,14 @@
1
+ import { IGroup, ILeaf } from '@leafer-ui/interface'
2
+ import { Bounds } from '@leafer-ui/draw'
3
+
4
+ import { IEditor } from '@leafer-in/interface'
5
+
6
+
7
+ export function simulate(editor: IEditor) {
8
+ const { simulateTarget, leafList: targetList } = editor
9
+ const { x, y, width, height } = new Bounds().setListWithFn(targetList.list, (leaf: ILeaf) => leaf.worldBoxBounds)
10
+
11
+ const parent = simulateTarget.parent = targetList.list[0].leafer.zoomLayer as IGroup // follow zoomLayer zoom / move
12
+ const { scaleX, scaleY, e: worldX, f: worldY } = parent.__world
13
+ simulateTarget.reset({ x: (x - worldX) / scaleX, y: (y - worldY) / scaleY, width: width / scaleX, height: height / scaleY })
14
+ }
@@ -0,0 +1,39 @@
1
+ import { LeafList } from '@leafer-ui/draw'
2
+
3
+ import { IEditor, IUI } from '@leafer-in/interface'
4
+
5
+ import { simulate } from './simulate'
6
+ import { updateMoveCursor } from './cursor'
7
+ import { EditorEvent } from '../event/EditorEvent'
8
+
9
+
10
+ export function onTarget(editor: IEditor, oldValue: IUI | IUI[]): void {
11
+ const { target } = editor
12
+ if (target) {
13
+ editor.leafList = target instanceof LeafList ? target : new LeafList(target instanceof Array ? target : target as IUI)
14
+ } else {
15
+ editor.leafList.reset()
16
+ }
17
+
18
+ editor.emitEvent(new EditorEvent(EditorEvent.SELECT, { editor, value: target, oldValue }))
19
+ editor.checkOpenedGroups()
20
+
21
+ if (editor.editing) {
22
+ editor.waitLeafer(() => {
23
+ if (editor.multiple) simulate(editor)
24
+ updateMoveCursor(editor)
25
+ editor.updateEditTool()
26
+ editor.update()
27
+ editor.listenTargetEvents()
28
+ })
29
+ } else {
30
+ editor.updateEditTool()
31
+ editor.removeTargetEvents()
32
+ }
33
+ }
34
+
35
+
36
+ export function onHover(editor: IEditor, oldValue: IUI): void {
37
+ editor.emitEvent(new EditorEvent(EditorEvent.HOVER, { editor, value: editor.hoverTarget, oldValue }))
38
+
39
+ }
@@ -0,0 +1,33 @@
1
+ import { IUI, IPointData } from '@leafer-ui/interface'
2
+ import { Event } from '@leafer-ui/draw'
3
+
4
+ import { IEditor, IEditorEvent } from '@leafer-in/interface'
5
+
6
+
7
+ function toList(value: IUI | IUI[]): IUI[] {
8
+ return value ? (value instanceof Array ? value : [value]) : []
9
+ }
10
+
11
+ export class EditorEvent extends Event implements IEditorEvent {
12
+
13
+ static SELECT = 'editor.select'
14
+ static HOVER = 'editor.hover'
15
+
16
+ declare readonly target: IUI
17
+ readonly editor: IEditor
18
+
19
+ readonly value: IUI | IUI[]
20
+ readonly oldValue: IUI | IUI[]
21
+
22
+ get list() { return toList(this.value) }
23
+ get oldList() { return toList(this.oldValue) }
24
+
25
+ readonly worldOrigin: IPointData
26
+ declare readonly origin: IPointData
27
+
28
+ constructor(type: string, data?: IEditorEvent) {
29
+ super(type)
30
+ if (data) Object.assign(this, data)
31
+ }
32
+
33
+ }
@@ -0,0 +1,23 @@
1
+ import { IEditorGroupEvent } from '@leafer-in/interface'
2
+
3
+ import { EditorEvent } from './EditorEvent'
4
+ import { IGroup } from '@leafer-ui/interface'
5
+ import { } from '../tool/InnerEditor'
6
+
7
+
8
+ export class EditorGroupEvent extends EditorEvent implements IEditorGroupEvent {
9
+
10
+ static GROUP = 'editor.group'
11
+ static BEFORE_UNGROUP = 'editor.before_ungroup'
12
+ static UNGROUP = 'editor.ungroup'
13
+
14
+ static OPEN = 'editor.open_group'
15
+ static CLOSE = 'editor.close_group'
16
+
17
+ readonly editTarget: IGroup
18
+
19
+ constructor(type: string, data?: IEditorGroupEvent) {
20
+ super(type, data)
21
+ }
22
+
23
+ }
@@ -0,0 +1,17 @@
1
+ import { IEditorMoveEvent } from '@leafer-in/interface'
2
+
3
+ import { EditorEvent } from './EditorEvent'
4
+
5
+
6
+ export class EditorMoveEvent extends EditorEvent implements IEditorMoveEvent {
7
+
8
+ static MOVE = 'editor.move'
9
+
10
+ readonly moveX: number
11
+ readonly moveY: number
12
+
13
+ constructor(type: string, data?: IEditorMoveEvent) {
14
+ super(type, data)
15
+ }
16
+
17
+ }
@@ -1,23 +1,17 @@
1
- import { IUI, IPointData } from '@leafer-ui/interface'
2
- import { IEditor, IEditorRotateEvent } from '@leafer-in/interface'
1
+ import { IEditorRotateEvent } from '@leafer-in/interface'
3
2
 
4
- import { Event } from '@leafer-ui/core'
3
+ import { EditorEvent } from './EditorEvent'
5
4
 
6
5
 
7
- export class EditorRotateEvent extends Event implements IEditorRotateEvent {
6
+ export class EditorRotateEvent extends EditorEvent implements IEditorRotateEvent {
8
7
 
9
8
  static ROTATE = 'editor.rotate'
10
9
 
11
- declare readonly target: IUI
12
- readonly editor: IEditor
13
-
14
10
  // rotateOf(origin, rotation)
15
- readonly origin: IPointData
16
11
  readonly rotation: number
17
12
 
18
13
  constructor(type: string, data?: IEditorRotateEvent) {
19
- super(type)
20
- if (data) Object.assign(this, data)
14
+ super(type, data)
21
15
  }
22
16
 
23
17
  }
@@ -0,0 +1,28 @@
1
+ import { IAround, IDragEvent, IMatrixData } from '@leafer-ui/interface'
2
+
3
+ import { IEditorScaleEvent } from '@leafer-in/interface'
4
+ import { Direction9 } from '@leafer-ui/draw'
5
+
6
+ import { EditorEvent } from './EditorEvent'
7
+
8
+
9
+ export class EditorScaleEvent extends EditorEvent implements IEditorScaleEvent {
10
+
11
+ static SCALE = 'editor.scale'
12
+
13
+ // scaleOf(origin, scaleX, scaleY, resize)
14
+ readonly scaleX: number
15
+ readonly scaleY: number
16
+ readonly transform?: IMatrixData
17
+
18
+ readonly drag: IDragEvent
19
+
20
+ readonly direction: Direction9
21
+ readonly lockRatio: boolean
22
+ readonly around: IAround
23
+
24
+ constructor(type: string, data?: IEditorScaleEvent) {
25
+ super(type, data)
26
+ }
27
+
28
+ }
@@ -0,0 +1,18 @@
1
+ import { IEditorSkewEvent } from '@leafer-in/interface'
2
+
3
+ import { EditorEvent } from './EditorEvent'
4
+
5
+
6
+ export class EditorSkewEvent extends EditorEvent implements IEditorSkewEvent {
7
+
8
+ static SKEW = 'editor.skew'
9
+
10
+ // skewOf(origin, skewX, skewY)
11
+ readonly skewX: number
12
+ readonly skewY: number
13
+
14
+ constructor(type: string, data?: IEditorSkewEvent) {
15
+ super(type, data)
16
+ }
17
+
18
+ }
@@ -0,0 +1,23 @@
1
+ import { IInnerEditorEvent, IInnerEditor } from '@leafer-in/interface'
2
+
3
+ import { EditorEvent } from './EditorEvent'
4
+ import { IUI } from '@leafer-ui/interface'
5
+ import { } from '../tool/InnerEditor'
6
+
7
+
8
+ export class InnerEditorEvent extends EditorEvent implements IInnerEditorEvent {
9
+
10
+ static BEFORE_OPEN = 'innerEditor.before_open'
11
+ static OPEN = 'innerEditor.open'
12
+
13
+ static BEFORE_CLOSE = 'innerEditor.before_close'
14
+ static CLOSE = 'innerEditor.close'
15
+
16
+ readonly editTarget: IUI
17
+ readonly innerEditor: IInnerEditor
18
+
19
+ constructor(type: string, data?: IInnerEditorEvent) {
20
+ super(type, data)
21
+ }
22
+
23
+ }
@@ -0,0 +1,183 @@
1
+ import { IBoundsData, IPointData, IAround, IAlign } from '@leafer-ui/interface'
2
+ import { AroundHelper, PointHelper, Direction9 } from '@leafer-ui/draw'
3
+
4
+ import { IEditorScaleEvent, IEditorSkewEvent, IEditorRotateEvent } from '@leafer-in/interface'
5
+
6
+
7
+ const { topLeft, top, topRight, right, bottomRight, bottom, bottomLeft, left } = Direction9
8
+ const { toPoint } = AroundHelper
9
+
10
+ export const EditDataHelper = {
11
+
12
+ getScaleData(bounds: IBoundsData, direction: Direction9, pointMove: IPointData, lockRatio: boolean | 'corner', around: IAround): IEditorScaleEvent {
13
+ let align: IAlign, origin = {} as IPointData, scaleX: number = 1, scaleY: number = 1
14
+ const { width, height } = bounds
15
+
16
+ if (around) {
17
+ pointMove.x *= 2
18
+ pointMove.y *= 2
19
+ }
20
+
21
+ // 防止变为0
22
+ if (Math.abs(pointMove.x) === width) pointMove.x += 0.1
23
+ if (Math.abs(pointMove.y) === height) pointMove.y += 0.1
24
+
25
+ const topScale = (-pointMove.y + height) / height
26
+ const rightScale = (pointMove.x + width) / width
27
+ const bottomScale = (pointMove.y + height) / height
28
+ const leftScale = (-pointMove.x + width) / width
29
+
30
+ switch (direction) {
31
+ case top:
32
+ scaleY = topScale
33
+ align = 'bottom'
34
+ break
35
+ case right:
36
+ scaleX = rightScale
37
+ align = 'left'
38
+ break
39
+ case bottom:
40
+ scaleY = bottomScale
41
+ align = 'top'
42
+ break
43
+ case left:
44
+ scaleX = leftScale
45
+ align = 'right'
46
+ break
47
+ case topLeft:
48
+ scaleY = topScale
49
+ scaleX = leftScale
50
+ align = 'bottom-right'
51
+ break
52
+ case topRight:
53
+ scaleY = topScale
54
+ scaleX = rightScale
55
+ align = 'bottom-left'
56
+ break
57
+ case bottomRight:
58
+ scaleY = bottomScale
59
+ scaleX = rightScale
60
+ align = 'top-left'
61
+ break
62
+ case bottomLeft:
63
+ scaleY = bottomScale
64
+ scaleX = leftScale
65
+ align = 'top-right'
66
+ }
67
+
68
+ if (lockRatio) {
69
+ const unlockSide = lockRatio === 'corner' && direction % 2
70
+ if (!unlockSide) {
71
+ const scale = Math.sqrt(Math.abs(scaleX * scaleY))
72
+ scaleX = scaleX < 0 ? -scale : scale
73
+ scaleY = scaleY < 0 ? -scale : scale
74
+ }
75
+ }
76
+
77
+ toPoint(around || align, bounds, origin)
78
+
79
+ return { origin, scaleX, scaleY, direction, lockRatio, around }
80
+ },
81
+
82
+ getRotateData(bounds: IBoundsData, direction: Direction9, current: IPointData, last: IPointData, around: IAround): IEditorRotateEvent {
83
+ let align: IAlign, origin = {} as IPointData
84
+
85
+ switch (direction) {
86
+ case topLeft:
87
+ align = 'bottom-right'
88
+ break
89
+ case topRight:
90
+ align = 'bottom-left'
91
+ break
92
+ case bottomRight:
93
+ align = 'top-left'
94
+ break
95
+ case bottomLeft:
96
+ align = 'top-right'
97
+ break
98
+ default:
99
+ align = 'center'
100
+ }
101
+
102
+ toPoint(around || align, bounds, origin)
103
+
104
+ return { origin, rotation: PointHelper.getRotation(last, origin, current) }
105
+ },
106
+
107
+ getSkewData(bounds: IBoundsData, direction: Direction9, move: IPointData, around: IAround): IEditorSkewEvent {
108
+ let align: IAlign, origin = {} as IPointData, skewX = 0, skewY = 0
109
+ let last: IPointData
110
+
111
+ switch (direction) {
112
+ case top:
113
+ last = { x: 0.5, y: 0 }
114
+ align = 'bottom'
115
+ skewX = 1
116
+ break
117
+ case bottom:
118
+ last = { x: 0.5, y: 1 }
119
+ align = 'top'
120
+ skewX = 1
121
+ break
122
+ case left:
123
+ last = { x: 0, y: 0.5 }
124
+ align = 'right'
125
+ skewY = 1
126
+ break
127
+ case right:
128
+ last = { x: 1, y: 0.5 }
129
+ align = 'left'
130
+ skewY = 1
131
+ }
132
+
133
+ const { x, y, width, height } = bounds
134
+
135
+ last.x = x + last.x * width
136
+ last.y = y + last.y * height
137
+
138
+ toPoint(around || align, bounds, origin)
139
+
140
+ const rotation = PointHelper.getRotation(last, origin, { x: last.x + (skewX ? move.x : 0), y: last.y + (skewY ? move.y : 0) })
141
+ skewX ? skewX = -rotation : skewY = rotation
142
+
143
+ return { origin, skewX, skewY }
144
+ },
145
+
146
+
147
+ getAround(around: IAround, altKey: boolean): IAround {
148
+ return (altKey && !around) ? 'center' : around
149
+ },
150
+
151
+ getRotateDirection(direction: number, rotation: number, totalDirection = 8): number {
152
+ direction = (direction + Math.round(rotation / (360 / totalDirection))) % totalDirection
153
+ if (direction < 0) direction += totalDirection
154
+ return direction
155
+ },
156
+
157
+ getFlipDirection(direction: Direction9, flipedX: boolean, flipedY: boolean): Direction9 {
158
+ if (flipedX) {
159
+ switch (direction) {
160
+ case left: direction = right; break
161
+ case topLeft: direction = topRight; break
162
+ case bottomLeft: direction = bottomRight; break
163
+ case right: direction = left; break
164
+ case topRight: direction = topLeft; break
165
+ case bottomRight: direction = bottomLeft; break
166
+ }
167
+ }
168
+
169
+ if (flipedY) {
170
+ switch (direction) {
171
+ case top: direction = bottom; break
172
+ case topLeft: direction = bottomLeft; break
173
+ case topRight: direction = bottomRight; break
174
+ case bottom: direction = top; break
175
+ case bottomLeft: direction = topLeft; break
176
+ case bottomRight: direction = topRight; break
177
+ }
178
+ }
179
+
180
+ return direction
181
+ }
182
+
183
+ }
@@ -0,0 +1,34 @@
1
+ import { IBounds, ILeafList, IUI } from '@leafer-ui/interface'
2
+ import { Answer } from '@leafer-ui/draw'
3
+
4
+ const { No, Yes, NoAndSkip, YesAndSkip } = Answer
5
+
6
+ export const EditSelectHelper = {
7
+
8
+ findOne(path: ILeafList): IUI {
9
+ return path.list.find((leaf) => leaf.editable) as IUI
10
+ },
11
+
12
+ findBounds(leaf: IUI, bounds: IBounds): Answer {
13
+ if (leaf.__.hittable && leaf.__.visible && !leaf.__.locked && bounds.hit(leaf.__world)) {
14
+
15
+ if (leaf.__.editable) {
16
+ if (leaf.isBranch && !leaf.__.hitChildren) {
17
+ return leaf.__.hitSelf ? YesAndSkip : NoAndSkip
18
+ } else if (leaf.isFrame) {
19
+ return bounds.includes(leaf.__layout.boxBounds, leaf.__world) ? YesAndSkip : No
20
+ } else {
21
+ if (bounds.hit(leaf.__layout.boxBounds, leaf.__world) && leaf.__.hitSelf) return Yes
22
+ }
23
+ }
24
+
25
+ return No
26
+
27
+ } else {
28
+
29
+ return leaf.isBranch ? NoAndSkip : No
30
+
31
+ }
32
+ }
33
+
34
+ }
@@ -0,0 +1,73 @@
1
+ import { IGroup, IGroupInputData, ILeaf, IUI } from '@leafer-ui/interface'
2
+ import { Group, Matrix } from '@leafer-ui/draw'
3
+
4
+
5
+ const order = (a: ILeaf, b: ILeaf) => a.parent.children.indexOf(a) - b.parent.children.indexOf(b)
6
+ const reverseOrder = (a: ILeaf, b: ILeaf) => b.parent.children.indexOf(b) - a.parent.children.indexOf(a)
7
+
8
+ export const EditorHelper = {
9
+
10
+ group(list: IUI[], element?: IUI, userGroup?: IGroup | IGroupInputData): IGroup {
11
+ list.sort(reverseOrder)
12
+ const { app, parent } = list[0]
13
+
14
+ let group: IGroup
15
+ if (userGroup && (userGroup as IGroup).add) {
16
+ group = userGroup as IGroup
17
+ } else {
18
+ group = new Group(userGroup)
19
+ }
20
+
21
+ parent.addAt(group, parent.children.indexOf(list[0]))
22
+ list.sort(order)
23
+
24
+ const matrx = new Matrix(element.worldTransform)
25
+ matrx.divideParent(parent.worldTransform)
26
+ group.setTransform(matrx)
27
+ group.editable = true
28
+ group.hitChildren = false
29
+
30
+ app.lockLayout()
31
+ list.forEach(child => child.dropTo(group))
32
+ app.unlockLayout()
33
+
34
+ return group
35
+ },
36
+
37
+ ungroup(list: IUI[]): IUI[] {
38
+ const { app } = list[0]
39
+ const ungroupList: IUI[] = []
40
+
41
+ app.lockLayout()
42
+ list.forEach(leaf => {
43
+ if (leaf.isBranch) {
44
+ const { parent, children } = leaf
45
+ while (children.length) {
46
+ ungroupList.push(children[0])
47
+ children[0].dropTo(parent, parent.children.indexOf(leaf))
48
+ }
49
+ leaf.remove()
50
+ } else {
51
+ ungroupList.push(leaf)
52
+ }
53
+ })
54
+ app.unlockLayout()
55
+
56
+ return ungroupList
57
+ },
58
+
59
+ toTop(list: IUI[]): void {
60
+ list.sort(order)
61
+ list.forEach(leaf => {
62
+ if (leaf.parent) leaf.parent.add(leaf)
63
+ })
64
+ },
65
+
66
+ toBottom(list: IUI[]): void {
67
+ list.sort(reverseOrder)
68
+ list.forEach(leaf => {
69
+ if (leaf.parent) leaf.parent.addAt(leaf, 0)
70
+ })
71
+ }
72
+
73
+ }