seat-editor 2.1.2 → 3.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.
Files changed (57) hide show
  1. package/dist/app/constant.d.ts +232 -0
  2. package/dist/app/constant.js +3683 -3045
  3. package/dist/app/new-board/page.js +5 -6
  4. package/dist/app/only-view/chair.d.ts +1 -0
  5. package/dist/app/only-view/chair.js +4 -0
  6. package/dist/app/only-view/constant.d.ts +22 -2
  7. package/dist/app/only-view/constant.js +4 -4
  8. package/dist/app/only-view/page.js +74 -37
  9. package/dist/app/only-view/user.d.ts +1 -0
  10. package/dist/app/only-view/user.js +4 -0
  11. package/dist/components/layer-v3/index.d.ts +23 -4
  12. package/dist/components/layer-v3/index.js +329 -146
  13. package/dist/components/layer-v4/index.d.ts +20 -0
  14. package/dist/components/layer-v4/index.js +445 -0
  15. package/dist/components/lib/index.d.ts +1 -1
  16. package/dist/components/lib/index.js +1 -1
  17. package/dist/features/board/index.js +1 -1
  18. package/dist/features/board-v2/index.js +1 -1
  19. package/dist/features/board-v3/board-slice.d.ts +1 -0
  20. package/dist/features/board-v3/board-slice.js +26 -3
  21. package/dist/features/board-v3/constant.d.ts +5 -0
  22. package/dist/features/board-v3/constant.js +5 -0
  23. package/dist/features/board-v3/index copy.d.ts +47 -0
  24. package/dist/features/board-v3/index copy.js +2073 -0
  25. package/dist/features/board-v3/index.js +1409 -647
  26. package/dist/features/board-v3/polygon.d.ts +28 -0
  27. package/dist/features/board-v3/polygon.js +109 -0
  28. package/dist/features/board-v3/rect.d.ts +9 -0
  29. package/dist/features/board-v3/rect.js +152 -0
  30. package/dist/features/board-v3/resize-element.d.ts +12 -0
  31. package/dist/features/board-v3/resize-element.js +40 -0
  32. package/dist/features/board-v3/utils.d.ts +162 -0
  33. package/dist/features/board-v3/utils.js +787 -0
  34. package/dist/features/package/index.js +1 -1
  35. package/dist/features/panel/index.js +130 -20
  36. package/dist/features/panel/panel-slice.d.ts +5 -0
  37. package/dist/features/panel/panel-slice.js +15 -0
  38. package/dist/features/panel/select-tool.js +11 -1
  39. package/dist/features/panel/selected-group.d.ts +2 -0
  40. package/dist/features/panel/selected-group.js +7 -0
  41. package/dist/features/panel/table-seat-square.d.ts +2 -0
  42. package/dist/features/panel/table-seat-square.js +9 -0
  43. package/dist/features/side-tool/index.js +13 -6
  44. package/dist/features/view-only/index.js +0 -1
  45. package/dist/features/view-only-2/index.js +0 -1
  46. package/dist/features/view-only-3/index.d.ts +68 -0
  47. package/dist/features/view-only-3/index.js +510 -0
  48. package/dist/features/view-only-3/utils.d.ts +1 -0
  49. package/dist/features/view-only-3/utils.js +3 -0
  50. package/dist/seat-editor.css +1 -1
  51. package/dist/utils/constant.d.ts +1 -0
  52. package/dist/utils/constant.js +11 -0
  53. package/dist/utils/format.d.ts +2 -0
  54. package/dist/utils/format.js +29 -0
  55. package/package.json +3 -1
  56. package/dist/features/view/index.d.ts +0 -19
  57. package/dist/features/view/index.js +0 -221
