@lazlon-platform/html-editor 0.1.0 → 0.2.1
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/package.json +14 -11
- package/.claude/settings.local.json +0 -9
- package/.github/workflows/ci.yml +0 -34
- package/demo/App.tsx +0 -62
- package/demo/EditorView/PageView/NodeContent.tsx +0 -35
- package/demo/EditorView/PageView/SnapLines.tsx +0 -28
- package/demo/EditorView/PageView/index.tsx +0 -45
- package/demo/EditorView/SelectionFrame/Corner.tsx +0 -24
- package/demo/EditorView/SelectionFrame/Edge.tsx +0 -21
- package/demo/EditorView/SelectionFrame/index.tsx +0 -27
- package/demo/EditorView/SelectionOverlay/ActionHud.tsx +0 -32
- package/demo/EditorView/SelectionOverlay/Rotation.tsx +0 -39
- package/demo/EditorView/SelectionOverlay/Toolbar.tsx +0 -128
- package/demo/EditorView/SelectionOverlay/index.tsx +0 -21
- package/demo/EditorView/Toolbar/index.tsx +0 -68
- package/demo/EditorView/index.tsx +0 -47
- package/demo/Navbar/index.tsx +0 -33
- package/demo/Sidebar/index.tsx +0 -71
- package/demo/hotkeys.ts +0 -93
- package/demo/main.tsx +0 -10
- package/demo/style.css +0 -1
- package/eslint.config.js +0 -43
- package/index.html +0 -14
- package/tests/createTestEditor.ts +0 -19
- package/tests/hooks/actions.test.tsx +0 -736
- package/tests/hooks/batch.test.tsx +0 -332
- package/tests/hooks/editor.test.tsx +0 -56
- package/tests/hooks/page.test.tsx +0 -135
- package/tests/hooks/pointer/pointer.test.tsx +0 -244
- package/tests/hooks/textMarks.test.tsx +0 -624
- package/tests/model/editor.test.ts +0 -384
- package/tests/model/history.test.ts +0 -293
- package/tests/model/node/group.test.ts +0 -294
- package/tests/model/node/image.test.ts +0 -150
- package/tests/model/node/polygon.test.ts +0 -408
- package/tests/model/node/text.test.ts +0 -158
- package/tests/model/node.test.ts +0 -276
- package/tests/model/page.test.ts +0 -150
- package/tests/setup.ts +0 -7
- package/tsconfig.json +0 -28
- package/vite.config.ts +0 -9
- package/vitest.config.ts +0 -13
|
@@ -1,736 +0,0 @@
|
|
|
1
|
-
import { act, renderHook } from "@testing-library/react"
|
|
2
|
-
import { beforeEach, describe, expect, it } from "vitest"
|
|
3
|
-
import {
|
|
4
|
-
clone,
|
|
5
|
-
useAddNodeAction,
|
|
6
|
-
useAlignAction,
|
|
7
|
-
useDuplicateAction,
|
|
8
|
-
useStackOrderAction,
|
|
9
|
-
useToggleLockAction,
|
|
10
|
-
useTrashAction,
|
|
11
|
-
} from "../../lib/hooks/actions"
|
|
12
|
-
import { EditorContext, PageContext } from "../../lib/hooks/editor"
|
|
13
|
-
import { ImageNode } from "../../lib/model/node/image"
|
|
14
|
-
import { Page } from "../../lib/model/page"
|
|
15
|
-
import { createTestEditor } from "../createTestEditor"
|
|
16
|
-
|
|
17
|
-
describe("action hooks", () => {
|
|
18
|
-
let editor: ReturnType<typeof createTestEditor>
|
|
19
|
-
let page: Page
|
|
20
|
-
|
|
21
|
-
beforeEach(() => {
|
|
22
|
-
editor = createTestEditor()
|
|
23
|
-
page = new Page(editor, { id: "page-1", width: 800, height: 600 })
|
|
24
|
-
editor.pages = new Map([["page-1", page]])
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
function wrapper({ children }: { children: React.ReactNode }) {
|
|
28
|
-
return (
|
|
29
|
-
<EditorContext value={editor}>
|
|
30
|
-
<PageContext value={page}>{children}</PageContext>
|
|
31
|
-
</EditorContext>
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
describe("clone", () => {
|
|
36
|
-
it("creates a deep copy of a node with new id", () => {
|
|
37
|
-
const original = new ImageNode(editor, page, {
|
|
38
|
-
id: "orig",
|
|
39
|
-
x: 10,
|
|
40
|
-
y: 20,
|
|
41
|
-
width: 100,
|
|
42
|
-
height: 50,
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
const cloned = clone(editor, original)
|
|
46
|
-
|
|
47
|
-
expect(cloned.id).not.toBe("orig")
|
|
48
|
-
expect(cloned.x).toBe(10)
|
|
49
|
-
expect(cloned.y).toBe(20)
|
|
50
|
-
expect(cloned.width).toBe(100)
|
|
51
|
-
expect(cloned.height).toBe(50)
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
it("applies prop transformations with object", () => {
|
|
55
|
-
const original = new ImageNode(editor, page, {
|
|
56
|
-
id: "orig",
|
|
57
|
-
x: 10,
|
|
58
|
-
y: 20,
|
|
59
|
-
width: 100,
|
|
60
|
-
height: 50,
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
const cloned = clone(editor, original, { x: 50, y: 60 })
|
|
64
|
-
|
|
65
|
-
expect(cloned.x).toBe(50)
|
|
66
|
-
expect(cloned.y).toBe(60)
|
|
67
|
-
})
|
|
68
|
-
|
|
69
|
-
it("applies prop transformations with function", () => {
|
|
70
|
-
const original = new ImageNode(editor, page, {
|
|
71
|
-
id: "orig",
|
|
72
|
-
x: 10,
|
|
73
|
-
y: 20,
|
|
74
|
-
width: 100,
|
|
75
|
-
height: 50,
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
const cloned = clone(editor, original, (props) => ({
|
|
79
|
-
x: props.x + 100,
|
|
80
|
-
y: props.y + 100,
|
|
81
|
-
}))
|
|
82
|
-
|
|
83
|
-
expect(cloned.x).toBe(110)
|
|
84
|
-
expect(cloned.y).toBe(120)
|
|
85
|
-
})
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
describe("useAddNodeAction", () => {
|
|
89
|
-
it("adds a new node to the page", () => {
|
|
90
|
-
const { result } = renderHook(() => useAddNodeAction(page), { wrapper })
|
|
91
|
-
|
|
92
|
-
act(() => {
|
|
93
|
-
result.current(ImageNode, { x: 50, y: 50, width: 200, height: 100 })
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
expect(page.nodes.size).toBe(1)
|
|
97
|
-
const [node] = page.nodes.values()
|
|
98
|
-
expect(node.x).toBe(50)
|
|
99
|
-
expect(node.y).toBe(50)
|
|
100
|
-
expect(node.width).toBe(200)
|
|
101
|
-
expect(node.height).toBe(100)
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
it("uses default positioning when no props provided", () => {
|
|
105
|
-
const { result } = renderHook(() => useAddNodeAction(page), { wrapper })
|
|
106
|
-
|
|
107
|
-
act(() => {
|
|
108
|
-
result.current(ImageNode)
|
|
109
|
-
})
|
|
110
|
-
|
|
111
|
-
const [node] = page.nodes.values()
|
|
112
|
-
// Default: center of page - 50
|
|
113
|
-
expect(node.x).toBe(Math.round(800 / 2) - 50)
|
|
114
|
-
expect(node.y).toBe(Math.round(600 / 2) - 50)
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
it("pushes history entry", () => {
|
|
118
|
-
const { result } = renderHook(() => useAddNodeAction(page), { wrapper })
|
|
119
|
-
|
|
120
|
-
act(() => {
|
|
121
|
-
result.current(ImageNode)
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
expect(editor.history.undoHistory).toHaveLength(1)
|
|
125
|
-
const [entry] = editor.history.undoHistory
|
|
126
|
-
expect(entry.redo[0]).toBe("add-node")
|
|
127
|
-
expect(entry.undo[0]).toBe("delete-node")
|
|
128
|
-
})
|
|
129
|
-
|
|
130
|
-
it("undo removes the added node", () => {
|
|
131
|
-
const { result } = renderHook(() => useAddNodeAction(page), { wrapper })
|
|
132
|
-
|
|
133
|
-
act(() => {
|
|
134
|
-
result.current(ImageNode)
|
|
135
|
-
})
|
|
136
|
-
|
|
137
|
-
expect(page.nodes.size).toBe(1)
|
|
138
|
-
|
|
139
|
-
act(() => {
|
|
140
|
-
editor.history.undo()
|
|
141
|
-
})
|
|
142
|
-
|
|
143
|
-
expect(page.nodes.size).toBe(0)
|
|
144
|
-
})
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
describe("useTrashAction", () => {
|
|
148
|
-
it("removes selected nodes from page", () => {
|
|
149
|
-
const node = new ImageNode(editor, page, {
|
|
150
|
-
id: "n1",
|
|
151
|
-
x: 0,
|
|
152
|
-
y: 0,
|
|
153
|
-
width: 100,
|
|
154
|
-
height: 50,
|
|
155
|
-
})
|
|
156
|
-
page.nodes = new Map([["n1", node]])
|
|
157
|
-
editor.selection = new Set([node])
|
|
158
|
-
|
|
159
|
-
const { result } = renderHook(() => useTrashAction(), { wrapper })
|
|
160
|
-
|
|
161
|
-
act(() => {
|
|
162
|
-
result.current()
|
|
163
|
-
})
|
|
164
|
-
|
|
165
|
-
expect(page.nodes.size).toBe(0)
|
|
166
|
-
expect(editor.selection.size).toBe(0)
|
|
167
|
-
})
|
|
168
|
-
|
|
169
|
-
it("removes only selected nodes", () => {
|
|
170
|
-
const node1 = new ImageNode(editor, page, {
|
|
171
|
-
id: "n1",
|
|
172
|
-
x: 0,
|
|
173
|
-
y: 0,
|
|
174
|
-
width: 100,
|
|
175
|
-
height: 50,
|
|
176
|
-
})
|
|
177
|
-
const node2 = new ImageNode(editor, page, {
|
|
178
|
-
id: "n2",
|
|
179
|
-
x: 100,
|
|
180
|
-
y: 0,
|
|
181
|
-
width: 100,
|
|
182
|
-
height: 50,
|
|
183
|
-
})
|
|
184
|
-
page.nodes = new Map([
|
|
185
|
-
["n1", node1],
|
|
186
|
-
["n2", node2],
|
|
187
|
-
])
|
|
188
|
-
editor.selection = new Set([node1])
|
|
189
|
-
|
|
190
|
-
const { result } = renderHook(() => useTrashAction(), { wrapper })
|
|
191
|
-
|
|
192
|
-
act(() => {
|
|
193
|
-
result.current()
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
expect(page.nodes.size).toBe(1)
|
|
197
|
-
expect(page.nodes.has("n2")).toBe(true)
|
|
198
|
-
})
|
|
199
|
-
|
|
200
|
-
it("does nothing when selection is empty", () => {
|
|
201
|
-
const { result } = renderHook(() => useTrashAction(), { wrapper })
|
|
202
|
-
|
|
203
|
-
act(() => {
|
|
204
|
-
result.current()
|
|
205
|
-
})
|
|
206
|
-
|
|
207
|
-
expect(editor.history.undoHistory).toHaveLength(0)
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
it("undo restores deleted nodes", () => {
|
|
211
|
-
const node = new ImageNode(editor, page, {
|
|
212
|
-
id: "n1",
|
|
213
|
-
x: 50,
|
|
214
|
-
y: 100,
|
|
215
|
-
width: 100,
|
|
216
|
-
height: 50,
|
|
217
|
-
})
|
|
218
|
-
page.nodes = new Map([["n1", node]])
|
|
219
|
-
editor.selection = new Set([node])
|
|
220
|
-
|
|
221
|
-
const { result } = renderHook(() => useTrashAction(), { wrapper })
|
|
222
|
-
|
|
223
|
-
act(() => {
|
|
224
|
-
result.current()
|
|
225
|
-
})
|
|
226
|
-
|
|
227
|
-
expect(page.nodes.size).toBe(0)
|
|
228
|
-
|
|
229
|
-
act(() => {
|
|
230
|
-
editor.history.undo()
|
|
231
|
-
})
|
|
232
|
-
|
|
233
|
-
expect(page.nodes.size).toBe(1)
|
|
234
|
-
expect(page.nodes.has("n1")).toBe(true)
|
|
235
|
-
})
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
describe("useDuplicateAction", () => {
|
|
239
|
-
it("duplicates selected nodes with default offset", () => {
|
|
240
|
-
const node = new ImageNode(editor, page, {
|
|
241
|
-
id: "n1",
|
|
242
|
-
x: 100,
|
|
243
|
-
y: 100,
|
|
244
|
-
width: 100,
|
|
245
|
-
height: 50,
|
|
246
|
-
})
|
|
247
|
-
page.nodes = new Map([["n1", node]])
|
|
248
|
-
editor.selection = new Set([node])
|
|
249
|
-
|
|
250
|
-
const { result } = renderHook(() => useDuplicateAction(), { wrapper })
|
|
251
|
-
|
|
252
|
-
act(() => {
|
|
253
|
-
result.current()
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
expect(page.nodes.size).toBe(2)
|
|
257
|
-
|
|
258
|
-
const newNode = [...page.nodes.values()].find((n) => n.id !== "n1")
|
|
259
|
-
expect(newNode?.x).toBe(110) // default offset is 10
|
|
260
|
-
expect(newNode?.y).toBe(110)
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
it("supports custom offset", () => {
|
|
264
|
-
const node = new ImageNode(editor, page, {
|
|
265
|
-
id: "n1",
|
|
266
|
-
x: 50,
|
|
267
|
-
y: 50,
|
|
268
|
-
width: 100,
|
|
269
|
-
height: 50,
|
|
270
|
-
})
|
|
271
|
-
page.nodes = new Map([["n1", node]])
|
|
272
|
-
editor.selection = new Set([node])
|
|
273
|
-
|
|
274
|
-
const { result } = renderHook(() => useDuplicateAction(), { wrapper })
|
|
275
|
-
|
|
276
|
-
act(() => {
|
|
277
|
-
result.current({ offset: 25 })
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
const newNode = [...page.nodes.values()].find((n) => n.id !== "n1")
|
|
281
|
-
expect(newNode?.x).toBe(75)
|
|
282
|
-
expect(newNode?.y).toBe(75)
|
|
283
|
-
})
|
|
284
|
-
|
|
285
|
-
it("selects duplicated nodes", () => {
|
|
286
|
-
const node = new ImageNode(editor, page, {
|
|
287
|
-
id: "n1",
|
|
288
|
-
x: 0,
|
|
289
|
-
y: 0,
|
|
290
|
-
width: 100,
|
|
291
|
-
height: 50,
|
|
292
|
-
})
|
|
293
|
-
page.nodes = new Map([["n1", node]])
|
|
294
|
-
editor.selection = new Set([node])
|
|
295
|
-
|
|
296
|
-
const { result } = renderHook(() => useDuplicateAction(), { wrapper })
|
|
297
|
-
|
|
298
|
-
act(() => {
|
|
299
|
-
result.current()
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
// Selection should be the new node(s), not the original
|
|
303
|
-
expect(editor.selection.size).toBe(1)
|
|
304
|
-
expect(editor.selection.has(node)).toBe(false)
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
it("does nothing when selection is empty", () => {
|
|
308
|
-
const { result } = renderHook(() => useDuplicateAction(), { wrapper })
|
|
309
|
-
|
|
310
|
-
act(() => {
|
|
311
|
-
result.current()
|
|
312
|
-
})
|
|
313
|
-
|
|
314
|
-
expect(editor.history.undoHistory).toHaveLength(0)
|
|
315
|
-
})
|
|
316
|
-
})
|
|
317
|
-
|
|
318
|
-
describe("useToggleLockAction", () => {
|
|
319
|
-
it("locks unlocked nodes", () => {
|
|
320
|
-
const node = new ImageNode(editor, page, {
|
|
321
|
-
id: "n1",
|
|
322
|
-
x: 0,
|
|
323
|
-
y: 0,
|
|
324
|
-
width: 100,
|
|
325
|
-
height: 50,
|
|
326
|
-
locked: false,
|
|
327
|
-
})
|
|
328
|
-
page.nodes = new Map([["n1", node]])
|
|
329
|
-
editor.selection = new Set([node])
|
|
330
|
-
|
|
331
|
-
const { result } = renderHook(() => useToggleLockAction(), { wrapper })
|
|
332
|
-
|
|
333
|
-
act(() => {
|
|
334
|
-
result.current()
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
expect(node.locked).toBe(true)
|
|
338
|
-
})
|
|
339
|
-
|
|
340
|
-
it("unlocks locked nodes", () => {
|
|
341
|
-
const node = new ImageNode(editor, page, {
|
|
342
|
-
id: "n1",
|
|
343
|
-
x: 0,
|
|
344
|
-
y: 0,
|
|
345
|
-
width: 100,
|
|
346
|
-
height: 50,
|
|
347
|
-
locked: true,
|
|
348
|
-
})
|
|
349
|
-
page.nodes = new Map([["n1", node]])
|
|
350
|
-
editor.selection = new Set([node])
|
|
351
|
-
|
|
352
|
-
const { result } = renderHook(() => useToggleLockAction(), { wrapper })
|
|
353
|
-
|
|
354
|
-
act(() => {
|
|
355
|
-
result.current()
|
|
356
|
-
})
|
|
357
|
-
|
|
358
|
-
expect(node.locked).toBe(false)
|
|
359
|
-
})
|
|
360
|
-
|
|
361
|
-
it("unlocks all if any are locked", () => {
|
|
362
|
-
const node1 = new ImageNode(editor, page, {
|
|
363
|
-
id: "n1",
|
|
364
|
-
x: 0,
|
|
365
|
-
y: 0,
|
|
366
|
-
width: 100,
|
|
367
|
-
height: 50,
|
|
368
|
-
locked: true,
|
|
369
|
-
})
|
|
370
|
-
const node2 = new ImageNode(editor, page, {
|
|
371
|
-
id: "n2",
|
|
372
|
-
x: 100,
|
|
373
|
-
y: 0,
|
|
374
|
-
width: 100,
|
|
375
|
-
height: 50,
|
|
376
|
-
locked: false,
|
|
377
|
-
})
|
|
378
|
-
page.nodes = new Map([
|
|
379
|
-
["n1", node1],
|
|
380
|
-
["n2", node2],
|
|
381
|
-
])
|
|
382
|
-
editor.selection = new Set([node1, node2])
|
|
383
|
-
|
|
384
|
-
const { result } = renderHook(() => useToggleLockAction(), { wrapper })
|
|
385
|
-
|
|
386
|
-
act(() => {
|
|
387
|
-
result.current()
|
|
388
|
-
})
|
|
389
|
-
|
|
390
|
-
// When any are locked, toggle unlocks all
|
|
391
|
-
expect(node1.locked).toBe(false)
|
|
392
|
-
expect(node2.locked).toBe(false)
|
|
393
|
-
})
|
|
394
|
-
})
|
|
395
|
-
|
|
396
|
-
describe("useAlignAction", () => {
|
|
397
|
-
it("aligns nodes to left edge", () => {
|
|
398
|
-
const n1 = new ImageNode(editor, page, {
|
|
399
|
-
id: "n1",
|
|
400
|
-
x: 50,
|
|
401
|
-
y: 0,
|
|
402
|
-
width: 100,
|
|
403
|
-
height: 50,
|
|
404
|
-
})
|
|
405
|
-
const n2 = new ImageNode(editor, page, {
|
|
406
|
-
id: "n2",
|
|
407
|
-
x: 150,
|
|
408
|
-
y: 0,
|
|
409
|
-
width: 100,
|
|
410
|
-
height: 50,
|
|
411
|
-
})
|
|
412
|
-
page.nodes = new Map([
|
|
413
|
-
["n1", n1],
|
|
414
|
-
["n2", n2],
|
|
415
|
-
])
|
|
416
|
-
editor.selection = new Set([n1, n2])
|
|
417
|
-
|
|
418
|
-
const { result } = renderHook(() => useAlignAction(), { wrapper })
|
|
419
|
-
|
|
420
|
-
act(() => {
|
|
421
|
-
result.current.left()
|
|
422
|
-
})
|
|
423
|
-
|
|
424
|
-
expect(n1.x).toBe(0)
|
|
425
|
-
expect(n2.x).toBe(0)
|
|
426
|
-
})
|
|
427
|
-
|
|
428
|
-
it("centers nodes horizontally", () => {
|
|
429
|
-
const n1 = new ImageNode(editor, page, {
|
|
430
|
-
id: "n1",
|
|
431
|
-
x: 0,
|
|
432
|
-
y: 0,
|
|
433
|
-
width: 100,
|
|
434
|
-
height: 50,
|
|
435
|
-
})
|
|
436
|
-
page.nodes = new Map([["n1", n1]])
|
|
437
|
-
editor.selection = new Set([n1])
|
|
438
|
-
|
|
439
|
-
const { result } = renderHook(() => useAlignAction(), { wrapper })
|
|
440
|
-
|
|
441
|
-
act(() => {
|
|
442
|
-
result.current.center()
|
|
443
|
-
})
|
|
444
|
-
|
|
445
|
-
// page.width = 800, node.width = 100, so center = (800 - 100) / 2 = 350
|
|
446
|
-
expect(n1.x).toBe(350)
|
|
447
|
-
})
|
|
448
|
-
|
|
449
|
-
it("aligns nodes to right edge", () => {
|
|
450
|
-
const n1 = new ImageNode(editor, page, {
|
|
451
|
-
id: "n1",
|
|
452
|
-
x: 0,
|
|
453
|
-
y: 0,
|
|
454
|
-
width: 100,
|
|
455
|
-
height: 50,
|
|
456
|
-
})
|
|
457
|
-
page.nodes = new Map([["n1", n1]])
|
|
458
|
-
editor.selection = new Set([n1])
|
|
459
|
-
|
|
460
|
-
const { result } = renderHook(() => useAlignAction(), { wrapper })
|
|
461
|
-
|
|
462
|
-
act(() => {
|
|
463
|
-
result.current.right()
|
|
464
|
-
})
|
|
465
|
-
|
|
466
|
-
// page.width = 800, node.width = 100, so right = 800 - 100 = 700
|
|
467
|
-
expect(n1.x).toBe(700)
|
|
468
|
-
})
|
|
469
|
-
|
|
470
|
-
it("aligns nodes to top edge", () => {
|
|
471
|
-
const n1 = new ImageNode(editor, page, {
|
|
472
|
-
id: "n1",
|
|
473
|
-
x: 0,
|
|
474
|
-
y: 100,
|
|
475
|
-
width: 100,
|
|
476
|
-
height: 50,
|
|
477
|
-
})
|
|
478
|
-
page.nodes = new Map([["n1", n1]])
|
|
479
|
-
editor.selection = new Set([n1])
|
|
480
|
-
|
|
481
|
-
const { result } = renderHook(() => useAlignAction(), { wrapper })
|
|
482
|
-
|
|
483
|
-
act(() => {
|
|
484
|
-
result.current.top()
|
|
485
|
-
})
|
|
486
|
-
|
|
487
|
-
expect(n1.y).toBe(0)
|
|
488
|
-
})
|
|
489
|
-
|
|
490
|
-
it("centers nodes vertically", () => {
|
|
491
|
-
const n1 = new ImageNode(editor, page, {
|
|
492
|
-
id: "n1",
|
|
493
|
-
x: 0,
|
|
494
|
-
y: 0,
|
|
495
|
-
width: 100,
|
|
496
|
-
height: 50,
|
|
497
|
-
})
|
|
498
|
-
page.nodes = new Map([["n1", n1]])
|
|
499
|
-
editor.selection = new Set([n1])
|
|
500
|
-
|
|
501
|
-
const { result } = renderHook(() => useAlignAction(), { wrapper })
|
|
502
|
-
|
|
503
|
-
act(() => {
|
|
504
|
-
result.current.middle()
|
|
505
|
-
})
|
|
506
|
-
|
|
507
|
-
// page.height = 600, node.height = 50, so middle = (600 - 50) / 2 = 275
|
|
508
|
-
expect(n1.y).toBe(275)
|
|
509
|
-
})
|
|
510
|
-
|
|
511
|
-
it("aligns nodes to bottom edge", () => {
|
|
512
|
-
const n1 = new ImageNode(editor, page, {
|
|
513
|
-
id: "n1",
|
|
514
|
-
x: 0,
|
|
515
|
-
y: 0,
|
|
516
|
-
width: 100,
|
|
517
|
-
height: 50,
|
|
518
|
-
})
|
|
519
|
-
page.nodes = new Map([["n1", n1]])
|
|
520
|
-
editor.selection = new Set([n1])
|
|
521
|
-
|
|
522
|
-
const { result } = renderHook(() => useAlignAction(), { wrapper })
|
|
523
|
-
|
|
524
|
-
act(() => {
|
|
525
|
-
result.current.bottom()
|
|
526
|
-
})
|
|
527
|
-
|
|
528
|
-
// page.height = 600, node.height = 50, so bottom = 600 - 50 = 550
|
|
529
|
-
expect(n1.y).toBe(550)
|
|
530
|
-
})
|
|
531
|
-
|
|
532
|
-
it("creates history entry for alignment", () => {
|
|
533
|
-
const n1 = new ImageNode(editor, page, {
|
|
534
|
-
id: "n1",
|
|
535
|
-
x: 50,
|
|
536
|
-
y: 0,
|
|
537
|
-
width: 100,
|
|
538
|
-
height: 50,
|
|
539
|
-
})
|
|
540
|
-
page.nodes = new Map([["n1", n1]])
|
|
541
|
-
editor.selection = new Set([n1])
|
|
542
|
-
|
|
543
|
-
const { result } = renderHook(() => useAlignAction(), { wrapper })
|
|
544
|
-
|
|
545
|
-
act(() => {
|
|
546
|
-
result.current.left()
|
|
547
|
-
})
|
|
548
|
-
|
|
549
|
-
expect(editor.history.undoHistory).toHaveLength(1)
|
|
550
|
-
|
|
551
|
-
act(() => {
|
|
552
|
-
editor.history.undo()
|
|
553
|
-
})
|
|
554
|
-
|
|
555
|
-
expect(n1.x).toBe(50)
|
|
556
|
-
})
|
|
557
|
-
})
|
|
558
|
-
|
|
559
|
-
describe("useStackOrderAction", () => {
|
|
560
|
-
it("brings node forward in stack", () => {
|
|
561
|
-
const n1 = new ImageNode(editor, page, {
|
|
562
|
-
id: "a",
|
|
563
|
-
x: 0,
|
|
564
|
-
y: 0,
|
|
565
|
-
width: 100,
|
|
566
|
-
height: 50,
|
|
567
|
-
})
|
|
568
|
-
const n2 = new ImageNode(editor, page, {
|
|
569
|
-
id: "b",
|
|
570
|
-
x: 0,
|
|
571
|
-
y: 0,
|
|
572
|
-
width: 100,
|
|
573
|
-
height: 50,
|
|
574
|
-
})
|
|
575
|
-
page.nodes = new Map([
|
|
576
|
-
["a", n1],
|
|
577
|
-
["b", n2],
|
|
578
|
-
])
|
|
579
|
-
editor.selection = new Set([n1])
|
|
580
|
-
|
|
581
|
-
const { result } = renderHook(() => useStackOrderAction(), { wrapper })
|
|
582
|
-
|
|
583
|
-
act(() => {
|
|
584
|
-
result.current.bringForward()
|
|
585
|
-
})
|
|
586
|
-
|
|
587
|
-
const order = [...page.nodes.keys()]
|
|
588
|
-
expect(order).toEqual(["b", "a"])
|
|
589
|
-
})
|
|
590
|
-
|
|
591
|
-
it("brings node backward in stack", () => {
|
|
592
|
-
const n1 = new ImageNode(editor, page, {
|
|
593
|
-
id: "a",
|
|
594
|
-
x: 0,
|
|
595
|
-
y: 0,
|
|
596
|
-
width: 100,
|
|
597
|
-
height: 50,
|
|
598
|
-
})
|
|
599
|
-
const n2 = new ImageNode(editor, page, {
|
|
600
|
-
id: "b",
|
|
601
|
-
x: 0,
|
|
602
|
-
y: 0,
|
|
603
|
-
width: 100,
|
|
604
|
-
height: 50,
|
|
605
|
-
})
|
|
606
|
-
page.nodes = new Map([
|
|
607
|
-
["a", n1],
|
|
608
|
-
["b", n2],
|
|
609
|
-
])
|
|
610
|
-
editor.selection = new Set([n2])
|
|
611
|
-
|
|
612
|
-
const { result } = renderHook(() => useStackOrderAction(), { wrapper })
|
|
613
|
-
|
|
614
|
-
act(() => {
|
|
615
|
-
result.current.bringBackward()
|
|
616
|
-
})
|
|
617
|
-
|
|
618
|
-
const order = [...page.nodes.keys()]
|
|
619
|
-
expect(order).toEqual(["b", "a"])
|
|
620
|
-
})
|
|
621
|
-
|
|
622
|
-
it("brings node to front", () => {
|
|
623
|
-
const n1 = new ImageNode(editor, page, {
|
|
624
|
-
id: "a",
|
|
625
|
-
x: 0,
|
|
626
|
-
y: 0,
|
|
627
|
-
width: 100,
|
|
628
|
-
height: 50,
|
|
629
|
-
})
|
|
630
|
-
const n2 = new ImageNode(editor, page, {
|
|
631
|
-
id: "b",
|
|
632
|
-
x: 0,
|
|
633
|
-
y: 0,
|
|
634
|
-
width: 100,
|
|
635
|
-
height: 50,
|
|
636
|
-
})
|
|
637
|
-
const n3 = new ImageNode(editor, page, {
|
|
638
|
-
id: "c",
|
|
639
|
-
x: 0,
|
|
640
|
-
y: 0,
|
|
641
|
-
width: 100,
|
|
642
|
-
height: 50,
|
|
643
|
-
})
|
|
644
|
-
page.nodes = new Map([
|
|
645
|
-
["a", n1],
|
|
646
|
-
["b", n2],
|
|
647
|
-
["c", n3],
|
|
648
|
-
])
|
|
649
|
-
editor.selection = new Set([n1])
|
|
650
|
-
|
|
651
|
-
const { result } = renderHook(() => useStackOrderAction(), { wrapper })
|
|
652
|
-
|
|
653
|
-
act(() => {
|
|
654
|
-
result.current.bringToFront()
|
|
655
|
-
})
|
|
656
|
-
|
|
657
|
-
const order = [...page.nodes.keys()]
|
|
658
|
-
expect(order).toEqual(["b", "c", "a"])
|
|
659
|
-
})
|
|
660
|
-
|
|
661
|
-
it("brings node to back", () => {
|
|
662
|
-
const n1 = new ImageNode(editor, page, {
|
|
663
|
-
id: "a",
|
|
664
|
-
x: 0,
|
|
665
|
-
y: 0,
|
|
666
|
-
width: 100,
|
|
667
|
-
height: 50,
|
|
668
|
-
})
|
|
669
|
-
const n2 = new ImageNode(editor, page, {
|
|
670
|
-
id: "b",
|
|
671
|
-
x: 0,
|
|
672
|
-
y: 0,
|
|
673
|
-
width: 100,
|
|
674
|
-
height: 50,
|
|
675
|
-
})
|
|
676
|
-
const n3 = new ImageNode(editor, page, {
|
|
677
|
-
id: "c",
|
|
678
|
-
x: 0,
|
|
679
|
-
y: 0,
|
|
680
|
-
width: 100,
|
|
681
|
-
height: 50,
|
|
682
|
-
})
|
|
683
|
-
page.nodes = new Map([
|
|
684
|
-
["a", n1],
|
|
685
|
-
["b", n2],
|
|
686
|
-
["c", n3],
|
|
687
|
-
])
|
|
688
|
-
editor.selection = new Set([n3])
|
|
689
|
-
|
|
690
|
-
const { result } = renderHook(() => useStackOrderAction(), { wrapper })
|
|
691
|
-
|
|
692
|
-
act(() => {
|
|
693
|
-
result.current.bringToBack()
|
|
694
|
-
})
|
|
695
|
-
|
|
696
|
-
const order = [...page.nodes.keys()]
|
|
697
|
-
expect(order).toEqual(["c", "a", "b"])
|
|
698
|
-
})
|
|
699
|
-
|
|
700
|
-
it("undo restores original order", () => {
|
|
701
|
-
const n1 = new ImageNode(editor, page, {
|
|
702
|
-
id: "a",
|
|
703
|
-
x: 0,
|
|
704
|
-
y: 0,
|
|
705
|
-
width: 100,
|
|
706
|
-
height: 50,
|
|
707
|
-
})
|
|
708
|
-
const n2 = new ImageNode(editor, page, {
|
|
709
|
-
id: "b",
|
|
710
|
-
x: 0,
|
|
711
|
-
y: 0,
|
|
712
|
-
width: 100,
|
|
713
|
-
height: 50,
|
|
714
|
-
})
|
|
715
|
-
page.nodes = new Map([
|
|
716
|
-
["a", n1],
|
|
717
|
-
["b", n2],
|
|
718
|
-
])
|
|
719
|
-
editor.selection = new Set([n1])
|
|
720
|
-
|
|
721
|
-
const { result } = renderHook(() => useStackOrderAction(), { wrapper })
|
|
722
|
-
|
|
723
|
-
act(() => {
|
|
724
|
-
result.current.bringToFront()
|
|
725
|
-
})
|
|
726
|
-
|
|
727
|
-
expect([...page.nodes.keys()]).toEqual(["b", "a"])
|
|
728
|
-
|
|
729
|
-
act(() => {
|
|
730
|
-
editor.history.undo()
|
|
731
|
-
})
|
|
732
|
-
|
|
733
|
-
expect([...page.nodes.keys()]).toEqual(["a", "b"])
|
|
734
|
-
})
|
|
735
|
-
})
|
|
736
|
-
})
|