seat-editor 3.3.13 → 3.3.15

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 (136) hide show
  1. package/dist/app/constant.d.ts +1 -0
  2. package/dist/app/constant.js +1 -0
  3. package/dist/app/layout.d.ts +6 -0
  4. package/dist/app/layout.jsx +27 -0
  5. package/dist/app/new-board/page.jsx +55 -0
  6. package/dist/app/old-board/page.d.ts +3 -0
  7. package/dist/app/old-board/page.jsx +510 -0
  8. package/dist/app/only-view/chair.d.ts +1 -0
  9. package/dist/app/only-view/chair.js +12 -0
  10. package/dist/app/only-view/constant.d.ts +60 -0
  11. package/dist/app/only-view/constant.js +1336 -0
  12. package/dist/app/only-view/page.jsx +248 -0
  13. package/dist/app/only-view/user.d.ts +1 -0
  14. package/dist/app/only-view/user.js +12 -0
  15. package/dist/app/page.d.ts +2 -0
  16. package/dist/app/page.jsx +13 -0
  17. package/dist/app/test/page.d.ts +2 -0
  18. package/dist/app/test/page.jsx +45 -0
  19. package/dist/app/v2/page.d.ts +2 -0
  20. package/dist/app/v2/page.jsx +13 -0
  21. package/dist/components/button-tools/index.d.ts +11 -0
  22. package/dist/components/button-tools/index.jsx +17 -0
  23. package/dist/components/form-tools/label.d.ts +2 -0
  24. package/dist/components/form-tools/label.jsx +63 -0
  25. package/dist/components/form-tools/shape.d.ts +8 -0
  26. package/dist/components/form-tools/shape.jsx +113 -0
  27. package/dist/components/input/number-indicator.d.ts +7 -0
  28. package/dist/components/input/number-indicator.jsx +36 -0
  29. package/dist/components/joystick/index.d.ts +12 -0
  30. package/dist/components/joystick/index.jsx +49 -0
  31. package/dist/components/layer/index.d.ts +19 -0
  32. package/dist/components/layer/index.jsx +383 -0
  33. package/dist/components/layer-v2/index.d.ts +19 -0
  34. package/dist/components/layer-v2/index.jsx +370 -0
  35. package/dist/components/layer-v3/index.d.ts +13 -0
  36. package/dist/components/layer-v3/index.jsx +631 -0
  37. package/dist/components/layer-v3/utils.d.ts +19 -0
  38. package/dist/components/layer-v3/utils.js +72 -0
  39. package/dist/components/layer-v4/constant.d.ts +60 -0
  40. package/dist/components/layer-v4/constant.js +93 -0
  41. package/dist/components/layer-v4/index.d.ts +24 -0
  42. package/dist/components/layer-v4/index.jsx +1046 -0
  43. package/dist/components/lib/index.d.ts +8 -0
  44. package/dist/components/lib/index.jsx +33 -0
  45. package/dist/components/modal-preview/index.d.ts +4 -0
  46. package/dist/components/modal-preview/index.jsx +11 -0
  47. package/dist/dto/event-handler.d.ts +1 -0
  48. package/dist/dto/event-handler.js +1 -0
  49. package/dist/dto/table.d.ts +80 -0
  50. package/dist/dto/table.js +1 -0
  51. package/dist/features/board/board-slice.d.ts +14 -0
  52. package/dist/features/board/board-slice.js +52 -0
  53. package/dist/features/board/index.d.ts +6 -0
  54. package/dist/features/board/index.jsx +725 -0
  55. package/dist/features/board-v2/board-slice.d.ts +14 -0
  56. package/dist/features/board-v2/board-slice.js +52 -0
  57. package/dist/features/board-v2/index.d.ts +8 -0
  58. package/dist/features/board-v2/index.jsx +869 -0
  59. package/dist/features/board-v3/board-slice.d.ts +19 -0
  60. package/dist/features/board-v3/board-slice.js +274 -0
  61. package/dist/features/board-v3/constant.d.ts +5 -0
  62. package/dist/features/board-v3/constant.js +5 -0
  63. package/dist/features/board-v3/history-slice.d.ts +27 -0
  64. package/dist/features/board-v3/history-slice.js +27 -0
  65. package/dist/features/board-v3/icons.d.ts +4 -0
  66. package/dist/features/board-v3/icons.jsx +100 -0
  67. package/dist/features/board-v3/index.d.ts +16 -0
  68. package/dist/features/board-v3/index.jsx +1678 -0
  69. package/dist/features/board-v3/polygon.d.ts +28 -0
  70. package/dist/features/board-v3/polygon.js +109 -0
  71. package/dist/features/board-v3/rect.d.ts +9 -0
  72. package/dist/features/board-v3/rect.js +152 -0
  73. package/dist/features/board-v3/resize-element.d.ts +12 -0
  74. package/dist/features/board-v3/resize-element.js +43 -0
  75. package/dist/features/board-v3/utils.d.ts +180 -0
  76. package/dist/features/board-v3/utils.js +1235 -0
  77. package/dist/features/navbar/index.d.ts +2 -0
  78. package/dist/features/navbar/index.jsx +5 -0
  79. package/dist/features/panel/index.d.ts +6 -0
  80. package/dist/features/panel/index.jsx +251 -0
  81. package/dist/features/panel/panel-slice.d.ts +23 -0
  82. package/dist/features/panel/panel-slice.js +46 -0
  83. package/dist/features/panel/select-tool.d.ts +6 -0
  84. package/dist/features/panel/select-tool.jsx +70 -0
  85. package/dist/features/panel/selected-group.d.ts +2 -0
  86. package/dist/features/panel/selected-group.jsx +93 -0
  87. package/dist/features/panel/square-circle-tool.d.ts +2 -0
  88. package/dist/features/panel/square-circle-tool.jsx +10 -0
  89. package/dist/features/panel/table-seat-circle.d.ts +2 -0
  90. package/dist/features/panel/table-seat-circle.jsx +36 -0
  91. package/dist/features/panel/table-seat-square.d.ts +2 -0
  92. package/dist/features/panel/table-seat-square.jsx +51 -0
  93. package/dist/features/panel/text-tool.d.ts +2 -0
  94. package/dist/features/panel/text-tool.jsx +57 -0
  95. package/dist/features/panel/upload-tool.d.ts +10 -0
  96. package/dist/features/panel/upload-tool.jsx +176 -0
  97. package/dist/features/panel/utils.d.ts +5 -0
  98. package/dist/features/panel/utils.js +47 -0
  99. package/dist/features/side-tool/index.d.ts +8 -0
  100. package/dist/features/side-tool/index.jsx +390 -0
  101. package/dist/features/side-tool/side-tool-slice.d.ts +16 -0
  102. package/dist/features/side-tool/side-tool-slice.js +28 -0
  103. package/dist/features/theme/theme-slice.d.ts +12 -0
  104. package/dist/features/theme/theme-slice.js +15 -0
  105. package/dist/features/view-only/index.d.ts +19 -0
  106. package/dist/features/view-only/index.jsx +205 -0
  107. package/dist/features/view-only-2/index.d.ts +19 -0
  108. package/dist/features/view-only-2/index.jsx +190 -0
  109. package/dist/features/view-only-3/index.d.ts +89 -0
  110. package/dist/features/view-only-3/index.jsx +590 -0
  111. package/dist/features/view-only-3/utils.d.ts +1 -0
  112. package/dist/features/view-only-3/utils.js +3 -0
  113. package/dist/hooks/use-redux.d.ts +4 -0
  114. package/dist/hooks/use-redux.js +3 -0
  115. package/dist/index.js +10 -0
  116. package/dist/libs/middleware.d.ts +2 -0
  117. package/dist/libs/middleware.js +5 -0
  118. package/dist/libs/rootReducer.d.ts +12 -0
  119. package/dist/libs/rootReducer.js +14 -0
  120. package/dist/libs/store.d.ts +18 -0
  121. package/dist/libs/store.js +19 -0
  122. package/dist/provider/antd-provider.d.ts +4 -0
  123. package/dist/provider/antd-provider.jsx +46 -0
  124. package/dist/provider/redux-provider.d.ts +3 -0
  125. package/dist/provider/redux-provider.jsx +6 -0
  126. package/dist/provider/store-provider.d.ts +4 -0
  127. package/dist/provider/store-provider.jsx +10 -0
  128. package/dist/utils/constant.d.ts +3 -0
  129. package/dist/utils/constant.js +13 -0
  130. package/dist/utils/format.d.ts +2 -0
  131. package/dist/utils/format.js +29 -0
  132. package/dist/utils/injectCss.d.ts +1 -0
  133. package/dist/utils/injectCss.js +13 -0
  134. package/dist/utils/regex.d.ts +3 -0
  135. package/dist/utils/regex.js +3 -0
  136. package/package.json +1 -1