@@ -0,0 +1,2073 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
+ import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
4
+ import { TransformWrapper, TransformComponent, } from "react-zoom-pan-pinch";
5
+ import { useAppSelector, useAppDispatch } from "../../hooks/use-redux";
6
+ import Layers from "../../components/layer-v3";
7
+ import { isEmpty, throttle } from "lodash";
8
+ import ModalPreview from "../../components/modal-preview";
9
+ import LayerView from "../view-only-2";
10
+ import { isEqual, debounce } from "lodash";
11
+ import { ZoomIn, ZoomOut } from "lucide-react";
12
+ import { Button } from "antd";
13
+ import { getAttributeElement, getAttributeElements } from "./resize-element";
14
+ const EXPAND_THRESHOLD = 200;
15
+ const EXPAND_AMOUNT = 1000;
16
+ const toolElement = ["square", "circle", "table-seat-circle"];
17
+ const idSelectionBoxGhost = "selection-box-ghost";
18
+ const nameShapeSelectionBoxGhost = "selection-box";
19
+ function matrixToXYWH({ x0, y0, w0, h0, scaleX, scaleY, a0x, a0y }) {
20
+ const widthReal = w0 * scaleX;
21
+ const heightReal = h0 * scaleY;
22
+ const xReal = a0x + (x0 - a0x) * scaleX;
23
+ const yReal = a0y + (y0 - a0y) * scaleY;
24
+ return { x: xReal, y: yReal, width: widthReal, height: heightReal };
25
+ }
26
+ const BoardTemplate = ({ onSelectComponent, viewOnly }) => {
27
+ var _a;
28
+ const dispatch = useAppDispatch();
29
+ const theme = useAppSelector((state) => state.theme);
30
+ const transformRef = useRef(null);
31
+ const instance = (_a = transformRef === null || transformRef === void 0 ? void 0 : transformRef.current) === null || _a === void 0 ? void 0 : _a.instance;
32
+ const context = instance === null || instance === void 0 ? void 0 : instance.getContext();
33
+ const containerRef = useRef(null);
34
+ const releaseGroupRef = useRef(false);
35
+ const dragGhostRef = useRef(false);
36
+ const dataSetGhost = useRef(null);
37
+ const ghostRef = useRef(null);
38
+ const [widthBoard, setWidthBoard] = useState(20000);
39
+ const [heightBoard, setHeightBoard] = useState(20000);
40
+ const [panningGroup, setPanningGroup] = useState(false);
41
+ const svgRef = useRef(null);
42
+ const [shadowShape, setShadowShape] = useState([]);
43
+ const [startPoint, setStartPoint] = useState(null);
44
+ const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
45
+ // const [moveComponent, setMoveComponent] = useState(false);
46
+ const [scale, setScale] = useState(1);
47
+ const [boardSize, setBoardSize] = useState({ width: 10000, height: 10000 });
48
+ const [minCoords, setMinCoords] = useState({ x: -100, y: -100 });
49
+ const lastPos = useRef({ x: 0, y: 0 });
50
+ const [hasInitialized, setHasInitialzed] = useState(false);
51
+ const extendBy = 2000; // seberapa banyak canvas ditambah saat pan ke tepi
52
+ const buffer = 100; //
53
+ const [isLoading, setIsLoading] = useState(false);
54
+ const activeTool = useAppSelector((state) => state.tool.active);
55
+ const grid = useAppSelector((state) => state.tool.grid);
56
+ const lockBackground = useAppSelector((state) => state.tool.lockBackground);
57
+ const { components: componentsProps, extraComponents: extraComponentsProps, flagChange, updateBy, } = useAppSelector((state) => state.board);
58
+ const { selectionLines } = useAppSelector((state) => state.panel);
59
+ useEffect(() => {
60
+ if (selectionLines) {
61
+ setSelectedLines(selectionLines);
62
+ const idSelected = dataElementSelectionGroupRef.current.map((item) => item.id);
63
+ const newSelectionData = [
64
+ ...componentsState,
65
+ ...extraComponentsState,
66
+ ].filter((comp) => idSelected.includes(comp.id));
67
+ dataElementSelectionGroupRef.current = newSelectionData;
68
+ }
69
+ }, [selectionLines]);
70
+ useEffect(() => {
71
+ var _a;
72
+ if (activeTool !== ((_a = shadowShape[0]) === null || _a === void 0 ? void 0 : _a.shape)) {
73
+ setShadowShape([]);
74
+ }
75
+ }, [activeTool]);
76
+ const minPosision = useMemo(() => {
77
+ var _a, _b;
78
+ const minX = (_a = context === null || context === void 0 ? void 0 : context.state) === null || _a === void 0 ? void 0 : _a.positionX;
79
+ const minY = (_b = context === null || context === void 0 ? void 0 : context.state) === null || _b === void 0 ? void 0 : _b.positionY;
80
+ return { minX, minY };
81
+ }, [context]);
82
+ // useEffect(() => {
83
+ // if (flagChange) {
84
+ // setComponentsState(componentsProps);
85
+ // setExtraComponentsState(extraComponentsProps);
86
+ // }
87
+ // }, [context]);
88
+ // const [isDragging, setIsDragging] = useState(false);
89
+ const [resizeDirection, setResizeDirection] = useState(null);
90
+ const backgroundColor = useAppSelector((state) => state.board.backgroundColor);
91
+ const selectedComponentProps = useAppSelector((state) => state.panel.selectedComponent);
92
+ const screenCTMRef = useRef(null);
93
+ const dragIndex = useRef(null);
94
+ const [componentsState, setComponentsState] = useState([]);
95
+ const [extraComponentsState, setExtraComponentsState] = useState([]);
96
+ const [selectedComponent, setSelectedComponent] = useState(null);
97
+ const [selectedLines, setSelectedLines] = useState([]);
98
+ const isSyncingFromRedux = useRef(false);
99
+ const startPos = useRef({ x: 0, y: 0 });
100
+ const isDragging = useRef(false);
101
+ const moveComponent = useRef(false);
102
+ useEffect(() => {
103
+ var _a, _b, _c, _d, _e, _f, _g, _h;
104
+ if (hasInitialized)
105
+ return;
106
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
107
+ componentsProps === null || componentsProps === void 0 ? void 0 : componentsProps.forEach((_) => {
108
+ var _a, _b, _c, _d;
109
+ let values = _;
110
+ if ((_a = values === null || values === void 0 ? void 0 : values.shape) === null || _a === void 0 ? void 0 : _a.includes("square")) {
111
+ minX = Math.min(minX, values.x);
112
+ minY = Math.min(minY, values.y);
113
+ maxX = Math.max(maxX, values.x + values.width);
114
+ maxY = Math.max(maxY, values.y + values.height);
115
+ }
116
+ if ((_b = values === null || values === void 0 ? void 0 : values.shape) === null || _b === void 0 ? void 0 : _b.includes("circle")) {
117
+ minX = Math.min(minX, values.x);
118
+ minY = Math.min(minY, values.y);
119
+ maxX = Math.max(maxX, values.x + values.width);
120
+ maxY = Math.max(maxY, values.y + values.height);
121
+ }
122
+ if ((_c = values === null || values === void 0 ? void 0 : values.shape) === null || _c === void 0 ? void 0 : _c.includes("table-seat-circle")) {
123
+ minX = Math.min(minX, values.x);
124
+ minY = Math.min(minY, values.y);
125
+ maxX = Math.max(maxX, values.x + values.width);
126
+ maxY = Math.max(maxY, values.y + values.height);
127
+ }
128
+ if ((_d = values === null || values === void 0 ? void 0 : values.shape) === null || _d === void 0 ? void 0 : _d.includes("image-table")) {
129
+ minX = Math.min(minX, values.x);
130
+ minY = Math.min(minY, values.y);
131
+ maxX = Math.max(maxX, values.x + values.width);
132
+ maxY = Math.max(maxY, values.y + values.height);
133
+ }
134
+ });
135
+ extraComponentsProps === null || extraComponentsProps === void 0 ? void 0 : extraComponentsProps.forEach((values) => {
136
+ var _a, _b;
137
+ if ((_a = values === null || values === void 0 ? void 0 : values.shape) === null || _a === void 0 ? void 0 : _a.includes("background")) {
138
+ minX = Math.min(minX, values.x);
139
+ minY = Math.min(minY, values.y);
140
+ maxX = Math.max(maxX, values.x + values.width);
141
+ maxY = Math.max(maxY, values.y + values.height);
142
+ }
143
+ if ((_b = values === null || values === void 0 ? void 0 : values.shape) === null || _b === void 0 ? void 0 : _b.includes("text")) {
144
+ minX = Math.min(minX, values.x);
145
+ minY = Math.min(minY, values.y);
146
+ maxX = Math.max(maxX, values.x + values.width);
147
+ maxY = Math.max(maxY, values.y + values.height);
148
+ }
149
+ });
150
+ let backgroundHasOne = false;
151
+ if ((extraComponentsProps === null || extraComponentsProps === void 0 ? void 0 : extraComponentsProps.length) === 1 &&
152
+ ((_b = (_a = extraComponentsProps === null || extraComponentsProps === void 0 ? void 0 : extraComponentsProps[0]) === null || _a === void 0 ? void 0 : _a.shape) === null || _b === void 0 ? void 0 : _b.includes("background"))) {
153
+ backgroundHasOne = true;
154
+ minX = (_c = extraComponentsProps === null || extraComponentsProps === void 0 ? void 0 : extraComponentsProps[0]) === null || _c === void 0 ? void 0 : _c.x;
155
+ minY = (_d = extraComponentsProps === null || extraComponentsProps === void 0 ? void 0 : extraComponentsProps[0]) === null || _d === void 0 ? void 0 : _d.y;
156
+ maxX = (_e = extraComponentsProps === null || extraComponentsProps === void 0 ? void 0 : extraComponentsProps[0]) === null || _e === void 0 ? void 0 : _e.width;
157
+ maxY = (_f = extraComponentsProps === null || extraComponentsProps === void 0 ? void 0 : extraComponentsProps[0]) === null || _f === void 0 ? void 0 : _f.height;
158
+ }
159
+ if ((extraComponentsProps === null || extraComponentsProps === void 0 ? void 0 : extraComponentsProps.length) < 1 &&
160
+ ["background", "text"].includes((_g = componentsProps === null || componentsProps === void 0 ? void 0 : componentsProps[0]) === null || _g === void 0 ? void 0 : _g.shape)) {
161
+ minX = minX - minX * 0.5;
162
+ minY = minY - minY * 0.5;
163
+ }
164
+ if ((minX !== Infinity || minY !== Infinity) && !activeTool) {
165
+ setMinCoords({ x: minX - 200, y: minY - 100 });
166
+ (_h = transformRef.current) === null || _h === void 0 ? void 0 : _h.setTransform(minX - 100, minY - 100, scale);
167
+ setHasInitialzed(true);
168
+ }
169
+ // }
170
+ // if (!backgroundHasOne) {
171
+ // setMinCoords({ x: minX - minX * 0.5, y: minY - minY * 0.5 });
172
+ // }
173
+ // if(maxX > boardSize.width){
174
+ // setBoardSize((prev) => ({ ...prev, width: maxX }));
175
+ // }
176
+ // if(maxY > boardSize.height){
177
+ // setBoardSize((prev) => ({ ...prev, height: maxY }));
178
+ // }
179
+ // return {
180
+ // minX: backgroundHasOne ? minX : minX - minX * 0.5,
181
+ // minY: backgroundHasOne ? minY : minY - minY * 0.5,
182
+ // width: maxX,
183
+ // height: maxY,
184
+ // };
185
+ }, [componentsProps, extraComponentsProps]);
186
+ const debouncedSyncToReduxSelected = useRef(debounce((data) => {
187
+ throttledDispatch(data);
188
+ }, 300)).current;
189
+ const updateQueue = useRef([]);
190
+ const isFlushing = useRef(false);
191
+ const updateFlushToRedux = () => {
192
+ if (isFlushing.current)
193
+ return;
194
+ isFlushing.current = true;
195
+ requestAnimationFrame(() => {
196
+ const batch = updateQueue.current;
197
+ updateQueue.current = [];
198
+ isFlushing.current = false;
199
+ dispatch({
200
+ type: "board/setNewComponents",
201
+ payload: batch === null || batch === void 0 ? void 0 : batch[0],
202
+ });
203
+ });
204
+ };
205
+ const queueUpdateComponents = (data) => {
206
+ dispatch({
207
+ type: "board/setNewComponents",
208
+ payload: data,
209
+ });
210
+ };
211
+ // const debouncedSyncComponents = useRef(
212
+ // debounce((data) => {
213
+ // dispatch({
214
+ // type: "board/setNewComponents",
215
+ // payload: data,
216
+ // });
217
+ // }, 0)
218
+ // );
219
+ const debouncedSyncExtraComponents = useRef(debounce((data) => {
220
+ dispatch({
221
+ type: "board/setNewExtraComponents",
222
+ payload: data,
223
+ });
224
+ }, 300));
225
+ // Redux → Local
226
+ useEffect(() => {
227
+ if (flagChange && updateBy === "global") {
228
+ if (!isEqual(componentsProps, componentsState)) {
229
+ isSyncingFromRedux.current = true;
230
+ setComponentsState(componentsProps !== null && componentsProps !== void 0 ? componentsProps : []);
231
+ }
232
+ if (!isEqual(extraComponentsProps, extraComponentsState)) {
233
+ isSyncingFromRedux.current = true;
234
+ setExtraComponentsState(extraComponentsProps !== null && extraComponentsProps !== void 0 ? extraComponentsProps : []);
235
+ }
236
+ if (!isEqual(selectedComponentProps, selectedComponent)) {
237
+ isSyncingFromRedux.current = true;
238
+ setSelectedComponent(selectedComponentProps !== null && selectedComponentProps !== void 0 ? selectedComponentProps : []);
239
+ }
240
+ dispatch({ type: "board/setFlagChange", payload: false });
241
+ }
242
+ }, [
243
+ componentsProps,
244
+ extraComponentsProps,
245
+ // selectedComponentProps,
246
+ // flagChange,
247
+ ]);
248
+ // Local → Redux
249
+ // const syncFromLocalToRedux = (
250
+ // newComponentState?: any,
251
+ // newExtraComponentState?: any
252
+ // ) => {
253
+ // console.log("MAU SYNC DARI LOCAL KE REDUX", newComponentState);
254
+ // if (
255
+ // !isEmpty(newComponentState) &&
256
+ // !isEqual(newComponentState, componentsProps)
257
+ // ) {
258
+ // dispatch({ type: "board/setUpdateBy", payload: "local" });
259
+ // queueUpdateComponents(newComponentState);
260
+ // }
261
+ // if (
262
+ // !isEmpty(newExtraComponentState) &&
263
+ // !isEqual(newExtraComponentState, extraComponentsProps)
264
+ // ) {
265
+ // debouncedSyncExtraComponents.current(newExtraComponentState);
266
+ // }
267
+ // if (
268
+ // !isEqual(componentsState, componentsProps) &&
269
+ // !isEqual(componentsState, []) &&
270
+ // isEmpty(newComponentState)
271
+ // ) {
272
+ // dispatch({ type: "board/setUpdateBy", payload: "local" });
273
+ // queueUpdateComponents(componentsState);
274
+ // }
275
+ // if (
276
+ // !isEqual(extraComponentsState, extraComponentsProps) &&
277
+ // !isEqual(extraComponentsState, []) &&
278
+ // isEmpty(newExtraComponentState)
279
+ // ) {
280
+ // debouncedSyncExtraComponents.current(extraComponentsState);
281
+ // }
282
+ // };
283
+ const updateComponentAttribute = (component) => {
284
+ console.log("MAU UPDATE COMPONENT", component);
285
+ if (!component)
286
+ return;
287
+ const previousComponent = componentsState.find((item) => item.id == (component === null || component === void 0 ? void 0 : component.id));
288
+ const previousExtraComponent = extraComponentsState.find((item) => item.id == (component === null || component === void 0 ? void 0 : component.id));
289
+ const isNotEqualPreviousComponent = !isEqual(previousComponent, component);
290
+ const isNotEqualPreviousExtraComponent = !isEqual(previousExtraComponent, component);
291
+ if (previousComponent && isNotEqualPreviousComponent) {
292
+ // update local state
293
+ const newComponent = componentsState.map((item) => {
294
+ if (item.id === (component === null || component === void 0 ? void 0 : component.id)) {
295
+ return Object.assign(Object.assign({}, item), component);
296
+ }
297
+ return item;
298
+ });
299
+ setComponentsState(newComponent);
300
+ // update redux
301
+ dispatch({ type: "board/setUpdateBy", payload: "local" });
302
+ queueUpdateComponents(newComponent);
303
+ }
304
+ if (previousExtraComponent && isNotEqualPreviousExtraComponent) {
305
+ // update local state
306
+ const newExtraComponent = extraComponentsState.map((item) => {
307
+ if (item.id === component.id) {
308
+ return Object.assign(Object.assign({}, item), component);
309
+ }
310
+ return item;
311
+ });
312
+ setExtraComponentsState(newExtraComponent);
313
+ // update redux
314
+ debouncedSyncExtraComponents.current(newExtraComponent);
315
+ }
316
+ };
317
+ const updateComponentsAttribute = (components) => {
318
+ // if(components.length === 0) return
319
+ console.log("MAU SYNC DARI LOCAL KE REDUX MANY", components);
320
+ const newComponents = components.map((item) => {
321
+ const previousComponent = componentsState.find((comp) => comp.id === (item === null || item === void 0 ? void 0 : item.id));
322
+ if (previousComponent) {
323
+ return Object.assign(Object.assign({}, previousComponent), item);
324
+ }
325
+ return item;
326
+ });
327
+ const newComponentState = componentsState.map((item) => {
328
+ const updatedItem = newComponents.find((comp) => comp.id === item.id);
329
+ return updatedItem ? Object.assign(Object.assign({}, item), updatedItem) : item;
330
+ });
331
+ setComponentsState(newComponentState);
332
+ dispatch({ type: "board/setUpdateBy", payload: "local" });
333
+ queueUpdateComponents(newComponentState);
334
+ };
335
+ const addComponents = (components) => {
336
+ const newComponentState = [...extraComponentsState, components];
337
+ // setComponentsState(newComponentState);
338
+ setExtraComponentsState(newComponentState);
339
+ dispatch({ type: "board/setUpdateBy", payload: "local" });
340
+ dispatch({ type: "board/setExtraComponent", payload: components });
341
+ };
342
+ const getSvgCoords = (e) => {
343
+ var _a;
344
+ const svg = svgRef.current;
345
+ const point = svg.createSVGPoint();
346
+ point.x = e.clientX;
347
+ point.y = e.clientY;
348
+ const transformed = point.matrixTransform((_a = svg.getScreenCTM()) === null || _a === void 0 ? void 0 : _a.inverse());
349
+ return { x: transformed.x, y: transformed.y };
350
+ };
351
+ const throttledDispatch = useCallback(throttle((component) => {
352
+ // dispatch({
353
+ // type: "board/updateComponent",
354
+ // payload: component,
355
+ // });
356
+ dispatch({
357
+ type: "panel/updateSelectedComponent",
358
+ payload: component,
359
+ });
360
+ }, 16), // 16ms ≈ 60fps
361
+ [dispatch]);
362
+ const getCursorStyle = () => {
363
+ if (activeTool === "select" && moveComponent.current) {
364
+ return "grabbing";
365
+ }
366
+ else if (activeTool === "select" && resizeDirection) {
367
+ return "grab";
368
+ }
369
+ else if (activeTool === "grab") {
370
+ return "grab";
371
+ }
372
+ else if (activeTool === "ruler") {
373
+ return "crosshair";
374
+ }
375
+ };
376
+ const handelZoomIn = () => {
377
+ var _a;
378
+ if (activeTool !== "grab") {
379
+ dispatch({
380
+ type: "tool/setActiveTool",
381
+ payload: "grab",
382
+ });
383
+ }
384
+ (_a = transformRef.current) === null || _a === void 0 ? void 0 : _a.zoomIn();
385
+ };
386
+ const handleZoomOut = () => {
387
+ var _a;
388
+ if (activeTool !== "grab") {
389
+ dispatch({
390
+ type: "tool/setActiveTool",
391
+ payload: "grab",
392
+ });
393
+ }
394
+ (_a = transformRef.current) === null || _a === void 0 ? void 0 : _a.zoomOut();
395
+ };
396
+ const [touch, setTouch] = useState(false);
397
+ function createTableGhost({ x, y, width, height, fill, shape, id = "ghost-element-create", }) {
398
+ // buat group dulu
399
+ const SVG_NS = "http://www.w3.org/2000/svg";
400
+ let el = null;
401
+ // tambahkan rectangle
402
+ if (shape === "square" ||
403
+ shape === "table-seat-circle" ||
404
+ shape === "selection-box") {
405
+ el = document.createElementNS(SVG_NS, "rect");
406
+ el.setAttribute("id", id);
407
+ el.setAttribute("x", x);
408
+ el.setAttribute("y", y);
409
+ el.setAttribute("width", width);
410
+ el.setAttribute("height", height);
411
+ el.setAttribute("fill", fill);
412
+ el.setAttribute("stroke", "blue");
413
+ el.setAttribute("data-table", JSON.stringify({
414
+ x,
415
+ y,
416
+ width,
417
+ height,
418
+ shape,
419
+ fill,
420
+ stroke: "blue",
421
+ rotation: 0,
422
+ }));
423
+ }
424
+ else if (shape === "circle") {
425
+ el = document.createElementNS(SVG_NS, "circle");
426
+ el.setAttribute("id", id);
427
+ el.setAttribute("cx", x);
428
+ el.setAttribute("cy", y);
429
+ el.setAttribute("r", Math.min(width, height) / 2);
430
+ el.setAttribute("fill", fill);
431
+ el.setAttribute("stroke", "blue");
432
+ el.setAttribute("data-table", JSON.stringify({
433
+ x,
434
+ y,
435
+ width,
436
+ height,
437
+ shape,
438
+ fill,
439
+ stroke: "blue",
440
+ rotation: 0,
441
+ }));
442
+ }
443
+ return el;
444
+ }
445
+ const handleUnSelectComponent = () => {
446
+ dispatch({ type: "panel/setSelectedComponent", payload: null });
447
+ setSelectedComponent(null);
448
+ setSelectedLines(null);
449
+ dispatch({ type: "panel/setShow", payload: false });
450
+ };
451
+ const startAngleRef = useRef(0);
452
+ const startRotationRef = useRef(0);
453
+ const ghostRotateElement = useRef(0);
454
+ const isResizeRef = useRef(false);
455
+ const isResizeSelectionRef = useRef(false);
456
+ const selectionDataRef = useRef([]);
457
+ // const selectionElementsRef = useRef<any>([]);
458
+ // const selectionElementRawsRef = useRef<any>([]);
459
+ const hadSelectionRef = useRef(false);
460
+ //FLAGE EVERY HANDLE EVENT
461
+ //CREATE
462
+ const isCreateElementRef = useRef(false);
463
+ //RESIZE
464
+ const onResizeSelectionRef = useRef(false);
465
+ //ROTATE
466
+ const isRotatingRef = useRef(false);
467
+ const rotationSelectionRef = useRef(0);
468
+ //DRAG/MOVE
469
+ //SELECT
470
+ const isSelectingRef = useRef(false);
471
+ const activeIdSelectedRef = useRef(null);
472
+ //SELECTION
473
+ const selectionLinesRef = useRef(null);
474
+ const onMakeSelectionRef = useRef(false);
475
+ // const dataElementSelectionGroupRawRef = useRef<Element[]>([]);
476
+ const onMoveSelectionBoxRef = useRef(false);
477
+ const onResizeSelectionBoxRef = useRef(false);
478
+ const dataElementSelectionGroupRef = useRef([]);
479
+ //polygon
480
+ const isPolygonRef = useRef(false);
481
+ const polygonElementRef = useRef([]);
482
+ const isOnMakePolygonRef = useRef(false);
483
+ const startPointPolygonLineRef = useRef({ x: 0, y: 0 });
484
+ // useEffect(() => {
485
+ // if(dataElementSelectionGroupRawRef.current.length > 0){
486
+ // const newElementSelectionGroupWar = []
487
+ // const selectAll = svgRef.current?.querySelectorAll(`[data-table]`)
488
+ // selectAll?.forEach((el: any) => {
489
+ // const data = JSON.parse(el.getAttribute("data-table"))
490
+ // dataElementSelectionGroupRawRef.current.forEach((item: any) => {
491
+ // const dataItem = JSON.parse(item.getAttribute("data-table"))
492
+ // if(dataItem.id === data.id){
493
+ // newElementSelectionGroupWar.push(el)
494
+ // }
495
+ // })
496
+ // })
497
+ // console.log(newElementSelectionGroupWar,dataElementSelectionGroupRawRef.current,selectAll,"masukk")
498
+ // dataElementSelectionGroupRawRef.current = newElementSelectionGroupWar
499
+ // }
500
+ // },[componentsState])
501
+ const handlePointerDown = (e) => {
502
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k;
503
+ const shiftActive = e.shiftKey;
504
+ console.log("shiftActive", shiftActive);
505
+ const svg = svgRef.current;
506
+ if (!e.isPrimary)
507
+ return;
508
+ if (!svg)
509
+ return;
510
+ isDragging.current = false;
511
+ let hasMoved = false;
512
+ releaseGroupRef.current = false;
513
+ const startX = e.clientX;
514
+ const startY = e.clientY;
515
+ const pt = svg.createSVGPoint();
516
+ pt.x = e.clientX;
517
+ pt.y = e.clientY;
518
+ const { x, y } = getSvgCoords(e);
519
+ //CREATE ELEMENT
520
+ const isInitialCreateElemente = toolElement.includes(activeTool) && !isCreateElementRef.current;
521
+ if (isInitialCreateElemente) {
522
+ const tables = createTableGhost({
523
+ x,
524
+ y,
525
+ width: 1,
526
+ height: 1,
527
+ fill: "red",
528
+ shape: activeTool,
529
+ });
530
+ // setGhostCreateElement(tables);
531
+ (_a = svgRef.current) === null || _a === void 0 ? void 0 : _a.appendChild(tables);
532
+ isCreateElementRef.current = true;
533
+ }
534
+ const targetSelection = e.target.closest("g[id='selection-lines']");
535
+ //ROTATE
536
+ const targetRotate = e.target.closest("circle[data-role]");
537
+ if (targetRotate) {
538
+ isRotatingRef.current = true;
539
+ }
540
+ // RESIZE
541
+ const targetGroup = e.target.closest("g[data-id]");
542
+ const targetPointPolygon = e.target.closest("circle[data-point]");
543
+ let idTarget = JSON.parse((targetGroup === null || targetGroup === void 0 ? void 0 : targetGroup.getAttribute("data-id")) || "{}");
544
+ const selectionTarget = (_c = (_b = targetSelection === null || targetSelection === void 0 ? void 0 : targetSelection.dataset) === null || _b === void 0 ? void 0 : _b.selection) === null || _c === void 0 ? void 0 : _c.replace("selection-", "");
545
+ const activeId = selectionTarget !== null && selectionTarget !== void 0 ? selectionTarget : idTarget;
546
+ const { g, inner } = getAttributeElement(svg, activeId);
547
+ if (targetGroup && shiftActive) {
548
+ const findById = [...componentsState, ...extraComponentsState].find((comp) => comp.id == activeId);
549
+ console.log("findById", findById);
550
+ const dataSelection = [...dataElementSelectionGroupRef.current, findById];
551
+ dataElementSelectionGroupRef.current = dataSelection;
552
+ }
553
+ const box = getGlobalBBox(svg, g);
554
+ const { x: initialXG, y: initialYG } = getTranslate(g);
555
+ const targetDragPosition = e.target.closest("circle[data-position]");
556
+ const { clientX, clientY } = e;
557
+ const allGroups = (_d = svgRef.current) === null || _d === void 0 ? void 0 : _d.querySelectorAll("g[data-id]");
558
+ // cari elemen group dengan data-table
559
+ // ambil elemen yang benar-benar ada di bawah pointer
560
+ const data = JSON.parse(((_e = ghostRef.current) === null || _e === void 0 ? void 0 : _e.getAttribute("data-table")) || "{}");
561
+ const cx = data.x + data.width / 2;
562
+ const cy = data.y + data.height / 2;
563
+ const dx = x - cx;
564
+ const dy = y - cy;
565
+ startAngleRef.current = Math.atan2(dy, dx); // simpan angle awal
566
+ startRotationRef.current = data.rotation || 0; // rotation existing
567
+ const hitPoint = document.elementFromPoint(clientX, clientY);
568
+ // CHECK FOR HIT ON SVG FOR SELECTION BOX
569
+ // MAKE AND UNMAKE SELECTION BOX START ------
570
+ const hadSelectionBox = ((_f = dataElementSelectionGroupRef.current) === null || _f === void 0 ? void 0 : _f.length) > 0;
571
+ console.log(dataElementSelectionGroupRef.current, "dataElementSelectionGroupRef.current");
572
+ const downInSelectionBox = hadSelectionBox && (hitPoint === null || hitPoint === void 0 ? void 0 : hitPoint.nodeName) !== "svg";
573
+ const downOutSelectionBox = hadSelectionBox && (hitPoint === null || hitPoint === void 0 ? void 0 : hitPoint.nodeName) === "svg";
574
+ const downBeforeHasSelectionBox = !downInSelectionBox && (hitPoint === null || hitPoint === void 0 ? void 0 : hitPoint.nodeName) === "svg";
575
+ const isRotateSelectionBox = targetRotate && hadSelectionBox;
576
+ let currentRotation = 0;
577
+ if (isRotateSelectionBox) {
578
+ currentRotation = rotationSelectionRef.current;
579
+ }
580
+ const isMightMove = !hadSelectionBox && activeId && !targetPointPolygon;
581
+ //JIKA PUNYA SELECTION TAPI DOWN DI ELEMENT LAIN
582
+ const downInMatchSelectionBox = downInSelectionBox &&
583
+ (hitPoint === null || hitPoint === void 0 ? void 0 : hitPoint.nodeName) !== "svg" &&
584
+ ((_g = dataElementSelectionGroupRef.current) === null || _g === void 0 ? void 0 : _g.some((data) => (data === null || data === void 0 ? void 0 : data.id) === activeId));
585
+ if (downInSelectionBox) {
586
+ hadSelectionRef.current = true;
587
+ }
588
+ if (downOutSelectionBox) {
589
+ hadSelectionRef.current = false;
590
+ onMoveSelectionBoxRef.current = false;
591
+ handleUnSelectComponent();
592
+ }
593
+ if (downBeforeHasSelectionBox) {
594
+ // RELEASE SELECT
595
+ releaseGroupRef.current = true;
596
+ // MAKING SELECTION BOX DOWN
597
+ onMakeSelectionRef.current = true;
598
+ onMoveSelectionBoxRef.current = false;
599
+ handleUnSelectComponent();
600
+ const boxSelection = createTableGhost({
601
+ x,
602
+ y,
603
+ width: 1,
604
+ height: 1,
605
+ fill: "transparent",
606
+ shape: nameShapeSelectionBoxGhost,
607
+ id: idSelectionBoxGhost,
608
+ });
609
+ (_h = svgRef.current) === null || _h === void 0 ? void 0 : _h.appendChild(boxSelection);
610
+ }
611
+ // MAKE AND UNMAKE SELECTION BOX END ------
612
+ // RESIZE SELECTION BOX
613
+ const resizeSide = (targetDragPosition === null || targetDragPosition === void 0 ? void 0 : targetDragPosition.dataset.position) || "{}";
614
+ const downAtResizePosition = resizeSide !== "{}";
615
+ const downOutResizePosition = resizeSide === "{}";
616
+ const downOutResizePositionAndInSelectionBox = downOutResizePosition && hadSelectionBox && !downOutSelectionBox;
617
+ const downAtResizePositionAndHasSelectionBox = downAtResizePosition && hadSelectionBox && !isRotateSelectionBox;
618
+ const isMightResizeElement = !isEmpty(selectedComponent) &&
619
+ downAtResizePosition &&
620
+ downAtResizePosition &&
621
+ !hadSelectionBox;
622
+ if (!downInMatchSelectionBox &&
623
+ !downAtResizePositionAndHasSelectionBox &&
624
+ !targetRotate &&
625
+ !isMightResizeElement &&
626
+ !targetPointPolygon &&
627
+ !isMightMove &&
628
+ !targetSelection) {
629
+ console.log("MAUK UNSELET");
630
+ hadSelectionRef.current = false;
631
+ onMoveSelectionBoxRef.current = false;
632
+ handleUnSelectComponent();
633
+ selectionLinesRef.current = null;
634
+ }
635
+ if (downOutResizePositionAndInSelectionBox) {
636
+ onMoveSelectionBoxRef.current = true;
637
+ }
638
+ if (downAtResizePosition) {
639
+ onResizeSelectionRef.current = true;
640
+ }
641
+ // targetGroup?.removeAttribute("transform");
642
+ const mousePositionInGroup = {
643
+ x: x - (box === null || box === void 0 ? void 0 : box.x),
644
+ y: y - (box === null || box === void 0 ? void 0 : box.y),
645
+ };
646
+ const offset = {
647
+ x: x - initialXG,
648
+ y: y - initialYG,
649
+ };
650
+ // create polygon
651
+ const isInitialPolyGon = activeTool === "polygon" && !isCreateElementRef.current;
652
+ if (isInitialPolyGon && (selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.shape) !== "polygon") {
653
+ const newPolygon = {
654
+ id: `${Date.now()}`,
655
+ shape: "polygon",
656
+ fill: "red",
657
+ points: [{ x, y }],
658
+ rotation: 0,
659
+ };
660
+ polygonElementRef.current = newPolygon;
661
+ // addComponents(newPolygon);
662
+ setSelectedComponent(newPolygon);
663
+ dispatch({
664
+ type: "panel/setSelectedComponent",
665
+ payload: newPolygon,
666
+ });
667
+ isOnMakePolygonRef.current = true;
668
+ }
669
+ const selectionLines = (_j = svgRef.current) === null || _j === void 0 ? void 0 : _j.querySelector("#selection-lines");
670
+ const boxSelection = (_k = svgRef.current) === null || _k === void 0 ? void 0 : _k.querySelector("#rect-box-selection");
671
+ // const allElementInSelection = svgRef.current?.querySelectorAll(
672
+ // "#ghost-element-has-selection"
673
+ // );
674
+ const pointerMoveGhost = (ev) => {
675
+ var _a;
676
+ //SVG POINTER
677
+ const pt = svg.createSVGPoint();
678
+ pt.x = ev.clientX;
679
+ pt.y = ev.clientY;
680
+ const pos = pt.matrixTransform(svg.getScreenCTM().inverse());
681
+ //RESIZE POLYGON
682
+ const onResizePolygon = (pos) => {
683
+ const dx = pos.x - x;
684
+ const dy = pos.y - y;
685
+ const { g, inner, element } = getAttributeElement(svg, activeId);
686
+ const index = Number(targetPointPolygon.getAttribute("id"));
687
+ const points = element === null || element === void 0 ? void 0 : element.getAttribute("points");
688
+ let arrayPoints = pointsStringToArray(points);
689
+ arrayPoints[index] = { x: x + dx, y: y + dy };
690
+ // targetPointPolygon.setAttribute("cx", `${x + dx}`);
691
+ // targetPointPolygon.setAttribute("cy", `${y + dy}`);
692
+ // targetPointPolygon.setAttribute("x", `${x + dx}`);
693
+ // targetPointPolygon.setAttribute("y", `${y + dy}`);
694
+ const newPointsAttr = arrayToSvgPointsAttr(arrayPoints);
695
+ element === null || element === void 0 ? void 0 : element.setAttribute("points", newPointsAttr);
696
+ };
697
+ if (targetPointPolygon && targetGroup) {
698
+ onResizePolygon(pos);
699
+ }
700
+ //ROTATE SELECTION BOX
701
+ const onRotateSelectionBox = (pos) => {
702
+ var _a, _b, _c, _d, _e;
703
+ const selectionLines = (_a = svgRef.current) === null || _a === void 0 ? void 0 : _a.querySelector("#selection-lines");
704
+ const circleHelper = svg.querySelector("#circle-help");
705
+ const lineHelper = svg.querySelector("#line-help");
706
+ const inner = (_b = selectionLines === null || selectionLines === void 0 ? void 0 : selectionLines.children) === null || _b === void 0 ? void 0 : _b[0];
707
+ const rect = svg.querySelector("#selection-lines");
708
+ const x0 = (selectedLines === null || selectedLines === void 0 ? void 0 : selectedLines.x) || 0;
709
+ const y0 = (selectedLines === null || selectedLines === void 0 ? void 0 : selectedLines.y) || 0;
710
+ const w0 = (selectedLines === null || selectedLines === void 0 ? void 0 : selectedLines.width) || 0;
711
+ const h0 = (selectedLines === null || selectedLines === void 0 ? void 0 : selectedLines.height) || 0;
712
+ const bbox = getGlobalBBox(svg, inner);
713
+ const cx = bbox.x + bbox.width / 2;
714
+ const cy = bbox.y + bbox.height / 2;
715
+ //start center
716
+ const dx0 = x - cx;
717
+ const dy0 = y - cy;
718
+ // ✅ ANGLE DARI CENTER
719
+ const dx = pos.x - cx;
720
+ const dy = pos.y - cy;
721
+ //start angle
722
+ const startAngle = Math.atan2(dy0, dx0);
723
+ const currentAngle = Math.atan2(dy, dx);
724
+ const newAngle = currentRotation + ((currentAngle - startAngle) * 180) / Math.PI;
725
+ const angle = (currentRotation * Math.PI) / 180;
726
+ // helper line
727
+ const centerX = w0 / 2;
728
+ const centerY = h0 / 2;
729
+ const cos = Math.cos(angle);
730
+ const sin = Math.sin(angle);
731
+ // GLOBAL → LOCAL
732
+ const localDx = dx * cos + dy * sin;
733
+ const localDy = -dx * sin + dy * cos;
734
+ // helper (LOCAL SPACE)
735
+ const x2 = centerX + localDx;
736
+ const y2 = centerY + localDy;
737
+ lineHelper === null || lineHelper === void 0 ? void 0 : lineHelper.setAttribute("x1", `${centerX}`);
738
+ lineHelper === null || lineHelper === void 0 ? void 0 : lineHelper.setAttribute("y1", `${centerY}`);
739
+ lineHelper === null || lineHelper === void 0 ? void 0 : lineHelper.setAttribute("x2", `${x2}`);
740
+ lineHelper === null || lineHelper === void 0 ? void 0 : lineHelper.setAttribute("y2", `${y2}`);
741
+ circleHelper === null || circleHelper === void 0 ? void 0 : circleHelper.setAttribute("cx", `${x2}`);
742
+ circleHelper === null || circleHelper === void 0 ? void 0 : circleHelper.setAttribute("cy", `${y2}`);
743
+ (_c = selectionLines === null || selectionLines === void 0 ? void 0 : selectionLines.children) === null || _c === void 0 ? void 0 : _c[0].setAttribute("transform", `rotate(${newAngle}, 0, 0)`);
744
+ const { tx, ty } = stabilizeRotation(x0, y0, w0, h0, currentRotation, newAngle);
745
+ selectionLines === null || selectionLines === void 0 ? void 0 : selectionLines.setAttribute("transform", `translate(${tx}, ${ty}) `);
746
+ // ROTATE ALL ELEMENT IN SELECTION
747
+ const allID = (_d = dataElementSelectionGroupRef.current) === null || _d === void 0 ? void 0 : _d.map((el) => el.id);
748
+ const allDataRealSelection = (_e = [
749
+ ...componentsState,
750
+ ...extraComponentsState,
751
+ ]) === null || _e === void 0 ? void 0 : _e.filter((el) => allID.includes(el.id));
752
+ const deltaAngle = newAngle - currentRotation;
753
+ const rad = (deltaAngle * Math.PI) / 180;
754
+ const cosA = Math.cos(rad);
755
+ const sinA = Math.sin(rad);
756
+ const allGroupsAtrribute = getAttributeElements(svg, allID);
757
+ allGroupsAtrribute.forEach(({ g, inner }, i) => {
758
+ var _a;
759
+ const el = (_a = dataElementSelectionGroupRef.current) === null || _a === void 0 ? void 0 : _a[i];
760
+ if (!el)
761
+ return;
762
+ const rotateBefore = el.rotation;
763
+ const ex = el.x;
764
+ const ey = el.y;
765
+ // 🔑 ROTATE AROUND SELECTION CENTER
766
+ const dx = ex - cx;
767
+ const dy = ey - cy;
768
+ // const angle = ((newAngle - currentRotation) * Math.PI) / 180;
769
+ // const cos = Math.cos(angle);
770
+ // const sin = Math.sin(angle);
771
+ const nx = cx + dx * cosA - dy * sinA;
772
+ const ny = cy + dx * sinA + dy * cosA;
773
+ g.setAttribute("data-is-rotating", "1");
774
+ inner.setAttribute("transform", `rotate(${rotateBefore + deltaAngle}, 0, 0)`);
775
+ g.setAttribute("transform", `translate(${nx}, ${ny})`);
776
+ });
777
+ };
778
+ if (isRotateSelectionBox) {
779
+ onRotateSelectionBox(pos);
780
+ }
781
+ //RESIZE SELECTION BOX
782
+ // const onResizeSelectionBox = (ev: MouseEvent) => {
783
+ // // ... (ambil data awal sama seperti kode kamu)
784
+ // console.log("RESIZE SELECTION BOX");
785
+ // const selectionLines = svg.querySelector("#selection-lines");
786
+ // const innerSelection = selectionLines?.children?.[0];
787
+ // const rotationSelection = getRotation((innerSelection as SVGGraphicsElement).transform.baseVal)
788
+ // const angle = (rotationSelection * Math.PI) / 180;
789
+ // const rect = innerSelection?.children?.[0];
790
+ // const dx = pos.x - x;
791
+ // const dy = pos.y - y;
792
+ // // Konversi delta mouse ke Local Space agar sesuai dengan arah kotak miring
793
+ // const deltaLocalX = dx * Math.cos(-angle) - dy * Math.sin(-angle);
794
+ // const deltaLocalY = dx * Math.sin(-angle) + dy * Math.cos(-angle);
795
+ // const gx: number = selectedLines?.x || 0;
796
+ // const gy: number = selectedLines?.y || 0;
797
+ // const gw: number = selectedLines?.width || 0;
798
+ // const gh: number = selectedLines?.height || 0;
799
+ // const oldSel = { x: gx, y: gy, width: gw, height: gh };
800
+ // const rule = RESIZE_RULES[resizeSide];
801
+ // const newWidth = Math.max(0, gw + rule.widthFactor * deltaLocalX);
802
+ // const newHeight = Math.max(0, gh + rule.heightFactor * deltaLocalY);
803
+ // // Ambil perpindahan lokal dari rule
804
+ // const mlX = rule.moveLocalX(deltaLocalX, deltaLocalY);
805
+ // const mlY = rule.moveLocalY(deltaLocalX, deltaLocalY);
806
+ // // Konversi perpindahan lokal ke World Space (mx, my)
807
+ // const mx = mlX * Math.cos(angle) - mlY * Math.sin(angle);
808
+ // const my = mlX * Math.sin(angle) + mlY * Math.cos(angle);
809
+ // // Objek Selection Baru (Penting: x dan y harus sinkron dengan mx, my)
810
+ // const newSelection = {
811
+ // x: gx + mx,
812
+ // y: gy + my,
813
+ // width: newWidth,
814
+ // height: newHeight,
815
+ // };
816
+ // // Update visual selection box
817
+ // applyResizeWithRotation({
818
+ // element: rect, group: selectionLines, resizeSide,
819
+ // widthOriginal: gw, heightOriginal: gh,
820
+ // deltaLocalX, deltaLocalY, xOriginal: gx, yOriginal: gy, angle,
821
+ // });
822
+ // const allID = dataElementSelectionGroupRef.current?.map(
823
+ // (el: any) => el.id
824
+ // );
825
+ // const allDataRealSelection = [
826
+ // ...componentsState,
827
+ // ...extraComponentsState,
828
+ // ]?.filter((el: any) => allID.includes(el.id));
829
+ // const allGroupsAttribute = getAttributeElements(svg, allID);
830
+ // // Update elemen-elemen di dalamnya
831
+ // allGroupsAttribute.forEach(({ g, element, inner }, i) => {
832
+ // const updated = mapElementToResize(
833
+ // allDataRealSelection[i],
834
+ // oldSel, // selection box lama
835
+ // newSelection, // selection box baru
836
+ // angle
837
+ // );
838
+ // applyResizeToSvgElement(element, g, updated, inner);
839
+ // });
840
+ // };
841
+ const onResizeSelectionBox = (ev) => {
842
+ var _a, _b, _c, _d;
843
+ console.log("RESIZE SELECTION BOX");
844
+ const selectionLines = svg.querySelector("#selection-lines");
845
+ const innerSelection = (_a = selectionLines === null || selectionLines === void 0 ? void 0 : selectionLines.children) === null || _a === void 0 ? void 0 : _a[0];
846
+ const rect = (_b = innerSelection === null || innerSelection === void 0 ? void 0 : innerSelection.children) === null || _b === void 0 ? void 0 : _b[0];
847
+ const rotationSelection = getRotation(innerSelection.transform.baseVal);
848
+ isResizeSelectionRef.current = true;
849
+ const gx = (selectedLines === null || selectedLines === void 0 ? void 0 : selectedLines.x) || 0;
850
+ const gy = (selectedLines === null || selectedLines === void 0 ? void 0 : selectedLines.y) || 0;
851
+ const gw = (selectedLines === null || selectedLines === void 0 ? void 0 : selectedLines.width) || 0;
852
+ const gh = (selectedLines === null || selectedLines === void 0 ? void 0 : selectedLines.height) || 0;
853
+ const oldSel = { x: gx, y: gy, width: gw, height: gh };
854
+ //Selection Resize
855
+ //angle
856
+ const angle = (rotationSelection * Math.PI) / 180;
857
+ //delta mouse di screen
858
+ const dx = pos.x - x;
859
+ const dy = pos.y - y;
860
+ //conversi ke local space (rotate balik)
861
+ const consA = Math.cos(-angle);
862
+ const sinA = Math.sin(-angle);
863
+ const deltaLocalX = dx * consA - dy * sinA;
864
+ const deltaLocalY = dx * sinA + dy * consA;
865
+ applyResizeWithRotation({
866
+ element: rect,
867
+ group: selectionLines,
868
+ resizeSide,
869
+ widthOriginal: gw,
870
+ heightOriginal: gh,
871
+ deltaLocalX,
872
+ deltaLocalY,
873
+ xOriginal: gx,
874
+ yOriginal: gy,
875
+ angle,
876
+ });
877
+ //end Resize selection
878
+ const allID = (_c = dataElementSelectionGroupRef.current) === null || _c === void 0 ? void 0 : _c.map((el) => el.id);
879
+ const allDataRealSelection = (_d = [
880
+ ...componentsState,
881
+ ...extraComponentsState,
882
+ ]) === null || _d === void 0 ? void 0 : _d.filter((el) => allID.includes(el.id));
883
+ const allGroupsAttribute = getAttributeElements(svg, allID);
884
+ const isValidResizeSide = Boolean(resizeSide);
885
+ if (!isValidResizeSide)
886
+ return;
887
+ const newSelection = calculateNewSelection(oldSel, pos, { x, y }, resizeSide, angle);
888
+ allGroupsAttribute.forEach(({ g, element, inner }, i) => {
889
+ const updated = mapElementToResize(allDataRealSelection[i], oldSel, newSelection, angle);
890
+ applyResizeToSvgElement(element, g, updated, inner);
891
+ });
892
+ };
893
+ if (downAtResizePositionAndHasSelectionBox)
894
+ onResizeSelectionBox(ev);
895
+ //SELECTION BOX MOVE
896
+ const onMoveSelectionBox = () => {
897
+ var _a, _b;
898
+ const selectionLines = svg.querySelector("#selection-lines");
899
+ const allID = (_a = dataElementSelectionGroupRef.current) === null || _a === void 0 ? void 0 : _a.map((el) => el.id);
900
+ const allDataRealSelection = (_b = [
901
+ ...componentsState,
902
+ ...extraComponentsState,
903
+ ]) === null || _b === void 0 ? void 0 : _b.filter((el) => allID.includes(el.id));
904
+ allDataRealSelection.forEach((item) => {
905
+ const { g } = getAttributeElement(svg, item.id);
906
+ const newX = pos.x - x;
907
+ const newY = pos.y - y;
908
+ g.setAttribute("transform", `translate(${item.x + newX}, ${item.y + newY})`);
909
+ });
910
+ const newX = pos.x - x;
911
+ const newY = pos.y - y;
912
+ selectionLines === null || selectionLines === void 0 ? void 0 : selectionLines.setAttribute("transform", `translate(${(selectedLines === null || selectedLines === void 0 ? void 0 : selectedLines.x) + newX}, ${(selectedLines === null || selectedLines === void 0 ? void 0 : selectedLines.y) + newY})`);
913
+ };
914
+ if (downOutResizePositionAndInSelectionBox && !isRotateSelectionBox)
915
+ onMoveSelectionBox();
916
+ // MAKING SELECTION BOX MOVE
917
+ const onMakeSelectionBox = (ev) => {
918
+ var _a, _b;
919
+ const svgSize = svg.getBoundingClientRect();
920
+ let selectionBoxGhost = svg.querySelector("#selection-box-ghost");
921
+ function rectIntersect(a, b) {
922
+ return !(a.x + a.width < b.x ||
923
+ a.x > b.x + b.width ||
924
+ a.y + a.height < b.y ||
925
+ a.y > b.y + b.height);
926
+ }
927
+ const selX = Math.min(pos.x, x);
928
+ const selY = Math.min(pos.y, y);
929
+ const selW = Math.abs(pos.x - x);
930
+ const selH = Math.abs(pos.y - y);
931
+ selectionBoxGhost === null || selectionBoxGhost === void 0 ? void 0 : selectionBoxGhost.setAttribute("x", String(selX));
932
+ selectionBoxGhost === null || selectionBoxGhost === void 0 ? void 0 : selectionBoxGhost.setAttribute("y", String(selY));
933
+ selectionBoxGhost === null || selectionBoxGhost === void 0 ? void 0 : selectionBoxGhost.setAttribute("width", String(selW));
934
+ selectionBoxGhost === null || selectionBoxGhost === void 0 ? void 0 : selectionBoxGhost.setAttribute("height", String(selH));
935
+ // FIND ALL COMPONENTS INSIDE SELECTION BOX
936
+ const allGroups = (_a = svgRef.current) === null || _a === void 0 ? void 0 : _a.querySelectorAll("g[data-id]");
937
+ const selLeft = selX - svgSize.left;
938
+ const selTop = selY - svgSize.top;
939
+ const selectedIds = [...allGroups]
940
+ .map((g) => JSON === null || JSON === void 0 ? void 0 : JSON.parse((g === null || g === void 0 ? void 0 : g.getAttribute("data-id")) || "{}"))
941
+ .filter((d) => {
942
+ const { element } = getAttributeElement(svg, d);
943
+ const box = getGlobalBBox(svg, element);
944
+ return (box.x < selLeft + selW &&
945
+ box.x + box.width > selLeft &&
946
+ box.y < selTop + selH &&
947
+ box.y + box.height > selTop);
948
+ })
949
+ .map((d) => `${d}`);
950
+ if (selectedIds.length === 0)
951
+ return;
952
+ const selectedComps = (_b = [...componentsState, ...extraComponentsState]
953
+ .filter((c) => selectedIds.map(String).includes(String(c.id)))) === null || _b === void 0 ? void 0 : _b.map((item) => {
954
+ const { element } = getAttributeElement(svg, item.id);
955
+ const { x, y } = getTranslate(g);
956
+ const box = getGlobalBBox(svg, element);
957
+ return Object.assign(Object.assign({}, item), { x: box.x, y: box.y, width: box.width, height: box.height });
958
+ });
959
+ dataElementSelectionGroupRef.current = selectedComps;
960
+ };
961
+ if (downBeforeHasSelectionBox)
962
+ onMakeSelectionBox(ev);
963
+ // CREATE GHOST ELEMENT ---
964
+ const onDrawNewElements = toolElement.includes(activeTool) && isCreateElementRef.current;
965
+ if (onDrawNewElements) {
966
+ const ghost = (_a = svgRef.current) === null || _a === void 0 ? void 0 : _a.querySelector("#ghost-element-create");
967
+ if (!ghost)
968
+ return;
969
+ const data = JSON.parse(ghost.getAttribute("data-table") || "{}");
970
+ const { x: ox, y: oy } = data;
971
+ // ---- HITUNG RECT BARU ----
972
+ const x1 = Math.min(ox, pos.x);
973
+ const y1 = Math.min(oy, pos.y);
974
+ const x2 = Math.max(ox, pos.x);
975
+ const y2 = Math.max(oy, pos.y);
976
+ const newX = x1;
977
+ const newY = y1;
978
+ const newW = x2 - x1;
979
+ const newH = y2 - y1;
980
+ // ---- RECT ----
981
+ if (["square", "table-seat-circle"].includes(activeTool)) {
982
+ ghost.setAttribute("x", String(newX));
983
+ ghost.setAttribute("y", String(newY));
984
+ ghost.setAttribute("width", String(newW));
985
+ ghost.setAttribute("height", String(newH));
986
+ }
987
+ // ---- CIRCLE (ambil dari bounding box) ----
988
+ if (activeTool === "circle") {
989
+ const cx = newX + newW / 2;
990
+ const cy = newY + newH / 2;
991
+ const r = Math.min(newW, newH) / 2;
992
+ ghost.setAttribute("cx", String(cx));
993
+ ghost.setAttribute("cy", String(cy));
994
+ ghost.setAttribute("r", String(r));
995
+ }
996
+ // ---- UPDATE DATA TABLE ----
997
+ ghost.setAttribute("data-table", JSON.stringify(Object.assign(Object.assign({}, data), { x: newX, y: newY, width: newW, height: newH })));
998
+ }
999
+ // RESIZE GHOST SINGLE ELEMENT ---
1000
+ function globalToLocal(px, py, tx, ty, angle) {
1001
+ const rad = (angle * Math.PI) / 180;
1002
+ // buang translate group A
1003
+ let x = px - tx;
1004
+ let y = py - ty;
1005
+ // balik rotasi group B
1006
+ const cos = Math.cos(-rad);
1007
+ const sin = Math.sin(-rad);
1008
+ return {
1009
+ x: x * cos - y * sin,
1010
+ y: x * sin + y * cos,
1011
+ };
1012
+ }
1013
+ const onResize = (ev) => {
1014
+ const activeId = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id;
1015
+ const svg = svgRef.current;
1016
+ const { g, element, inner, seats } = getAttributeElement(svg, activeId);
1017
+ // ORIGINAL size sebelum resize
1018
+ const rotate = getRotation(inner.transform.baseVal);
1019
+ const widthOriginal = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.width;
1020
+ const heightOriginal = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.height;
1021
+ const xOriginal = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.x;
1022
+ const yOriginal = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.y;
1023
+ // Hitung pointer → local SVG coordinate
1024
+ const co = svg.createSVGPoint();
1025
+ co.x = ev.clientX;
1026
+ co.y = ev.clientY;
1027
+ const pos = co.matrixTransform(svg.getScreenCTM().inverse());
1028
+ // 1. angle rotasi elemen
1029
+ const angle = (rotate * Math.PI) / 180;
1030
+ // 2. delta mouse di screen
1031
+ const dx = pos.x - x;
1032
+ const dy = pos.y - y;
1033
+ // 3. konversi ke local space (rotate balik)
1034
+ const cosA = Math.cos(-angle);
1035
+ const sinA = Math.sin(-angle);
1036
+ const deltaLocalX = dx * cosA - dy * sinA;
1037
+ const deltaLocalY = dx * sinA + dy * cosA;
1038
+ applyResizeWithRotation({
1039
+ element,
1040
+ group: g,
1041
+ resizeSide,
1042
+ widthOriginal,
1043
+ heightOriginal,
1044
+ deltaLocalX,
1045
+ deltaLocalY,
1046
+ xOriginal,
1047
+ yOriginal,
1048
+ angle,
1049
+ seats,
1050
+ });
1051
+ isResizeRef.current = true;
1052
+ };
1053
+ // const hasSelectedOneElement =
1054
+ // !isEmpty(selectedComponent) &&
1055
+ // onResizeSelectionRef.current &&
1056
+ // downAtResizePosition;
1057
+ if (!isEmpty(selectedComponent) && isMightResizeElement)
1058
+ onResize(ev);
1059
+ //ROTATE
1060
+ const onRotate = (ev) => {
1061
+ var _a;
1062
+ const svg = svgRef.current;
1063
+ if (!svg || !selectedComponent)
1064
+ return;
1065
+ const lineHelper = svg.querySelector("#line-help");
1066
+ const circleHelper = svg.querySelector("#circle-help");
1067
+ const rect = svg.querySelector("#selection-lines");
1068
+ const { inner, g } = getAttributeElement(svg, selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id);
1069
+ if (!inner || !g)
1070
+ return;
1071
+ // mouse → SVG space
1072
+ const w0 = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.width;
1073
+ const h0 = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.height;
1074
+ // 🔴 GLOBAL BBOX (INI KUNCI)
1075
+ const bbox = getGlobalBBox(svg, inner);
1076
+ if (!bbox)
1077
+ return;
1078
+ const cx = bbox.x + bbox.width / 2;
1079
+ const cy = bbox.y + bbox.height / 2;
1080
+ // vector awal & sekarang (GLOBAL)
1081
+ const dx0 = x - cx;
1082
+ const dy0 = y - cy;
1083
+ const dx = pos.x - cx;
1084
+ const dy = pos.y - cy;
1085
+ const startAngle = Math.atan2(dy0, dx0);
1086
+ const currentAngle = Math.atan2(dy, dx);
1087
+ const newAngle = (selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.rotation) +
1088
+ ((currentAngle - startAngle) * 180) / Math.PI;
1089
+ // helper line
1090
+ const centerX = w0 / 2;
1091
+ const centerY = h0 / 2;
1092
+ const angle = (selectedComponent.rotation * Math.PI) / 180;
1093
+ const cos = Math.cos(angle);
1094
+ const sin = Math.sin(angle);
1095
+ // GLOBAL → LOCAL
1096
+ const localDx = dx * cos + dy * sin;
1097
+ const localDy = -dx * sin + dy * cos;
1098
+ // helper (LOCAL SPACE)
1099
+ const x2 = centerX + localDx;
1100
+ const y2 = centerY + localDy;
1101
+ lineHelper === null || lineHelper === void 0 ? void 0 : lineHelper.setAttribute("x1", `${centerX}`);
1102
+ lineHelper === null || lineHelper === void 0 ? void 0 : lineHelper.setAttribute("y1", `${centerY}`);
1103
+ lineHelper === null || lineHelper === void 0 ? void 0 : lineHelper.setAttribute("x2", `${x2}`);
1104
+ lineHelper === null || lineHelper === void 0 ? void 0 : lineHelper.setAttribute("y2", `${y2}`);
1105
+ circleHelper === null || circleHelper === void 0 ? void 0 : circleHelper.setAttribute("cx", `${x2}`);
1106
+ circleHelper === null || circleHelper === void 0 ? void 0 : circleHelper.setAttribute("cy", `${y2}`);
1107
+ // rotate di local space
1108
+ inner.setAttribute("transform", `rotate(${newAngle}, 0, 0)`);
1109
+ // stabilize translate
1110
+ const { tx, ty } = selectedComponent.shape === "polygon"
1111
+ ? stabilizeTranslateOnRotate({
1112
+ points: selectedComponentProps === null || selectedComponentProps === void 0 ? void 0 : selectedComponentProps.points,
1113
+ oldAngle: selectedComponent.rotation,
1114
+ newAngle,
1115
+ tx: selectedComponent.x,
1116
+ ty: selectedComponent.y,
1117
+ })
1118
+ : stabilizeRotation(selectedComponent.x, selectedComponent.y, selectedComponent.width, selectedComponent.height, selectedComponent.rotation, newAngle);
1119
+ g.setAttribute("transform", `translate(${tx}, ${ty})`);
1120
+ ghostRotateElement.current = newAngle;
1121
+ rect === null || rect === void 0 ? void 0 : rect.setAttribute("transform", `translate(${tx}, ${ty})`);
1122
+ (_a = rect === null || rect === void 0 ? void 0 : rect.children) === null || _a === void 0 ? void 0 : _a[0].setAttribute("transform", `rotate(${newAngle}, 0, 0)`);
1123
+ // helpGroup?.setAttribute("transform", `rotate(${-newAngle}, 0, 0)`);
1124
+ // rect?.setAttribute("visibility", "hidden");
1125
+ };
1126
+ if (targetRotate && !isRotateSelectionBox)
1127
+ onRotate(ev);
1128
+ const onMove = (ev) => {
1129
+ var _a, _b;
1130
+ // const selectionLines = svg.querySelector("#selection-lines");
1131
+ const dx = ev.clientX - startX;
1132
+ const dy = ev.clientY - startY;
1133
+ const distance = Math.sqrt(dx * dx + dy * dy);
1134
+ // onPanning(ev);
1135
+ if (!hasMoved && distance > 0) {
1136
+ // only move ghost if the mouse has moved more than 5 pixels
1137
+ hasMoved = true;
1138
+ }
1139
+ const newX = pos.x - offset.x;
1140
+ const newY = pos.y - offset.y;
1141
+ let activeId = JSON.parse((targetGroup === null || targetGroup === void 0 ? void 0 : targetGroup.getAttribute("data-id")) || "{}");
1142
+ const selection = (_b = (_a = targetSelection === null || targetSelection === void 0 ? void 0 : targetSelection.dataset) === null || _a === void 0 ? void 0 : _a.selection) === null || _b === void 0 ? void 0 : _b.replace("selection-", "");
1143
+ const { g } = getAttributeElement(svg, selection !== null && selection !== void 0 ? selection : activeId);
1144
+ g === null || g === void 0 ? void 0 : g.setAttribute("transform", `translate(${newX}, ${newY})`);
1145
+ // selectionLines?.setAttribute("transform", `translate(${newX}, ${newY})`);
1146
+ };
1147
+ if (isMightMove) {
1148
+ onMove(ev);
1149
+ }
1150
+ };
1151
+ const pointerHandleUp = (e) => {
1152
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
1153
+ //POLYGON RESIZE
1154
+ if (targetSelection) {
1155
+ console.log("KLIK UP SELECTION");
1156
+ //show
1157
+ dispatch({ type: "panel/setShow", payload: true });
1158
+ }
1159
+ if (targetPointPolygon && targetGroup) {
1160
+ const { element } = getAttributeElement(svg, targetGroup.dataset.id);
1161
+ const points = element === null || element === void 0 ? void 0 : element.getAttribute("points");
1162
+ const pointsArray = pointsStringToArray(points);
1163
+ const findById = [...componentsState, ...extraComponentsState].find((c) => c.id === targetGroup.dataset.id);
1164
+ const newDataComponent = Object.assign(Object.assign({}, findById), { points: pointsArray });
1165
+ updateComponentAttribute(newDataComponent);
1166
+ setSelectedComponent(newDataComponent);
1167
+ dispatch({
1168
+ type: "panel/setSelectedComponent",
1169
+ payload: newDataComponent,
1170
+ });
1171
+ selectionLinesRef.current = newDataComponent;
1172
+ }
1173
+ //POLYGON
1174
+ const isInitialPolyGon = activeTool === "polygon" && (selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.shape) === "polygon";
1175
+ if (isInitialPolyGon) {
1176
+ const closing = isClosingPolygon(x, y, selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.points);
1177
+ const newCoord = closing ? selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.points[0] : { x, y };
1178
+ const newPoints = Object.assign(Object.assign({}, selectedComponent), { points: [...selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.points, newCoord] });
1179
+ if (closing && ((_a = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.points) === null || _a === void 0 ? void 0 : _a.length) > 2) {
1180
+ const { g, inner } = getAttributeElement(svg, selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id);
1181
+ const { height, width } = getGlobalBBox(svg, inner);
1182
+ isOnMakePolygonRef.current = false;
1183
+ setSelectedComponent(null);
1184
+ dispatch({
1185
+ type: "panel/setSelectedComponent",
1186
+ payload: null,
1187
+ });
1188
+ polygonElementRef.current = [];
1189
+ addComponents(Object.assign(Object.assign({}, newPoints), { x: 0, y: 0, width, height }));
1190
+ (_b = svg.querySelector("#selection-box-ghost")) === null || _b === void 0 ? void 0 : _b.remove();
1191
+ (_c = svg.querySelector("#polyline-helper")) === null || _c === void 0 ? void 0 : _c.remove();
1192
+ }
1193
+ else {
1194
+ setSelectedComponent(newPoints);
1195
+ dispatch({
1196
+ type: "panel/setSelectedComponent",
1197
+ payload: newPoints,
1198
+ });
1199
+ polygonElementRef.current = newPoints;
1200
+ }
1201
+ }
1202
+ // ROTATE
1203
+ if (targetRotate && !isRotateSelectionBox) {
1204
+ const activeId = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id;
1205
+ const { g, inner, element } = getAttributeElement(svg, activeId);
1206
+ const boxGroup = getGlobalBBox(svg, inner);
1207
+ const findById = [...componentsState, ...extraComponentsState].find((c) => c.id === activeId);
1208
+ const { width, height } = getSvgElementSize(element);
1209
+ const { x, y } = getTranslate(g);
1210
+ const newDataComponent = Object.assign(Object.assign({}, findById), { x,
1211
+ y, rotation: ghostRotateElement.current });
1212
+ const boxSelection = {
1213
+ x,
1214
+ y,
1215
+ width,
1216
+ height,
1217
+ rotation: ghostRotateElement.current,
1218
+ shape: "selection-box",
1219
+ id: `${Date.now()}`,
1220
+ };
1221
+ if (boxGroup)
1222
+ setSelectedLines(boxSelection);
1223
+ updateComponentAttribute(newDataComponent);
1224
+ setSelectedComponent(newDataComponent);
1225
+ dispatch({
1226
+ type: "panel/setSelectedComponent",
1227
+ payload: newDataComponent,
1228
+ });
1229
+ //LOGIC FOR SET SHOW
1230
+ dispatch({ type: "panel/setShow", payload: false });
1231
+ }
1232
+ //CREATE NEW ELEMENT
1233
+ if (toolElement.includes(activeTool) && isCreateElementRef.current) {
1234
+ const ghostElementNew = (_d = svgRef.current) === null || _d === void 0 ? void 0 : _d.querySelector("#ghost-element-create");
1235
+ if (!ghostElementNew)
1236
+ return;
1237
+ const dataOriginal = JSON.parse(ghostElementNew.getAttribute("data-table") || "{}");
1238
+ const newComponent = Object.assign(Object.assign({ id: `${Date.now()}` }, dataOriginal), { seatCount: 4 });
1239
+ const newExtraComponent = [...extraComponentsState, newComponent];
1240
+ //ADD TO REDUX
1241
+ setExtraComponentsState(newExtraComponent);
1242
+ // setComponentsState(newComponentState);
1243
+ addComponents(newComponent);
1244
+ // syncFromLocalToRedux(newComponentState);
1245
+ //REMOVE GHOST ELEMENT
1246
+ (_e = svgRef.current) === null || _e === void 0 ? void 0 : _e.querySelectorAll("#ghost-element-create").forEach((el) => el.remove());
1247
+ isCreateElementRef.current = false;
1248
+ }
1249
+ // CREATE POLYGON
1250
+ const hasSelectedOneElement = !isEmpty(selectedComponent) &&
1251
+ onResizeSelectionRef.current &&
1252
+ downAtResizePosition;
1253
+ const idDataset = targetGroup === null || targetGroup === void 0 ? void 0 : targetGroup.dataset.id;
1254
+ const isSelectElement = !hasMoved &&
1255
+ !releaseGroupRef.current &&
1256
+ !targetRotate &&
1257
+ !hasSelectedOneElement &&
1258
+ idDataset &&
1259
+ activeTool === "select";
1260
+ // &&
1261
+ // !hadSelectionBox;
1262
+ console.log({ isSelectElement }, !hasMoved, !releaseGroupRef.current, !targetRotate, !hasSelectedOneElement, idDataset, activeTool, hadSelectionBox);
1263
+ if (isSelectElement) {
1264
+ //KLIK UP
1265
+ console.log("KLIK UP");
1266
+ const findById = [...componentsState, ...extraComponentsState].find((component) => component.id === idDataset);
1267
+ if (!findById) {
1268
+ return;
1269
+ }
1270
+ const { g, inner, element } = getAttributeElement(svg, idDataset);
1271
+ if (shiftActive) {
1272
+ const allElementSelectionGroup = dataElementSelectionGroupRef.current;
1273
+ // HITUNG BOUNDING BOX
1274
+ const xs = allElementSelectionGroup.map((c) => c.x);
1275
+ const ys = allElementSelectionGroup.map((c) => c.y);
1276
+ const x2 = allElementSelectionGroup.map((c) => c.x + c.width);
1277
+ const y2 = allElementSelectionGroup.map((c) => c.y + c.height);
1278
+ const selectionBox = {
1279
+ x: Math.min(...xs),
1280
+ y: Math.min(...ys),
1281
+ width: Math.max(...x2) - Math.min(...xs),
1282
+ height: Math.max(...y2) - Math.min(...ys),
1283
+ stroke: "red",
1284
+ shape: "selection-box",
1285
+ fill: "transparent",
1286
+ id: `${Date.now()}`,
1287
+ rotation: 0,
1288
+ };
1289
+ selectionLinesRef.current = selectionBox;
1290
+ setSelectedLines(selectionBox);
1291
+ dispatch({
1292
+ type: "panel/setSelectedGroup",
1293
+ payload: allElementSelectionGroup,
1294
+ });
1295
+ setSelectedComponent(null);
1296
+ dispatch({
1297
+ type: "panel/setSelectedComponent",
1298
+ payload: null,
1299
+ });
1300
+ }
1301
+ else {
1302
+ const boxGroup = getGlobalBBox(svg, inner);
1303
+ // const { x, y } = getTranslate(g);
1304
+ const targets = ["table-seat-square", "table-seat-circle"];
1305
+ const isNotRealSize = targets.includes(findById === null || findById === void 0 ? void 0 : findById.shape);
1306
+ let boxSelection = Object.assign(Object.assign({}, findById), {
1307
+ // ...boxGroup,
1308
+ // x,
1309
+ // y,
1310
+ // width,
1311
+ // height: height ,
1312
+ // width,
1313
+ // height,
1314
+ // x:boxGroup.x,
1315
+ // y:boxGroup.y,
1316
+ shape: "selection-box" });
1317
+ if ((findById === null || findById === void 0 ? void 0 : findById.shape) === "polygon") {
1318
+ const boxGroup = getGlobalBBox(svg, g);
1319
+ boxSelection = Object.assign(Object.assign(Object.assign(Object.assign(Object.assign(Object.assign({}, boxSelection), (boxGroup ? { x: boxGroup.x } : {})), (boxGroup ? { y: boxGroup.y } : {})), (boxGroup ? { width: boxGroup.width } : {})), (boxGroup ? { height: boxGroup.height } : {})), { shape: "selection-box" });
1320
+ }
1321
+ if ((boxSelection === null || boxSelection === void 0 ? void 0 : boxSelection.x) &&
1322
+ (boxSelection === null || boxSelection === void 0 ? void 0 : boxSelection.y) &&
1323
+ (boxSelection === null || boxSelection === void 0 ? void 0 : boxSelection.width) &&
1324
+ (boxSelection === null || boxSelection === void 0 ? void 0 : boxSelection.height)) {
1325
+ setSelectedLines(boxSelection);
1326
+ setSelectedComponent(findById);
1327
+ dispatch({
1328
+ type: "panel/setSelectedComponent",
1329
+ payload: findById,
1330
+ });
1331
+ selectionLinesRef.current = boxSelection;
1332
+ }
1333
+ }
1334
+ //LOGIC FOR SET SHOW
1335
+ dispatch({ type: "panel/setShow", payload: true });
1336
+ }
1337
+ //UPDATE DATASET TO STATE IF RESIZE
1338
+ if (isMightResizeElement) {
1339
+ const activeId = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id;
1340
+ const svg = svgRef.current;
1341
+ const { g, element } = getAttributeElement(svg, activeId);
1342
+ const { width, height } = getSvgElementSize(element);
1343
+ const { x, y } = getTranslate(g);
1344
+ const newSelectedComponent = Object.assign(Object.assign({}, selectedComponent), { x: x, y: y, width: width, height: height });
1345
+ const newSelection = Object.assign(Object.assign({}, selectedComponent), { x: x, y: y, width: width, height: height, shape: "selection-box", id: `${Date.now()}` });
1346
+ setSelectedLines(newSelection);
1347
+ // dragGhostRef.current = false;
1348
+ setSelectedComponent(newSelectedComponent);
1349
+ dispatch({
1350
+ type: "panel/setSelectedComponent",
1351
+ payload: newSelectedComponent,
1352
+ });
1353
+ updateComponentAttribute(newSelectedComponent);
1354
+ //LOGIC FOR SET SHOW
1355
+ dispatch({ type: "panel/setShow", payload: false });
1356
+ // isResizeRef.current = false;
1357
+ }
1358
+ //MAKING SELECTION BOX UP
1359
+ const isMakingSelectionBoxUp = downBeforeHasSelectionBox && activeTool === "select";
1360
+ if (isMakingSelectionBoxUp
1361
+ // &&
1362
+ // !hadSelectionRef.current &&
1363
+ // !isResizeSelectionRef.current
1364
+ ) {
1365
+ console.log("MAKING SELECTION BOX UP");
1366
+ const allElementSelectionGroup = dataElementSelectionGroupRef.current;
1367
+ // if(allElementSelectionGroup.length > 1){
1368
+ const findAllPolygonGroup = allElementSelectionGroup.filter((c) => c.shape === "polygon");
1369
+ // HITUNG BOUNDING BOX
1370
+ let xs = allElementSelectionGroup.map((c) => c.x);
1371
+ let ys = allElementSelectionGroup.map((c) => c.y);
1372
+ let x2 = allElementSelectionGroup.map((c) => c.x + c.width);
1373
+ let y2 = allElementSelectionGroup.map((c) => c.y + c.height);
1374
+ findAllPolygonGroup.forEach((polygon) => {
1375
+ const { points } = polygon;
1376
+ for (let i = 0; i < points.numberOfItems; i++) {
1377
+ const point = points.getItem(i);
1378
+ xs.push(point.x);
1379
+ ys.push(point.y);
1380
+ x2.push(point.x);
1381
+ y2.push(point.y);
1382
+ }
1383
+ });
1384
+ const selectionBox = {
1385
+ x: Math.min(...xs),
1386
+ y: Math.min(...ys),
1387
+ width: Math.max(...x2) - Math.min(...xs),
1388
+ height: Math.max(...y2) - Math.min(...ys),
1389
+ stroke: "red",
1390
+ shape: "selection-box",
1391
+ fill: "transparent",
1392
+ id: `${Date.now()}`,
1393
+ rotation: 0,
1394
+ };
1395
+ function hasInvalidNumber(obj) {
1396
+ return Object.values(obj).some((v) => typeof v === "number" && !Number.isFinite(v));
1397
+ }
1398
+ if (!hasInvalidNumber(selectionBox)) {
1399
+ selectionLinesRef.current = selectionBox;
1400
+ setSelectedLines(selectionBox);
1401
+ dispatch({
1402
+ type: "panel/setSelectedGroup",
1403
+ payload: allElementSelectionGroup,
1404
+ });
1405
+ }
1406
+ setSelectedComponent(null);
1407
+ dispatch({
1408
+ type: "panel/setSelectedComponent",
1409
+ payload: null,
1410
+ });
1411
+ rotationSelectionRef.current = 0;
1412
+ (_g = (_f = svgRef.current) === null || _f === void 0 ? void 0 : _f.querySelectorAll("#selection-box-ghost")) === null || _g === void 0 ? void 0 : _g.forEach((el) => el.remove());
1413
+ }
1414
+ //RESIZE SELECTION BOX UP
1415
+ if (downAtResizePositionAndHasSelectionBox || isRotateSelectionBox) {
1416
+ const selectionLines = (_h = svgRef.current) === null || _h === void 0 ? void 0 : _h.querySelector("#selection-lines");
1417
+ isResizeSelectionRef.current = false;
1418
+ const results = (_j = dataElementSelectionGroupRef.current) === null || _j === void 0 ? void 0 : _j.map((item) => {
1419
+ const { g, element, inner } = getAttributeElement(svg, item.id);
1420
+ const { x, y } = getTranslate(g);
1421
+ const { width, height } = getSvgElementSize(element);
1422
+ const rotate = getRotation(inner.transform.baseVal);
1423
+ const getBBox = getGlobalBBox(svg, element);
1424
+ const newDataComponent = Object.assign(Object.assign({}, item), { x,
1425
+ y,
1426
+ width,
1427
+ height, rotation: rotate });
1428
+ const newDataSelection = Object.assign(Object.assign({}, newDataComponent), { x: getBBox.x, y: getBBox.y, width: getBBox.width, height: getBBox.height, shape: "selection-box", id: `${Date.now()}` });
1429
+ return {
1430
+ newDataComponent,
1431
+ newDataSelection,
1432
+ };
1433
+ });
1434
+ const newDataComponent = results.map((r) => r.newDataComponent);
1435
+ // const dataSelection = results.map((r) => r.newDataSelection);
1436
+ const linesSelection = getTranslate(selectionLines);
1437
+ const currentRotation = getRotation((_l = (_k = selectionLines === null || selectionLines === void 0 ? void 0 : selectionLines.children[0]) === null || _k === void 0 ? void 0 : _k.transform) === null || _l === void 0 ? void 0 : _l.baseVal);
1438
+ rotationSelectionRef.current = currentRotation;
1439
+ const sizeSelection = getSvgElementSize((_m = selectionLines === null || selectionLines === void 0 ? void 0 : selectionLines.children[0]) === null || _m === void 0 ? void 0 : _m.children[0]);
1440
+ updateComponentsAttribute(newDataComponent);
1441
+ const newDataSelection = Object.assign(Object.assign(Object.assign({}, linesSelection), sizeSelection), { shape: "selection-box", id: `${Date.now()}`, rotation: currentRotation });
1442
+ dispatch({
1443
+ type: "panel/setSelectedGroup",
1444
+ payload: newDataComponent,
1445
+ });
1446
+ setSelectedComponent(null);
1447
+ dispatch({
1448
+ type: "panel/setSelectedComponent",
1449
+ payload: null,
1450
+ });
1451
+ setSelectedLines(newDataSelection);
1452
+ dataElementSelectionGroupRef.current = newDataComponent;
1453
+ // // remove ghost element
1454
+ // svgRef.current
1455
+ // ?.querySelectorAll("#ghost-element-has-selection")
1456
+ // .forEach((el) => el.remove());
1457
+ hadSelectionRef.current = false;
1458
+ }
1459
+ //MOVE SELECTION BOX UP
1460
+ if (downOutResizePositionAndInSelectionBox &&
1461
+ !shiftActive &&
1462
+ !isRotateSelectionBox) {
1463
+ console.log("MOVE SELECTION BOX UP");
1464
+ const selectionLines = (_o = svgRef.current) === null || _o === void 0 ? void 0 : _o.querySelector("#selection-lines");
1465
+ isResizeSelectionRef.current = false;
1466
+ const results = (_p = dataElementSelectionGroupRef.current) === null || _p === void 0 ? void 0 : _p.map((item) => {
1467
+ const { g, element, inner } = getAttributeElement(svg, item.id);
1468
+ const { x, y } = getTranslate(g);
1469
+ const getBBox = getGlobalBBox(svg, element);
1470
+ const newDataComponent = Object.assign(Object.assign({}, item), { x,
1471
+ y });
1472
+ return {
1473
+ newDataComponent,
1474
+ };
1475
+ });
1476
+ const newDataComponent = results.map((r) => r.newDataComponent);
1477
+ // const dataSelection = results.map((r) => r.newDataSelection);
1478
+ const linesSelection = getTranslate(selectionLines);
1479
+ const currentRotation = getRotation((_r = (_q = selectionLines === null || selectionLines === void 0 ? void 0 : selectionLines.children[0]) === null || _q === void 0 ? void 0 : _q.transform) === null || _r === void 0 ? void 0 : _r.baseVal);
1480
+ rotationSelectionRef.current = currentRotation;
1481
+ const sizeSelection = getSvgElementSize((_s = selectionLines === null || selectionLines === void 0 ? void 0 : selectionLines.children[0]) === null || _s === void 0 ? void 0 : _s.children[0]);
1482
+ updateComponentsAttribute(newDataComponent);
1483
+ const newDataSelection = Object.assign(Object.assign(Object.assign({}, linesSelection), sizeSelection), { shape: "selection-box", id: `${Date.now()}-from-move`, rotation: currentRotation });
1484
+ dispatch({
1485
+ type: "panel/setSelectedGroup",
1486
+ payload: newDataComponent,
1487
+ });
1488
+ setSelectedComponent(null);
1489
+ dispatch({
1490
+ type: "panel/setSelectedComponent",
1491
+ payload: null,
1492
+ });
1493
+ if ((_t = newDataSelection === null || newDataSelection === void 0 ? void 0 : newDataSelection.height) === null || _t === void 0 ? void 0 : _t.id) {
1494
+ newDataSelection.height = newDataSelection.height.id;
1495
+ }
1496
+ if (newDataSelection === null || newDataSelection === void 0 ? void 0 : newDataSelection.x) {
1497
+ setSelectedLines(newDataSelection);
1498
+ }
1499
+ dataElementSelectionGroupRef.current = newDataComponent;
1500
+ hadSelectionRef.current = false;
1501
+ }
1502
+ //DELETE GHOST ELEMENT BISA MEMBU
1503
+ (_u = svgRef.current) === null || _u === void 0 ? void 0 : _u.querySelectorAll("#ghost-element").forEach((el) => el.remove());
1504
+ dataSetGhost.current = null;
1505
+ //UPDATE DATASET TO STATE IF MOVE
1506
+ // if (releaseGroupRef.current) return;
1507
+ if (isMightMove &&
1508
+ !isMakingSelectionBoxUp &&
1509
+ !isSelectElement &&
1510
+ !isMightResizeElement) {
1511
+ console.log("MOVE ONE ELEMENT");
1512
+ // console.log({ idActive, datasetUp });
1513
+ let activeId = JSON.parse((targetGroup === null || targetGroup === void 0 ? void 0 : targetGroup.getAttribute("data-id")) || "{}");
1514
+ const selection = (_w = (_v = targetSelection === null || targetSelection === void 0 ? void 0 : targetSelection.dataset) === null || _v === void 0 ? void 0 : _v.selection) === null || _w === void 0 ? void 0 : _w.replace("selection-", "");
1515
+ const activeIdSelection = selection !== null && selection !== void 0 ? selection : activeId;
1516
+ const findData = [...componentsState, ...extraComponentsState].find((item) => item.id == activeIdSelection);
1517
+ const { g } = getAttributeElement(svg, activeIdSelection);
1518
+ const bbox = getTranslate(g);
1519
+ const updateComponent = Object.assign(Object.assign({}, findData), { x: bbox === null || bbox === void 0 ? void 0 : bbox.x, y: bbox === null || bbox === void 0 ? void 0 : bbox.y });
1520
+ const selectionLines = Object.assign(Object.assign({}, selectedLines), { x: bbox === null || bbox === void 0 ? void 0 : bbox.x, y: bbox === null || bbox === void 0 ? void 0 : bbox.y, shape: "selection-box" });
1521
+ if (findData) {
1522
+ setSelectedLines(selectionLines);
1523
+ setSelectedComponent(updateComponent);
1524
+ dispatch({
1525
+ type: "panel/updateSelectedComponent",
1526
+ payload: updateComponent,
1527
+ });
1528
+ updateComponentAttribute(updateComponent);
1529
+ }
1530
+ }
1531
+ window.removeEventListener("pointermove", pointerMoveGhost);
1532
+ window.removeEventListener("pointerup", pointerHandleUp);
1533
+ };
1534
+ window.addEventListener("pointermove", pointerMoveGhost);
1535
+ window.addEventListener("pointerup", pointerHandleUp);
1536
+ };
1537
+ const handlePointerMove = (e) => {
1538
+ var _a;
1539
+ const svg = svgRef.current;
1540
+ const pt = svg.createSVGPoint();
1541
+ pt.x = e.clientX;
1542
+ pt.y = e.clientY;
1543
+ const pos = pt.matrixTransform(svg.getScreenCTM().inverse());
1544
+ if (isOnMakePolygonRef === null || isOnMakePolygonRef === void 0 ? void 0 : isOnMakePolygonRef.current) {
1545
+ const polylineHelper = svg === null || svg === void 0 ? void 0 : svg.querySelector("#polyline-helper");
1546
+ const startPoint = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.points[((_a = selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.points) === null || _a === void 0 ? void 0 : _a.length) - 1];
1547
+ ;
1548
+ const newPoints = [...selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.points, { x: pos.x, y: pos.y }];
1549
+ const points = newPoints.map((item) => `${item.x},${item.y}`).join(" ");
1550
+ polylineHelper === null || polylineHelper === void 0 ? void 0 : polylineHelper.setAttribute("points", points);
1551
+ //lin helper from start to move
1552
+ }
1553
+ };
1554
+ return (_jsxs(_Fragment, { children: [_jsx(ModalPreview, { children: _jsx(LayerView, { statusKey: "status" }) }), touch && _jsx(Button, { onClick: () => setTouch(false), children: "Stop Touch" }), _jsxs("div", { className: "relative w-full h-screen flex-1 overflow-hidden", ref: containerRef, children: [_jsx("div", { className: "absolute bottom-5 left-1/2 transform -translate-x-1/2 z-10", children: _jsxs("div", { className: "flex gap-2", children: [_jsx(Button, { icon: _jsx(ZoomIn, {}), onClick: handelZoomIn }), _jsx(Button, { icon: _jsx(ZoomOut, {}), onClick: handleZoomOut })] }) }), _jsxs(TransformWrapper, { ref: transformRef, panning: {
1555
+ disabled: [
1556
+ "node",
1557
+ "select",
1558
+ "square",
1559
+ "circle",
1560
+ "table-seat-circle",
1561
+ ].includes(activeTool),
1562
+ }, centerZoomedOut: true,
1563
+ // onTransformed={handleTransformed}
1564
+ minScale: 0.1, maxScale: 1000, initialScale: scale, pinch: { step: 1 }, wheel: { disabled: true }, smooth: true, doubleClick: { step: 1, disabled: activeTool === "select" }, disablePadding: true, centerOnInit: true, children: [_jsx("div", { className: "absolute bottom-[60px] left-1/2 transform -translate-x-1/2 z-10" }), _jsxs(TransformComponent, { wrapperStyle: {
1565
+ width: "100%",
1566
+ height: "100%",
1567
+ }, contentStyle: { width: boardSize.width, height: boardSize.height }, children: [isLoading && (_jsx("div", { className: "absolute z-20 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2", children: "Loading..." })), _jsxs("svg", { id: "workspace", ref: svgRef, width: boardSize.width, height: boardSize.height, viewBox: `${minCoords.x} ${minCoords.y} ${boardSize.width} ${boardSize.height}`,
1568
+ // viewBox={`0 0 ${widthBoard} ${heightBoard}`}
1569
+ // className="h-screen"
1570
+ // onMouseUp={handleMouseUp}
1571
+ // onPointerMove={handleMouseMove}
1572
+ onPointerDown: handlePointerDown, onPointerMove: handlePointerMove,
1573
+ // onMouseEnter={handleMouseEnter}
1574
+ // onMouseUp={handleEnd}
1575
+ // onTouchMove={handleMouseMove}
1576
+ // onTouchEnd={handleMouseUp}
1577
+ // onClick={(e) => {
1578
+ // e.stopPropagation();
1579
+ // handleMouseClick(e);
1580
+ // }}
1581
+ // onMouseLeave={handleMouseLeave}
1582
+ xmlns: "http://www.w3.org/2000/svg", preserveAspectRatio: "xMidYMid meet", style: {
1583
+ // background: backgroundColor,
1584
+ background: "black",
1585
+ display: "block",
1586
+ cursor: activeTool === "ruler" ? "crosshair" : "auto",
1587
+ touchAction: "none",
1588
+ pointerEvents: "all",
1589
+ userSelect: "none",
1590
+ }, children: [_jsx(Layers, { shadowShape: shadowShape, components: [
1591
+ ...extraComponentsState,
1592
+ ...componentsState,
1593
+ polygonElementRef === null || polygonElementRef === void 0 ? void 0 : polygonElementRef.current,
1594
+ ], style: {
1595
+ cursor: getCursorStyle(),
1596
+ },
1597
+ // onClick={handleSelectComponent}
1598
+ // onMouseDown={handleMouseDown}
1599
+ // onMouseUp={handleMouseUp}
1600
+ // onBlur={handleUnSelectComponent}
1601
+ selectedComponent: selectedComponent, selectionLines: selectedLines, activeTool: activeTool }), activeTool === "polygon" && (_jsx("polyline", { id: "polyline-helper", stroke: "white", fill: "transparent", strokeWidth: 2 })), activeTool === "ruler" && (_jsxs(_Fragment, { children: [_jsxs("g", { id: "horizontal-ruler", children: [_jsx("rect", { x: "0", y: "0", width: window.innerWidth, height: "30", fill: "#e0e0e0" }), _jsx("g", { stroke: "#888", "font-size": "10", "text-anchor": "middle", children: Array.from({ length: window.innerWidth / 50 }, (_, i) => (_jsxs("g", { children: [_jsx("line", { x1: i * 50, y1: "0", x2: i * 50, y2: "10" }), _jsx("text", { x: i * 50, y: "15", children: i * 50 })] }, i))) })] }), _jsxs("g", { id: "vertical-ruler", children: [_jsx("rect", { x: "0", y: "0", width: "30", height: window.innerHeight, fill: "#e0e0e0" }), _jsx("g", { stroke: "#888", "font-size": "10", "text-anchor": "middle", children: Array.from({ length: window.innerHeight / 10 }, (_, i) => (_jsxs("g", { children: [_jsx("line", { x1: "0", y1: i * 50, x2: "10", y2: i * 50 }), _jsx("text", { x: "15", y: i * 50, children: i * 50 })] }, i))) })] })] })), grid && (_jsxs("g", { stroke: "#ddd", strokeWidth: 0.5, children: [Array.from({ length: widthBoard / (10 * scale) }, (_, i) => (_jsx("line", { x1: i * 10 * scale, y1: 0, x2: i * 10 * scale, y2: heightBoard }, `v-${i}`))), Array.from({ length: heightBoard / (10 * scale) }, (_, i) => (_jsx("line", { x1: 0, y1: i * 10 * scale, x2: widthBoard, y2: i * 10 * scale }, `h-${i}`)))] }))] })] })] })] })] }));
1602
+ };
1603
+ export default BoardTemplate;
1604
+ function getGlobalBBox(svg, el) {
1605
+ var _a;
1606
+ const bbox = el === null || el === void 0 ? void 0 : el.getBBox();
1607
+ // matrix dari element ke screen pixel
1608
+ const m = el === null || el === void 0 ? void 0 : el.getScreenCTM();
1609
+ if (!m)
1610
+ return null;
1611
+ // matrix dari screen pixel ke svg user coords
1612
+ const screenToSvg = (_a = svg.getScreenCTM()) === null || _a === void 0 ? void 0 : _a.inverse();
1613
+ if (!screenToSvg)
1614
+ return null;
1615
+ function transformPoint(x, y) {
1616
+ const p = new DOMPoint(x, y)
1617
+ .matrixTransform(m)
1618
+ .matrixTransform(screenToSvg);
1619
+ return { x: p.x, y: p.y };
1620
+ }
1621
+ const p1 = transformPoint(bbox.x, bbox.y);
1622
+ const p2 = transformPoint(bbox.x + bbox.width, bbox.y);
1623
+ const p3 = transformPoint(bbox.x + bbox.width, bbox.y + bbox.height);
1624
+ const p4 = transformPoint(bbox.x, bbox.y + bbox.height);
1625
+ const xs = [p1.x, p2.x, p3.x, p4.x];
1626
+ const ys = [p1.y, p2.y, p3.y, p4.y];
1627
+ // const rotation = Math.atan2(p2.y - p1.y, p2.x - p1.x) * (180 / Math.PI);
1628
+ return {
1629
+ x: Math.min(...xs),
1630
+ y: Math.min(...ys),
1631
+ width: Math.max(...xs) - Math.min(...xs),
1632
+ height: Math.max(...ys) - Math.min(...ys),
1633
+ // rotation
1634
+ };
1635
+ }
1636
+ function localToWorld(dx, dy, angle) {
1637
+ const cosR = Math.cos(angle);
1638
+ const sinR = Math.sin(angle);
1639
+ return {
1640
+ x: dx * cosR - dy * sinR,
1641
+ y: dx * sinR + dy * cosR,
1642
+ };
1643
+ }
1644
+ export function getTranslate(el) {
1645
+ if (!el)
1646
+ return {
1647
+ x: null,
1648
+ y: null,
1649
+ };
1650
+ const t = el.getAttribute("transform") || "";
1651
+ // cari pola translate( x , y )
1652
+ const match = t.match(/translate\(\s*([-\d.]+)[ ,]+([-\d.]+)\s*\)/);
1653
+ if (!match)
1654
+ return { x: 0, y: 0 };
1655
+ return {
1656
+ x: parseFloat(match[1]),
1657
+ y: parseFloat(match[2]),
1658
+ };
1659
+ }
1660
+ export function getSize(el) {
1661
+ const bbox = el.getBBox();
1662
+ return {
1663
+ width: bbox.width,
1664
+ height: bbox.height,
1665
+ };
1666
+ }
1667
+ function stabilizeRotation(tx, ty, W, H, oldAngle, newAngle) {
1668
+ const cx = W / 2;
1669
+ const cy = H / 2;
1670
+ const oldA = (oldAngle * Math.PI) / 180;
1671
+ const newA = (newAngle * Math.PI) / 180;
1672
+ // old center global
1673
+ const oldGx = tx + (cx * Math.cos(oldA) - cy * Math.sin(oldA));
1674
+ const oldGy = ty + (cx * Math.sin(oldA) + cy * Math.cos(oldA));
1675
+ // new center global
1676
+ const newGx = tx + (cx * Math.cos(newA) - cy * Math.sin(newA));
1677
+ const newGy = ty + (cx * Math.sin(newA) + cy * Math.cos(newA));
1678
+ // delta shift
1679
+ const dx = oldGx - newGx;
1680
+ const dy = oldGy - newGy;
1681
+ return { tx: tx + dx, ty: ty + dy };
1682
+ }
1683
+ const SNAP_RADIUS = 10;
1684
+ function isClosingPolygon(mouseX, mouseY, points) {
1685
+ const p0 = points[0]; // titik awal
1686
+ const dx = mouseX - p0.x;
1687
+ const dy = mouseY - p0.y;
1688
+ const dist = Math.sqrt(dx * dx + dy * dy);
1689
+ return dist < SNAP_RADIUS;
1690
+ }
1691
+ function calculateNewSelection(oldSel, pos, // Mouse Position saat ini (World)
1692
+ startPos, // Mouse Position saat MouseDown (World)
1693
+ side, angleRad // Sudut rotasi selection saat ini
1694
+ ) {
1695
+ // 1. Hitung selisih mouse di layar (World Delta)
1696
+ const dx = pos.x - startPos.x;
1697
+ const dy = pos.y - startPos.y;
1698
+ // 2. Proyeksikan ke sumbu kotak (Local Delta)
1699
+ // Ini kunci agar narik miring tetap dihitung sebagai lebar/tinggi
1700
+ const localDX = dx * Math.cos(-angleRad) - dy * Math.sin(-angleRad);
1701
+ const localDY = dx * Math.sin(-angleRad) + dy * Math.cos(-angleRad);
1702
+ const rule = RESIZE_RULES[side];
1703
+ // 3. Hitung dimensi baru (selalu positif)
1704
+ const width = Math.max(1, oldSel.width + (rule.widthFactor * localDX));
1705
+ const height = Math.max(1, oldSel.height + (rule.heightFactor * localDY));
1706
+ // 4. Hitung perpindahan Anchor (Local Space)
1707
+ // Berapa banyak (x,y) harus bergeser agar sisi seberang diam
1708
+ const mlX = rule.moveLocalX(localDX, localDY);
1709
+ const mlY = rule.moveLocalY(localDX, localDY);
1710
+ // 5. Putar balik perpindahan ke World Space
1711
+ const worldMX = mlX * Math.cos(angleRad) - mlY * Math.sin(angleRad);
1712
+ const worldMY = mlX * Math.sin(angleRad) + mlY * Math.cos(angleRad);
1713
+ return {
1714
+ x: oldSel.x + worldMX,
1715
+ y: oldSel.y + worldMY,
1716
+ width,
1717
+ height
1718
+ };
1719
+ }
1720
+ // function mapElementToResize(el: any, oldSel: any, newSel: any): any {
1721
+ // const rx = (el.x - oldSel.x) / oldSel.width;
1722
+ // const ry = (el.y - oldSel.y) / oldSel.height;
1723
+ // const rw = el.width / oldSel.width;
1724
+ // const rh = el.height / oldSel.height;
1725
+ // return {
1726
+ // ...el,
1727
+ // x: newSel.x + rx * newSel.width,
1728
+ // y: newSel.y + ry * newSel.height,
1729
+ // width: rw * newSel.width,
1730
+ // height: rh * newSel.height,
1731
+ // rotation: el.rotation,
1732
+ // };
1733
+ // }
1734
+ // function mapElementToResize(
1735
+ // el: any,
1736
+ // oldSel: any, // x, y, width, height original
1737
+ // newSel: any, // x, y, width, height hasil resize
1738
+ // angleRad: number
1739
+ // ) {
1740
+ // // 1. Hitung jarak elemen dari pojok kiri atas (Top-Left) original di World Space
1741
+ // const dx = el.x - oldSel.x;
1742
+ // const dy = el.y - oldSel.y;
1743
+ // // 2. UN-ROTATE: Ubah jarak World ke jarak Local (seolah-olah kotak tidak miring)
1744
+ // const cosA = Math.cos(-angleRad);
1745
+ // const sinA = Math.sin(-angleRad);
1746
+ // const localX = dx * cosA - dy * sinA;
1747
+ // const localY = dx * sinA + dy * cosA;
1748
+ // // 3. SCALE: Hitung rasio perubahan ukuran
1749
+ // const scaleX = newSel.width / oldSel.width || 1;
1750
+ // const scaleY = newSel.height / oldSel.height || 1;
1751
+ // // 4. Hitung posisi baru di Local Space
1752
+ // const newLocalX = localX * scaleX;
1753
+ // const newLocalY = localY * scaleY;
1754
+ // // 5. RE-ROTATE: Kembalikan posisi lokal ke World Space sesuai rotasi saat ini
1755
+ // const cosA2 = Math.cos(angleRad);
1756
+ // const sinA2 = Math.sin(angleRad);
1757
+ // const newDx = newLocalX * cosA2 - newLocalY * sinA2;
1758
+ // const newDy = newLocalX * sinA2 + newLocalY * cosA2;
1759
+ // return {
1760
+ // ...el,
1761
+ // x: newSel.x + newDx, // Posisi baru = Origin Baru + Jarak Baru yang sudah diputar
1762
+ // y: newSel.y + newDy,
1763
+ // width: el.width * scaleX,
1764
+ // height: el.height * scaleY,
1765
+ // };
1766
+ // }
1767
+ // function mapElementToResize(
1768
+ // el: any,
1769
+ // oldSel: any,
1770
+ // newSel: any,
1771
+ // angleRad: number // Sudut dari Selection Box
1772
+ // ) {
1773
+ // // 1. Hitung Rasio Skala Selection Box
1774
+ // const scaleX_Sel = newSel.width / oldSel.width || 1;
1775
+ // const scaleY_Sel = newSel.height / oldSel.height || 1;
1776
+ // // 2. Hitung Titik Pusat (Penting untuk rotasi)
1777
+ // const cxOld = oldSel.x + oldSel.width / 2;
1778
+ // const cyOld = oldSel.y + oldSel.height / 2;
1779
+ // const cxNew = newSel.x + newSel.width / 2;
1780
+ // const cyNew = newSel.y + newSel.height / 2;
1781
+ // // 3. POSISI: Konversi Global -> Local -> Scale -> Global
1782
+ // // Menggunakan fungsi toLocal dan toGlobal bawaan Anda
1783
+ // const local = toLocal(el.x, el.y, cxOld, cyOld, angleRad);
1784
+ // const newLocalX = local.x * scaleX_Sel;
1785
+ // const newLocalY = local.y * scaleY_Sel;
1786
+ // const global = toGlobal(newLocalX, newLocalY, cxNew, cyNew, angleRad);
1787
+ // // 4. UKURAN: Proyeksi Skala berdasarkan perbedaan rotasi
1788
+ // // Kita hitung seberapa besar pengaruh scaleX/Y seleksi terhadap lebar/tinggi elemen
1789
+ // const elAngleRad = (el.rotation * Math.PI) / 180;
1790
+ // const relativeAngle = elAngleRad - angleRad;
1791
+ // const cosR = Math.abs(Math.cos(relativeAngle));
1792
+ // const sinR = Math.abs(Math.sin(relativeAngle));
1793
+ // // Menentukan skala elemen berdasarkan orientasinya terhadap selection box
1794
+ // // Jika beda 90 derajat, scaleX elemen akan memakai scaleY seleksi
1795
+ // const finalScaleX = (scaleX_Sel * cosR) + (scaleY_Sel * sinR);
1796
+ // const finalScaleY = (scaleX_Sel * sinR) + (scaleY_Sel * cosR);
1797
+ // const newWidth = el.width * finalScaleX;
1798
+ // const newHeight = el.height * finalScaleY;
1799
+ // return {
1800
+ // ...el,
1801
+ // x: global.x,
1802
+ // y: global.y,
1803
+ // width: newWidth,
1804
+ // height: newHeight,
1805
+ // // Update center point properti jika diperlukan
1806
+ // cx: global.x + newWidth / 2,
1807
+ // cy: global.y + newHeight / 2,
1808
+ // rotation: el.rotation,
1809
+ // };
1810
+ // }
1811
+ function mapElementToResize(el, oldSel, newSel, angleRad) {
1812
+ // 1. Skala selection
1813
+ const scaleX = newSel.width / oldSel.width || 1;
1814
+ const scaleY = newSel.height / oldSel.height || 1;
1815
+ // 2. Center selection
1816
+ const cxOld = oldSel.x + oldSel.width / 2;
1817
+ const cyOld = oldSel.y + oldSel.height / 2;
1818
+ const cxNew = newSel.x + newSel.width / 2;
1819
+ const cyNew = newSel.y + newSel.height / 2;
1820
+ // 3. POSISI (rotate → scale → rotate back)
1821
+ const local = toLocal(el.x, el.y, cxOld, cyOld, angleRad);
1822
+ const scaledLocal = {
1823
+ x: local.x * scaleX,
1824
+ y: local.y * scaleY,
1825
+ };
1826
+ const global = toGlobal(scaledLocal.x, scaledLocal.y, cxNew, cyNew, angleRad);
1827
+ // 4. UKURAN (TANPA trig / rotasi)
1828
+ const newWidth = el.width * scaleX;
1829
+ const newHeight = el.height * scaleY;
1830
+ return Object.assign(Object.assign({}, el), { x: global.x, y: global.y, width: newWidth, height: newHeight, cx: global.x + newWidth / 2, cy: global.y + newHeight / 2, rotation: el.rotation });
1831
+ }
1832
+ export function getPolygonCenter(points) {
1833
+ if (!points.length) {
1834
+ return { cx: 0, cy: 0 };
1835
+ }
1836
+ let minX = Infinity;
1837
+ let minY = Infinity;
1838
+ let maxX = -Infinity;
1839
+ let maxY = -Infinity;
1840
+ for (const p of points) {
1841
+ if (p.x < minX)
1842
+ minX = p.x;
1843
+ if (p.y < minY)
1844
+ minY = p.y;
1845
+ if (p.x > maxX)
1846
+ maxX = p.x;
1847
+ if (p.y > maxY)
1848
+ maxY = p.y;
1849
+ }
1850
+ return {
1851
+ cx: (minX + maxX) / 2,
1852
+ cy: (minY + maxY) / 2,
1853
+ };
1854
+ }
1855
+ function stabilizeTranslateOnRotate({ points, oldAngle, newAngle, tx, ty, }) {
1856
+ // center polygon (bbox center)
1857
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
1858
+ for (const p of points) {
1859
+ minX = Math.min(minX, p.x);
1860
+ minY = Math.min(minY, p.y);
1861
+ maxX = Math.max(maxX, p.x);
1862
+ maxY = Math.max(maxY, p.y);
1863
+ }
1864
+ const cx = (minX + maxX) / 2;
1865
+ const cy = (minY + maxY) / 2;
1866
+ const r0 = (oldAngle * Math.PI) / 180;
1867
+ const r1 = (newAngle * Math.PI) / 180;
1868
+ const x0 = cx * Math.cos(r0) - cy * Math.sin(r0);
1869
+ const y0 = cx * Math.sin(r0) + cy * Math.cos(r0);
1870
+ const x1 = cx * Math.cos(r1) - cy * Math.sin(r1);
1871
+ const y1 = cx * Math.sin(r1) + cy * Math.cos(r1);
1872
+ return {
1873
+ tx: tx + (x0 - x1),
1874
+ ty: ty + (y0 - y1),
1875
+ };
1876
+ }
1877
+ function pointsStringToArray(pointsStr) {
1878
+ return pointsStr
1879
+ .trim()
1880
+ .split(/\s+/) // pisah antar titik
1881
+ .map((pair) => {
1882
+ const [x, y] = pair.split(",").map(Number);
1883
+ return { x, y };
1884
+ })
1885
+ .filter((p) => !Number.isNaN(p.x) && !Number.isNaN(p.y));
1886
+ }
1887
+ function arrayToSvgPointsAttr(points) {
1888
+ return points.map((p) => `${p.x},${p.y}`).join(" ");
1889
+ }
1890
+ function getSvgElementSize(el) {
1891
+ if (el instanceof SVGRectElement) {
1892
+ return {
1893
+ width: el.width.baseVal.value,
1894
+ height: el.height.baseVal.value,
1895
+ };
1896
+ }
1897
+ if (el instanceof SVGCircleElement) {
1898
+ const r = el.r.baseVal.value;
1899
+ return {
1900
+ width: r * 2,
1901
+ height: r * 2,
1902
+ };
1903
+ }
1904
+ if (el instanceof SVGEllipseElement) {
1905
+ return {
1906
+ width: el.rx.baseVal.value * 2,
1907
+ height: el.ry.baseVal.value * 2,
1908
+ };
1909
+ }
1910
+ return {
1911
+ width: 0,
1912
+ height: 0,
1913
+ };
1914
+ }
1915
+ export function applyResizeToSvgElement(element, group, resize, inner) {
1916
+ const tagName = element.tagName.toLowerCase();
1917
+ console.log({ resize });
1918
+ switch (tagName) {
1919
+ case "rect":
1920
+ case "image": {
1921
+ element.setAttribute("width", String(resize.width));
1922
+ element.setAttribute("height", String(resize.height));
1923
+ // group.setAttribute("transform", `translate(${resize.x}, ${resize.y})`);
1924
+ // 🔑 translate by CENTER, not top-left
1925
+ group.setAttribute("transform", `translate(${resize.cx - resize.width / 2}, ${resize.cy - resize.height / 2})`);
1926
+ break;
1927
+ }
1928
+ case "circle": {
1929
+ const r = Math.min(resize.width, resize.height) / 2;
1930
+ element.setAttribute("r", String(r));
1931
+ element.setAttribute("cx", String(resize.width / 2));
1932
+ element.setAttribute("cy", String(resize.height / 2));
1933
+ group.setAttribute("transform", `translate(${resize.x}, ${resize.y})`);
1934
+ // inner
1935
+ break;
1936
+ }
1937
+ case "ellipse": {
1938
+ element.setAttribute("rx", String(resize.width / 2));
1939
+ element.setAttribute("ry", String(resize.height / 2));
1940
+ break;
1941
+ }
1942
+ default: {
1943
+ console.warn(`[applyResizeToSvgElement] Unsupported SVG tag: ${tagName}`);
1944
+ }
1945
+ }
1946
+ // translate selalu di group
1947
+ }
1948
+ function applySelectionToSvg(selectionLines, boxSelection, sel) {
1949
+ selectionLines === null || selectionLines === void 0 ? void 0 : selectionLines.setAttribute("transform", `translate(${sel.x}, ${sel.y})`);
1950
+ boxSelection === null || boxSelection === void 0 ? void 0 : boxSelection.setAttribute("width", String(sel.width));
1951
+ boxSelection === null || boxSelection === void 0 ? void 0 : boxSelection.setAttribute("height", String(sel.height));
1952
+ }
1953
+ function applySizeToSvgElement(element, width, height) {
1954
+ const tag = element.tagName.toLowerCase();
1955
+ if (tag === "rect" || tag === "image") {
1956
+ console.log({ width, height });
1957
+ element.setAttribute("width", String(width));
1958
+ element.setAttribute("height", String(height));
1959
+ return;
1960
+ }
1961
+ if (tag === "circle") {
1962
+ const r = Math.max(0, Math.min(width, height) / 2);
1963
+ element.setAttribute("r", String(r));
1964
+ element.setAttribute("cx", String(width / 2));
1965
+ element.setAttribute("cy", String(height / 2));
1966
+ return;
1967
+ }
1968
+ if (tag === "ellipse") {
1969
+ element.setAttribute("rx", String(width / 2));
1970
+ element.setAttribute("ry", String(height / 2));
1971
+ return;
1972
+ }
1973
+ }
1974
+ const RESIZE_RULES = {
1975
+ "bottom-right": {
1976
+ widthFactor: 1,
1977
+ heightFactor: 1,
1978
+ moveLocalX: () => 0,
1979
+ moveLocalY: () => 0,
1980
+ },
1981
+ "bottom-left": {
1982
+ widthFactor: -1,
1983
+ heightFactor: 1,
1984
+ moveLocalX: (dx) => dx,
1985
+ moveLocalY: () => 0,
1986
+ },
1987
+ "top-left": {
1988
+ widthFactor: -1,
1989
+ heightFactor: -1,
1990
+ moveLocalX: (dx) => dx,
1991
+ moveLocalY: (_, dy) => dy,
1992
+ },
1993
+ "top-right": {
1994
+ widthFactor: 1,
1995
+ heightFactor: -1,
1996
+ moveLocalX: () => 0,
1997
+ moveLocalY: (_, dy) => dy,
1998
+ },
1999
+ left: {
2000
+ widthFactor: -1,
2001
+ heightFactor: 0,
2002
+ moveLocalX: (dx) => dx,
2003
+ moveLocalY: () => 0,
2004
+ },
2005
+ right: {
2006
+ widthFactor: 1,
2007
+ heightFactor: 0,
2008
+ moveLocalX: () => 0,
2009
+ moveLocalY: () => 0,
2010
+ },
2011
+ top: {
2012
+ widthFactor: 0,
2013
+ heightFactor: -1,
2014
+ moveLocalX: () => 0,
2015
+ moveLocalY: (_, dy) => dy,
2016
+ },
2017
+ bottom: {
2018
+ widthFactor: 0,
2019
+ heightFactor: 1,
2020
+ moveLocalX: () => 0,
2021
+ moveLocalY: () => 0,
2022
+ },
2023
+ };
2024
+ export function applyResizeWithRotation({ element, group, resizeSide, widthOriginal, heightOriginal, deltaLocalX, deltaLocalY, xOriginal, yOriginal, angle, seats, }) {
2025
+ const rule = RESIZE_RULES[resizeSide];
2026
+ let newWidth = widthOriginal;
2027
+ let newHeight = heightOriginal;
2028
+ if (rule.widthFactor !== 0) {
2029
+ newWidth = widthOriginal + rule.widthFactor * deltaLocalX;
2030
+ }
2031
+ if (rule.heightFactor !== 0) {
2032
+ newHeight = heightOriginal + rule.heightFactor * deltaLocalY;
2033
+ }
2034
+ // clamp
2035
+ newWidth = Math.max(0, newWidth);
2036
+ newHeight = Math.max(0, newHeight);
2037
+ // 🔹 apply size (rect / circle / ellipse)
2038
+ applySizeToSvgElement(element, newWidth, newHeight);
2039
+ // 🔹 move anchor (kalau perlu)
2040
+ const moveLocalX = rule.moveLocalX(deltaLocalX, deltaLocalY);
2041
+ const moveLocalY = rule.moveLocalY(deltaLocalX, deltaLocalY);
2042
+ if (moveLocalX !== 0 || moveLocalY !== 0) {
2043
+ const { x: mx, y: my } = localToWorld(moveLocalX, moveLocalY, angle);
2044
+ group.setAttribute("transform", `translate(${xOriginal + mx}, ${yOriginal + my})`);
2045
+ }
2046
+ }
2047
+ function getRotation(transformList) {
2048
+ for (let i = 0; i < (transformList === null || transformList === void 0 ? void 0 : transformList.numberOfItems); i++) {
2049
+ const t = transformList.getItem(i);
2050
+ if (t.type === SVGTransform.SVG_TRANSFORM_ROTATE) {
2051
+ return t.angle;
2052
+ }
2053
+ }
2054
+ return 0;
2055
+ }
2056
+ function toLocal(x, y, cx, cy, angle) {
2057
+ const dx = x - cx;
2058
+ const dy = y - cy;
2059
+ const cos = Math.cos(-angle);
2060
+ const sin = Math.sin(-angle);
2061
+ return {
2062
+ x: dx * cos - dy * sin,
2063
+ y: dx * sin + dy * cos,
2064
+ };
2065
+ }
2066
+ function toGlobal(lx, ly, cx, cy, angle) {
2067
+ const cos = Math.cos(angle);
2068
+ const sin = Math.sin(angle);
2069
+ return {
2070
+ x: cx + lx * cos - ly * sin,
2071
+ y: cy + lx * sin + ly * cos,
2072
+ };
2073
+ }