seat-editor 1.4.20 → 1.4.21

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.
@@ -0,0 +1,44 @@
1
+ "use client";
2
+ import { Button, ColorPicker, Flex, Form, Input, InputNumber } from "antd";
3
+ const SectionLabel = () => {
4
+ return (<div className="py-2">
5
+ <h1 className="heading-s">Section Labeling</h1>
6
+ <Form.Item label="Labels in square" name={"labels"}>
7
+ <Form.List name="labels">
8
+ {(fields, { add, remove }) => (<>
9
+ {fields.map((field) => (<div key={field.key}>
10
+ <Flex gap={2}>
11
+ <Form.Item name={[field.name, "label"]} label="Text">
12
+ <Input />
13
+ </Form.Item>
14
+ <Form.Item name={[field.name, "fontColor"]} label="Color" getValueFromEvent={(color) => color.toHexString()}>
15
+ <ColorPicker allowClear format="hex" defaultFormat="hex"/>
16
+ </Form.Item>
17
+ </Flex>
18
+ <Flex gap={2}>
19
+ <Form.Item name={[field.name, "x"]} label="X">
20
+ <InputNumber />
21
+ </Form.Item>
22
+ <Form.Item name={[field.name, "y"]} label="Y">
23
+ <InputNumber />
24
+ </Form.Item>
25
+ <Form.Item name={[field.name, "fontSize"]} label="Size">
26
+ <InputNumber suffix="px"/>
27
+ </Form.Item>
28
+ </Flex>
29
+ </div>))}
30
+ <Flex gap={2}>
31
+ <Button type="primary" onClick={() => add()} className="btn btn-primary">
32
+ Add
33
+ </Button>
34
+ <Button type="primary" onClick={() => remove(fields.length - 1)} className="btn btn-primary">
35
+ Remove
36
+ </Button>
37
+ </Flex>
38
+ </>)}
39
+ </Form.List>
40
+ </Form.Item>
41
+ <div className="divider-dashed"/>
42
+ </div>);
43
+ };
44
+ export default SectionLabel;
@@ -0,0 +1,66 @@
1
+ "use client";
2
+ import { ColorPicker, Flex, Form, InputNumber, Select } from "antd";
3
+ const SectionShape = () => {
4
+ const optionsShape = [
5
+ {
6
+ value: "circle",
7
+ label: "Circle",
8
+ },
9
+ {
10
+ value: "square",
11
+ label: "Square",
12
+ },
13
+ {
14
+ value: "table-seat-circle",
15
+ label: "Table Seat Circle",
16
+ },
17
+ {
18
+ label: "Image Table",
19
+ value: "image-table",
20
+ },
21
+ ];
22
+ return (<div className="py-2">
23
+ <h1 className="heading-s">Shape</h1>
24
+ <Flex gap={2} className="w-full">
25
+ <Form.Item label="Name" name="shape" className="w-full">
26
+ <Select options={optionsShape} className="w-full"/>
27
+ </Form.Item>
28
+ </Flex>
29
+ <Flex gap={2} className="w-full">
30
+ <Form.Item label="Width" name="width" className="w-full">
31
+ <InputNumber suffix="px"/>
32
+ </Form.Item>
33
+ <Form.Item label="Height" name="height" className="w-full">
34
+ <InputNumber suffix="px"/>
35
+ </Form.Item>
36
+ </Flex>
37
+ <Flex gap={2}>
38
+ <Form.Item label="Position X" name="x" className="w-full">
39
+ <InputNumber />
40
+ </Form.Item>
41
+ <Form.Item label="Position Y" name="y" className="w-full">
42
+ <InputNumber />
43
+ </Form.Item>
44
+ <Form.Item label="Rotation" name="rotation" className="w-full">
45
+ <InputNumber />
46
+ </Form.Item>
47
+ </Flex>
48
+ <Flex gap={2}>
49
+ <Form.Item label="Fill" name={"fill"} getValueFromEvent={(color) => color.toHexString()} className="w-full ">
50
+ <ColorPicker allowClear format="hex" defaultFormat="hex"/>
51
+ </Form.Item>
52
+ <Form.Item label="Stroke" name={"stroke"} getValueFromEvent={(color) => color.toHexString()} className="w-full ">
53
+ <ColorPicker allowClear format="hex" defaultFormat="hex"/>
54
+ </Form.Item>
55
+ </Flex>
56
+ <Flex>
57
+ <Form.Item label="Stroke Width" name={"strokeWidth"} className="w-full">
58
+ <InputNumber />
59
+ </Form.Item>
60
+ <Form.Item label="opacity" name={"opacity"} className="w-full">
61
+ <InputNumber step={0.1} max={1} min={0}/>
62
+ </Form.Item>
63
+ </Flex>
64
+ </div>);
65
+ };
66
+ export default SectionShape;
@@ -0,0 +1,36 @@
1
+ "use client";
2
+ import { useState, useEffect } from "react";
3
+ import { Input, Button, Flex, Form } from "antd";
4
+ import { ArrowRight, ArrowLeft } from "lucide-react";
5
+ const NumberIndicator = ({ name, defaultValue, onChange }) => {
6
+ const [value, setValue] = useState(0);
7
+ const form = Form.useFormInstance();
8
+ useEffect(() => {
9
+ if (defaultValue) {
10
+ setValue(defaultValue);
11
+ form.setFieldsValue({ [name]: defaultValue });
12
+ }
13
+ }, [defaultValue]);
14
+ const handlePrev = () => {
15
+ setValue(value - 1);
16
+ form.setFieldsValue({ [name]: value - 1 });
17
+ onChange(value - 1);
18
+ };
19
+ const handleNext = () => {
20
+ setValue(value + 1);
21
+ form.setFieldsValue({ [name]: value + 1 });
22
+ onChange(value + 1);
23
+ };
24
+ return (<Flex gap={2}>
25
+ <Button onClick={handlePrev}>
26
+ <ArrowLeft />
27
+ </Button>
28
+ <Form.Item name={name} noStyle>
29
+ <Input className="flex text-center" type="number" value={value} name={name} onChange={(e) => setValue(parseInt(e.target.value))}/>
30
+ </Form.Item>
31
+ <Button onClick={handleNext}>
32
+ <ArrowRight />
33
+ </Button>
34
+ </Flex>);
35
+ };
36
+ export default NumberIndicator;
@@ -0,0 +1,49 @@
1
+ // Joystick.tsx
2
+ import React, { useRef, useState } from "react";
3
+ export const Joystick = ({ size = 120, onMove, onEnd, }) => {
4
+ const baseRef = useRef(null);
5
+ const [position, setPosition] = useState({ x: 0, y: 0 });
6
+ const handleMove = (e) => {
7
+ const base = baseRef.current;
8
+ if (!base)
9
+ return;
10
+ const rect = base.getBoundingClientRect();
11
+ const isTouch = "touches" in e;
12
+ const clientX = isTouch ? e.touches[0].clientX : e.clientX;
13
+ const clientY = isTouch ? e.touches[0].clientY : e.clientY;
14
+ const centerX = rect.left + rect.width / 2;
15
+ const centerY = rect.top + rect.height / 2;
16
+ const dx = clientX - centerX;
17
+ const dy = clientY - centerY;
18
+ const distance = Math.min(Math.sqrt(Math.pow(dx, 2) + Math.pow(dy, 2)), size / 2 - 20);
19
+ const angle = Math.atan2(dy, dx);
20
+ const x = Math.cos(angle) * distance;
21
+ const y = Math.sin(angle) * distance;
22
+ setPosition({ x, y });
23
+ onMove === null || onMove === void 0 ? void 0 : onMove({ x, y });
24
+ };
25
+ const handleEnd = () => {
26
+ setPosition({ x: 0, y: 0 });
27
+ onEnd === null || onEnd === void 0 ? void 0 : onEnd();
28
+ };
29
+ return (<div ref={baseRef} onMouseMove={(e) => e.buttons === 1 && handleMove(e)} onMouseUp={handleEnd} onMouseLeave={handleEnd} onTouchMove={handleMove} onTouchEnd={handleEnd} style={{
30
+ width: size,
31
+ height: size,
32
+ background: "#ddd",
33
+ borderRadius: "50%",
34
+ position: "relative",
35
+ touchAction: "none",
36
+ userSelect: "none",
37
+ }}>
38
+ <div onMouseDown={handleMove} onTouchStart={handleMove} style={{
39
+ width: 40,
40
+ height: 40,
41
+ background: "#3E97FF",
42
+ borderRadius: "50%",
43
+ position: "absolute",
44
+ left: `calc(50% + ${position.x}px - 20px)`,
45
+ top: `calc(50% + ${position.y}px - 20px)`,
46
+ touchAction: "none",
47
+ }}/>
48
+ </div>);
49
+ };
@@ -0,0 +1,361 @@
1
+ "use client";
2
+ import { omit } from "lodash";
3
+ const Layers = ({ shadowShape, components, onClick, selectedComponent, selectedTable, activeTool, onMouseDown, onMouseUp, onBlur, selectedTableColor, mode = "edit", style, onTouchEnd, onTouchMove, onTouchStart }) => {
4
+ const renderShadowShape = (item) => {
5
+ const { id, x, y, width, height, fill, opacity, rotation, shape, fontColor, text, seatFill, labels, } = item;
6
+ const commonProps = { fill, opacity };
7
+ switch (shape) {
8
+ case "square":
9
+ return (<rect key={id} x={x} y={y} width={width} height={height} {...commonProps}/>);
10
+ case "circle":
11
+ return (<circle key={id} cx={x + width / 2} cy={y + height / 2} r={width / 2} {...commonProps}/>);
12
+ case "diamond":
13
+ return (<rect key={id} x={x} y={y} width={width} height={height} transform={`rotate(${rotation}, ${x}, ${y})`} {...commonProps}/>);
14
+ case "table-seat-circle": {
15
+ const seatCount = item.seatCount;
16
+ const openSpace = item.openSpace; // nilai antara 0 (tidak ada ruang) sampai maksimal ~0.9
17
+ const centerX = x + width / 2;
18
+ const centerY = y + height / 2;
19
+ const radius = width;
20
+ const seatRadius = width / 4;
21
+ const fullAngle = 2 * Math.PI;
22
+ const availableAngle = fullAngle * (1 - openSpace); // sudut yang dipakai untuk kursi
23
+ const angleStart = (fullAngle - availableAngle) / 2; // agar tetap seimbang
24
+ const angleStep = availableAngle / seatCount;
25
+ const seatCircles = Array.from({ length: seatCount }, (_, i) => {
26
+ const angle = angleStart + i * angleStep;
27
+ const cx = centerX + radius * Math.cos(angle);
28
+ const cy = centerY + radius * Math.sin(angle);
29
+ return { cx, cy };
30
+ });
31
+ return (<g key={id} transform={`rotate(${rotation} ${x + width / 2} ${y + height / 2})`}>
32
+ <circle cx={centerX} cy={centerY} r={width - 15} {...commonProps} opacity={id === (selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id) ? 0.5 : opacity}/>
33
+ {labels === null || labels === void 0 ? void 0 : labels.map((_, index) => {
34
+ var _a, _b, _c, _d;
35
+ return (<text key={`${id}-label-${index}`} x={x + width / 2 + ((_a = _ === null || _ === void 0 ? void 0 : _.x) !== null && _a !== void 0 ? _a : 0)} y={y + height / 2 + ((_b = _ === null || _ === void 0 ? void 0 : _.y) !== null && _b !== void 0 ? _b : 0)} fill={(_c = _ === null || _ === void 0 ? void 0 : _.fontColor) !== null && _c !== void 0 ? _c : "black"} fontSize={`${(_d = _ === null || _ === void 0 ? void 0 : _.fontSize) !== null && _d !== void 0 ? _d : 10}px`} fontWeight="bold" textAnchor="middle" dominantBaseline="middle" transform={`rotate(${rotation} ${x + width / 2} ${y + height / 2})`}>
36
+ {_ === null || _ === void 0 ? void 0 : _.label}
37
+ </text>);
38
+ })}
39
+ <g fill="#e6b9c0" opacity={id === (selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id) ? 0.5 : opacity} stroke="#c49ba3" strokeWidth="1" key={`${id}-seats`}>
40
+ {seatCircles.map(({ cx, cy }, i) => (<circle key={`${id}-seat-${i}`} cx={cx} cy={cy} r={seatRadius} fill={seatFill}/>))}
41
+ </g>
42
+ </g>);
43
+ }
44
+ case "table-seat-square": {
45
+ const seatCount = item.seatCount || 6;
46
+ const openSpace = item.openSpace || 0; // from 0 to 0.9
47
+ const seatRadius = width / 6;
48
+ // split seats evenly on top and bottom
49
+ const seatCountPerSide = Math.ceil(seatCount / 2);
50
+ const availableWidth = width * (1 - openSpace);
51
+ const startX = x + (width * openSpace) / 2;
52
+ const spacing = seatCountPerSide > 1 ? availableWidth / (seatCountPerSide - 1) : 0;
53
+ const topSeats = Array.from({ length: seatCountPerSide }, (_, i) => ({
54
+ cx: startX + i * spacing,
55
+ cy: y - seatRadius * 1.5,
56
+ }));
57
+ const bottomSeats = Array.from({ length: seatCount - seatCountPerSide }, // in case it's odd
58
+ (_, i) => ({
59
+ cx: startX + i * spacing,
60
+ cy: y + height + seatRadius * 1.5,
61
+ }));
62
+ return (<g key={id}>
63
+ {/* Square Table */}
64
+ <rect x={x} y={y} width={width} height={height} {...commonProps}/>
65
+
66
+ {/* Seats */}
67
+ <g fill="#e6b9c0" fillOpacity="0.5" stroke="#c49ba3" strokeWidth="1" key={`${id}-seats`}>
68
+ {[...topSeats, ...bottomSeats].map(({ cx, cy }, i) => (<circle key={`${id}-seat-${i}`} cx={cx} cy={cy} r={seatRadius}/>))}
69
+ </g>
70
+ </g>);
71
+ }
72
+ case "text":
73
+ return (<g key={id} onClick={() => onClick(item)}>
74
+ <rect x={x} y={y} width={width} height={height} fill="transparent" opacity={opacity}/>
75
+ <text x={x + width / 2} y={y + height / 2} textAnchor="middle" dominantBaseline="middle" fill={fontColor} fontSize={height * 0.6} opacity={opacity}>
76
+ {text}
77
+ </text>
78
+ </g>);
79
+ default:
80
+ return null;
81
+ }
82
+ };
83
+ const renderShape = (item) => {
84
+ const { id, x, y, width, height, fill, opacity, rotation, shape, text, stroke, strokeWidth, labels, fontSize, fontColor, label, seatFill, src, } = item;
85
+ const commonProps = {
86
+ fill,
87
+ opacity,
88
+ onClick: (e) => {
89
+ // e.stopPropagation();
90
+ onClick(item);
91
+ },
92
+ stroke,
93
+ strokeWidth,
94
+ onMouseDown: (e) => {
95
+ // e.stopPropagation();
96
+ onMouseDown === null || onMouseDown === void 0 ? void 0 : onMouseDown(e, item);
97
+ },
98
+ onMouseUp: (e) => {
99
+ // e.stopPropagation();
100
+ onMouseUp === null || onMouseUp === void 0 ? void 0 : onMouseUp(e);
101
+ },
102
+ onBlur: (e) => {
103
+ // e.stopPropagation();
104
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur();
105
+ },
106
+ onTouchMove: (e) => {
107
+ e.stopPropagation();
108
+ onTouchMove === null || onTouchMove === void 0 ? void 0 : onTouchMove(e);
109
+ },
110
+ onTouchStart: (e) => {
111
+ e.stopPropagation();
112
+ onTouchStart === null || onTouchStart === void 0 ? void 0 : onTouchStart(e, item);
113
+ },
114
+ onTouchEnd: (e) => {
115
+ e.stopPropagation();
116
+ onTouchEnd === null || onTouchEnd === void 0 ? void 0 : onTouchEnd(e);
117
+ },
118
+ onDoubleClick: (e) => {
119
+ e.stopPropagation();
120
+ onClick === null || onClick === void 0 ? void 0 : onClick(item);
121
+ },
122
+ };
123
+ switch (shape) {
124
+ case "square":
125
+ return (<g key={id}>
126
+ <rect key={id} x={x} y={y} width={width} height={height} fill={selectedTableColor !== null && selectedTableColor !== void 0 ? selectedTableColor : fill} style={Object.assign({ cursor: mode === "view" ? "pointer" : "default" }, style)} opacity={id === (selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id) ? 0.5 : opacity} {...omit(commonProps, "opacity")} transform={`rotate(${rotation} ${x + width / 2} ${y + height / 2})`}/>
127
+ {labels === null || labels === void 0 ? void 0 : labels.map((_, index) => {
128
+ var _a, _b, _c, _d;
129
+ return (<text key={`${id}-label-${index}`} x={x + width / 2 + ((_a = _ === null || _ === void 0 ? void 0 : _.x) !== null && _a !== void 0 ? _a : 0)} y={y + height / 2 + ((_b = _ === null || _ === void 0 ? void 0 : _.y) !== null && _b !== void 0 ? _b : 0)} fill={(_c = _ === null || _ === void 0 ? void 0 : _.fontColor) !== null && _c !== void 0 ? _c : "black"} fontSize={`${(_d = _ === null || _ === void 0 ? void 0 : _.fontSize) !== null && _d !== void 0 ? _d : 10}px`} fontWeight="bold" textAnchor="middle" dominantBaseline="middle" transform={`rotate(${rotation} ${x + width / 2} ${y + height / 2})`} onClick={(e) => {
130
+ e.stopPropagation();
131
+ onClick(item);
132
+ }}>
133
+ {_ === null || _ === void 0 ? void 0 : _.label}
134
+ </text>);
135
+ })}
136
+ </g>);
137
+ case "circle":
138
+ return (<g key={id}>
139
+ <circle key={id} cx={x + width / 2} cy={y + height / 2} r={width / 2} style={Object.assign({ cursor: mode === "view" ? "pointer" : "default" }, style)} fill={selectedTableColor !== null && selectedTableColor !== void 0 ? selectedTableColor : fill} opacity={id === (selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id) ? 0.5 : opacity} {...omit(commonProps, "opacity")}/>
140
+ {labels === null || labels === void 0 ? void 0 : labels.map((_, index) => {
141
+ var _a, _b, _c, _d;
142
+ return (<text key={`${id}-label-${index}`} x={x + width / 2 + ((_a = _ === null || _ === void 0 ? void 0 : _.x) !== null && _a !== void 0 ? _a : 0)} y={y + height / 2 + ((_b = _ === null || _ === void 0 ? void 0 : _.y) !== null && _b !== void 0 ? _b : 0)} fill={(_c = _ === null || _ === void 0 ? void 0 : _.fontColor) !== null && _c !== void 0 ? _c : "black"} fontSize={`${(_d = _ === null || _ === void 0 ? void 0 : _.fontSize) !== null && _d !== void 0 ? _d : 10}px`} fontWeight="bold" textAnchor="middle" dominantBaseline="middle" transform={`rotate(${rotation} ${x + width / 2} ${y + height / 2})`} onClick={(e) => {
143
+ e.stopPropagation();
144
+ onClick(item);
145
+ }}>
146
+ {_ === null || _ === void 0 ? void 0 : _.label}
147
+ </text>);
148
+ })}
149
+ </g>);
150
+ case "diamond":
151
+ return (<g key={id}>
152
+ <rect key={id} x={x} y={y} width={width} height={height} style={Object.assign({ cursor: mode === "view" ? "pointer" : "default" }, style)} transform={`rotate(${rotation}, ${x}, ${y})`} {...commonProps} fill={selectedTableColor !== null && selectedTableColor !== void 0 ? selectedTableColor : fill}/>
153
+ <text x={x + width / 2} y={y + height / 2} fill={fontColor !== null && fontColor !== void 0 ? fontColor : "black"} fontSize={`${fontSize !== null && fontSize !== void 0 ? fontSize : 10}px`} fontWeight="bold" textAnchor="middle" dominantBaseline="middle">
154
+ {label}
155
+ </text>
156
+ </g>);
157
+ case "table-seat-circle": {
158
+ const seatCount = item.seatCount;
159
+ const openSpace = item.openSpace; // nilai antara 0 (tidak ada ruang) sampai maksimal ~0.9
160
+ const centerX = x + width / 2;
161
+ const centerY = y + height / 2;
162
+ const radius = width;
163
+ const seatRadius = width / 4;
164
+ const fullAngle = 2 * Math.PI;
165
+ const availableAngle = fullAngle * (1 - openSpace); // sudut yang dipakai untuk kursi
166
+ const angleStart = (fullAngle - availableAngle) / 2; // agar tetap seimbang
167
+ const angleStep = availableAngle / seatCount;
168
+ const seatCircles = Array.from({ length: seatCount }, (_, i) => {
169
+ const angle = angleStart + i * angleStep;
170
+ const cx = centerX + radius * Math.cos(angle);
171
+ const cy = centerY + radius * Math.sin(angle);
172
+ return { cx, cy };
173
+ });
174
+ return (<g key={id} transform={`rotate(${rotation} ${x + width / 2} ${y + height / 2})`}>
175
+ <circle cx={centerX} style={Object.assign({ cursor: mode === "view" ? "pointer" : "default" }, style)} cy={centerY} r={width - 15} fill={selectedTableColor !== null && selectedTableColor !== void 0 ? selectedTableColor : fill} {...commonProps} opacity={id === (selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id) ? 0.5 : opacity}/>
176
+ {labels === null || labels === void 0 ? void 0 : labels.map((_, index) => {
177
+ var _a, _b, _c, _d;
178
+ return (<text key={`${id}-label-${index}`} x={x + width / 2 + ((_a = _ === null || _ === void 0 ? void 0 : _.x) !== null && _a !== void 0 ? _a : 0)} y={y + height / 2 + ((_b = _ === null || _ === void 0 ? void 0 : _.y) !== null && _b !== void 0 ? _b : 0)} fill={(_c = _ === null || _ === void 0 ? void 0 : _.fontColor) !== null && _c !== void 0 ? _c : "black"} fontSize={`${(_d = _ === null || _ === void 0 ? void 0 : _.fontSize) !== null && _d !== void 0 ? _d : 10}px`} fontWeight="bold" textAnchor="middle" dominantBaseline="middle" transform={`rotate(${rotation} ${x + width / 2} ${y + height / 2})`} onClick={(e) => {
179
+ e.stopPropagation();
180
+ onClick(item);
181
+ }}>
182
+ {_ === null || _ === void 0 ? void 0 : _.label}
183
+ </text>);
184
+ })}
185
+ <g fill="#e6b9c0" opacity={id === (selectedComponent === null || selectedComponent === void 0 ? void 0 : selectedComponent.id) ? 0.5 : opacity} stroke="#c49ba3" key={`${id}-seats`} strokeWidth="1">
186
+ {seatCircles.map(({ cx, cy }, i) => (<circle key={`${id}-seat-${i}`} cx={cx} cy={cy} r={seatRadius} fill={seatFill}/>))}
187
+ </g>
188
+ </g>);
189
+ }
190
+ case "table-seat-square": {
191
+ const seatCount = item.seatCount || 6;
192
+ const openSpace = item.openSpace || 0; // from 0 to 0.9
193
+ const seatRadius = width / 6;
194
+ // split seats evenly on top and bottom
195
+ const seatCountPerSide = Math.ceil(seatCount / 2);
196
+ const availableWidth = width * (1 - openSpace);
197
+ const startX = x + (width * openSpace) / 2;
198
+ const spacing = seatCountPerSide > 1 ? availableWidth / (seatCountPerSide - 1) : 0;
199
+ const topSeats = Array.from({ length: seatCountPerSide }, (_, i) => ({
200
+ cx: startX + i * spacing,
201
+ cy: y - seatRadius * 1.5,
202
+ }));
203
+ const bottomSeats = Array.from({ length: seatCount - seatCountPerSide }, // in case it's odd
204
+ (_, i) => ({
205
+ cx: startX + i * spacing,
206
+ cy: y + height + seatRadius * 1.5,
207
+ }));
208
+ return (<g key={id} transform={`rotate(${rotation}, ${x}, ${y})`}>
209
+ {/* Square Table */}
210
+ <rect x={x} y={y} style={Object.assign({ cursor: mode === "view" ? "pointer" : "default" }, style)} width={width} height={height} {...commonProps} fill={selectedTableColor !== null && selectedTableColor !== void 0 ? selectedTableColor : fill}/>
211
+ <text x={x + width / 2} y={y + height / 2} fill={fontColor !== null && fontColor !== void 0 ? fontColor : "black"} fontSize={`${fontSize !== null && fontSize !== void 0 ? fontSize : 10}px`} fontWeight="bold" textAnchor="middle" dominantBaseline="middle">
212
+ {label}
213
+ </text>
214
+ {/* Seats */}
215
+ <g fill="#e6b9c0" fillOpacity="0.5" stroke="#c49ba3" strokeWidth="1" key={`${id}-seats`}>
216
+ {[...topSeats, ...bottomSeats].map(({ cx, cy }, i) => (<circle key={`${id}-seat-${i}`} cx={cx} cy={cy} r={seatRadius}/>))}
217
+ </g>
218
+ </g>);
219
+ }
220
+ case "text":
221
+ return (<g key={id}>
222
+ <rect x={x} y={y} width={width} height={height} fill="transparent" opacity={opacity} onClick={(e) => {
223
+ e.stopPropagation();
224
+ onClick(item);
225
+ }}/>
226
+ <text x={x + width / 2} y={y + height / 2} textAnchor="middle" dominantBaseline="middle" fill={fontColor} fontSize={fontSize !== null && fontSize !== void 0 ? fontSize : height * 0.6} opacity={opacity} {...omit(commonProps, ["fill", "opacity"])}>
227
+ {text}
228
+ </text>
229
+ </g>);
230
+ case "image-table":
231
+ case "background":
232
+ return (<g key={id} onClick={() => onClick(item)}>
233
+ <image href={src} x={x} y={y} width={width} height={height} transform={`rotate(${rotation} ${x + width / 2} ${y + height / 2})`} {...commonProps}/>
234
+ {labels === null || labels === void 0 ? void 0 : labels.map((_, index) => {
235
+ var _a, _b, _c, _d;
236
+ return (<text key={index} x={x + width / 2 + ((_a = _ === null || _ === void 0 ? void 0 : _.x) !== null && _a !== void 0 ? _a : 0)} y={y + height / 2 + ((_b = _ === null || _ === void 0 ? void 0 : _.y) !== null && _b !== void 0 ? _b : 0)} fill={(_c = _ === null || _ === void 0 ? void 0 : _.fontColor) !== null && _c !== void 0 ? _c : "black"} fontSize={`${(_d = _ === null || _ === void 0 ? void 0 : _.fontSize) !== null && _d !== void 0 ? _d : 10}px`} fontWeight="bold" textAnchor="middle" dominantBaseline="middle" transform={`rotate(${rotation} ${x + width / 2} ${y + height / 2})`}>
237
+ {_ === null || _ === void 0 ? void 0 : _.label}
238
+ </text>);
239
+ })}
240
+ </g>);
241
+ default:
242
+ return <g key={id}/>;
243
+ }
244
+ };
245
+ let date = new Date();
246
+ return (<g key={`${date}`}>
247
+ {components === null || components === void 0 ? void 0 : components.map(renderShape)}
248
+ {shadowShape === null || shadowShape === void 0 ? void 0 : shadowShape.map(renderShadowShape)}
249
+
250
+ {selectedComponent && (<>
251
+ {[
252
+ "square",
253
+ "circle",
254
+ "diamond",
255
+ "text",
256
+ "background",
257
+ "image-table",
258
+ ].includes(selectedComponent.shape) && (<g key={`${selectedComponent.id}-selection`}>
259
+ <rect x={selectedComponent.x - 25} y={selectedComponent.y - 25} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
260
+ e.stopPropagation();
261
+ onMouseDown(e, selectedComponent, "top-left");
262
+ }} onTouchStart={(e) => {
263
+ e.stopPropagation();
264
+ onTouchStart(e, selectedComponent, "top-left");
265
+ }} onTouchMove={(e) => {
266
+ e.stopPropagation();
267
+ onTouchMove(e);
268
+ }}/>
269
+ <rect x={selectedComponent.x + (selectedComponent.width - 15) / 2} y={selectedComponent.y - 25} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
270
+ e.stopPropagation();
271
+ onMouseDown(e, selectedComponent, "top-center");
272
+ }} onTouchStart={(e) => {
273
+ e.stopPropagation();
274
+ onTouchStart(e, selectedComponent, "top-center");
275
+ }} onTouchMove={(e) => {
276
+ e.stopPropagation();
277
+ onTouchMove(e);
278
+ }}/>
279
+ <rect x={selectedComponent.x - 25} y={selectedComponent.y + (selectedComponent.height - 15) / 2} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
280
+ e.stopPropagation();
281
+ onMouseDown(e, selectedComponent, "left-center");
282
+ }} onTouchStart={(e) => {
283
+ e.stopPropagation();
284
+ onTouchStart(e, selectedComponent, "left-center");
285
+ }} onTouchMove={(e) => {
286
+ e.stopPropagation();
287
+ onTouchMove(e);
288
+ }}/>
289
+ <rect x={selectedComponent.x + selectedComponent.width + 5} y={selectedComponent.y - 25} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
290
+ e.stopPropagation();
291
+ onMouseDown(e, selectedComponent, "top-right");
292
+ }} onTouchStart={(e) => {
293
+ e.stopPropagation();
294
+ onTouchStart(e, selectedComponent, "top-right");
295
+ }} onTouchMove={(e) => {
296
+ e.stopPropagation();
297
+ onTouchMove(e);
298
+ }}/>
299
+ <rect x={selectedComponent.x + selectedComponent.width + 5} y={selectedComponent.y + selectedComponent.height + 5} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
300
+ e.stopPropagation();
301
+ onMouseDown(e, selectedComponent, "bottom-right");
302
+ }} onTouchStart={(e) => {
303
+ e.stopPropagation();
304
+ onTouchStart(e, selectedComponent, "bottom-right");
305
+ }} onTouchMove={(e) => {
306
+ e.stopPropagation();
307
+ onTouchMove(e);
308
+ }}/>
309
+ <rect x={selectedComponent.x + (selectedComponent.width - 15) / 2} y={selectedComponent.y + selectedComponent.height + 5} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
310
+ e.stopPropagation();
311
+ onMouseDown(e, selectedComponent, "bottom-center");
312
+ }} onTouchStart={(e) => {
313
+ e.stopPropagation();
314
+ onTouchStart(e, selectedComponent, "bottom-center");
315
+ }} onTouchMove={(e) => {
316
+ e.stopPropagation();
317
+ onTouchMove(e);
318
+ }}/>
319
+ <rect x={selectedComponent.x + selectedComponent.width + 5} y={selectedComponent.y + (selectedComponent.height - 15) / 2} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
320
+ e.stopPropagation();
321
+ onMouseDown(e, selectedComponent, "right-center");
322
+ }} onTouchStart={(e) => {
323
+ e.stopPropagation();
324
+ onTouchStart(e, selectedComponent, "right-center");
325
+ }} onTouchMove={(e) => {
326
+ e.stopPropagation();
327
+ onTouchMove(e);
328
+ }}/>
329
+ <rect x={selectedComponent.x - 25} y={selectedComponent.y + selectedComponent.height + 5} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
330
+ e.stopPropagation();
331
+ onMouseDown(e, selectedComponent, "bottom-left");
332
+ }} onTouchStart={(e) => {
333
+ e.stopPropagation();
334
+ onTouchStart(e, selectedComponent, "bottom-left");
335
+ }} onTouchMove={(e) => {
336
+ e.stopPropagation();
337
+ onTouchMove(e);
338
+ }}/>
339
+ </g>)}
340
+ {["table-seat-circle", "table-seat-square"].includes(selectedComponent.shape) && (<g key={selectedComponent.id}>
341
+ <rect x={selectedComponent.x - selectedComponent.width} y={selectedComponent.y - selectedComponent.height} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
342
+ e.stopPropagation();
343
+ onMouseDown(e, selectedComponent, "top-left");
344
+ }}/>
345
+ <rect x={selectedComponent.x + selectedComponent.width * 2 - 10} y={selectedComponent.y - selectedComponent.height} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
346
+ e.stopPropagation();
347
+ onMouseDown(e, selectedComponent, "top-right");
348
+ }}/>
349
+ <rect x={selectedComponent.x + selectedComponent.width * 2 - 10} y={selectedComponent.y + selectedComponent.height * 2} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
350
+ e.stopPropagation();
351
+ onMouseDown(e, selectedComponent, "bottom-right");
352
+ }}/>
353
+ <rect x={selectedComponent.x - selectedComponent.width} y={selectedComponent.y + selectedComponent.height * 2} width={20} height={20} fill="black" stroke="white" strokeWidth="2" transform={`scale(${selectedComponent.scale})`} onMouseDown={(e) => {
354
+ e.stopPropagation();
355
+ onMouseDown(e, selectedComponent, "b-left");
356
+ }}/>
357
+ </g>)}
358
+ </>)}
359
+ </g>);
360
+ };
361
+ export default Layers;
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { useEffect } from "react";
3
+ import Board from "../../features/board";
4
+ import SideTool from "../../features/side-tool";
5
+ import ControlPanels from "../../features/panel";
6
+ import LayerView from "../../features/view";
7
+ import { useAppDispatch } from "../../hooks/use-redux";
8
+ const TableEditor = ({ componentProps = [], extraComponentProps = [], }) => {
9
+ const dispatch = useAppDispatch();
10
+ useEffect(() => {
11
+ if (componentProps.length > 0) {
12
+ dispatch({
13
+ type: "board/setNewComponents",
14
+ payload: componentProps,
15
+ });
16
+ }
17
+ if (extraComponentProps.length > 0) {
18
+ dispatch({
19
+ type: "board/setNewExtraComponent",
20
+ payload: extraComponentProps,
21
+ });
22
+ }
23
+ }, [componentProps, extraComponentProps]);
24
+ return (<>
25
+ <div className="w-full h-screen flex relative">
26
+ <SideTool />
27
+ <Board />
28
+ <ControlPanels />
29
+ </div>
30
+ </>);
31
+ };
32
+ export default TableEditor;
33
+ export { LayerView };
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { Modal } from "antd";
3
+ import { useAppSelector, useAppDispatch } from "../../hooks/use-redux";
4
+ const ModalPreview = ({ children }) => {
5
+ const { isPreview } = useAppSelector((state) => state.tool);
6
+ const dispatch = useAppDispatch();
7
+ return (<Modal open={isPreview} onCancel={() => dispatch({ type: "tool/setTooglePreview", payload: false })} width={700} title="Preview Board" centered footer={null}>
8
+ <div className="flex flex-col p-4 h-[500px]">{children}</div>
9
+ </Modal>);
10
+ };
11
+ export default ModalPreview;