@@ -0,0 +1,869 @@
1
+ "use client";
2
+ import React, { useCallback, useEffect, useRef, useState } from "react";
3
+ import { TransformWrapper, TransformComponent, MiniMap, } from "react-zoom-pan-pinch";
4
+ import { useAppSelector, useAppDispatch } from "../../hooks/use-redux";
5
+ import Layers from "../../components/layer-v2";
6
+ import { throttle } from "lodash";
7
+ import ModalPreview from "../../components/modal-preview";
8
+ import LayerView from "../view-only-2";
9
+ import { isEqual, debounce } from "lodash";
10
+ import { ZoomIn, ZoomOut } from "lucide-react";
11
+ import { Button } from "antd";
12
+ const BoardTemplate = ({ onSelectComponent, viewOnly }) => {
13
+ var _a, _b;
14
+ const dispatch = useAppDispatch();
15
+ const theme = useAppSelector((state) => state.theme);
16
+ const transformRef = useRef(null);
17
+ const containerRef = useRef(null);
18
+ const [widthBoard, setWidthBoard] = useState(0);
19
+ const [heightBoard, setHeightBoard] = useState(0);
20
+ const svgRef = useRef(null);
21
+ const [shadowShape, setShadowShape] = useState([]);
22
+ const [startPoint, setStartPoint] = useState(null);
23
+ const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 });
24
+ // const [moveComponent, setMoveComponent] = useState(false);
25
+ const [scale, setScale] = useState(1);
26
+ const activeTool = useAppSelector((state) => state.tool.active);
27
+ const grid = useAppSelector((state) => state.tool.grid);
28
+ const lockBackground = useAppSelector((state) => state.tool.lockBackground);
29
+ const { components: componentsProps, extraComponents: extraComponentsProps, flagChange, } = useAppSelector((state) => state.board);
30
+ const isTouching = useRef(false);
31
+ useEffect(() => {
32
+ var _a;
33
+ if (activeTool !== ((_a = shadowShape[0]) === null || _a === void 0 ? void 0 : _a.shape)) {
34
+ setShadowShape([]);
35
+ }
36
+ }, [activeTool]);
37
+ // const [isDragging, setIsDragging] = useState(false);
38
+ const [resizeDirection, setResizeDirection] = useState(null);
39
+ const backgroundColor = useAppSelector((state) => state.board.backgroundColor);
40
+ const selectedComponentProps = useAppSelector((state) => state.panel.selectedComponent);
41
+ const screenCTMRef = useRef(null);
42
+ const dragIndex = useRef(null);
43
+ const [componentsState, setComponentsState] = useState([]);
44
+ const [extraComponentsState, setExtraComponentsState] = useState([]);
45
+ const [selectedComponent, setSelectedComponent] = useState(null);
46
+ const isSyncingFromRedux = useRef(false);
47
+ const startPos = useRef({ x: 0, y: 0 });
48
+ const isDragging = useRef(false);
49
+ const moveComponent = useRef(false);
50
+ const debouncedSyncToReduxSelected = useRef(debounce((data) => {
51
+ throttledDispatch(data);
52
+ }, 300)).current;
53
+ const debouncedSyncComponents = useRef(debounce((data) => {
54
+ dispatch({
55
+ type: "board/setNewComponents",
56
+ payload: data,
57
+ });
58
+ }, 300));
59
+ const debouncedSyncExtraComponents = useRef(debounce((data) => {
60
+ dispatch({
61
+ type: "board/setNewExtraComponents",
62
+ payload: data,
63
+ });
64
+ }, 300));
65
+ // Redux → Local
66
+ useEffect(() => {
67
+ if (flagChange) {
68
+ if (!isEqual(componentsProps, componentsState)) {
69
+ isSyncingFromRedux.current = true;
70
+ setComponentsState(componentsProps !== null && componentsProps !== void 0 ? componentsProps : []);
71
+ }
72
+ if (!isEqual(extraComponentsProps, extraComponentsState)) {
73
+ isSyncingFromRedux.current = true;
74
+ setExtraComponentsState(extraComponentsProps !== null && extraComponentsProps !== void 0 ? extraComponentsProps : []);
75
+ }
76
+ if (!isEqual(selectedComponentProps, selectedComponent)) {
77
+ isSyncingFromRedux.current = true;
78
+ setSelectedComponent(selectedComponentProps !== null && selectedComponentProps !== void 0 ? selectedComponentProps : []);
79
+ }
80
+ dispatch({ type: "board/setFlagChange", payload: false });
81
+ }
82
+ }, [
83
+ componentsProps,
84
+ extraComponentsProps,
85
+ selectedComponentProps,
86
+ flagChange,
87
+ ]);
88
+ // Local → Redux
89
+ useEffect(() => {
90
+ if (isSyncingFromRedux.current) {
91
+ isSyncingFromRedux.current = false;
92
+ return;
93
+ }
94
+ if (!isEqual(componentsState, componentsProps) &&
95
+ !isEqual(componentsState, [])) {
96
+ debouncedSyncComponents.current(componentsState);
97
+ }
98
+ if (!isEqual(extraComponentsState, extraComponentsProps) &&
99
+ !isEqual(extraComponentsState, [])) {
100
+ debouncedSyncExtraComponents.current(extraComponentsState);
101
+ }
102
+ }, [componentsState, extraComponentsState]);
103
+ const handleAddComponent = (shape) => {
104
+ dispatch({
105
+ type: activeTool === "text"
106
+ ? "board/setExtraComponent"
107
+ : "board/addComponent",
108
+ payload: Object.assign(Object.assign({}, shape), { fill: theme === null || theme === void 0 ? void 0 : theme.primaryColor }),
109
+ });
110
+ const payload = Object.assign(Object.assign({}, shape), { fill: theme === null || theme === void 0 ? void 0 : theme.primaryColor });
111
+ if (activeTool === "text") {
112
+ setExtraComponentsState((prev) => [...prev, payload]);
113
+ }
114
+ else {
115
+ setComponentsState((prev) => [...prev, payload]);
116
+ }
117
+ };
118
+ const getSvgCoords = (e) => {
119
+ var _a;
120
+ const svg = svgRef.current;
121
+ const point = svg.createSVGPoint();
122
+ point.x = e.clientX;
123
+ point.y = e.clientY;
124
+ const transformed = point.matrixTransform((_a = svg.getScreenCTM()) === null || _a === void 0 ? void 0 : _a.inverse());
125
+ return { x: transformed.x, y: transformed.y };
126
+ };
127
+ const createShape = (x, y, fill) => ({
128
+ x,
129
+ y,
130
+ width: 50,
131
+ height: 50,
132
+ shape: activeTool,
133
+ id: Date.now(),
134
+ fill,
135
+ rotation: activeTool === "diamond" ? 45 : 0,
136
+ seatCount: activeTool === "table-seat-circle" ? 6 : 0,
137
+ openSpace: activeTool === "table-seat-circle" ? 0 : undefined,
138
+ seatFill: activeTool === "table-seat-circle" ? "#DADADA" : undefined,
139
+ text: activeTool === "text" ? "Text" : "",
140
+ fontColor: activeTool === "text" ? "#DADADA" : undefined,
141
+ points: [],
142
+ });
143
+ const getCoords = (e) => {
144
+ const svgRect = svgRef.current.getBoundingClientRect();
145
+ if ("touches" in e) {
146
+ return {
147
+ x: e.touches[0].clientX,
148
+ y: e.touches[0].clientY,
149
+ };
150
+ }
151
+ return {
152
+ x: e.clientX - svgRect.x,
153
+ y: e.clientY - svgRect.y,
154
+ };
155
+ };
156
+ const handleMouseDown = (e, item, direction) => {
157
+ if (!item && !direction) {
158
+ if (["node", "select"].includes(activeTool)) {
159
+ setSelectedComponent(null);
160
+ dispatch({ type: "panel/setSelectedComponent", payload: null });
161
+ }
162
+ return;
163
+ }
164
+ if (["node", "select"].includes(activeTool) && item && !direction) {
165
+ const coords = getCoords(e);
166
+ // startPos.current = { x: e.clientX, y: e.clientY };
167
+ // const rectBox = (e?.target as HTMLElement).getBoundingClientRect();
168
+ if (activeTool === "node") {
169
+ moveComponent.current = true;
170
+ isDragging.current = false;
171
+ dispatch({ type: "panel/setShow", payload: false });
172
+ // dispatch({
173
+ // type: "tool/setActiveTool",
174
+ // payload: "node",
175
+ // });
176
+ }
177
+ if (activeTool === "select") {
178
+ isDragging.current = true;
179
+ moveComponent.current = false;
180
+ dispatch({ type: "panel/setShow", payload: true });
181
+ }
182
+ const rectBox = (e === null || e === void 0 ? void 0 : e.target).getBoundingClientRect();
183
+ setDragOffset({
184
+ x: coords.x - rectBox.left + ((item === null || item === void 0 ? void 0 : item.strokeWidth) || 0),
185
+ y: coords.y - rectBox.top + ((item === null || item === void 0 ? void 0 : item.strokeWidth) || 0),
186
+ });
187
+ setSelectedComponent(item);
188
+ dispatch({
189
+ type: "panel/setSelectedComponent",
190
+ payload: item,
191
+ });
192
+ // const handleMouseMove = (event: MouseEvent) => {
193
+ // const dx = Math.abs(event.clientX - startPos.current.x);
194
+ // const dy = Math.abs(event.clientY - startPos.current.y);
195
+ // if (dx > 1 || dy > 1) {
196
+ // if (!moveComponent.current && item) {
197
+ // dispatch({ type: "panel/setSelectedComponent", payload: item });
198
+ // setSelectedComponent(item);
199
+ // }
200
+ // moveComponent.current = true;
201
+ // }
202
+ // };
203
+ // const handleMouseUp = () => {
204
+ // document.removeEventListener("mousemove", handleMouseMove);
205
+ // document.removeEventListener("mouseup", handleMouseUp);
206
+ // if (moveComponent.current) {
207
+ // moveComponent.current = false;
208
+ // } else {
209
+ // isDragging.current = true;
210
+ // moveComponent.current = false;
211
+ // setResizeDirection(direction);
212
+ // if (item) {
213
+ // dispatch({ type: "panel/setSelectedComponent", payload: item });
214
+ // dispatch({ type: "panel/setShow", payload: true });
215
+ // setSelectedComponent(item);
216
+ // }
217
+ // }
218
+ // };
219
+ // Pasang ke `document` agar global
220
+ // document.addEventListener("mousemove", handleMouseMove);
221
+ // document.addEventListener("mouseup", handleMouseUp);
222
+ }
223
+ // if(activeTool === "select" && item && !direction) {
224
+ // dispatch({
225
+ // type: "tool/setActiveTool",
226
+ // payload: "node",
227
+ // });
228
+ // }
229
+ if (activeTool === "select" && direction) {
230
+ setResizeDirection(direction);
231
+ }
232
+ };
233
+ const handleMouseEnter = () => {
234
+ if (![
235
+ "square",
236
+ "circle",
237
+ "diamond",
238
+ "table-seat-circle",
239
+ "table-seat-square",
240
+ "text",
241
+ ].includes(activeTool))
242
+ return;
243
+ };
244
+ const handleMouseLeave = () => {
245
+ if (![
246
+ "square",
247
+ "circle",
248
+ "diamond",
249
+ "table-seat-circle",
250
+ "table-seat-square",
251
+ "text",
252
+ ].includes(activeTool))
253
+ return;
254
+ setShadowShape([]);
255
+ };
256
+ const handleMouseClick = (e) => {
257
+ if (["select", "node"].includes(activeTool) && selectedComponent) {
258
+ handleUnSelectComponent();
259
+ return;
260
+ }
261
+ if (![
262
+ "square",
263
+ "circle",
264
+ "diamond",
265
+ "table-seat-circle",
266
+ "table-seat-square",
267
+ "text",
268
+ ].includes(activeTool)) {
269
+ return;
270
+ }
271
+ const { x, y } = getSvgCoords(e);
272
+ setStartPoint({ x, y });
273
+ handleAddComponent(createShape(x, y, theme === null || theme === void 0 ? void 0 : theme.primaryColor));
274
+ };
275
+ const throttledDispatch = useCallback(throttle((component) => {
276
+ // dispatch({
277
+ // type: "board/updateComponent",
278
+ // payload: component,
279
+ // });
280
+ dispatch({
281
+ type: "panel/updateSelectedComponent",
282
+ payload: component,
283
+ });
284
+ }, 16), // 16ms ≈ 60fps
285
+ [dispatch]);
286
+ const handleMouseMove = (e) => {
287
+ var _a;
288
+ if ([
289
+ "square",
290
+ "circle",
291
+ "diamond",
292
+ "table-seat-circle",
293
+ "table-seat-square",
294
+ "text",
295
+ "ruler",
296
+ ].includes(activeTool)) {
297
+ const { x, y } = getSvgCoords(e);
298
+ setShadowShape([createShape(x, y, theme === null || theme === void 0 ? void 0 : theme.primaryColor)]);
299
+ return;
300
+ }
301
+ if ((selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.shape) === "background" && lockBackground)
302
+ return;
303
+ if (activeTool === "node" &&
304
+ selectedComponent &&
305
+ moveComponent.current &&
306
+ dragOffset &&
307
+ // !isTouching.current &&
308
+ !resizeDirection) {
309
+ const workspaceRect = e.currentTarget.getBoundingClientRect();
310
+ const coords = getCoords(e);
311
+ const newX = coords.x - workspaceRect.left - (dragOffset === null || dragOffset === void 0 ? void 0 : dragOffset.x);
312
+ const newY = coords.y - workspaceRect.top - (dragOffset === null || dragOffset === void 0 ? void 0 : dragOffset.y);
313
+ let newPosition = {
314
+ x: newX / scale,
315
+ y: newY / scale,
316
+ };
317
+ requestAnimationFrame(() => {
318
+ setComponentsState((prev) => {
319
+ return prev.map((component) => {
320
+ if (component.id === selectedComponent.id) {
321
+ return Object.assign(Object.assign({}, component), { x: newPosition.x, y: newPosition.y });
322
+ }
323
+ return component;
324
+ });
325
+ });
326
+ setExtraComponentsState((prev) => {
327
+ return prev.map((component) => {
328
+ if (component.id === selectedComponent.id) {
329
+ return Object.assign(Object.assign({}, component), { x: newPosition.x, y: newPosition.y });
330
+ }
331
+ return component;
332
+ });
333
+ });
334
+ setSelectedComponent((prev) => {
335
+ return Object.assign(Object.assign({}, prev), { x: newPosition.x, y: newPosition.y });
336
+ });
337
+ });
338
+ debouncedSyncToReduxSelected(Object.assign(Object.assign({}, selectedComponent), newPosition));
339
+ return;
340
+ }
341
+ if (activeTool === "select" && resizeDirection) {
342
+ const coords = getCoords(e);
343
+ const workspaceRect = e.currentTarget.getBoundingClientRect();
344
+ const instance = (_a = transformRef.current) === null || _a === void 0 ? void 0 : _a.instance;
345
+ if (!instance)
346
+ return;
347
+ const { positionX, positionY } = instance.transformState;
348
+ const currentX = (coords.x - workspaceRect.left + 25 - positionX) / scale;
349
+ const currentY = (coords.y - workspaceRect.top + 25 - positionY) / scale;
350
+ const { x, y, width, height } = selectedComponent;
351
+ const relativeX = coords.x - workspaceRect.left;
352
+ const relativeY = coords.y - workspaceRect.top;
353
+ // 🧠 Ini posisi world (unscaled)
354
+ const worldX = (relativeX - positionX) / scale;
355
+ const worldY = (relativeY - positionY) / scale;
356
+ let newShape = Object.assign({}, selectedComponent);
357
+ if (!["table-seat-circle", "table-seat-square"].includes(selectedComponent.shape)) {
358
+ switch (resizeDirection) {
359
+ case "top-left":
360
+ // if (currentX < x || currentY < y) return;
361
+ newShape.width = width + (x - currentX);
362
+ newShape.height = height + (y - currentY);
363
+ newShape.x = currentX;
364
+ newShape.y = currentY;
365
+ break;
366
+ case "top-right":
367
+ newShape.width = currentX - newShape.x;
368
+ newShape.height = newShape.height + (newShape.y - currentY);
369
+ newShape.y = currentY;
370
+ break;
371
+ case "bottom-left":
372
+ newShape.width += newShape.x - currentX;
373
+ newShape.height = currentY - newShape.y;
374
+ newShape.x = currentX;
375
+ break;
376
+ case "bottom-right":
377
+ newShape.width = currentX - newShape.x;
378
+ newShape.height = currentY - newShape.y;
379
+ break;
380
+ case "left-center":
381
+ newShape.width += newShape.x - currentX;
382
+ newShape.x = currentX;
383
+ break;
384
+ case "right-center":
385
+ newShape.width = currentX - newShape.x;
386
+ break;
387
+ case "top-center": {
388
+ const delta = newShape.y - worldY;
389
+ newShape.height += delta;
390
+ newShape.y = worldY;
391
+ break;
392
+ }
393
+ case "bottom-center":
394
+ newShape.height = currentY - newShape.y;
395
+ break;
396
+ default:
397
+ break;
398
+ }
399
+ }
400
+ else if (["table-seat-circle", "table-seat-square"].includes(selectedComponent.shape)) {
401
+ switch (resizeDirection) {
402
+ case "top-left":
403
+ newShape.width = width + (x - currentX);
404
+ newShape.height = height + (y - currentY);
405
+ newShape.x = currentX;
406
+ newShape.y = currentY;
407
+ break;
408
+ case "top-right":
409
+ newShape.width = currentX - newShape.x;
410
+ newShape.height = newShape.height + (newShape.y - currentY);
411
+ newShape.y = currentY;
412
+ break;
413
+ case "bottom-left":
414
+ newShape.width += newShape.x - currentX;
415
+ newShape.height = currentY - newShape.y;
416
+ newShape.x = currentX;
417
+ break;
418
+ case "bottom-right":
419
+ newShape.width = currentX - newShape.x;
420
+ newShape.height = currentY - newShape.y;
421
+ break;
422
+ default:
423
+ break;
424
+ }
425
+ }
426
+ requestAnimationFrame(() => {
427
+ setComponentsState((prev) => {
428
+ return prev.map((component) => {
429
+ if (component.id === selectedComponent.id) {
430
+ return Object.assign(Object.assign({}, component), newShape);
431
+ }
432
+ return component;
433
+ });
434
+ });
435
+ setExtraComponentsState((prev) => {
436
+ return prev.map((component) => {
437
+ if (component.id === selectedComponent.id) {
438
+ return Object.assign(Object.assign({}, component), newShape);
439
+ }
440
+ return component;
441
+ });
442
+ });
443
+ setSelectedComponent(newShape);
444
+ });
445
+ debouncedSyncToReduxSelected(newShape);
446
+ }
447
+ };
448
+ const handleMouseUp = () => {
449
+ if (["select", "node"].includes(activeTool) && selectedComponent) {
450
+ setResizeDirection(null);
451
+ moveComponent.current = false;
452
+ }
453
+ };
454
+ useEffect(() => {
455
+ if (containerRef.current) {
456
+ setWidthBoard(containerRef.current.offsetWidth);
457
+ setHeightBoard(containerRef.current.offsetHeight);
458
+ }
459
+ }, [(_a = containerRef === null || containerRef === void 0 ? void 0 : containerRef.current) === null || _a === void 0 ? void 0 : _a.offsetWidth, (_b = containerRef === null || containerRef === void 0 ? void 0 : containerRef.current) === null || _b === void 0 ? void 0 : _b.offsetHeight, viewOnly]);
460
+ const handleUnSelectComponent = () => {
461
+ dispatch({ type: "panel/setSelectedComponent", payload: null });
462
+ setSelectedComponent(null);
463
+ };
464
+ // const handleTouchStart = (
465
+ // e: React.TouchEvent<
466
+ // SVGRectElement | SVGCircleElement | SVGTextElement | SVGImageElement
467
+ // >,
468
+ // items: any,
469
+ // direction?: string
470
+ // ) => {
471
+ // if (activeTool === "node" && !direction && items) {
472
+ // // dispatch({ type: "panel/setShow", payload: false });
473
+ // // setMoveComponent(true);
474
+ // moveComponent.current = true;
475
+ // isTouching.current = true;
476
+ // // dispatch({ type: "panel/setSelectedComponent", payload: item });
477
+ // onSelectComponent && onSelectComponent(items);
478
+ // dispatch({ type: "panel/setSelectedComponent", payload: items });
479
+ // setSelectedComponent(items);
480
+ // // selectedComponentRef.current = items;
481
+ // const touch = e.touches[0];
482
+ // const svg = e.currentTarget.ownerSVGElement;
483
+ // const pt = svg.createSVGPoint();
484
+ // if (!svg) return;
485
+ // pt.x = touch.clientX;
486
+ // pt.y = touch.clientY;
487
+ // const cursorpt = pt.matrixTransform(svg.getScreenCTM()?.inverse());
488
+ // screenCTMRef.current = svg.getScreenCTM()?.inverse() || null;
489
+ // setDragOffset({
490
+ // x: cursorpt.x - items.x,
491
+ // y: cursorpt.y - items.y,
492
+ // });
493
+ // // dragOffset.current = {
494
+ // // x: cursorpt.x - items.x,
495
+ // // y: cursorpt.y - items.y
496
+ // // }
497
+ // } else if (activeTool === "select" && direction) {
498
+ // setResizeDirection(direction);
499
+ // // setIsDragging(true);
500
+ // // setMoveComponent(true);
501
+ // moveComponent.current = true;
502
+ // isDragging.current = true;
503
+ // isTouching.current = true;
504
+ // const touch = e.touches[0];
505
+ // const svg = e.currentTarget.ownerSVGElement;
506
+ // const pt = svg.createSVGPoint();
507
+ // if (!svg) return;
508
+ // pt.x = touch.clientX;
509
+ // pt.y = touch.clientY;
510
+ // const cursorpt = pt.matrixTransform(svg.getScreenCTM()?.inverse());
511
+ // screenCTMRef.current = svg.getScreenCTM()?.inverse() || null;
512
+ // setDragOffset({
513
+ // x: cursorpt.x - items.x,
514
+ // y: cursorpt.y - items.y,
515
+ // });
516
+ // // dragOffset.current = {
517
+ // // x: cursorpt.x - items.x,
518
+ // // y: cursorpt.y - items.y
519
+ // // }
520
+ // }
521
+ // };
522
+ // const handleTouchMove = (
523
+ // e: React.TouchEvent<
524
+ // SVGRectElement | SVGCircleElement | SVGTextElement | SVGImageElement
525
+ // >
526
+ // ) => {
527
+ // if (
528
+ // activeTool === "node" &&
529
+ // moveComponent &&
530
+ // isTouching.current &&
531
+ // !resizeDirection
532
+ // ) {
533
+ // isDragging.current = true;
534
+ // moveComponent.current = true;
535
+ // const touch = e.touches[0];
536
+ // const svg = e.currentTarget.ownerSVGElement;
537
+ // if (!svg) return;
538
+ // const pt = svg.createSVGPoint();
539
+ // pt.x = touch.clientX;
540
+ // pt.y = touch.clientY;
541
+ // const cursorpt = pt.matrixTransform(screenCTMRef.current);
542
+ // requestAnimationFrame(() => {
543
+ // if (selectedComponent?.shape != "background") {
544
+ // setComponentsState((prev) => {
545
+ // return prev.map((component) => {
546
+ // if (component.id === selectedComponent.id) {
547
+ // return {
548
+ // ...component,
549
+ // x: cursorpt.x - dragOffset?.x,
550
+ // y: cursorpt.y - dragOffset?.y,
551
+ // };
552
+ // }
553
+ // return component;
554
+ // });
555
+ // });
556
+ // }
557
+ // if (selectedComponent?.shape == "background") {
558
+ // setExtraComponentsState((prev) => {
559
+ // return prev.map((component) => {
560
+ // if (component.id === selectedComponent.id) {
561
+ // return {
562
+ // ...component,
563
+ // x: cursorpt.x - dragOffset?.x,
564
+ // y: cursorpt.y - dragOffset?.y,
565
+ // };
566
+ // }
567
+ // return component;
568
+ // });
569
+ // });
570
+ // }
571
+ // setSelectedComponent({
572
+ // ...selectedComponent,
573
+ // x: cursorpt.x - dragOffset?.x,
574
+ // y: cursorpt.y - dragOffset?.y,
575
+ // });
576
+ // });
577
+ // // updateComponentRef({
578
+ // // ...(typeof selectedComponent === "object" && selectedComponent !== null
579
+ // // ? selectedComponent
580
+ // // : {}),
581
+ // // x: cursorpt.x - dragOffset?.current.x,
582
+ // // y: cursorpt.y - dragOffset?.current.y,
583
+ // // });
584
+ // } else if (
585
+ // activeTool === "select" &&
586
+ // moveComponent.current &&
587
+ // isTouching.current &&
588
+ // resizeDirection
589
+ // ) {
590
+ // // setIsDragging(true);
591
+ // isDragging.current = true;
592
+ // const touch = e.touches[0];
593
+ // const svg = e.currentTarget.ownerSVGElement;
594
+ // if (!svg) return;
595
+ // const pt = svg.createSVGPoint();
596
+ // pt.x = touch.clientX;
597
+ // pt.y = touch.clientY;
598
+ // const cursorpt = pt.matrixTransform(screenCTMRef.current);
599
+ // let newShape = { ...selectedComponent };
600
+ // switch (resizeDirection) {
601
+ // case "top-left":
602
+ // newShape.width = newShape.width + (newShape.x - cursorpt.x);
603
+ // newShape.height = newShape.height + (newShape.y - cursorpt.y);
604
+ // newShape.x = cursorpt.x;
605
+ // newShape.y = cursorpt.y;
606
+ // break;
607
+ // case "top-right":
608
+ // newShape.width = cursorpt.x - newShape.x;
609
+ // newShape.height = newShape.height + (newShape.y - cursorpt.y);
610
+ // newShape.y = cursorpt.y;
611
+ // break;
612
+ // case "bottom-left":
613
+ // newShape.width = newShape.width + (newShape.x - cursorpt.x);
614
+ // newShape.height = cursorpt.y - newShape.y;
615
+ // newShape.x = cursorpt.x;
616
+ // break;
617
+ // case "bottom-right":
618
+ // newShape.width = cursorpt.x - newShape.x;
619
+ // newShape.height = cursorpt.y - newShape.y;
620
+ // break;
621
+ // case "top-center":
622
+ // newShape.height = newShape.height + (newShape.y - cursorpt.y);
623
+ // newShape.y = cursorpt.y;
624
+ // break;
625
+ // case "bottom-center":
626
+ // newShape.height = cursorpt.y - newShape.y;
627
+ // break;
628
+ // case "right-center":
629
+ // newShape.width = cursorpt.x - newShape.x;
630
+ // break;
631
+ // case "left-center":
632
+ // newShape.width = newShape.width + (newShape.x - cursorpt.x);
633
+ // newShape.x = cursorpt.x;
634
+ // break;
635
+ // default:
636
+ // break;
637
+ // }
638
+ // // updateComponentRef({
639
+ // // ...(typeof selectedComponent === "object" && selectedComponent !== null
640
+ // // ? selectedComponent
641
+ // // : {}),
642
+ // // ...newShape,
643
+ // // });
644
+ // requestAnimationFrame(() => {
645
+ // setComponentsState((prev) => {
646
+ // return prev.map((component) => {
647
+ // if (component.id === selectedComponent.id) {
648
+ // return {
649
+ // ...component,
650
+ // ...newShape,
651
+ // };
652
+ // }
653
+ // return component;
654
+ // });
655
+ // });
656
+ // setExtraComponentsState((prev) => {
657
+ // return prev.map((component) => {
658
+ // if (component.id === selectedComponent.id) {
659
+ // return {
660
+ // ...component,
661
+ // ...newShape,
662
+ // };
663
+ // }
664
+ // return component;
665
+ // });
666
+ // });
667
+ // setSelectedComponent({
668
+ // ...selectedComponent,
669
+ // ...newShape,
670
+ // });
671
+ // });
672
+ // debouncedSyncToReduxSelected(newShape);
673
+ // }
674
+ // };
675
+ // const handleTouchEnd = () => {
676
+ // // setMoveComponent(false);
677
+ // // setIsDragging(false);
678
+ // moveComponent.current = false;
679
+ // isDragging.current = false;
680
+ // isTouching.current = false;
681
+ // setResizeDirection(null);
682
+ // };
683
+ const getCursorStyle = () => {
684
+ if (activeTool === "select" && moveComponent.current) {
685
+ return "grabbing";
686
+ }
687
+ else if (activeTool === "select" && resizeDirection) {
688
+ return "grab";
689
+ }
690
+ else if (activeTool === "grab") {
691
+ return "grab";
692
+ }
693
+ else if (activeTool === "ruler") {
694
+ return "crosshair";
695
+ }
696
+ };
697
+ const renderMiniMap = () => {
698
+ return (<MiniMap width={250} height={250}>
699
+ <div className="w-full h-full">
700
+ <svg id="workspace" width="100%" height="100%" viewBox={`0 0 ${widthBoard} ${heightBoard}`} className="h-screen w-full" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" style={{
701
+ background: "#f5f5f5",
702
+ display: "block",
703
+ }}>
704
+ <Layers shadowShape={shadowShape} components={[...extraComponentsState, ...componentsState]} activeTool={activeTool}
705
+ // onClick={handleSelectComponent}
706
+ // onMouseDown={handleMouseDown}
707
+ // onMouseUp={handleMouseUp}
708
+ // onBlur={handleUnSelectComponent}
709
+ selectedComponent={selectedComponent}/>
710
+ </svg>
711
+ </div>
712
+ </MiniMap>);
713
+ };
714
+ const handelZoomIn = () => {
715
+ var _a;
716
+ if (activeTool !== "grab") {
717
+ dispatch({
718
+ type: "tool/setActiveTool",
719
+ payload: "grab",
720
+ });
721
+ }
722
+ (_a = transformRef.current) === null || _a === void 0 ? void 0 : _a.zoomIn();
723
+ };
724
+ const handleZoomOut = () => {
725
+ var _a;
726
+ if (activeTool !== "grab") {
727
+ dispatch({
728
+ type: "tool/setActiveTool",
729
+ payload: "grab",
730
+ });
731
+ }
732
+ (_a = transformRef.current) === null || _a === void 0 ? void 0 : _a.zoomOut();
733
+ };
734
+ // const handleSelectComponent = (item: Component) => {
735
+ // if (activeTool === "select" && item) {
736
+ // if (item.shape === "background") {
737
+ // dispatch({ type: "panel/setSelectedComponent", payload: item });
738
+ // setSelectedComponent(item);
739
+ // return;
740
+ // }
741
+ // if (item.id === selectedComponent?.id) {
742
+ // handleUnSelectComponent();
743
+ // return;
744
+ // }
745
+ // dispatch({ type: "panel/setSelectedComponent", payload: item });
746
+ // setSelectedComponent(item);
747
+ // // setSelectedComponent(item);
748
+ // throttledDispatch(item);
749
+ // }
750
+ // };
751
+ // activeTool === "select" &&
752
+ // moveComponent &&
753
+ // isTouching.current &&
754
+ // !resizeDirection
755
+ const [nodes, setNodes] = useState([
756
+ { x: 80, y: 40 },
757
+ { x: 220, y: 40 },
758
+ { x: 260, y: 160 },
759
+ { x: 40, y: 160 },
760
+ ]);
761
+ const addNode = () => {
762
+ if (nodes.length < 30) {
763
+ const midIndex = Math.floor(nodes.length / 2);
764
+ const prev = nodes[midIndex - 1];
765
+ const curr = nodes[midIndex];
766
+ const newNode = {
767
+ x: (prev.x + curr.x) / 2,
768
+ y: (prev.y + curr.y) / 2,
769
+ };
770
+ const newNodes = [...nodes];
771
+ newNodes.splice(midIndex, 0, newNode);
772
+ setNodes(newNodes);
773
+ }
774
+ };
775
+ const removeNode = () => {
776
+ if (nodes.length > 3) {
777
+ const midIndex = Math.floor(nodes.length / 2);
778
+ const newNodes = [...nodes];
779
+ newNodes.splice(midIndex, 1);
780
+ setNodes(newNodes);
781
+ }
782
+ };
783
+ return (<>
784
+ <ModalPreview>
785
+ <LayerView statusKey="status"/>
786
+ </ModalPreview>
787
+ <div className="relative w-full h-screen flex-1 overflow-hidden" ref={containerRef}>
788
+ <div className="absolute bottom-5 left-1/2 transform -translate-x-1/2 z-10">
789
+ <div className="flex gap-2">
790
+ <Button icon={<ZoomIn />} onClick={handelZoomIn}/>
791
+ <Button icon={<ZoomOut />} onClick={handleZoomOut}/>
792
+ <Button onClick={addNode}>Add Node</Button>
793
+ <Button onClick={removeNode}>Remove Node</Button>
794
+ </div>
795
+ </div>
796
+ <TransformWrapper ref={transformRef} panning={{ disabled: ["node", "select"].includes(activeTool) }} centerZoomedOut={true} onTransformed={({ state: { scale } }) => setScale(scale)} minScale={1} // sangat kecil = bisa zoom out jauh
797
+ maxScale={1000} initialScale={1} pinch={{ step: 1 }} smooth={true} doubleClick={{ step: 1, disabled: activeTool === "select" }} disablePadding>
798
+ {scale > 1 && (<div className="absolute bottom-[60px] left-1/2 transform -translate-x-1/2 z-10">
799
+ {renderMiniMap()}
800
+ </div>)}
801
+ <TransformComponent wrapperStyle={{
802
+ width: "100%",
803
+ height: "100%",
804
+ overflow: "hidden",
805
+ }} contentStyle={{
806
+ width: "100%",
807
+ height: "100%",
808
+ }}>
809
+ <svg id="workspace" ref={svgRef} width="100%" height="100%" viewBox={`0 0 ${widthBoard} ${heightBoard}`} className="h-screen" onMouseUp={handleMouseUp} onMouseMove={handleMouseMove}
810
+ // onMouseEnter={handleMouseEnter}
811
+ // onMouseUp={handleEnd}
812
+ onTouchMove={handleMouseMove} onTouchEnd={handleMouseUp} onClick={(e) => {
813
+ e.stopPropagation();
814
+ handleMouseClick(e);
815
+ }}
816
+ // onMouseLeave={handleMouseLeave}
817
+ xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid meet" style={{
818
+ background: backgroundColor,
819
+ display: "block",
820
+ cursor: activeTool === "ruler" ? "crosshair" : "auto",
821
+ touchAction: "none",
822
+ }}>
823
+ <Layers shadowShape={shadowShape} components={[...extraComponentsState, ...componentsState]} style={{
824
+ cursor: getCursorStyle(),
825
+ }}
826
+ // onClick={handleSelectComponent}
827
+ onMouseDown={handleMouseDown}
828
+ // onMouseUp={handleMouseUp}
829
+ // onBlur={handleUnSelectComponent}
830
+ selectedComponent={selectedComponent} activeTool={activeTool} onTouchStart={handleMouseDown}/>
831
+
832
+ {activeTool === "ruler" && (<>
833
+ <g id="horizontal-ruler">
834
+ <rect x="0" y="0" width={window.innerWidth} height="30" fill="#e0e0e0"/>
835
+ <g stroke="#888" font-size="10" text-anchor="middle">
836
+ {Array.from({ length: window.innerWidth / 50 }, (_, i) => (<g key={i}>
837
+ <line x1={i * 50} y1="0" x2={i * 50} y2="10"/>
838
+ <text x={i * 50} y="15">
839
+ {i * 50}
840
+ </text>
841
+ </g>))}
842
+ </g>
843
+ </g>
844
+ <g id="vertical-ruler">
845
+ <rect x="0" y="0" width="30" height={window.innerHeight} fill="#e0e0e0"/>
846
+ <g stroke="#888" font-size="10" text-anchor="middle">
847
+ {Array.from({ length: window.innerHeight / 10 }, (_, i) => (<g key={i}>
848
+ <line x1="0" y1={i * 50} x2="10" y2={i * 50}/>
849
+ <text x="15" y={i * 50}>
850
+ {i * 50}
851
+ </text>
852
+ </g>))}
853
+ </g>
854
+ </g>
855
+ </>)}
856
+ {grid && (<g stroke="#ddd" strokeWidth={0.5}>
857
+ {/* Vertical lines */}
858
+ {Array.from({ length: widthBoard / 10 }, (_, i) => (<line key={`v-${i}`} x1={i * 10} y1={0} x2={i * 10} y2={heightBoard}/>))}
859
+
860
+ {/* Horizontal lines */}
861
+ {Array.from({ length: heightBoard / 10 }, (_, i) => (<line key={`h-${i}`} x1={0} y1={i * 10} x2={widthBoard} y2={i * 10}/>))}
862
+ </g>)}
863
+ </svg>
864
+ </TransformComponent>
865
+ </TransformWrapper>
866
+ </div>
867
+ </>);
868
+ };
869
+ export default BoardTemplate;