@nex125/seatmap-editor 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2180 -1637
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +1483 -0
- package/dist/index.css.map +1 -0
- package/dist/index.d.cts +42 -4
- package/dist/index.d.ts +42 -4
- package/dist/index.js +2181 -1638
- package/dist/index.js.map +1 -1
- package/package.json +5 -2
package/dist/index.cjs
CHANGED
|
@@ -23,16 +23,75 @@ var BaseTool = class {
|
|
|
23
23
|
};
|
|
24
24
|
|
|
25
25
|
// src/tools/PanTool.ts
|
|
26
|
-
var PanTool = class extends BaseTool {
|
|
26
|
+
var PanTool = class _PanTool extends BaseTool {
|
|
27
27
|
name = "pan";
|
|
28
28
|
cursor = "grab";
|
|
29
29
|
isPanning = false;
|
|
30
30
|
lastX = 0;
|
|
31
31
|
lastY = 0;
|
|
32
|
+
panVelocity = { x: 0, y: 0 };
|
|
33
|
+
lastPanSampleTime = 0;
|
|
34
|
+
inertiaRaf = 0;
|
|
35
|
+
inertiaLastTime = 0;
|
|
36
|
+
panInertiaJelly = 55;
|
|
37
|
+
panInertiaCarry;
|
|
38
|
+
panInertiaFriction;
|
|
39
|
+
panInertiaMinSpeed;
|
|
40
|
+
static DEFAULT_PAN_VELOCITY_BLEND = 0.3;
|
|
41
|
+
static DEFAULT_PAN_STOP_DELTA = 0.3;
|
|
42
|
+
static DEFAULT_PAN_RELEASE_IDLE_MS = 90;
|
|
43
|
+
panVelocityBlend = _PanTool.DEFAULT_PAN_VELOCITY_BLEND;
|
|
44
|
+
panStopDelta = _PanTool.DEFAULT_PAN_STOP_DELTA;
|
|
45
|
+
panReleaseIdleMs = _PanTool.DEFAULT_PAN_RELEASE_IDLE_MS;
|
|
46
|
+
setInertiaOptions(options) {
|
|
47
|
+
this.panInertiaJelly = options.panInertiaJelly ?? this.panInertiaJelly;
|
|
48
|
+
this.panInertiaCarry = options.panInertiaCarry;
|
|
49
|
+
this.panInertiaFriction = options.panInertiaFriction;
|
|
50
|
+
this.panInertiaMinSpeed = options.panInertiaMinSpeed;
|
|
51
|
+
this.panVelocityBlend = options.panVelocityBlend !== void 0 ? Math.max(0.05, Math.min(0.95, options.panVelocityBlend)) : _PanTool.DEFAULT_PAN_VELOCITY_BLEND;
|
|
52
|
+
this.panStopDelta = options.panStopDelta !== void 0 ? Math.max(0, Math.min(4, options.panStopDelta)) : _PanTool.DEFAULT_PAN_STOP_DELTA;
|
|
53
|
+
this.panReleaseIdleMs = options.panReleaseIdleMs !== void 0 ? Math.max(0, Math.min(400, options.panReleaseIdleMs)) : _PanTool.DEFAULT_PAN_RELEASE_IDLE_MS;
|
|
54
|
+
}
|
|
55
|
+
stopPanInertia() {
|
|
56
|
+
if (this.inertiaRaf) {
|
|
57
|
+
cancelAnimationFrame(this.inertiaRaf);
|
|
58
|
+
this.inertiaRaf = 0;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
startPanInertia(viewport) {
|
|
62
|
+
this.stopPanInertia();
|
|
63
|
+
const jelly = Math.max(0, Math.min(100, this.panInertiaJelly)) / 100;
|
|
64
|
+
const carry = this.panInertiaCarry !== void 0 ? Math.max(0, Math.min(0.98, this.panInertiaCarry)) : 0.58 + jelly * 0.28;
|
|
65
|
+
const baseFriction = this.panInertiaFriction !== void 0 ? Math.max(0.8, Math.min(0.995, this.panInertiaFriction)) : 0.88 + jelly * 0.08;
|
|
66
|
+
const minSpeed = this.panInertiaMinSpeed !== void 0 ? Math.max(1e-3, Math.min(0.05, this.panInertiaMinSpeed)) : 0.012 - jelly * 4e-3;
|
|
67
|
+
let velocity = {
|
|
68
|
+
x: this.panVelocity.x * carry,
|
|
69
|
+
y: this.panVelocity.y * carry
|
|
70
|
+
};
|
|
71
|
+
if (Math.hypot(velocity.x, velocity.y) < minSpeed) return;
|
|
72
|
+
this.inertiaLastTime = performance.now();
|
|
73
|
+
const tick = () => {
|
|
74
|
+
const now = performance.now();
|
|
75
|
+
const dt = Math.min(32, Math.max(8, now - this.inertiaLastTime));
|
|
76
|
+
this.inertiaLastTime = now;
|
|
77
|
+
const decay = Math.pow(baseFriction, dt / 16.67);
|
|
78
|
+
velocity = { x: velocity.x * decay, y: velocity.y * decay };
|
|
79
|
+
if (Math.hypot(velocity.x, velocity.y) < minSpeed) {
|
|
80
|
+
this.inertiaRaf = 0;
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
viewport.pan(velocity.x * dt, velocity.y * dt);
|
|
84
|
+
this.inertiaRaf = requestAnimationFrame(tick);
|
|
85
|
+
};
|
|
86
|
+
this.inertiaRaf = requestAnimationFrame(tick);
|
|
87
|
+
}
|
|
32
88
|
onPointerDown(e) {
|
|
89
|
+
this.stopPanInertia();
|
|
33
90
|
this.isPanning = true;
|
|
34
91
|
this.lastX = e.screenX;
|
|
35
92
|
this.lastY = e.screenY;
|
|
93
|
+
this.panVelocity = { x: 0, y: 0 };
|
|
94
|
+
this.lastPanSampleTime = performance.now();
|
|
36
95
|
}
|
|
37
96
|
onPointerMove(e, viewport) {
|
|
38
97
|
if (!this.isPanning) return;
|
|
@@ -40,13 +99,30 @@ var PanTool = class extends BaseTool {
|
|
|
40
99
|
const dy = e.screenY - this.lastY;
|
|
41
100
|
this.lastX = e.screenX;
|
|
42
101
|
this.lastY = e.screenY;
|
|
102
|
+
const now = performance.now();
|
|
103
|
+
const dt = Math.max(1, now - this.lastPanSampleTime);
|
|
104
|
+
this.lastPanSampleTime = now;
|
|
105
|
+
const nextVelocity = { x: dx / dt, y: dy / dt };
|
|
106
|
+
const isNearlyStopped = Math.hypot(dx, dy) < this.panStopDelta;
|
|
107
|
+
this.panVelocity = isNearlyStopped ? { x: this.panVelocity.x * 0.35, y: this.panVelocity.y * 0.35 } : {
|
|
108
|
+
x: this.panVelocity.x * (1 - this.panVelocityBlend) + nextVelocity.x * this.panVelocityBlend,
|
|
109
|
+
y: this.panVelocity.y * (1 - this.panVelocityBlend) + nextVelocity.y * this.panVelocityBlend
|
|
110
|
+
};
|
|
43
111
|
viewport.pan(dx, dy);
|
|
44
112
|
}
|
|
45
|
-
onPointerUp() {
|
|
113
|
+
onPointerUp(_e, viewport) {
|
|
114
|
+
if (this.isPanning) {
|
|
115
|
+
const timeSinceLastSample = performance.now() - this.lastPanSampleTime;
|
|
116
|
+
if (timeSinceLastSample > this.panReleaseIdleMs || Math.hypot(this.panVelocity.x, this.panVelocity.y) < 0.01) {
|
|
117
|
+
this.panVelocity = { x: 0, y: 0 };
|
|
118
|
+
}
|
|
119
|
+
this.startPanInertia(viewport);
|
|
120
|
+
}
|
|
46
121
|
this.isPanning = false;
|
|
47
122
|
}
|
|
48
123
|
onDeactivate() {
|
|
49
124
|
this.isPanning = false;
|
|
125
|
+
this.stopPanInertia();
|
|
50
126
|
}
|
|
51
127
|
};
|
|
52
128
|
var GRID = 20;
|
|
@@ -91,6 +167,8 @@ var SelectTool = class extends BaseTool {
|
|
|
91
167
|
const hits = this.spatialIndex.queryPoint({ x: e.worldX, y: e.worldY }, 12);
|
|
92
168
|
const seatHit = this.pickNearestSeatHit(hits, { x: e.worldX, y: e.worldY });
|
|
93
169
|
const sectionHit = hits.find((h) => h.type === "section");
|
|
170
|
+
const seatSection = seatHit ? venue.sections.find((section) => section.id === seatHit.sectionId) ?? null : null;
|
|
171
|
+
const treatSeatHitAsSection = Boolean(seatSection && seatmapCore.isDancefloorSection(seatSection));
|
|
94
172
|
if (this.sectionResizeEnabled) {
|
|
95
173
|
const clickedSection = sectionHit ? venue.sections.find((s) => s.id === sectionHit.sectionId) ?? null : null;
|
|
96
174
|
if (clickedSection) {
|
|
@@ -126,11 +204,12 @@ var SelectTool = class extends BaseTool {
|
|
|
126
204
|
return;
|
|
127
205
|
}
|
|
128
206
|
if (clickedSection && !seatHit) {
|
|
207
|
+
this.beginSectionDrag(venue, store, clickedSection);
|
|
129
208
|
return;
|
|
130
209
|
}
|
|
131
210
|
}
|
|
132
211
|
}
|
|
133
|
-
if (seatHit?.seatId && store.getState().selectedSeatIds.has(seatHit.seatId)) {
|
|
212
|
+
if (seatHit?.seatId && !treatSeatHitAsSection && store.getState().selectedSeatIds.has(seatHit.seatId)) {
|
|
134
213
|
const selectedIds = store.getState().selectedSeatIds;
|
|
135
214
|
const sectionId = seatHit.sectionId;
|
|
136
215
|
const originals = /* @__PURE__ */ new Map();
|
|
@@ -155,24 +234,11 @@ var SelectTool = class extends BaseTool {
|
|
|
155
234
|
return;
|
|
156
235
|
}
|
|
157
236
|
}
|
|
158
|
-
|
|
159
|
-
|
|
237
|
+
const sectionToDragId = treatSeatHitAsSection ? seatHit?.sectionId : sectionHit?.sectionId;
|
|
238
|
+
if (sectionToDragId && (treatSeatHitAsSection || !seatHit)) {
|
|
239
|
+
const section = venue.sections.find((s) => s.id === sectionToDragId);
|
|
160
240
|
if (section) {
|
|
161
|
-
|
|
162
|
-
const sectionIds = selectedSectionIds.has(section.id) && selectedSectionIds.size > 1 ? [...selectedSectionIds] : [section.id];
|
|
163
|
-
const originalPositions = /* @__PURE__ */ new Map();
|
|
164
|
-
for (const sec of venue.sections) {
|
|
165
|
-
if (sectionIds.includes(sec.id)) {
|
|
166
|
-
originalPositions.set(sec.id, { ...sec.position });
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
this.dragMode = {
|
|
170
|
-
type: "section",
|
|
171
|
-
primarySectionId: section.id,
|
|
172
|
-
sectionIds,
|
|
173
|
-
originalPositions,
|
|
174
|
-
delta: { x: 0, y: 0 }
|
|
175
|
-
};
|
|
241
|
+
this.beginSectionDrag(venue, store, section);
|
|
176
242
|
return;
|
|
177
243
|
}
|
|
178
244
|
}
|
|
@@ -327,8 +393,10 @@ var SelectTool = class extends BaseTool {
|
|
|
327
393
|
const hits = this.spatialIndex.queryPoint({ x: e.worldX, y: e.worldY }, 12);
|
|
328
394
|
const seatHit = this.pickNearestSeatHit(hits, { x: e.worldX, y: e.worldY });
|
|
329
395
|
const sectionHit = hits.find((h) => h.type === "section");
|
|
396
|
+
const seatSection = seatHit ? store.getState().venue?.sections.find((section) => section.id === seatHit.sectionId) ?? null : null;
|
|
397
|
+
const treatSeatHitAsSection = Boolean(seatSection && seatmapCore.isDancefloorSection(seatSection));
|
|
330
398
|
const isMulti = e.ctrlKey || e.shiftKey || e.metaKey;
|
|
331
|
-
if (seatHit?.seatId) {
|
|
399
|
+
if (seatHit?.seatId && !treatSeatHitAsSection) {
|
|
332
400
|
this.resizeTargetSectionId = seatHit.sectionId;
|
|
333
401
|
if (isMulti) {
|
|
334
402
|
store.getState().toggleSeat(seatHit.seatId);
|
|
@@ -339,22 +407,28 @@ var SelectTool = class extends BaseTool {
|
|
|
339
407
|
store.getState().setSelection([seatHit.seatId]);
|
|
340
408
|
store.getState().selectSection(seatHit.sectionId);
|
|
341
409
|
}
|
|
342
|
-
} else
|
|
410
|
+
} else {
|
|
411
|
+
const sectionId = treatSeatHitAsSection ? seatHit?.sectionId : sectionHit?.sectionId;
|
|
412
|
+
if (!sectionId) {
|
|
413
|
+
if (!isMulti) {
|
|
414
|
+
if (this.sectionResizeEnabled) {
|
|
415
|
+
this.resizeTargetSectionId = null;
|
|
416
|
+
}
|
|
417
|
+
store.getState().clearSelection();
|
|
418
|
+
}
|
|
419
|
+
this.reset();
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
343
422
|
if (this.sectionResizeEnabled) {
|
|
344
|
-
this.resizeTargetSectionId =
|
|
423
|
+
this.resizeTargetSectionId = sectionId;
|
|
345
424
|
} else {
|
|
346
425
|
if (isMulti) {
|
|
347
|
-
store.getState().toggleSection(
|
|
426
|
+
store.getState().toggleSection(sectionId);
|
|
348
427
|
} else {
|
|
349
428
|
store.getState().clearSelection();
|
|
350
|
-
store.getState().selectSection(
|
|
429
|
+
store.getState().selectSection(sectionId);
|
|
351
430
|
}
|
|
352
431
|
}
|
|
353
|
-
} else if (!isMulti) {
|
|
354
|
-
if (this.sectionResizeEnabled) {
|
|
355
|
-
this.resizeTargetSectionId = null;
|
|
356
|
-
}
|
|
357
|
-
store.getState().clearSelection();
|
|
358
432
|
}
|
|
359
433
|
}
|
|
360
434
|
this.reset();
|
|
@@ -395,6 +469,23 @@ var SelectTool = class extends BaseTool {
|
|
|
395
469
|
}
|
|
396
470
|
});
|
|
397
471
|
}
|
|
472
|
+
beginSectionDrag(venue, store, section) {
|
|
473
|
+
const selectedSectionIds = store.getState().selectedSectionIds;
|
|
474
|
+
const sectionIds = selectedSectionIds.has(section.id) && selectedSectionIds.size > 1 ? [...selectedSectionIds] : [section.id];
|
|
475
|
+
const originalPositions = /* @__PURE__ */ new Map();
|
|
476
|
+
for (const sec of venue.sections) {
|
|
477
|
+
if (sectionIds.includes(sec.id)) {
|
|
478
|
+
originalPositions.set(sec.id, { ...sec.position });
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
this.dragMode = {
|
|
482
|
+
type: "section",
|
|
483
|
+
primarySectionId: section.id,
|
|
484
|
+
sectionIds,
|
|
485
|
+
originalPositions,
|
|
486
|
+
delta: { x: 0, y: 0 }
|
|
487
|
+
};
|
|
488
|
+
}
|
|
398
489
|
commitDrag(store, dropWorld) {
|
|
399
490
|
const venue = store.getState().venue;
|
|
400
491
|
if (!venue) return;
|
|
@@ -1027,6 +1118,8 @@ var SelectTool = class extends BaseTool {
|
|
|
1027
1118
|
}
|
|
1028
1119
|
};
|
|
1029
1120
|
var CLOSE_THRESHOLD = 15;
|
|
1121
|
+
var DANCEFLOOR_SEAT_ID_SUFFIX = "dancefloor-seat";
|
|
1122
|
+
var DANCEFLOOR_ROW_ID_SUFFIX = "dancefloor-row";
|
|
1030
1123
|
var AddSectionTool = class extends BaseTool {
|
|
1031
1124
|
constructor(history, categoryId = "") {
|
|
1032
1125
|
super();
|
|
@@ -1036,8 +1129,10 @@ var AddSectionTool = class extends BaseTool {
|
|
|
1036
1129
|
name = "add-section";
|
|
1037
1130
|
cursor = "crosshair";
|
|
1038
1131
|
mode = "rectangle";
|
|
1132
|
+
sectionKind = "section";
|
|
1039
1133
|
points = [];
|
|
1040
1134
|
onPointsChange;
|
|
1135
|
+
onSectionCreated;
|
|
1041
1136
|
setCategoryId(id) {
|
|
1042
1137
|
this.categoryId = id;
|
|
1043
1138
|
}
|
|
@@ -1046,6 +1141,20 @@ var AddSectionTool = class extends BaseTool {
|
|
|
1046
1141
|
this.points = [];
|
|
1047
1142
|
this.notifyChange();
|
|
1048
1143
|
}
|
|
1144
|
+
setSectionKind(sectionKind) {
|
|
1145
|
+
if (this.sectionKind === sectionKind) return;
|
|
1146
|
+
this.sectionKind = sectionKind;
|
|
1147
|
+
this.points = [];
|
|
1148
|
+
this.notifyChange();
|
|
1149
|
+
}
|
|
1150
|
+
hasPendingDraft() {
|
|
1151
|
+
return this.points.length > 0;
|
|
1152
|
+
}
|
|
1153
|
+
cancelDrawing() {
|
|
1154
|
+
if (this.points.length === 0) return;
|
|
1155
|
+
this.points = [];
|
|
1156
|
+
this.notifyChange();
|
|
1157
|
+
}
|
|
1049
1158
|
onPointerDown(e, _viewport, store) {
|
|
1050
1159
|
if (this.mode === "rectangle") {
|
|
1051
1160
|
if (this.points.length === 0) {
|
|
@@ -1107,11 +1216,12 @@ var AddSectionTool = class extends BaseTool {
|
|
|
1107
1216
|
}));
|
|
1108
1217
|
const newSection = {
|
|
1109
1218
|
id: seatmapCore.generateId(),
|
|
1110
|
-
label: `Section ${Date.now().toString(36).slice(-3).toUpperCase()}`,
|
|
1219
|
+
label: this.sectionKind === "stage" ? "Stage" : this.sectionKind === "dancefloor" ? "Dancefloor" : `Section ${Date.now().toString(36).slice(-3).toUpperCase()}`,
|
|
1220
|
+
kind: this.sectionKind,
|
|
1111
1221
|
position: { x: cx, y: cy },
|
|
1112
1222
|
rotation: 0,
|
|
1113
|
-
categoryId: this.categoryId,
|
|
1114
|
-
rows: [],
|
|
1223
|
+
categoryId: this.sectionKind === "stage" ? "" : this.categoryId,
|
|
1224
|
+
rows: this.sectionKind === "dancefloor" ? this.createDancefloorRows(this.categoryId) : [],
|
|
1115
1225
|
outline
|
|
1116
1226
|
};
|
|
1117
1227
|
this.history.execute({
|
|
@@ -1120,6 +1230,7 @@ var AddSectionTool = class extends BaseTool {
|
|
|
1120
1230
|
const v = store.getState().venue;
|
|
1121
1231
|
if (!v) return;
|
|
1122
1232
|
store.getState().setVenue({ ...v, sections: [...v.sections, newSection] });
|
|
1233
|
+
this.onSectionCreated?.(newSection.id);
|
|
1123
1234
|
},
|
|
1124
1235
|
undo: () => {
|
|
1125
1236
|
const v = store.getState().venue;
|
|
@@ -1139,12 +1250,27 @@ var AddSectionTool = class extends BaseTool {
|
|
|
1139
1250
|
{ x: a.x, y: b.y }
|
|
1140
1251
|
];
|
|
1141
1252
|
}
|
|
1253
|
+
createDancefloorRows(categoryId) {
|
|
1254
|
+
const dancefloorSeat = {
|
|
1255
|
+
id: seatmapCore.generateId(DANCEFLOOR_SEAT_ID_SUFFIX),
|
|
1256
|
+
label: "Dancefloor",
|
|
1257
|
+
position: { x: 0, y: 0 },
|
|
1258
|
+
status: "available",
|
|
1259
|
+
categoryId
|
|
1260
|
+
};
|
|
1261
|
+
return [
|
|
1262
|
+
{
|
|
1263
|
+
id: seatmapCore.generateId(DANCEFLOOR_ROW_ID_SUFFIX),
|
|
1264
|
+
label: "DF",
|
|
1265
|
+
seats: [dancefloorSeat]
|
|
1266
|
+
}
|
|
1267
|
+
];
|
|
1268
|
+
}
|
|
1142
1269
|
notifyChange() {
|
|
1143
1270
|
this.onPointsChange?.(this.points, false);
|
|
1144
1271
|
}
|
|
1145
1272
|
onDeactivate() {
|
|
1146
|
-
this.
|
|
1147
|
-
this.notifyChange();
|
|
1273
|
+
this.cancelDrawing();
|
|
1148
1274
|
}
|
|
1149
1275
|
};
|
|
1150
1276
|
var GRID2 = 20;
|
|
@@ -1276,7 +1402,7 @@ var AddRowTool = class extends BaseTool {
|
|
|
1276
1402
|
const sectionHits = hits.filter((h) => h.type === "section");
|
|
1277
1403
|
if (sectionHits.length === 0) return null;
|
|
1278
1404
|
const sectionIds = [...new Set(sectionHits.map((h) => h.sectionId))];
|
|
1279
|
-
const sections = sectionIds.map((id) => venue.sections.find((s) => s.id === id)).filter((s) => Boolean(s));
|
|
1405
|
+
const sections = sectionIds.map((id) => venue.sections.find((s) => s.id === id)).filter((s) => Boolean(s)).filter((section) => !seatmapCore.isStageSection(section) && !seatmapCore.isDancefloorSection(section));
|
|
1280
1406
|
if (sections.length === 0) return null;
|
|
1281
1407
|
const containing = sections.find((section) => {
|
|
1282
1408
|
if (section.outline.length < 3) return true;
|
|
@@ -1322,7 +1448,7 @@ var AddSeatTool = class extends BaseTool {
|
|
|
1322
1448
|
const sectionHits = hits.filter((h) => h.type === "section");
|
|
1323
1449
|
if (sectionHits.length === 0) return;
|
|
1324
1450
|
const sectionIds = [...new Set(sectionHits.map((h) => h.sectionId))];
|
|
1325
|
-
let section = sectionIds.map((id) => venue.sections.find((s) => s.id === id)).filter((s) => Boolean(s)).find((s) => {
|
|
1451
|
+
let section = sectionIds.map((id) => venue.sections.find((s) => s.id === id)).filter((s) => Boolean(s)).filter((s) => !seatmapCore.isStageSection(s) && !seatmapCore.isDancefloorSection(s)).find((s) => {
|
|
1326
1452
|
if (s.outline.length < 3) return true;
|
|
1327
1453
|
const c2 = Math.cos(-s.rotation);
|
|
1328
1454
|
const s22 = Math.sin(-s.rotation);
|
|
@@ -1335,7 +1461,7 @@ var AddSeatTool = class extends BaseTool {
|
|
|
1335
1461
|
return seatmapCore.pointInPolygon(local, s.outline);
|
|
1336
1462
|
});
|
|
1337
1463
|
if (!section) {
|
|
1338
|
-
section = sectionIds.map((id) => venue.sections.find((s) => s.id === id)).filter((s) => Boolean(s)).sort(
|
|
1464
|
+
section = sectionIds.map((id) => venue.sections.find((s) => s.id === id)).filter((s) => Boolean(s)).filter((s) => !seatmapCore.isStageSection(s) && !seatmapCore.isDancefloorSection(s)).sort(
|
|
1339
1465
|
(a, b) => Math.hypot(e.worldX - a.position.x, e.worldY - a.position.y) - Math.hypot(e.worldX - b.position.x, e.worldY - b.position.y)
|
|
1340
1466
|
)[0];
|
|
1341
1467
|
}
|
|
@@ -1450,33 +1576,100 @@ var AddSeatTool = class extends BaseTool {
|
|
|
1450
1576
|
return candidate;
|
|
1451
1577
|
}
|
|
1452
1578
|
};
|
|
1579
|
+
function PanIcon(props) {
|
|
1580
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1581
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 11V6.6a1.6 1.6 0 1 1 3.2 0V10", strokeLinecap: "round" }),
|
|
1582
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M11.2 10V5.2a1.6 1.6 0 1 1 3.2 0v5", strokeLinecap: "round" }),
|
|
1583
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M14.4 10V6.8a1.6 1.6 0 1 1 3.2 0v6.2c0 4-2 6.2-6 6.2h-.4c-3.3 0-5.8-2.2-6.4-5.4l-.7-3.7a1.4 1.4 0 1 1 2.7-.6l.6 2.5", strokeLinecap: "round" })
|
|
1584
|
+
] });
|
|
1585
|
+
}
|
|
1586
|
+
function SelectIcon(props) {
|
|
1587
|
+
return /* @__PURE__ */ jsxRuntime.jsx("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 4.5v14l4.4-3 2.5 4.1 2.1-1.3-2.5-4.1 5-.3L5 4.5Z", strokeLinejoin: "round" }) });
|
|
1588
|
+
}
|
|
1589
|
+
function SectionIcon(props) {
|
|
1590
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1591
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "4.5", y: "4.5", width: "15", height: "15", rx: "2.5" }),
|
|
1592
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 9h6M9 15h6", strokeLinecap: "round" })
|
|
1593
|
+
] });
|
|
1594
|
+
}
|
|
1595
|
+
function RowIcon(props) {
|
|
1596
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1597
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4.5 8h15M4.5 12h15M4.5 16h15", strokeLinecap: "round" }),
|
|
1598
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "6.4", cy: "8", r: "0.8", fill: "currentColor", stroke: "none" }),
|
|
1599
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "6.4", cy: "12", r: "0.8", fill: "currentColor", stroke: "none" }),
|
|
1600
|
+
/* @__PURE__ */ jsxRuntime.jsx("circle", { cx: "6.4", cy: "16", r: "0.8", fill: "currentColor", stroke: "none" })
|
|
1601
|
+
] });
|
|
1602
|
+
}
|
|
1603
|
+
function SeatIcon(props) {
|
|
1604
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1605
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M7 12.5V9.4a2.4 2.4 0 1 1 4.8 0v3.1", strokeLinecap: "round" }),
|
|
1606
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5 17v-3.1a1.9 1.9 0 0 1 1.9-1.9h8.2a1.9 1.9 0 0 1 1.9 1.9V17", strokeLinecap: "round" }),
|
|
1607
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M4.8 17.8h14.4", strokeLinecap: "round" })
|
|
1608
|
+
] });
|
|
1609
|
+
}
|
|
1610
|
+
function UndoIcon(props) {
|
|
1611
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1612
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8.5 8.5H5v3.5", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1613
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M5.2 8.7c1.8-2 4.2-3.2 7-3.2 5 0 8.8 3.7 8.8 8.5", strokeLinecap: "round" })
|
|
1614
|
+
] });
|
|
1615
|
+
}
|
|
1616
|
+
function RedoIcon(props) {
|
|
1617
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1618
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15.5 8.5H19v3.5", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1619
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M18.8 8.7c-1.8-2-4.2-3.2-7-3.2C6.8 5.5 3 9.2 3 14", strokeLinecap: "round" })
|
|
1620
|
+
] });
|
|
1621
|
+
}
|
|
1622
|
+
function FitIcon(props) {
|
|
1623
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1624
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9 4.8H4.8V9M15 4.8h4.2V9M9 19.2H4.8V15M15 19.2h4.2V15", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1625
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "8.2", y: "8.2", width: "7.6", height: "7.6", rx: "1.4" })
|
|
1626
|
+
] });
|
|
1627
|
+
}
|
|
1628
|
+
function GridIcon(props) {
|
|
1629
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1630
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "5", width: "14", height: "14", rx: "2" }),
|
|
1631
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 5v14M5 12h14", strokeLinecap: "round" })
|
|
1632
|
+
] });
|
|
1633
|
+
}
|
|
1634
|
+
function HintIcon(props) {
|
|
1635
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1636
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9.4 9.2a2.7 2.7 0 1 1 4.8 1.6c-.6.8-1.6 1.4-1.8 2.7", strokeLinecap: "round" }),
|
|
1637
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 17.4h.01M8.7 19h6.6", strokeLinecap: "round" }),
|
|
1638
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 3.8a8.2 8.2 0 1 1 0 16.4 8.2 8.2 0 0 1 0-16.4Z" })
|
|
1639
|
+
] });
|
|
1640
|
+
}
|
|
1641
|
+
function SaveIcon(props) {
|
|
1642
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1643
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 4.5v9.2M8.4 10.7 12 14.3l3.6-3.6", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1644
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "16.2", width: "14", height: "3.8", rx: "1.2" })
|
|
1645
|
+
] });
|
|
1646
|
+
}
|
|
1647
|
+
function LoadIcon(props) {
|
|
1648
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1649
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 14.7V5.5M8.4 9.3 12 5.7l3.6 3.6", strokeLinecap: "round", strokeLinejoin: "round" }),
|
|
1650
|
+
/* @__PURE__ */ jsxRuntime.jsx("rect", { x: "5", y: "16.2", width: "14", height: "3.8", rx: "1.2" })
|
|
1651
|
+
] });
|
|
1652
|
+
}
|
|
1653
|
+
function SettingsIcon(props) {
|
|
1654
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", "aria-hidden": true, ...props, children: [
|
|
1655
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M12 8.2a3.8 3.8 0 1 1 0 7.6 3.8 3.8 0 0 1 0-7.6Z" }),
|
|
1656
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1657
|
+
"path",
|
|
1658
|
+
{
|
|
1659
|
+
d: "m18.8 12 .9 1.6-1.5 2.6-1.8-.1a6.8 6.8 0 0 1-1.4.8L14.4 19h-3l-.6-2.1a6.8 6.8 0 0 1-1.4-.8l-1.8.1-1.5-2.6.9-1.6a6.7 6.7 0 0 1 0-1.6l-.9-1.6 1.5-2.6 1.8.1c.4-.3.9-.6 1.4-.8l.6-2.1h3l.6 2.1c.5.2 1 .5 1.4.8l1.8-.1 1.5 2.6-.9 1.6c.1.5.1 1.1 0 1.6Z",
|
|
1660
|
+
strokeLinecap: "round",
|
|
1661
|
+
strokeLinejoin: "round"
|
|
1662
|
+
}
|
|
1663
|
+
)
|
|
1664
|
+
] });
|
|
1665
|
+
}
|
|
1453
1666
|
var tools = [
|
|
1454
|
-
{ id: "pan", label: "Pan", icon:
|
|
1455
|
-
{ id: "select", label: "Select", icon:
|
|
1456
|
-
{ id: "add-section", label: "Section", icon:
|
|
1457
|
-
{ id: "add-row", label: "Row", icon:
|
|
1458
|
-
{ id: "add-seat", label: "Seat", icon:
|
|
1667
|
+
{ id: "pan", label: "Pan", icon: PanIcon },
|
|
1668
|
+
{ id: "select", label: "Select", icon: SelectIcon },
|
|
1669
|
+
{ id: "add-section", label: "Section", icon: SectionIcon },
|
|
1670
|
+
{ id: "add-row", label: "Row", icon: RowIcon },
|
|
1671
|
+
{ id: "add-seat", label: "Seat", icon: SeatIcon }
|
|
1459
1672
|
];
|
|
1460
|
-
var btnBase = {
|
|
1461
|
-
padding: "6px 10px",
|
|
1462
|
-
borderWidth: 1,
|
|
1463
|
-
borderStyle: "solid",
|
|
1464
|
-
borderColor: "#3a3a5a",
|
|
1465
|
-
borderRadius: 6,
|
|
1466
|
-
background: "#2a2a4a",
|
|
1467
|
-
color: "#e0e0e0",
|
|
1468
|
-
cursor: "pointer",
|
|
1469
|
-
fontSize: 13,
|
|
1470
|
-
fontFamily: "system-ui",
|
|
1471
|
-
display: "flex",
|
|
1472
|
-
alignItems: "center",
|
|
1473
|
-
gap: 4
|
|
1474
|
-
};
|
|
1475
|
-
var activeBtnStyle = {
|
|
1476
|
-
...btnBase,
|
|
1477
|
-
background: "#4a4a7a",
|
|
1478
|
-
borderColor: "#6a6aaa"
|
|
1479
|
-
};
|
|
1480
1673
|
function Toolbar({
|
|
1481
1674
|
activeTool,
|
|
1482
1675
|
onToolChange,
|
|
@@ -1490,112 +1683,171 @@ function Toolbar({
|
|
|
1490
1683
|
onFitView,
|
|
1491
1684
|
onSave,
|
|
1492
1685
|
onLoad,
|
|
1686
|
+
showHints,
|
|
1687
|
+
onToggleHints,
|
|
1688
|
+
isEditorSettingsOpen,
|
|
1689
|
+
onToggleEditorSettings,
|
|
1493
1690
|
style
|
|
1494
1691
|
}) {
|
|
1692
|
+
const getToolbarButtonClassName = (isActive = false, isHighlighted = false) => `seatmap-editor__toolbar-button${isActive ? " is-active" : ""}${isHighlighted ? " is-highlighted" : ""}`;
|
|
1495
1693
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1496
1694
|
"div",
|
|
1497
1695
|
{
|
|
1696
|
+
className: `seatmap-editor__toolbar${showHints ? " has-shortcuts-row" : ""}`,
|
|
1498
1697
|
style: {
|
|
1499
|
-
display: "flex",
|
|
1500
|
-
background: "#1a1a2e",
|
|
1501
|
-
borderBottom: "1px solid #2a2a4a",
|
|
1502
1698
|
...style
|
|
1503
1699
|
},
|
|
1504
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1700
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__toolbar-row seatmap-editor__toolbar-row--primary", children: [
|
|
1701
|
+
tools.map((tool) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1702
|
+
"button",
|
|
1703
|
+
{
|
|
1704
|
+
type: "button",
|
|
1705
|
+
onClick: () => onToolChange(tool.id),
|
|
1706
|
+
className: `${getToolbarButtonClassName(activeTool === tool.id)} seatmap-editor__toolbar-tool-button`,
|
|
1707
|
+
title: tool.label,
|
|
1708
|
+
children: [
|
|
1709
|
+
/* @__PURE__ */ jsxRuntime.jsx(tool.icon, { className: "seatmap-editor__toolbar-icon" }),
|
|
1710
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: tool.label })
|
|
1711
|
+
]
|
|
1513
1712
|
},
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1713
|
+
tool.id
|
|
1714
|
+
)),
|
|
1715
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__toolbar-divider" }),
|
|
1716
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1717
|
+
"button",
|
|
1718
|
+
{
|
|
1719
|
+
type: "button",
|
|
1720
|
+
onClick: onUndo,
|
|
1721
|
+
disabled: !canUndo,
|
|
1722
|
+
className: getToolbarButtonClassName(),
|
|
1723
|
+
title: "Undo (Ctrl+Z)",
|
|
1724
|
+
children: [
|
|
1725
|
+
/* @__PURE__ */ jsxRuntime.jsx(UndoIcon, { className: "seatmap-editor__toolbar-icon" }),
|
|
1726
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Undo" })
|
|
1727
|
+
]
|
|
1728
|
+
}
|
|
1729
|
+
),
|
|
1730
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1731
|
+
"button",
|
|
1732
|
+
{
|
|
1733
|
+
type: "button",
|
|
1734
|
+
onClick: onRedo,
|
|
1735
|
+
disabled: !canRedo,
|
|
1736
|
+
className: getToolbarButtonClassName(),
|
|
1737
|
+
title: "Redo (Ctrl+Shift+Z)",
|
|
1738
|
+
children: [
|
|
1739
|
+
/* @__PURE__ */ jsxRuntime.jsx(RedoIcon, { className: "seatmap-editor__toolbar-icon" }),
|
|
1740
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Redo" })
|
|
1741
|
+
]
|
|
1742
|
+
}
|
|
1743
|
+
),
|
|
1744
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__toolbar-divider" }),
|
|
1745
|
+
/* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: onFitView, className: getToolbarButtonClassName(), title: "Fit to view", children: [
|
|
1746
|
+
/* @__PURE__ */ jsxRuntime.jsx(FitIcon, { className: "seatmap-editor__toolbar-icon" }),
|
|
1747
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Fit" })
|
|
1748
|
+
] }),
|
|
1749
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1750
|
+
"button",
|
|
1751
|
+
{
|
|
1752
|
+
type: "button",
|
|
1753
|
+
onClick: onToggleGridOptions,
|
|
1754
|
+
className: getToolbarButtonClassName(isGridOptionsOpen, gridEnabled),
|
|
1755
|
+
title: "Show grid options",
|
|
1756
|
+
children: [
|
|
1757
|
+
/* @__PURE__ */ jsxRuntime.jsx(GridIcon, { className: "seatmap-editor__toolbar-icon" }),
|
|
1758
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Grid" })
|
|
1759
|
+
]
|
|
1760
|
+
}
|
|
1761
|
+
),
|
|
1762
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1763
|
+
"button",
|
|
1764
|
+
{
|
|
1765
|
+
type: "button",
|
|
1766
|
+
onClick: onToggleHints,
|
|
1767
|
+
className: getToolbarButtonClassName(showHints, showHints),
|
|
1768
|
+
title: "Toggle inline editor hints",
|
|
1769
|
+
children: [
|
|
1770
|
+
/* @__PURE__ */ jsxRuntime.jsx(HintIcon, { className: "seatmap-editor__toolbar-icon" }),
|
|
1771
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Hints" })
|
|
1772
|
+
]
|
|
1773
|
+
}
|
|
1774
|
+
),
|
|
1775
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
1776
|
+
"button",
|
|
1777
|
+
{
|
|
1778
|
+
type: "button",
|
|
1779
|
+
onClick: onToggleEditorSettings,
|
|
1780
|
+
className: getToolbarButtonClassName(isEditorSettingsOpen, isEditorSettingsOpen),
|
|
1781
|
+
title: "Editor settings",
|
|
1782
|
+
children: [
|
|
1783
|
+
/* @__PURE__ */ jsxRuntime.jsx(SettingsIcon, { className: "seatmap-editor__toolbar-icon" }),
|
|
1784
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Settings" })
|
|
1785
|
+
]
|
|
1786
|
+
}
|
|
1787
|
+
),
|
|
1788
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__toolbar-divider" }),
|
|
1789
|
+
/* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: onSave, className: getToolbarButtonClassName(), title: "Export venue as JSON", children: [
|
|
1790
|
+
/* @__PURE__ */ jsxRuntime.jsx(SaveIcon, { className: "seatmap-editor__toolbar-icon" }),
|
|
1791
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Save" })
|
|
1792
|
+
] }),
|
|
1793
|
+
/* @__PURE__ */ jsxRuntime.jsxs("button", { type: "button", onClick: onLoad, className: getToolbarButtonClassName(), title: "Import venue from JSON", children: [
|
|
1794
|
+
/* @__PURE__ */ jsxRuntime.jsx(LoadIcon, { className: "seatmap-editor__toolbar-icon" }),
|
|
1795
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: "Load" })
|
|
1796
|
+
] }),
|
|
1797
|
+
showHints && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__toolbar-shortcuts-panel", "aria-label": "Keyboard shortcuts", children: [
|
|
1798
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__toolbar-shortcuts-title", children: "Keyboard shortcuts" }),
|
|
1799
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__toolbar-shortcuts-row", children: [
|
|
1800
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1801
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { children: "H" }),
|
|
1802
|
+
" / ",
|
|
1803
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { children: "1" }),
|
|
1804
|
+
" - Pan"
|
|
1805
|
+
] }),
|
|
1806
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1807
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { children: "V" }),
|
|
1808
|
+
" / ",
|
|
1809
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { children: "2" }),
|
|
1810
|
+
" - Select"
|
|
1811
|
+
] }),
|
|
1812
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1813
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { children: "S" }),
|
|
1814
|
+
" / ",
|
|
1815
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { children: "3" }),
|
|
1816
|
+
" - Add Section"
|
|
1817
|
+
] })
|
|
1818
|
+
] }),
|
|
1819
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__toolbar-shortcuts-row", children: [
|
|
1820
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1821
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { children: "R" }),
|
|
1822
|
+
" / ",
|
|
1823
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { children: "4" }),
|
|
1824
|
+
" - Add Row"
|
|
1825
|
+
] }),
|
|
1826
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1827
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { children: "A" }),
|
|
1828
|
+
" / ",
|
|
1829
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { children: "5" }),
|
|
1830
|
+
" - Add Seat"
|
|
1831
|
+
] }),
|
|
1832
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1833
|
+
/* @__PURE__ */ jsxRuntime.jsx("kbd", { children: "Space" }),
|
|
1834
|
+
" (toggle) - Toggle Pan"
|
|
1835
|
+
] })
|
|
1836
|
+
] })
|
|
1837
|
+
] })
|
|
1838
|
+
] })
|
|
1552
1839
|
}
|
|
1553
1840
|
);
|
|
1554
1841
|
}
|
|
1555
|
-
var labelStyle = {
|
|
1556
|
-
fontSize: 11,
|
|
1557
|
-
color: "#9e9e9e",
|
|
1558
|
-
marginBottom: 2,
|
|
1559
|
-
fontFamily: "system-ui"
|
|
1560
|
-
};
|
|
1561
|
-
var inputStyle = {
|
|
1562
|
-
width: "100%",
|
|
1563
|
-
padding: "4px 8px",
|
|
1564
|
-
background: "#2a2a4a",
|
|
1565
|
-
border: "1px solid #3a3a5a",
|
|
1566
|
-
borderRadius: 4,
|
|
1567
|
-
color: "#e0e0e0",
|
|
1568
|
-
fontSize: 13,
|
|
1569
|
-
fontFamily: "system-ui",
|
|
1570
|
-
boxSizing: "border-box"
|
|
1571
|
-
};
|
|
1572
|
-
var selectStyle = { ...inputStyle, cursor: "pointer" };
|
|
1573
|
-
var btnDanger = {
|
|
1574
|
-
padding: "3px 8px",
|
|
1575
|
-
border: "1px solid #5a2a2a",
|
|
1576
|
-
borderRadius: 4,
|
|
1577
|
-
background: "#3a1a1a",
|
|
1578
|
-
color: "#f48888",
|
|
1579
|
-
cursor: "pointer",
|
|
1580
|
-
fontSize: 12,
|
|
1581
|
-
fontFamily: "system-ui"
|
|
1582
|
-
};
|
|
1583
|
-
var btnSmall = {
|
|
1584
|
-
padding: "3px 8px",
|
|
1585
|
-
border: "1px solid #3a3a5a",
|
|
1586
|
-
borderRadius: 4,
|
|
1587
|
-
background: "#2a2a4a",
|
|
1588
|
-
color: "#e0e0e0",
|
|
1589
|
-
cursor: "pointer",
|
|
1590
|
-
fontSize: 12,
|
|
1591
|
-
fontFamily: "system-ui"
|
|
1592
|
-
};
|
|
1593
1842
|
function freshVenue(store) {
|
|
1594
1843
|
return store.getState().venue;
|
|
1595
1844
|
}
|
|
1596
1845
|
function setVenue(store, venue) {
|
|
1597
1846
|
store.getState().setVenue(venue);
|
|
1598
1847
|
}
|
|
1848
|
+
function isSectionSeatLayoutLocked(section) {
|
|
1849
|
+
return seatmapCore.isStageSection(section) || seatmapCore.isDancefloorSection(section);
|
|
1850
|
+
}
|
|
1599
1851
|
function PropertyPanel({
|
|
1600
1852
|
venue,
|
|
1601
1853
|
selectedSeatIds,
|
|
@@ -1668,7 +1920,9 @@ function PropertyPanel({
|
|
|
1668
1920
|
const updateSectionCategory = (sectionId, categoryId) => {
|
|
1669
1921
|
const v = freshVenue(store);
|
|
1670
1922
|
if (!v) return;
|
|
1671
|
-
const
|
|
1923
|
+
const targetSection = v.sections.find((s) => s.id === sectionId);
|
|
1924
|
+
if (!targetSection || seatmapCore.isStageSection(targetSection)) return;
|
|
1925
|
+
const oldCatId = targetSection.categoryId;
|
|
1672
1926
|
history.execute({
|
|
1673
1927
|
description: `Change section category`,
|
|
1674
1928
|
execute: () => {
|
|
@@ -1743,12 +1997,15 @@ function PropertyPanel({
|
|
|
1743
1997
|
if (sectionIds.length === 0) return;
|
|
1744
1998
|
const v = freshVenue(store);
|
|
1745
1999
|
if (!v) return;
|
|
1746
|
-
const targetIds = new Set(
|
|
2000
|
+
const targetIds = new Set(
|
|
2001
|
+
v.sections.filter((section) => sectionIds.includes(section.id) && !seatmapCore.isStageSection(section)).map((section) => section.id)
|
|
2002
|
+
);
|
|
2003
|
+
if (targetIds.size === 0) return;
|
|
1747
2004
|
const previousCategoryBySectionId = new Map(
|
|
1748
2005
|
v.sections.filter((section) => targetIds.has(section.id)).map((section) => [section.id, section.categoryId])
|
|
1749
2006
|
);
|
|
1750
2007
|
history.execute({
|
|
1751
|
-
description: `Change category for ${
|
|
2008
|
+
description: `Change category for ${targetIds.size} section(s)`,
|
|
1752
2009
|
execute: () => {
|
|
1753
2010
|
const cur = freshVenue(store);
|
|
1754
2011
|
if (!cur) return;
|
|
@@ -1788,26 +2045,6 @@ function PropertyPanel({
|
|
|
1788
2045
|
}
|
|
1789
2046
|
});
|
|
1790
2047
|
};
|
|
1791
|
-
const deleteSection = (sectionId) => {
|
|
1792
|
-
const v = freshVenue(store);
|
|
1793
|
-
if (!v) return;
|
|
1794
|
-
const removed = v.sections.find((s) => s.id === sectionId);
|
|
1795
|
-
if (!removed) return;
|
|
1796
|
-
history.execute({
|
|
1797
|
-
description: `Delete section "${removed.label}"`,
|
|
1798
|
-
execute: () => {
|
|
1799
|
-
const cur = freshVenue(store);
|
|
1800
|
-
if (!cur) return;
|
|
1801
|
-
setVenue(store, { ...cur, sections: cur.sections.filter((s) => s.id !== sectionId) });
|
|
1802
|
-
store.getState().clearSelection();
|
|
1803
|
-
},
|
|
1804
|
-
undo: () => {
|
|
1805
|
-
const cur = freshVenue(store);
|
|
1806
|
-
if (!cur) return;
|
|
1807
|
-
setVenue(store, { ...cur, sections: [...cur.sections, removed] });
|
|
1808
|
-
}
|
|
1809
|
-
});
|
|
1810
|
-
};
|
|
1811
2048
|
const deleteRow = (sectionId, rowId) => {
|
|
1812
2049
|
const v = freshVenue(store);
|
|
1813
2050
|
if (!v) return;
|
|
@@ -1881,11 +2118,43 @@ function PropertyPanel({
|
|
|
1881
2118
|
}
|
|
1882
2119
|
});
|
|
1883
2120
|
};
|
|
2121
|
+
const deleteSelectedObjects = () => {
|
|
2122
|
+
if (selectedSeatIds.size === 0 && selectedSectionIds.size === 0) return;
|
|
2123
|
+
const v = freshVenue(store);
|
|
2124
|
+
if (!v) return;
|
|
2125
|
+
const selectedSectionIdSet = new Set(selectedSectionIds);
|
|
2126
|
+
const selectedSeatIdSet = new Set(selectedSeatIds);
|
|
2127
|
+
const previousVenue = v;
|
|
2128
|
+
const nextVenue = {
|
|
2129
|
+
...v,
|
|
2130
|
+
sections: v.sections.filter((section) => !selectedSectionIdSet.has(section.id)).map((section) => ({
|
|
2131
|
+
...section,
|
|
2132
|
+
rows: section.rows.map((row) => ({
|
|
2133
|
+
...row,
|
|
2134
|
+
seats: row.seats.filter((seat) => !selectedSeatIdSet.has(seat.id))
|
|
2135
|
+
}))
|
|
2136
|
+
})),
|
|
2137
|
+
tables: v.tables.map((table) => ({
|
|
2138
|
+
...table,
|
|
2139
|
+
seats: table.seats.filter((seat) => !selectedSeatIdSet.has(seat.id))
|
|
2140
|
+
}))
|
|
2141
|
+
};
|
|
2142
|
+
history.execute({
|
|
2143
|
+
description: "Delete selected objects",
|
|
2144
|
+
execute: () => {
|
|
2145
|
+
setVenue(store, nextVenue);
|
|
2146
|
+
store.getState().clearSelection();
|
|
2147
|
+
},
|
|
2148
|
+
undo: () => {
|
|
2149
|
+
setVenue(store, previousVenue);
|
|
2150
|
+
}
|
|
2151
|
+
});
|
|
2152
|
+
};
|
|
1884
2153
|
const addSingleSeat = (sectionId) => {
|
|
1885
2154
|
const v = freshVenue(store);
|
|
1886
2155
|
if (!v) return;
|
|
1887
2156
|
const sec = v.sections.find((s) => s.id === sectionId);
|
|
1888
|
-
if (!sec) return;
|
|
2157
|
+
if (!sec || isSectionSeatLayoutLocked(sec)) return;
|
|
1889
2158
|
let targetRow = sec.rows[sec.rows.length - 1];
|
|
1890
2159
|
const newSeat = {
|
|
1891
2160
|
id: seatmapCore.generateId(),
|
|
@@ -1989,7 +2258,7 @@ function PropertyPanel({
|
|
|
1989
2258
|
});
|
|
1990
2259
|
};
|
|
1991
2260
|
if (!venue) {
|
|
1992
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2261
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel seatmap-editor__panel-muted", style, children: "No venue loaded" });
|
|
1993
2262
|
}
|
|
1994
2263
|
const selectedSeatsEverywhere = [];
|
|
1995
2264
|
for (const section of venue.sections) {
|
|
@@ -2008,24 +2277,29 @@ function PropertyPanel({
|
|
|
2008
2277
|
const isMixedSeatStatus = selectedSeatStatusIds.size > 1;
|
|
2009
2278
|
const selectedSeatStatusId = selectedSeatStatusIds.size > 0 ? [...selectedSeatStatusIds][0] : seatmapCore.AVAILABLE_STATUS_ID;
|
|
2010
2279
|
const hasMultipleSelectedSections = selectedSections.length > 1;
|
|
2280
|
+
const selectedNonStageSections = selectedSections.filter((section) => !seatmapCore.isStageSection(section));
|
|
2281
|
+
const hasSelectedStage = selectedSections.some((section) => seatmapCore.isStageSection(section));
|
|
2011
2282
|
const selectedSectionIdsList = selectedSections.map((section) => section.id);
|
|
2012
2283
|
const selectedSectionLabels = new Set(selectedSections.map((section) => section.label));
|
|
2013
|
-
const selectedSectionCategoryIds = new Set(
|
|
2284
|
+
const selectedSectionCategoryIds = new Set(selectedNonStageSections.map((section) => section.categoryId));
|
|
2014
2285
|
const sharedLabelValue = selectedSectionLabels.size === 1 ? selectedSections[0]?.label ?? "" : "";
|
|
2015
|
-
const sharedCategoryValue = selectedSectionCategoryIds.size === 1 ?
|
|
2016
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2017
|
-
selectedSeatIds.size > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2018
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2286
|
+
const sharedCategoryValue = selectedNonStageSections.length === 0 ? "" : selectedSectionCategoryIds.size === 1 ? selectedNonStageSections[0]?.categoryId ?? "" : "__mixed__";
|
|
2287
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel", style, children: [
|
|
2288
|
+
selectedSeatIds.size > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section", children: [
|
|
2289
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section-header", children: [
|
|
2290
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-title", children: [
|
|
2291
|
+
"Seat Config (",
|
|
2292
|
+
selectedSeatIds.size,
|
|
2293
|
+
" selected)"
|
|
2294
|
+
] }),
|
|
2295
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: deleteSelectedObjects, className: "seatmap-editor__panel-button seatmap-editor__panel-button--danger", title: "Delete selected objects", children: "Delete Selected" })
|
|
2022
2296
|
] }),
|
|
2023
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2024
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2297
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section", children: [
|
|
2298
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Seat Status" }),
|
|
2025
2299
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2026
2300
|
"select",
|
|
2027
2301
|
{
|
|
2028
|
-
|
|
2302
|
+
className: "seatmap-editor__panel-select",
|
|
2029
2303
|
value: isMixedSeatStatus ? "__mixed__" : selectedSeatStatusId,
|
|
2030
2304
|
onChange: (e) => updateSelectedSeatStatus(e.target.value),
|
|
2031
2305
|
children: [
|
|
@@ -2035,13 +2309,13 @@ function PropertyPanel({
|
|
|
2035
2309
|
}
|
|
2036
2310
|
)
|
|
2037
2311
|
] }),
|
|
2038
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2039
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2312
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section", children: [
|
|
2313
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-label", children: [
|
|
2040
2314
|
"Selected Seats (",
|
|
2041
2315
|
selectedSeatIds.size,
|
|
2042
2316
|
")"
|
|
2043
2317
|
] }),
|
|
2044
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2318
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-list seatmap-editor__panel-scroll seatmap-editor__panel-scroll--sm", children: Array.from(selectedSeatIds).map((seatId) => {
|
|
2045
2319
|
let found = null;
|
|
2046
2320
|
for (const section2 of venue.sections) {
|
|
2047
2321
|
for (const row2 of section2.rows) {
|
|
@@ -2058,20 +2332,9 @@ function PropertyPanel({
|
|
|
2058
2332
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2059
2333
|
"div",
|
|
2060
2334
|
{
|
|
2061
|
-
|
|
2062
|
-
display: "flex",
|
|
2063
|
-
alignItems: "center",
|
|
2064
|
-
gap: 6,
|
|
2065
|
-
padding: "2px 6px",
|
|
2066
|
-
fontSize: 12,
|
|
2067
|
-
fontFamily: "system-ui",
|
|
2068
|
-
color: "#e0e0e0",
|
|
2069
|
-
background: "#2a2a4a",
|
|
2070
|
-
marginBottom: 2,
|
|
2071
|
-
borderRadius: 4
|
|
2072
|
-
},
|
|
2335
|
+
className: "seatmap-editor__panel-list-item",
|
|
2073
2336
|
children: [
|
|
2074
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", {
|
|
2337
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "seatmap-editor__panel-text seatmap-editor__panel-content-grow", children: [
|
|
2075
2338
|
section.label,
|
|
2076
2339
|
" \xB7 Row ",
|
|
2077
2340
|
row.label,
|
|
@@ -2082,7 +2345,7 @@ function PropertyPanel({
|
|
|
2082
2345
|
"button",
|
|
2083
2346
|
{
|
|
2084
2347
|
onClick: () => deleteSeat(section.id, row.id, seat.id),
|
|
2085
|
-
|
|
2348
|
+
className: "seatmap-editor__panel-button seatmap-editor__panel-button--danger seatmap-editor__panel-button--tiny",
|
|
2086
2349
|
title: "Delete seat",
|
|
2087
2350
|
children: "\u2715"
|
|
2088
2351
|
}
|
|
@@ -2093,20 +2356,23 @@ function PropertyPanel({
|
|
|
2093
2356
|
);
|
|
2094
2357
|
}) })
|
|
2095
2358
|
] }),
|
|
2096
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2359
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-divider" })
|
|
2097
2360
|
] }),
|
|
2098
|
-
selectedSeatIds.size === 0 && selectedSections.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2099
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2100
|
-
"
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2105
|
-
|
|
2361
|
+
selectedSeatIds.size === 0 && selectedSections.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section", children: [
|
|
2362
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section-header", children: [
|
|
2363
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-title", children: [
|
|
2364
|
+
"Section / Stage / Dancefloor Config",
|
|
2365
|
+
selectedSections.length > 1 ? ` (${selectedSections.length} selected)` : ""
|
|
2366
|
+
] }),
|
|
2367
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: deleteSelectedObjects, className: "seatmap-editor__panel-button seatmap-editor__panel-button--danger", title: "Delete selected objects", children: "Delete Selected" })
|
|
2368
|
+
] }),
|
|
2369
|
+
hasMultipleSelectedSections && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section seatmap-editor__panel-section--card", children: [
|
|
2370
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section", children: [
|
|
2371
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Label (apply to all selected)" }),
|
|
2106
2372
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2107
2373
|
"input",
|
|
2108
2374
|
{
|
|
2109
|
-
|
|
2375
|
+
className: "seatmap-editor__panel-input",
|
|
2110
2376
|
value: sharedLabelValue,
|
|
2111
2377
|
placeholder: "Mixed labels",
|
|
2112
2378
|
onChange: (e) => updateSelectedSectionsLabel(selectedSectionIdsList, e.target.value)
|
|
@@ -2114,129 +2380,125 @@ function PropertyPanel({
|
|
|
2114
2380
|
)
|
|
2115
2381
|
] }),
|
|
2116
2382
|
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2117
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2383
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Category (apply to all selected)" }),
|
|
2118
2384
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2119
2385
|
"select",
|
|
2120
2386
|
{
|
|
2121
|
-
|
|
2387
|
+
className: "seatmap-editor__panel-select",
|
|
2122
2388
|
value: sharedCategoryValue,
|
|
2123
2389
|
onChange: (e) => updateSelectedSectionsCategory(selectedSectionIdsList, e.target.value),
|
|
2390
|
+
disabled: selectedNonStageSections.length === 0,
|
|
2124
2391
|
children: [
|
|
2392
|
+
selectedNonStageSections.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Not applicable for stage" }),
|
|
2125
2393
|
sharedCategoryValue === "__mixed__" && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "__mixed__", disabled: true, children: "Mixed" }),
|
|
2126
2394
|
venue.categories.map((cat) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: cat.id, children: cat.name }, cat.id))
|
|
2127
2395
|
]
|
|
2128
2396
|
}
|
|
2129
|
-
)
|
|
2397
|
+
),
|
|
2398
|
+
hasSelectedStage && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Stage selection is excluded from category changes." })
|
|
2130
2399
|
] })
|
|
2131
2400
|
] }),
|
|
2132
|
-
selectedSections.map((selectedSection) => /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2133
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
] }),
|
|
2138
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => deleteSection(selectedSection.id), style: btnDanger, title: "Delete section", children: "Delete" })
|
|
2139
|
-
] }),
|
|
2401
|
+
selectedSections.map((selectedSection) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section seatmap-editor__panel-section--card", children: [
|
|
2402
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-section-header", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-muted", children: [
|
|
2403
|
+
"ID: ",
|
|
2404
|
+
selectedSection.id
|
|
2405
|
+
] }) }),
|
|
2140
2406
|
!hasMultipleSelectedSections && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2141
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2142
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2407
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section", children: [
|
|
2408
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Label" }),
|
|
2143
2409
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2144
2410
|
"input",
|
|
2145
2411
|
{
|
|
2146
|
-
|
|
2412
|
+
className: "seatmap-editor__panel-input",
|
|
2147
2413
|
value: selectedSection.label,
|
|
2148
2414
|
onChange: (e) => updateSectionLabel(selectedSection.id, e.target.value)
|
|
2149
2415
|
}
|
|
2150
2416
|
)
|
|
2151
2417
|
] }),
|
|
2152
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2153
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2154
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2418
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section", children: [
|
|
2419
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Category" }),
|
|
2420
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2155
2421
|
"select",
|
|
2156
2422
|
{
|
|
2157
|
-
|
|
2423
|
+
className: "seatmap-editor__panel-select",
|
|
2158
2424
|
value: selectedSection.categoryId,
|
|
2159
2425
|
onChange: (e) => updateSectionCategory(selectedSection.id, e.target.value),
|
|
2160
|
-
|
|
2426
|
+
disabled: seatmapCore.isStageSection(selectedSection),
|
|
2427
|
+
children: [
|
|
2428
|
+
seatmapCore.isStageSection(selectedSection) && /* @__PURE__ */ jsxRuntime.jsx("option", { value: "", disabled: true, children: "Not applicable for stage" }),
|
|
2429
|
+
venue.categories.map((cat) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: cat.id, children: cat.name }, cat.id))
|
|
2430
|
+
]
|
|
2161
2431
|
}
|
|
2162
|
-
)
|
|
2432
|
+
),
|
|
2433
|
+
seatmapCore.isStageSection(selectedSection) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Stage does not use pricing category." })
|
|
2163
2434
|
] })
|
|
2164
2435
|
] }),
|
|
2165
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2166
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
"div",
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2436
|
+
seatmapCore.isStageSection(selectedSection) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-section seatmap-editor__panel-section--card seatmap-editor__panel-muted", children: "Stage areas do not support rows or seats." }) : seatmapCore.isDancefloorSection(selectedSection) ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-section seatmap-editor__panel-section--card seatmap-editor__panel-muted", children: "Dancefloor works as one selectable area seat. Resize the section shape to adjust its footprint." }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2437
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-section", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section-header", children: [
|
|
2438
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-label", children: [
|
|
2439
|
+
"Rows (",
|
|
2440
|
+
selectedSection.rows.length,
|
|
2441
|
+
") \xB7",
|
|
2442
|
+
" ",
|
|
2443
|
+
selectedSection.rows.reduce((t, r) => t + r.seats.length, 0),
|
|
2444
|
+
" seats"
|
|
2445
|
+
] }),
|
|
2446
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => addSingleSeat(selectedSection.id), className: "seatmap-editor__panel-button", title: "Add a single seat to the last row", children: "+ Seat" })
|
|
2447
|
+
] }) }),
|
|
2448
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-list seatmap-editor__panel-scroll seatmap-editor__panel-scroll--md", children: selectedSection.rows.map((row) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2449
|
+
"div",
|
|
2450
|
+
{
|
|
2451
|
+
className: "seatmap-editor__panel-list-item",
|
|
2452
|
+
children: [
|
|
2453
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "seatmap-editor__panel-text seatmap-editor__panel-text--strong seatmap-editor__panel-text--mono-min", children: [
|
|
2454
|
+
"Row ",
|
|
2455
|
+
row.label
|
|
2456
|
+
] }),
|
|
2457
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "seatmap-editor__panel-muted seatmap-editor__panel-content-grow", children: [
|
|
2458
|
+
row.seats.length,
|
|
2459
|
+
" seats"
|
|
2460
|
+
] }),
|
|
2461
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2462
|
+
"button",
|
|
2463
|
+
{
|
|
2464
|
+
onClick: () => deleteRow(selectedSection.id, row.id),
|
|
2465
|
+
className: "seatmap-editor__panel-button seatmap-editor__panel-button--danger seatmap-editor__panel-button--tiny",
|
|
2466
|
+
title: `Delete row ${row.label}`,
|
|
2467
|
+
children: "\u2715"
|
|
2468
|
+
}
|
|
2469
|
+
)
|
|
2470
|
+
]
|
|
2190
2471
|
},
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
row.label
|
|
2195
|
-
] }),
|
|
2196
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { style: { flex: 1, color: "#9e9e9e" }, children: [
|
|
2197
|
-
row.seats.length,
|
|
2198
|
-
" seats"
|
|
2199
|
-
] }),
|
|
2200
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2201
|
-
"button",
|
|
2202
|
-
{
|
|
2203
|
-
onClick: () => deleteRow(selectedSection.id, row.id),
|
|
2204
|
-
style: { ...btnDanger, padding: "1px 5px", fontSize: 11 },
|
|
2205
|
-
title: `Delete row ${row.label}`,
|
|
2206
|
-
children: "\u2715"
|
|
2207
|
-
}
|
|
2208
|
-
)
|
|
2209
|
-
]
|
|
2210
|
-
},
|
|
2211
|
-
row.id
|
|
2212
|
-
)) })
|
|
2472
|
+
row.id
|
|
2473
|
+
)) })
|
|
2474
|
+
] })
|
|
2213
2475
|
] }, selectedSection.id))
|
|
2214
2476
|
] }),
|
|
2215
2477
|
selectedSections.length === 0 && selectedSeatIds.size === 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2216
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2217
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2218
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2478
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-title", children: "Venue Config" }),
|
|
2479
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section", children: [
|
|
2480
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Venue Name" }),
|
|
2219
2481
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2220
2482
|
"input",
|
|
2221
2483
|
{
|
|
2222
|
-
|
|
2484
|
+
className: "seatmap-editor__panel-input",
|
|
2223
2485
|
value: venue.name,
|
|
2224
2486
|
onChange: (e) => updateVenueName(e.target.value)
|
|
2225
2487
|
}
|
|
2226
2488
|
)
|
|
2227
2489
|
] }),
|
|
2228
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2229
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2490
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section", children: [
|
|
2491
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Venue ID" }),
|
|
2230
2492
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2231
2493
|
"input",
|
|
2232
2494
|
{
|
|
2233
|
-
|
|
2495
|
+
className: "seatmap-editor__panel-input",
|
|
2234
2496
|
value: venue.id,
|
|
2235
2497
|
onChange: (e) => updateVenueId(e.target.value)
|
|
2236
2498
|
}
|
|
2237
2499
|
)
|
|
2238
2500
|
] }),
|
|
2239
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2501
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-muted seatmap-editor__panel-muted--small", children: [
|
|
2240
2502
|
"Stats: ",
|
|
2241
2503
|
venue.sections.length,
|
|
2242
2504
|
" sections \xB7",
|
|
@@ -2244,31 +2506,11 @@ function PropertyPanel({
|
|
|
2244
2506
|
venue.sections.reduce((t, s) => t + s.rows.reduce((rt, r) => rt + r.seats.length, 0), 0),
|
|
2245
2507
|
" seats"
|
|
2246
2508
|
] }),
|
|
2247
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2248
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2249
|
-
venue.backgroundImage ? /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2250
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2251
|
-
|
|
2252
|
-
{
|
|
2253
|
-
style: {
|
|
2254
|
-
width: "100%",
|
|
2255
|
-
height: 80,
|
|
2256
|
-
borderRadius: 4,
|
|
2257
|
-
border: "1px solid #3a3a5a",
|
|
2258
|
-
overflow: "hidden",
|
|
2259
|
-
marginBottom: 8
|
|
2260
|
-
},
|
|
2261
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2262
|
-
"img",
|
|
2263
|
-
{
|
|
2264
|
-
src: venue.backgroundImage,
|
|
2265
|
-
alt: "Background",
|
|
2266
|
-
style: { width: "100%", height: "100%", objectFit: "cover", display: "block" }
|
|
2267
|
-
}
|
|
2268
|
-
)
|
|
2269
|
-
}
|
|
2270
|
-
),
|
|
2271
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { ...labelStyle, marginBottom: 4 }, children: [
|
|
2509
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-divider" }),
|
|
2510
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Background Image" }),
|
|
2511
|
+
venue.backgroundImage ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-section", children: [
|
|
2512
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-img-preview", children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: venue.backgroundImage, alt: "Background" }) }),
|
|
2513
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-label", children: [
|
|
2272
2514
|
"Opacity: ",
|
|
2273
2515
|
Math.round((venue.backgroundImageOpacity ?? 0.5) * 100),
|
|
2274
2516
|
"%"
|
|
@@ -2281,12 +2523,12 @@ function PropertyPanel({
|
|
|
2281
2523
|
max: 100,
|
|
2282
2524
|
value: Math.round((venue.backgroundImageOpacity ?? 0.5) * 100),
|
|
2283
2525
|
onChange: (e) => onBackgroundOpacityChange?.(parseInt(e.target.value) / 100),
|
|
2284
|
-
|
|
2526
|
+
className: "seatmap-editor__panel-range"
|
|
2285
2527
|
}
|
|
2286
2528
|
),
|
|
2287
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2288
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2289
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2529
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-grid-2", children: [
|
|
2530
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2531
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Width" }),
|
|
2290
2532
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2291
2533
|
"input",
|
|
2292
2534
|
{
|
|
@@ -2297,12 +2539,12 @@ function PropertyPanel({
|
|
|
2297
2539
|
onChange: (e) => onBackgroundSizeChange?.({
|
|
2298
2540
|
width: Math.max(1, Number.parseInt(e.target.value, 10) || 1)
|
|
2299
2541
|
}),
|
|
2300
|
-
|
|
2542
|
+
className: "seatmap-editor__panel-input"
|
|
2301
2543
|
}
|
|
2302
2544
|
)
|
|
2303
2545
|
] }),
|
|
2304
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2305
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2546
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
2547
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-label", children: "Height" }),
|
|
2306
2548
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2307
2549
|
"input",
|
|
2308
2550
|
{
|
|
@@ -2313,68 +2555,44 @@ function PropertyPanel({
|
|
|
2313
2555
|
onChange: (e) => onBackgroundSizeChange?.({
|
|
2314
2556
|
height: Math.max(1, Number.parseInt(e.target.value, 10) || 1)
|
|
2315
2557
|
}),
|
|
2316
|
-
|
|
2558
|
+
className: "seatmap-editor__panel-input"
|
|
2317
2559
|
}
|
|
2318
2560
|
)
|
|
2319
2561
|
] })
|
|
2320
2562
|
] }),
|
|
2321
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
"input",
|
|
2337
|
-
{
|
|
2338
|
-
type: "checkbox",
|
|
2339
|
-
checked: venue.backgroundImageKeepAspectRatio ?? true,
|
|
2340
|
-
onChange: (e) => onBackgroundKeepAspectRatioChange?.(e.target.checked)
|
|
2341
|
-
}
|
|
2342
|
-
),
|
|
2343
|
-
"Keep aspect ratio"
|
|
2344
|
-
]
|
|
2345
|
-
}
|
|
2346
|
-
),
|
|
2347
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 6, marginTop: 8 }, children: [
|
|
2348
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onUploadBackground, style: btnSmall, children: "Replace" }),
|
|
2349
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onRemoveBackground, style: btnDanger, children: "Remove" })
|
|
2563
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "seatmap-editor__panel-row seatmap-editor__panel-text", children: [
|
|
2564
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2565
|
+
"input",
|
|
2566
|
+
{
|
|
2567
|
+
type: "checkbox",
|
|
2568
|
+
className: "seatmap-editor__panel-checkbox",
|
|
2569
|
+
checked: venue.backgroundImageKeepAspectRatio ?? true,
|
|
2570
|
+
onChange: (e) => onBackgroundKeepAspectRatioChange?.(e.target.checked)
|
|
2571
|
+
}
|
|
2572
|
+
),
|
|
2573
|
+
"Keep aspect ratio"
|
|
2574
|
+
] }),
|
|
2575
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-row", children: [
|
|
2576
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onUploadBackground, className: "seatmap-editor__panel-button", children: "Replace" }),
|
|
2577
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: onRemoveBackground, className: "seatmap-editor__panel-button seatmap-editor__panel-button--danger", children: "Remove" })
|
|
2350
2578
|
] })
|
|
2351
2579
|
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
2352
2580
|
"button",
|
|
2353
2581
|
{
|
|
2354
2582
|
onClick: onUploadBackground,
|
|
2355
|
-
|
|
2583
|
+
className: "seatmap-editor__panel-button seatmap-editor__panel-button--full",
|
|
2356
2584
|
children: "Upload Image"
|
|
2357
2585
|
}
|
|
2358
2586
|
)
|
|
2359
2587
|
] })
|
|
2360
2588
|
] });
|
|
2361
2589
|
}
|
|
2362
|
-
var btnSmall2 = {
|
|
2363
|
-
padding: "3px 8px",
|
|
2364
|
-
border: "1px solid #3a3a5a",
|
|
2365
|
-
borderRadius: 4,
|
|
2366
|
-
background: "#2a2a4a",
|
|
2367
|
-
color: "#e0e0e0",
|
|
2368
|
-
cursor: "pointer",
|
|
2369
|
-
fontSize: 12,
|
|
2370
|
-
fontFamily: "system-ui"
|
|
2371
|
-
};
|
|
2372
2590
|
function replaceCategoryInVenue(venue, categoryId, replacementCategoryId) {
|
|
2373
2591
|
return {
|
|
2374
2592
|
...venue,
|
|
2375
2593
|
sections: venue.sections.map((section) => ({
|
|
2376
2594
|
...section,
|
|
2377
|
-
categoryId: section.categoryId === categoryId ? replacementCategoryId : section.categoryId,
|
|
2595
|
+
categoryId: seatmapCore.isStageSection(section) ? "" : section.categoryId === categoryId ? replacementCategoryId : section.categoryId,
|
|
2378
2596
|
rows: section.rows.map((row) => ({
|
|
2379
2597
|
...row,
|
|
2380
2598
|
seats: row.seats.map(
|
|
@@ -2399,20 +2617,25 @@ function CategoryManager({
|
|
|
2399
2617
|
history,
|
|
2400
2618
|
store,
|
|
2401
2619
|
fetchCategoryPrices,
|
|
2402
|
-
style
|
|
2620
|
+
style,
|
|
2621
|
+
locale = "en-US",
|
|
2622
|
+
currency = "BYN"
|
|
2403
2623
|
}) {
|
|
2404
2624
|
const [newName, setNewName] = react.useState("");
|
|
2405
|
-
const [newColor, setNewColor] = react.useState("#
|
|
2625
|
+
const [newColor, setNewColor] = react.useState("#dfcd72");
|
|
2406
2626
|
const [editingId, setEditingId] = react.useState(null);
|
|
2407
2627
|
const [editingName, setEditingName] = react.useState("");
|
|
2408
|
-
const [editingColor, setEditingColor] = react.useState("#
|
|
2628
|
+
const [editingColor, setEditingColor] = react.useState("#dfcd72");
|
|
2409
2629
|
const [isPriceManagerOpen, setIsPriceManagerOpen] = react.useState(false);
|
|
2410
2630
|
const [isFetchingPrices, setIsFetchingPrices] = react.useState(false);
|
|
2411
2631
|
const [fetchError, setFetchError] = react.useState(null);
|
|
2412
2632
|
const [syncStatus, setSyncStatus] = react.useState("not-synced");
|
|
2413
2633
|
const [overridePriceDrafts, setOverridePriceDrafts] = react.useState({});
|
|
2414
2634
|
if (!venue) return null;
|
|
2415
|
-
const formatPrice = (price) =>
|
|
2635
|
+
const formatPrice = (price) => new Intl.NumberFormat(locale, {
|
|
2636
|
+
style: "currency",
|
|
2637
|
+
currency
|
|
2638
|
+
}).format(Number.isFinite(price) ? price : 0);
|
|
2416
2639
|
const effectivePrice = (category) => {
|
|
2417
2640
|
if (category.isPriceOverridden && Number.isFinite(category.overriddenPrice)) {
|
|
2418
2641
|
return category.overriddenPrice;
|
|
@@ -2546,7 +2769,7 @@ function CategoryManager({
|
|
|
2546
2769
|
});
|
|
2547
2770
|
setSyncStatus("synced");
|
|
2548
2771
|
} catch (error) {
|
|
2549
|
-
const message = error instanceof Error ? error.message : "Failed to
|
|
2772
|
+
const message = error instanceof Error ? error.message : "Failed to load prices.";
|
|
2550
2773
|
setFetchError(message);
|
|
2551
2774
|
const currentVenue = store.getState().venue;
|
|
2552
2775
|
if (currentVenue) {
|
|
@@ -2647,92 +2870,57 @@ function CategoryManager({
|
|
|
2647
2870
|
};
|
|
2648
2871
|
});
|
|
2649
2872
|
};
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
height: 20,
|
|
2653
|
-
borderRadius: 999,
|
|
2654
|
-
border: "1px solid #4a4a6a",
|
|
2655
|
-
padding: 2,
|
|
2656
|
-
display: "inline-flex",
|
|
2657
|
-
alignItems: "center",
|
|
2658
|
-
transition: "all 0.12s ease"
|
|
2659
|
-
};
|
|
2660
|
-
const switchThumbBase = {
|
|
2661
|
-
width: 14,
|
|
2662
|
-
height: 14,
|
|
2663
|
-
borderRadius: "50%",
|
|
2664
|
-
background: "#e0e0e0",
|
|
2665
|
-
transition: "transform 0.12s ease"
|
|
2666
|
-
};
|
|
2667
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: 16, ...style }, children: [
|
|
2668
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontWeight: 600, color: "#e0e0e0", fontSize: 14, fontFamily: "system-ui", marginBottom: 12 }, children: "Pricing Categories" }),
|
|
2873
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel", style, children: [
|
|
2874
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-title", children: "Pricing categories" }),
|
|
2669
2875
|
venue.categories.map((cat) => {
|
|
2670
2876
|
const isEditing = editingId === cat.id;
|
|
2671
2877
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2672
2878
|
"div",
|
|
2673
2879
|
{
|
|
2674
|
-
|
|
2675
|
-
display: "flex",
|
|
2676
|
-
alignItems: "center",
|
|
2677
|
-
gap: 8,
|
|
2678
|
-
flexWrap: "wrap",
|
|
2679
|
-
marginBottom: 6,
|
|
2680
|
-
padding: "4px 8px",
|
|
2681
|
-
borderRadius: 4,
|
|
2682
|
-
background: "#2a2a4a"
|
|
2683
|
-
},
|
|
2880
|
+
className: "seatmap-editor__panel-list-item",
|
|
2684
2881
|
children: [
|
|
2685
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2882
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "seatmap-editor__color-picker-shell", children: [
|
|
2883
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2884
|
+
"span",
|
|
2885
|
+
{
|
|
2886
|
+
"aria-hidden": "true",
|
|
2887
|
+
className: "seatmap-editor__color-picker-dot",
|
|
2888
|
+
style: { background: isEditing ? editingColor : cat.color }
|
|
2889
|
+
}
|
|
2890
|
+
),
|
|
2891
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2892
|
+
"input",
|
|
2893
|
+
{
|
|
2894
|
+
type: "color",
|
|
2895
|
+
value: isEditing ? editingColor : cat.color,
|
|
2896
|
+
onChange: (e) => isEditing && setEditingColor(e.target.value),
|
|
2897
|
+
disabled: !isEditing,
|
|
2898
|
+
className: "seatmap-editor__color-picker-input",
|
|
2899
|
+
"data-editable": isEditing ? "true" : "false",
|
|
2900
|
+
title: isEditing ? "Pick category color" : "Enable edit mode to change color"
|
|
2901
|
+
}
|
|
2902
|
+
)
|
|
2903
|
+
] }),
|
|
2695
2904
|
isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
2696
2905
|
"input",
|
|
2697
2906
|
{
|
|
2698
2907
|
value: editingName,
|
|
2699
2908
|
onChange: (e) => setEditingName(e.target.value),
|
|
2700
2909
|
onKeyDown: (e) => e.key === "Enter" && saveEdit(),
|
|
2701
|
-
|
|
2702
|
-
flex: 1,
|
|
2703
|
-
minWidth: 0,
|
|
2704
|
-
padding: "2px 6px",
|
|
2705
|
-
background: "#1f1f38",
|
|
2706
|
-
border: "1px solid #3a3a5a",
|
|
2707
|
-
borderRadius: 4,
|
|
2708
|
-
color: "#e0e0e0",
|
|
2709
|
-
fontSize: 12,
|
|
2710
|
-
fontFamily: "system-ui"
|
|
2711
|
-
}
|
|
2910
|
+
className: "seatmap-editor__panel-input seatmap-editor__panel-input--grow"
|
|
2712
2911
|
}
|
|
2713
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2912
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-text seatmap-editor__panel-text--truncate", children: cat.name }),
|
|
2714
2913
|
isEditing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2715
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: saveEdit,
|
|
2716
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setEditingId(null),
|
|
2914
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: saveEdit, className: "seatmap-editor__panel-button seatmap-editor__panel-button--tiny", children: "Save" }),
|
|
2915
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setEditingId(null), className: "seatmap-editor__panel-button seatmap-editor__panel-button--tiny", children: "Cancel" })
|
|
2717
2916
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
2718
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2719
|
-
|
|
2720
|
-
{
|
|
2721
|
-
style: {
|
|
2722
|
-
color: "#8f8fa4",
|
|
2723
|
-
fontSize: 12,
|
|
2724
|
-
fontFamily: "system-ui",
|
|
2725
|
-
marginRight: 2
|
|
2726
|
-
},
|
|
2727
|
-
children: formatPrice(effectivePrice(cat))
|
|
2728
|
-
}
|
|
2729
|
-
),
|
|
2730
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => startEdit(cat), style: { ...btnSmall2, padding: "1px 6px", fontSize: 11 }, children: "Edit" }),
|
|
2917
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__panel-muted seatmap-editor__panel-price", children: formatPrice(effectivePrice(cat)) }),
|
|
2918
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => startEdit(cat), className: "seatmap-editor__panel-button seatmap-editor__panel-button--tiny", children: "Edit" }),
|
|
2731
2919
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2732
2920
|
"button",
|
|
2733
2921
|
{
|
|
2734
2922
|
onClick: () => removeCategory(cat.id),
|
|
2735
|
-
|
|
2923
|
+
className: "seatmap-editor__panel-button seatmap-editor__panel-button--tiny",
|
|
2736
2924
|
disabled: venue.categories.length <= 1,
|
|
2737
2925
|
title: venue.categories.length <= 1 ? "At least one category is required" : "Delete category",
|
|
2738
2926
|
children: "\u2715"
|
|
@@ -2744,16 +2932,21 @@ function CategoryManager({
|
|
|
2744
2932
|
cat.id
|
|
2745
2933
|
);
|
|
2746
2934
|
}),
|
|
2747
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2748
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2749
|
-
"
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2935
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-row seatmap-editor__panel-row--spaced", children: [
|
|
2936
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "seatmap-editor__color-picker-shell seatmap-editor__color-picker-shell--lg", children: [
|
|
2937
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "seatmap-editor__color-picker-dot", style: { background: newColor } }),
|
|
2938
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2939
|
+
"input",
|
|
2940
|
+
{
|
|
2941
|
+
type: "color",
|
|
2942
|
+
value: newColor,
|
|
2943
|
+
onChange: (e) => setNewColor(e.target.value),
|
|
2944
|
+
className: "seatmap-editor__color-picker-input",
|
|
2945
|
+
"data-editable": "true",
|
|
2946
|
+
title: "Pick new category color"
|
|
2947
|
+
}
|
|
2948
|
+
)
|
|
2949
|
+
] }),
|
|
2757
2950
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2758
2951
|
"input",
|
|
2759
2952
|
{
|
|
@@ -2761,300 +2954,158 @@ function CategoryManager({
|
|
|
2761
2954
|
value: newName,
|
|
2762
2955
|
onChange: (e) => setNewName(e.target.value),
|
|
2763
2956
|
onKeyDown: (e) => e.key === "Enter" && addCategory(),
|
|
2764
|
-
|
|
2765
|
-
flex: 1,
|
|
2766
|
-
padding: "4px 8px",
|
|
2767
|
-
background: "#2a2a4a",
|
|
2768
|
-
border: "1px solid #3a3a5a",
|
|
2769
|
-
borderRadius: 4,
|
|
2770
|
-
color: "#e0e0e0",
|
|
2771
|
-
fontSize: 13,
|
|
2772
|
-
fontFamily: "system-ui"
|
|
2773
|
-
}
|
|
2957
|
+
className: "seatmap-editor__panel-input seatmap-editor__panel-input--grow"
|
|
2774
2958
|
}
|
|
2775
2959
|
),
|
|
2776
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: addCategory,
|
|
2960
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: addCategory, className: "seatmap-editor__panel-button", children: "Add" })
|
|
2777
2961
|
] }),
|
|
2778
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2962
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-actions-end", children: /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: openPriceManager, className: "seatmap-editor__panel-button", children: "Manage prices" }) }),
|
|
2779
2963
|
isPriceManagerOpen && /* @__PURE__ */ jsxRuntime.jsx(
|
|
2780
2964
|
"div",
|
|
2781
2965
|
{
|
|
2782
|
-
|
|
2783
|
-
position: "fixed",
|
|
2784
|
-
inset: 0,
|
|
2785
|
-
background: "rgba(8, 8, 18, 0.65)",
|
|
2786
|
-
zIndex: 1e3,
|
|
2787
|
-
display: "flex",
|
|
2788
|
-
alignItems: "center",
|
|
2789
|
-
justifyContent: "center",
|
|
2790
|
-
padding: 16
|
|
2791
|
-
},
|
|
2966
|
+
className: "seatmap-editor__modal-backdrop",
|
|
2792
2967
|
onClick: () => setIsPriceManagerOpen(false),
|
|
2793
2968
|
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
2794
2969
|
"div",
|
|
2795
2970
|
{
|
|
2796
|
-
|
|
2797
|
-
width: "min(860px, 95vw)",
|
|
2798
|
-
maxHeight: "80vh",
|
|
2799
|
-
overflow: "auto",
|
|
2800
|
-
background: "#17172b",
|
|
2801
|
-
border: "1px solid #3a3a5a",
|
|
2802
|
-
borderRadius: 10,
|
|
2803
|
-
padding: 16
|
|
2804
|
-
},
|
|
2971
|
+
className: "seatmap-editor__modal",
|
|
2805
2972
|
onClick: (event) => event.stopPropagation(),
|
|
2806
2973
|
children: [
|
|
2807
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2808
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2809
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
2974
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__modal-header", children: [
|
|
2975
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-title", children: "Category prices" }),
|
|
2976
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__modal-actions", children: [
|
|
2810
2977
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2811
2978
|
"button",
|
|
2812
2979
|
{
|
|
2813
2980
|
onClick: syncPricesFromBackend,
|
|
2814
2981
|
disabled: !fetchCategoryPrices || isFetchingPrices,
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
cursor: !fetchCategoryPrices || isFetchingPrices ? "not-allowed" : "pointer"
|
|
2819
|
-
},
|
|
2820
|
-
title: fetchCategoryPrices ? "Fetch latest category prices from backend" : "No backend price fetch configured",
|
|
2821
|
-
children: isFetchingPrices ? "Syncing..." : "Sync from Backend"
|
|
2982
|
+
className: "seatmap-editor__panel-button",
|
|
2983
|
+
title: fetchCategoryPrices ? "Load latest prices from backend" : "Backend price sync is not configured",
|
|
2984
|
+
children: isFetchingPrices ? "Syncing..." : "Sync with backend"
|
|
2822
2985
|
}
|
|
2823
2986
|
),
|
|
2824
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setIsPriceManagerOpen(false),
|
|
2987
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setIsPriceManagerOpen(false), className: "seatmap-editor__panel-button", children: "Close" })
|
|
2825
2988
|
] })
|
|
2826
2989
|
] }),
|
|
2827
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2990
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-muted seatmap-editor__panel-muted--spaced", children: "Backend prices are read-only. Override temporarily uses a custom category price for this seatmap." }),
|
|
2828
2991
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2829
2992
|
"div",
|
|
2830
2993
|
{
|
|
2831
|
-
|
|
2832
|
-
marginBottom: 10,
|
|
2833
|
-
color: syncStatus === "synced" ? "#8fd3a6" : syncStatus === "failed" ? "#ff9a9a" : "#b8b8cc",
|
|
2834
|
-
fontSize: 12,
|
|
2835
|
-
fontFamily: "system-ui"
|
|
2836
|
-
},
|
|
2994
|
+
className: syncStatus === "synced" ? "seatmap-editor__status-line seatmap-editor__status-line--success" : syncStatus === "failed" ? "seatmap-editor__status-line seatmap-editor__status-line--error" : "seatmap-editor__status-line seatmap-editor__status-line--idle",
|
|
2837
2995
|
children: [
|
|
2838
2996
|
"Sync status:",
|
|
2839
2997
|
" ",
|
|
2840
|
-
syncStatus === "not-synced" ? "Not synced" : syncStatus === "syncing" ? "Syncing..." : syncStatus === "synced" ? "Synced" : "
|
|
2998
|
+
syncStatus === "not-synced" ? "Not synced" : syncStatus === "syncing" ? "Syncing..." : syncStatus === "synced" ? "Synced" : "Error"
|
|
2841
2999
|
]
|
|
2842
3000
|
}
|
|
2843
3001
|
),
|
|
2844
|
-
fetchError && /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
2845
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
2846
|
-
"div",
|
|
2847
|
-
{
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
|
|
2876
|
-
|
|
2877
|
-
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
|
|
3002
|
+
fetchError && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__status-line seatmap-editor__status-line--error", children: fetchError }),
|
|
3003
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__table-grid", children: [
|
|
3004
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__table-head", children: "Category" }),
|
|
3005
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__table-head", children: "Backend" }),
|
|
3006
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__table-head", children: "Override" }),
|
|
3007
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__table-head", children: "Override price" }),
|
|
3008
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__table-head", children: "Effective price" }),
|
|
3009
|
+
venue.categories.map((category) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3010
|
+
"div",
|
|
3011
|
+
{
|
|
3012
|
+
className: "seatmap-editor__table-row",
|
|
3013
|
+
children: [
|
|
3014
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__table-category-cell", children: [
|
|
3015
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3016
|
+
"span",
|
|
3017
|
+
{
|
|
3018
|
+
className: "seatmap-editor__table-category-swatch",
|
|
3019
|
+
"aria-hidden": "true",
|
|
3020
|
+
style: { background: category.color }
|
|
3021
|
+
}
|
|
3022
|
+
),
|
|
3023
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__table-category-name", children: category.name })
|
|
3024
|
+
] }),
|
|
3025
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__table-amount", children: formatPrice(category.backendPrice) }),
|
|
3026
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3027
|
+
"button",
|
|
3028
|
+
{
|
|
3029
|
+
type: "button",
|
|
3030
|
+
role: "switch",
|
|
3031
|
+
"aria-checked": Boolean(category.isPriceOverridden),
|
|
3032
|
+
onClick: () => toggleOverride(category.id, !category.isPriceOverridden),
|
|
3033
|
+
className: `seatmap-editor__switch-track${category.isPriceOverridden ? " is-checked" : ""}`,
|
|
3034
|
+
title: category.isPriceOverridden ? "Disable override" : "Enable override",
|
|
3035
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__switch-thumb" })
|
|
3036
|
+
}
|
|
3037
|
+
),
|
|
3038
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__override-editor", children: [
|
|
3039
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3040
|
+
"input",
|
|
3041
|
+
{
|
|
3042
|
+
type: "text",
|
|
3043
|
+
inputMode: "decimal",
|
|
3044
|
+
value: overridePriceDrafts[category.id] ?? "",
|
|
3045
|
+
disabled: !category.isPriceOverridden,
|
|
3046
|
+
onChange: (event) => setOverridePriceDrafts((current) => ({ ...current, [category.id]: event.target.value })),
|
|
3047
|
+
onKeyDown: (event) => {
|
|
3048
|
+
if (event.key === "Enter") {
|
|
3049
|
+
commitOverridePrice(category.id);
|
|
2884
3050
|
}
|
|
2885
|
-
|
|
2886
|
-
|
|
2887
|
-
|
|
2888
|
-
|
|
3051
|
+
if (event.key === "Escape") {
|
|
3052
|
+
resetOverrideDraft(category);
|
|
3053
|
+
}
|
|
3054
|
+
},
|
|
3055
|
+
className: `seatmap-editor__override-input${category.isPriceOverridden ? "" : " is-disabled"}`
|
|
3056
|
+
}
|
|
3057
|
+
),
|
|
3058
|
+
category.isPriceOverridden && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__override-actions", children: [
|
|
2889
3059
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2890
3060
|
"button",
|
|
2891
3061
|
{
|
|
2892
3062
|
type: "button",
|
|
2893
|
-
|
|
2894
|
-
"
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
...switchTrackBase,
|
|
2898
|
-
background: category.isPriceOverridden ? "#2d6a3d" : "#2a2a4a",
|
|
2899
|
-
borderColor: category.isPriceOverridden ? "#57b26f" : "#4a4a6a",
|
|
2900
|
-
cursor: "pointer"
|
|
2901
|
-
},
|
|
2902
|
-
title: category.isPriceOverridden ? "Disable override" : "Enable override",
|
|
2903
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
2904
|
-
"span",
|
|
2905
|
-
{
|
|
2906
|
-
style: {
|
|
2907
|
-
...switchThumbBase,
|
|
2908
|
-
transform: category.isPriceOverridden ? "translateX(14px)" : "translateX(0)"
|
|
2909
|
-
}
|
|
2910
|
-
}
|
|
2911
|
-
)
|
|
3063
|
+
onClick: () => adjustOverrideDraft(category.id, 0.01),
|
|
3064
|
+
className: "seatmap-editor__table-action-button",
|
|
3065
|
+
title: "Increase override price",
|
|
3066
|
+
children: "+"
|
|
2912
3067
|
}
|
|
2913
3068
|
),
|
|
2914
|
-
/* @__PURE__ */ jsxRuntime.
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
2936
|
-
|
|
2937
|
-
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
2945
|
-
|
|
2946
|
-
|
|
2947
|
-
|
|
2948
|
-
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
display: "inline-flex",
|
|
2955
|
-
gap: 2
|
|
2956
|
-
},
|
|
2957
|
-
children: [
|
|
2958
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2959
|
-
"button",
|
|
2960
|
-
{
|
|
2961
|
-
type: "button",
|
|
2962
|
-
onClick: () => adjustOverrideDraft(category.id, 0.01),
|
|
2963
|
-
style: {
|
|
2964
|
-
width: 20,
|
|
2965
|
-
height: 20,
|
|
2966
|
-
borderRadius: 4,
|
|
2967
|
-
border: "1px solid #4a4a6a",
|
|
2968
|
-
background: "#2a2a4a",
|
|
2969
|
-
color: "#d8d8ee",
|
|
2970
|
-
fontSize: 12,
|
|
2971
|
-
lineHeight: 1,
|
|
2972
|
-
padding: 0,
|
|
2973
|
-
cursor: "pointer"
|
|
2974
|
-
},
|
|
2975
|
-
title: "Increase override price",
|
|
2976
|
-
children: "+"
|
|
2977
|
-
}
|
|
2978
|
-
),
|
|
2979
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
2980
|
-
"button",
|
|
2981
|
-
{
|
|
2982
|
-
type: "button",
|
|
2983
|
-
onClick: () => adjustOverrideDraft(category.id, -0.01),
|
|
2984
|
-
style: {
|
|
2985
|
-
width: 20,
|
|
2986
|
-
height: 20,
|
|
2987
|
-
borderRadius: 4,
|
|
2988
|
-
border: "1px solid #4a4a6a",
|
|
2989
|
-
background: "#2a2a4a",
|
|
2990
|
-
color: "#d8d8ee",
|
|
2991
|
-
fontSize: 12,
|
|
2992
|
-
lineHeight: 1,
|
|
2993
|
-
padding: 0,
|
|
2994
|
-
cursor: "pointer"
|
|
2995
|
-
},
|
|
2996
|
-
title: "Decrease override price",
|
|
2997
|
-
children: "-"
|
|
2998
|
-
}
|
|
2999
|
-
),
|
|
3000
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3001
|
-
"button",
|
|
3002
|
-
{
|
|
3003
|
-
type: "button",
|
|
3004
|
-
onClick: () => commitOverridePrice(category.id),
|
|
3005
|
-
disabled: !isDraftChanged(category),
|
|
3006
|
-
style: {
|
|
3007
|
-
width: 20,
|
|
3008
|
-
height: 20,
|
|
3009
|
-
borderRadius: 4,
|
|
3010
|
-
border: "1px solid #2f7b44",
|
|
3011
|
-
background: isDraftChanged(category) ? "#2d6a3d" : "#244832",
|
|
3012
|
-
color: "#d8ffe4",
|
|
3013
|
-
fontSize: 11,
|
|
3014
|
-
lineHeight: 1,
|
|
3015
|
-
padding: 0,
|
|
3016
|
-
opacity: isDraftChanged(category) ? 1 : 0.55,
|
|
3017
|
-
cursor: isDraftChanged(category) ? "pointer" : "not-allowed"
|
|
3018
|
-
},
|
|
3019
|
-
title: "Apply override price",
|
|
3020
|
-
children: "\u2713"
|
|
3021
|
-
}
|
|
3022
|
-
),
|
|
3023
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3024
|
-
"button",
|
|
3025
|
-
{
|
|
3026
|
-
type: "button",
|
|
3027
|
-
onClick: () => resetOverrideDraft(category),
|
|
3028
|
-
disabled: !isDraftChanged(category),
|
|
3029
|
-
style: {
|
|
3030
|
-
width: 20,
|
|
3031
|
-
height: 20,
|
|
3032
|
-
borderRadius: 4,
|
|
3033
|
-
border: "1px solid #8b3f4d",
|
|
3034
|
-
background: isDraftChanged(category) ? "#6f2c3b" : "#4d2730",
|
|
3035
|
-
color: "#ffd9df",
|
|
3036
|
-
fontSize: 11,
|
|
3037
|
-
lineHeight: 1,
|
|
3038
|
-
padding: 0,
|
|
3039
|
-
opacity: isDraftChanged(category) ? 1 : 0.55,
|
|
3040
|
-
cursor: isDraftChanged(category) ? "pointer" : "not-allowed"
|
|
3041
|
-
},
|
|
3042
|
-
title: "Cancel override price changes",
|
|
3043
|
-
children: "\u2715"
|
|
3044
|
-
}
|
|
3045
|
-
)
|
|
3046
|
-
]
|
|
3047
|
-
}
|
|
3048
|
-
)
|
|
3049
|
-
] }),
|
|
3050
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#e0e0e0", fontSize: 12, fontFamily: "system-ui", fontWeight: 600, whiteSpace: "nowrap" }, children: formatPrice(effectivePrice(category)) })
|
|
3051
|
-
]
|
|
3052
|
-
},
|
|
3053
|
-
category.id
|
|
3054
|
-
))
|
|
3055
|
-
]
|
|
3056
|
-
}
|
|
3057
|
-
)
|
|
3069
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3070
|
+
"button",
|
|
3071
|
+
{
|
|
3072
|
+
type: "button",
|
|
3073
|
+
onClick: () => adjustOverrideDraft(category.id, -0.01),
|
|
3074
|
+
className: "seatmap-editor__table-action-button",
|
|
3075
|
+
title: "Decrease override price",
|
|
3076
|
+
children: "-"
|
|
3077
|
+
}
|
|
3078
|
+
),
|
|
3079
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3080
|
+
"button",
|
|
3081
|
+
{
|
|
3082
|
+
type: "button",
|
|
3083
|
+
onClick: () => commitOverridePrice(category.id),
|
|
3084
|
+
disabled: !isDraftChanged(category),
|
|
3085
|
+
className: "seatmap-editor__table-action-button seatmap-editor__table-action-button--apply",
|
|
3086
|
+
title: "Apply override price",
|
|
3087
|
+
children: "\u2713"
|
|
3088
|
+
}
|
|
3089
|
+
),
|
|
3090
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3091
|
+
"button",
|
|
3092
|
+
{
|
|
3093
|
+
type: "button",
|
|
3094
|
+
onClick: () => resetOverrideDraft(category),
|
|
3095
|
+
disabled: !isDraftChanged(category),
|
|
3096
|
+
className: "seatmap-editor__table-action-button seatmap-editor__table-action-button--reset",
|
|
3097
|
+
title: "Revert override changes",
|
|
3098
|
+
children: "\u2715"
|
|
3099
|
+
}
|
|
3100
|
+
)
|
|
3101
|
+
] })
|
|
3102
|
+
] }),
|
|
3103
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__table-effective-price", children: formatPrice(effectivePrice(category)) })
|
|
3104
|
+
]
|
|
3105
|
+
},
|
|
3106
|
+
category.id
|
|
3107
|
+
))
|
|
3108
|
+
] })
|
|
3058
3109
|
]
|
|
3059
3110
|
}
|
|
3060
3111
|
)
|
|
@@ -3079,81 +3130,57 @@ function LayerPanel({
|
|
|
3079
3130
|
return null;
|
|
3080
3131
|
};
|
|
3081
3132
|
const selectedSectionIdFromSeats = selectedSeatIds.size > 0 ? findSectionForSeat([...selectedSeatIds][0]) : null;
|
|
3082
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
3083
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
3084
|
-
|
|
3085
|
-
|
|
3086
|
-
|
|
3087
|
-
|
|
3088
|
-
|
|
3089
|
-
"
|
|
3090
|
-
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3094
|
-
|
|
3095
|
-
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
|
|
3102
|
-
|
|
3103
|
-
|
|
3104
|
-
|
|
3105
|
-
|
|
3106
|
-
|
|
3107
|
-
|
|
3108
|
-
|
|
3109
|
-
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
3113
|
-
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
),
|
|
3132
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { color: "#9e9e9e", fontSize: 11, fontFamily: "system-ui" }, children: [
|
|
3133
|
-
section.rows.length,
|
|
3134
|
-
" rows, ",
|
|
3135
|
-
seatCount,
|
|
3136
|
-
" seats"
|
|
3137
|
-
] })
|
|
3138
|
-
] })
|
|
3139
|
-
]
|
|
3140
|
-
},
|
|
3141
|
-
section.id
|
|
3142
|
-
);
|
|
3143
|
-
}),
|
|
3144
|
-
venue.sections.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#9e9e9e", fontSize: 13, fontFamily: "system-ui" }, children: "No sections yet. Use the Add Section tool." })
|
|
3133
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel", style, children: [
|
|
3134
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-title", children: "Layers" }),
|
|
3135
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3136
|
+
"div",
|
|
3137
|
+
{
|
|
3138
|
+
className: "seatmap-editor__panel-list",
|
|
3139
|
+
role: "listbox",
|
|
3140
|
+
"aria-label": "Venue sections",
|
|
3141
|
+
"aria-multiselectable": "true",
|
|
3142
|
+
children: venue.sections.map((section) => {
|
|
3143
|
+
const seatCount = section.rows.reduce((t, r) => t + r.seats.length, 0);
|
|
3144
|
+
const isActive = storeSelectedSectionIds.has(section.id) || section.id === selectedSectionIdFromSeats;
|
|
3145
|
+
const catColor = venue.categories.find((c) => c.id === section.categoryId)?.color ?? "var(--seatmap-editor-text-muted, #666)";
|
|
3146
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3147
|
+
"button",
|
|
3148
|
+
{
|
|
3149
|
+
type: "button",
|
|
3150
|
+
role: "option",
|
|
3151
|
+
"aria-selected": isActive,
|
|
3152
|
+
onClick: (event) => onSelectSection(section.id, {
|
|
3153
|
+
multi: event.ctrlKey || event.metaKey
|
|
3154
|
+
}),
|
|
3155
|
+
className: `seatmap-editor__panel-list-item seatmap-editor__panel-list-item--interactive seatmap-editor__panel-list-item--interactive-button${isActive ? " is-active" : ""}`,
|
|
3156
|
+
children: [
|
|
3157
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3158
|
+
"div",
|
|
3159
|
+
{
|
|
3160
|
+
className: "seatmap-editor__table-category-swatch",
|
|
3161
|
+
"aria-hidden": "true",
|
|
3162
|
+
style: { background: catColor }
|
|
3163
|
+
}
|
|
3164
|
+
),
|
|
3165
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-content-grow", children: [
|
|
3166
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-text seatmap-editor__panel-text--truncate", children: section.label }),
|
|
3167
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-muted seatmap-editor__panel-muted--small", children: [
|
|
3168
|
+
section.rows.length,
|
|
3169
|
+
" rows, ",
|
|
3170
|
+
seatCount,
|
|
3171
|
+
" seats"
|
|
3172
|
+
] })
|
|
3173
|
+
] })
|
|
3174
|
+
]
|
|
3175
|
+
},
|
|
3176
|
+
section.id
|
|
3177
|
+
);
|
|
3178
|
+
})
|
|
3179
|
+
}
|
|
3180
|
+
),
|
|
3181
|
+
venue.sections.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-muted", children: "No sections yet. Use the Add Section tool." })
|
|
3145
3182
|
] });
|
|
3146
3183
|
}
|
|
3147
|
-
var btnSmall3 = {
|
|
3148
|
-
padding: "3px 8px",
|
|
3149
|
-
border: "1px solid #3a3a5a",
|
|
3150
|
-
borderRadius: 4,
|
|
3151
|
-
background: "#2a2a4a",
|
|
3152
|
-
color: "#e0e0e0",
|
|
3153
|
-
cursor: "pointer",
|
|
3154
|
-
fontSize: 12,
|
|
3155
|
-
fontFamily: "system-ui"
|
|
3156
|
-
};
|
|
3157
3184
|
function sanitizeStatusId(name) {
|
|
3158
3185
|
const slug = name.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
3159
3186
|
return slug || seatmapCore.generateId();
|
|
@@ -3180,10 +3207,10 @@ function replaceStatusInVenue(venue, statusId, replacementStatusId) {
|
|
|
3180
3207
|
}
|
|
3181
3208
|
function StatusManager({ venue, history, store, style }) {
|
|
3182
3209
|
const [newName, setNewName] = react.useState("");
|
|
3183
|
-
const [newColor, setNewColor] = react.useState("#
|
|
3210
|
+
const [newColor, setNewColor] = react.useState("#dfcd72");
|
|
3184
3211
|
const [editingId, setEditingId] = react.useState(null);
|
|
3185
3212
|
const [editingName, setEditingName] = react.useState("");
|
|
3186
|
-
const [editingColor, setEditingColor] = react.useState("#
|
|
3213
|
+
const [editingColor, setEditingColor] = react.useState("#dfcd72");
|
|
3187
3214
|
const statusIds = react.useMemo(() => new Set(venue?.seatStatuses.map((status) => status.id) ?? []), [venue]);
|
|
3188
3215
|
if (!venue) return null;
|
|
3189
3216
|
const addStatus = () => {
|
|
@@ -3276,63 +3303,56 @@ function StatusManager({ venue, history, store, style }) {
|
|
|
3276
3303
|
}
|
|
3277
3304
|
});
|
|
3278
3305
|
};
|
|
3279
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
3280
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
3306
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel", style, children: [
|
|
3307
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-title", children: "Seat Statuses" }),
|
|
3281
3308
|
venue.seatStatuses.map((status) => {
|
|
3282
3309
|
const isEditing = editingId === status.id;
|
|
3283
3310
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3284
3311
|
"div",
|
|
3285
3312
|
{
|
|
3286
|
-
|
|
3287
|
-
display: "flex",
|
|
3288
|
-
alignItems: "center",
|
|
3289
|
-
gap: 8,
|
|
3290
|
-
flexWrap: "wrap",
|
|
3291
|
-
marginBottom: 6,
|
|
3292
|
-
padding: "4px 8px",
|
|
3293
|
-
borderRadius: 4,
|
|
3294
|
-
background: "#2a2a4a"
|
|
3295
|
-
},
|
|
3313
|
+
className: "seatmap-editor__panel-list-item",
|
|
3296
3314
|
children: [
|
|
3297
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3315
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "seatmap-editor__color-picker-shell", children: [
|
|
3316
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3317
|
+
"span",
|
|
3318
|
+
{
|
|
3319
|
+
"aria-hidden": "true",
|
|
3320
|
+
className: "seatmap-editor__color-picker-dot",
|
|
3321
|
+
style: { background: isEditing ? editingColor : status.color }
|
|
3322
|
+
}
|
|
3323
|
+
),
|
|
3324
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3325
|
+
"input",
|
|
3326
|
+
{
|
|
3327
|
+
type: "color",
|
|
3328
|
+
value: isEditing ? editingColor : status.color,
|
|
3329
|
+
onChange: (e) => isEditing && setEditingColor(e.target.value),
|
|
3330
|
+
disabled: !isEditing,
|
|
3331
|
+
className: "seatmap-editor__color-picker-input",
|
|
3332
|
+
"data-editable": isEditing ? "true" : "false",
|
|
3333
|
+
title: isEditing ? "Pick status color" : "Enable edit to change color"
|
|
3334
|
+
}
|
|
3335
|
+
)
|
|
3336
|
+
] }),
|
|
3307
3337
|
isEditing ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3308
3338
|
"input",
|
|
3309
3339
|
{
|
|
3310
3340
|
value: editingName,
|
|
3311
3341
|
onChange: (e) => setEditingName(e.target.value),
|
|
3312
3342
|
onKeyDown: (e) => e.key === "Enter" && saveEdit(),
|
|
3313
|
-
|
|
3314
|
-
flex: 1,
|
|
3315
|
-
minWidth: 0,
|
|
3316
|
-
padding: "2px 6px",
|
|
3317
|
-
background: "#1f1f38",
|
|
3318
|
-
border: "1px solid #3a3a5a",
|
|
3319
|
-
borderRadius: 4,
|
|
3320
|
-
color: "#e0e0e0",
|
|
3321
|
-
fontSize: 12,
|
|
3322
|
-
fontFamily: "system-ui"
|
|
3323
|
-
}
|
|
3343
|
+
className: "seatmap-editor__panel-input seatmap-editor__panel-input--grow"
|
|
3324
3344
|
}
|
|
3325
|
-
) : /* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
3345
|
+
) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__panel-text seatmap-editor__panel-content-grow", children: status.name }),
|
|
3326
3346
|
isEditing ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3327
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: saveEdit,
|
|
3328
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setEditingId(null),
|
|
3347
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: saveEdit, className: "seatmap-editor__panel-button seatmap-editor__panel-button--tiny", children: "Save" }),
|
|
3348
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setEditingId(null), className: "seatmap-editor__panel-button seatmap-editor__panel-button--tiny", children: "Cancel" })
|
|
3329
3349
|
] }) : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
3330
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => startEdit(status),
|
|
3350
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => startEdit(status), className: "seatmap-editor__panel-button seatmap-editor__panel-button--tiny", children: "Edit" }),
|
|
3331
3351
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3332
3352
|
"button",
|
|
3333
3353
|
{
|
|
3334
3354
|
onClick: () => removeStatus(status.id),
|
|
3335
|
-
|
|
3355
|
+
className: "seatmap-editor__panel-button seatmap-editor__panel-button--tiny",
|
|
3336
3356
|
disabled: status.id === seatmapCore.AVAILABLE_STATUS_ID,
|
|
3337
3357
|
title: status.id === seatmapCore.AVAILABLE_STATUS_ID ? "Available status cannot be removed" : "Delete status",
|
|
3338
3358
|
children: "\u2715"
|
|
@@ -3344,16 +3364,21 @@ function StatusManager({ venue, history, store, style }) {
|
|
|
3344
3364
|
status.id
|
|
3345
3365
|
);
|
|
3346
3366
|
}),
|
|
3347
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
3348
|
-
/* @__PURE__ */ jsxRuntime.
|
|
3349
|
-
"
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3367
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__panel-row seatmap-editor__panel-row--spaced", children: [
|
|
3368
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "seatmap-editor__color-picker-shell seatmap-editor__color-picker-shell--lg", children: [
|
|
3369
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { "aria-hidden": "true", className: "seatmap-editor__color-picker-dot", style: { background: newColor } }),
|
|
3370
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3371
|
+
"input",
|
|
3372
|
+
{
|
|
3373
|
+
type: "color",
|
|
3374
|
+
value: newColor,
|
|
3375
|
+
onChange: (e) => setNewColor(e.target.value),
|
|
3376
|
+
className: "seatmap-editor__color-picker-input",
|
|
3377
|
+
"data-editable": "true",
|
|
3378
|
+
title: "Pick new status color"
|
|
3379
|
+
}
|
|
3380
|
+
)
|
|
3381
|
+
] }),
|
|
3357
3382
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3358
3383
|
"input",
|
|
3359
3384
|
{
|
|
@@ -3361,19 +3386,10 @@ function StatusManager({ venue, history, store, style }) {
|
|
|
3361
3386
|
value: newName,
|
|
3362
3387
|
onChange: (e) => setNewName(e.target.value),
|
|
3363
3388
|
onKeyDown: (e) => e.key === "Enter" && addStatus(),
|
|
3364
|
-
|
|
3365
|
-
flex: 1,
|
|
3366
|
-
padding: "4px 8px",
|
|
3367
|
-
background: "#2a2a4a",
|
|
3368
|
-
border: "1px solid #3a3a5a",
|
|
3369
|
-
borderRadius: 4,
|
|
3370
|
-
color: "#e0e0e0",
|
|
3371
|
-
fontSize: 13,
|
|
3372
|
-
fontFamily: "system-ui"
|
|
3373
|
-
}
|
|
3389
|
+
className: "seatmap-editor__panel-input seatmap-editor__panel-input--grow"
|
|
3374
3390
|
}
|
|
3375
3391
|
),
|
|
3376
|
-
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: addStatus,
|
|
3392
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { onClick: addStatus, className: "seatmap-editor__panel-button", children: "Add" })
|
|
3377
3393
|
] })
|
|
3378
3394
|
] });
|
|
3379
3395
|
}
|
|
@@ -3387,6 +3403,26 @@ function fitBackgroundToBounds(boundsWidth, boundsHeight, imageWidth, imageHeigh
|
|
|
3387
3403
|
aspectRatio: safeImageWidth / safeImageHeight
|
|
3388
3404
|
};
|
|
3389
3405
|
}
|
|
3406
|
+
function easeOutBack(t, overshoot) {
|
|
3407
|
+
const c1 = Math.max(0, overshoot);
|
|
3408
|
+
const c3 = c1 + 1;
|
|
3409
|
+
const p = t - 1;
|
|
3410
|
+
return 1 + c3 * p * p * p + c1 * p * p;
|
|
3411
|
+
}
|
|
3412
|
+
function getViewportStateForFitBounds(viewport, aabb, padding = 40) {
|
|
3413
|
+
const contentW = aabb.maxX - aabb.minX;
|
|
3414
|
+
const contentH = aabb.maxY - aabb.minY;
|
|
3415
|
+
if (contentW <= 0 || contentH <= 0) return null;
|
|
3416
|
+
if (viewport.screenWidth <= 0 || viewport.screenHeight <= 0) return null;
|
|
3417
|
+
const minZoom = 0.05;
|
|
3418
|
+
const maxZoom = 4;
|
|
3419
|
+
const scaleX = (viewport.screenWidth - padding * 2) / contentW;
|
|
3420
|
+
const scaleY = (viewport.screenHeight - padding * 2) / contentH;
|
|
3421
|
+
const zoom = Math.min(maxZoom, Math.max(minZoom, Math.min(scaleX, scaleY)));
|
|
3422
|
+
const x = -(aabb.minX + contentW / 2) + viewport.screenWidth / (2 * zoom);
|
|
3423
|
+
const y = -(aabb.minY + contentH / 2) + viewport.screenHeight / (2 * zoom);
|
|
3424
|
+
return { x, y, zoom };
|
|
3425
|
+
}
|
|
3390
3426
|
function getBackgroundRectInWorld(venue) {
|
|
3391
3427
|
const width = Math.max(1, venue.backgroundImageWidth ?? venue.bounds.width);
|
|
3392
3428
|
const height = Math.max(1, venue.backgroundImageHeight ?? venue.bounds.height);
|
|
@@ -3399,6 +3435,129 @@ function getBackgroundRectInWorld(venue) {
|
|
|
3399
3435
|
height
|
|
3400
3436
|
};
|
|
3401
3437
|
}
|
|
3438
|
+
var MOTION_SETTINGS_STORAGE_KEY = "seatmap-editor-motion-settings-v1";
|
|
3439
|
+
var DEFAULT_MOTION_SETTINGS = {
|
|
3440
|
+
sectionDrawJelly: 46,
|
|
3441
|
+
fitViewJelly: 52,
|
|
3442
|
+
panInertiaJelly: 55,
|
|
3443
|
+
pointerScrollZoomJelly: 52,
|
|
3444
|
+
useAdvancedMotion: false,
|
|
3445
|
+
sectionDrawDurationMs: 620,
|
|
3446
|
+
sectionDrawCenterPullPct: 22,
|
|
3447
|
+
sectionDrawZoomBoostPct: 5,
|
|
3448
|
+
sectionDrawOvershootPct: 72,
|
|
3449
|
+
fitViewDurationMs: 680,
|
|
3450
|
+
fitViewOvershootPct: 90,
|
|
3451
|
+
panInertiaCarryPct: 73,
|
|
3452
|
+
panInertiaFrictionPct: 92,
|
|
3453
|
+
panInertiaMinSpeedMilli: 10,
|
|
3454
|
+
panVelocityBlendPct: 30,
|
|
3455
|
+
panStopDeltaMilli: 300,
|
|
3456
|
+
panReleaseIdleMs: 90,
|
|
3457
|
+
pointerScrollZoomDurationMs: 180,
|
|
3458
|
+
pointerScrollZoomStrengthPct: 22,
|
|
3459
|
+
pointerScrollZoomDeltaDivisor: 680
|
|
3460
|
+
};
|
|
3461
|
+
function clampPercent(n) {
|
|
3462
|
+
return Math.max(0, Math.min(100, n));
|
|
3463
|
+
}
|
|
3464
|
+
function clampRange(n, min, max) {
|
|
3465
|
+
return Math.max(min, Math.min(max, n));
|
|
3466
|
+
}
|
|
3467
|
+
function loadMotionSettings() {
|
|
3468
|
+
if (typeof window === "undefined") return DEFAULT_MOTION_SETTINGS;
|
|
3469
|
+
try {
|
|
3470
|
+
const raw = window.localStorage.getItem(MOTION_SETTINGS_STORAGE_KEY);
|
|
3471
|
+
if (!raw) return DEFAULT_MOTION_SETTINGS;
|
|
3472
|
+
const parsed = JSON.parse(raw);
|
|
3473
|
+
return {
|
|
3474
|
+
sectionDrawJelly: clampPercent(Number(parsed.sectionDrawJelly ?? DEFAULT_MOTION_SETTINGS.sectionDrawJelly)),
|
|
3475
|
+
fitViewJelly: clampPercent(Number(parsed.fitViewJelly ?? DEFAULT_MOTION_SETTINGS.fitViewJelly)),
|
|
3476
|
+
panInertiaJelly: clampPercent(Number(parsed.panInertiaJelly ?? DEFAULT_MOTION_SETTINGS.panInertiaJelly)),
|
|
3477
|
+
pointerScrollZoomJelly: clampPercent(
|
|
3478
|
+
Number(parsed.pointerScrollZoomJelly ?? DEFAULT_MOTION_SETTINGS.pointerScrollZoomJelly)
|
|
3479
|
+
),
|
|
3480
|
+
useAdvancedMotion: Boolean(parsed.useAdvancedMotion ?? DEFAULT_MOTION_SETTINGS.useAdvancedMotion),
|
|
3481
|
+
sectionDrawDurationMs: clampRange(
|
|
3482
|
+
Number(parsed.sectionDrawDurationMs ?? DEFAULT_MOTION_SETTINGS.sectionDrawDurationMs),
|
|
3483
|
+
100,
|
|
3484
|
+
3e3
|
|
3485
|
+
),
|
|
3486
|
+
sectionDrawCenterPullPct: clampRange(
|
|
3487
|
+
Number(parsed.sectionDrawCenterPullPct ?? DEFAULT_MOTION_SETTINGS.sectionDrawCenterPullPct),
|
|
3488
|
+
0,
|
|
3489
|
+
100
|
|
3490
|
+
),
|
|
3491
|
+
sectionDrawZoomBoostPct: clampRange(
|
|
3492
|
+
Number(parsed.sectionDrawZoomBoostPct ?? DEFAULT_MOTION_SETTINGS.sectionDrawZoomBoostPct),
|
|
3493
|
+
0,
|
|
3494
|
+
50
|
|
3495
|
+
),
|
|
3496
|
+
sectionDrawOvershootPct: clampRange(
|
|
3497
|
+
Number(parsed.sectionDrawOvershootPct ?? DEFAULT_MOTION_SETTINGS.sectionDrawOvershootPct),
|
|
3498
|
+
0,
|
|
3499
|
+
180
|
|
3500
|
+
),
|
|
3501
|
+
fitViewDurationMs: clampRange(
|
|
3502
|
+
Number(parsed.fitViewDurationMs ?? DEFAULT_MOTION_SETTINGS.fitViewDurationMs),
|
|
3503
|
+
100,
|
|
3504
|
+
3e3
|
|
3505
|
+
),
|
|
3506
|
+
fitViewOvershootPct: clampRange(
|
|
3507
|
+
Number(parsed.fitViewOvershootPct ?? DEFAULT_MOTION_SETTINGS.fitViewOvershootPct),
|
|
3508
|
+
0,
|
|
3509
|
+
180
|
|
3510
|
+
),
|
|
3511
|
+
panInertiaCarryPct: clampRange(
|
|
3512
|
+
Number(parsed.panInertiaCarryPct ?? DEFAULT_MOTION_SETTINGS.panInertiaCarryPct),
|
|
3513
|
+
0,
|
|
3514
|
+
95
|
|
3515
|
+
),
|
|
3516
|
+
panInertiaFrictionPct: clampRange(
|
|
3517
|
+
Number(parsed.panInertiaFrictionPct ?? DEFAULT_MOTION_SETTINGS.panInertiaFrictionPct),
|
|
3518
|
+
70,
|
|
3519
|
+
99
|
|
3520
|
+
),
|
|
3521
|
+
panInertiaMinSpeedMilli: clampRange(
|
|
3522
|
+
Number(parsed.panInertiaMinSpeedMilli ?? DEFAULT_MOTION_SETTINGS.panInertiaMinSpeedMilli),
|
|
3523
|
+
1,
|
|
3524
|
+
50
|
|
3525
|
+
),
|
|
3526
|
+
panVelocityBlendPct: clampRange(
|
|
3527
|
+
Number(parsed.panVelocityBlendPct ?? DEFAULT_MOTION_SETTINGS.panVelocityBlendPct),
|
|
3528
|
+
5,
|
|
3529
|
+
95
|
|
3530
|
+
),
|
|
3531
|
+
panStopDeltaMilli: clampRange(
|
|
3532
|
+
Number(parsed.panStopDeltaMilli ?? DEFAULT_MOTION_SETTINGS.panStopDeltaMilli),
|
|
3533
|
+
0,
|
|
3534
|
+
4e3
|
|
3535
|
+
),
|
|
3536
|
+
panReleaseIdleMs: clampRange(
|
|
3537
|
+
Number(parsed.panReleaseIdleMs ?? DEFAULT_MOTION_SETTINGS.panReleaseIdleMs),
|
|
3538
|
+
0,
|
|
3539
|
+
400
|
|
3540
|
+
),
|
|
3541
|
+
pointerScrollZoomDurationMs: clampRange(
|
|
3542
|
+
Number(parsed.pointerScrollZoomDurationMs ?? DEFAULT_MOTION_SETTINGS.pointerScrollZoomDurationMs),
|
|
3543
|
+
60,
|
|
3544
|
+
600
|
|
3545
|
+
),
|
|
3546
|
+
pointerScrollZoomStrengthPct: clampRange(
|
|
3547
|
+
Number(parsed.pointerScrollZoomStrengthPct ?? DEFAULT_MOTION_SETTINGS.pointerScrollZoomStrengthPct),
|
|
3548
|
+
8,
|
|
3549
|
+
55
|
|
3550
|
+
),
|
|
3551
|
+
pointerScrollZoomDeltaDivisor: clampRange(
|
|
3552
|
+
Number(parsed.pointerScrollZoomDeltaDivisor ?? DEFAULT_MOTION_SETTINGS.pointerScrollZoomDeltaDivisor),
|
|
3553
|
+
250,
|
|
3554
|
+
1400
|
|
3555
|
+
)
|
|
3556
|
+
};
|
|
3557
|
+
} catch {
|
|
3558
|
+
return DEFAULT_MOTION_SETTINGS;
|
|
3559
|
+
}
|
|
3560
|
+
}
|
|
3402
3561
|
function PolygonPreviewOverlay({
|
|
3403
3562
|
points,
|
|
3404
3563
|
closeable,
|
|
@@ -3410,59 +3569,53 @@ function PolygonPreviewOverlay({
|
|
|
3410
3569
|
const svgPoints = screenPoints.map((p) => `${p.x},${p.y}`).join(" ");
|
|
3411
3570
|
const first = screenPoints[0];
|
|
3412
3571
|
const last = screenPoints[screenPoints.length - 1];
|
|
3572
|
+
const isRectanglePreview = mode === "rectangle" && screenPoints.length >= 3;
|
|
3413
3573
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3414
3574
|
"svg",
|
|
3415
3575
|
{
|
|
3416
|
-
|
|
3417
|
-
position: "absolute",
|
|
3418
|
-
inset: 0,
|
|
3419
|
-
width: "100%",
|
|
3420
|
-
height: "100%",
|
|
3421
|
-
pointerEvents: "none",
|
|
3422
|
-
zIndex: 10
|
|
3423
|
-
},
|
|
3576
|
+
className: "seatmap-editor__overlay-svg seatmap-editor__overlay-svg--polygon",
|
|
3424
3577
|
children: [
|
|
3425
|
-
screenPoints.length >= 2 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3578
|
+
screenPoints.length >= 2 && (isRectanglePreview ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
3579
|
+
"polygon",
|
|
3580
|
+
{
|
|
3581
|
+
className: "seatmap-editor__overlay-polygon",
|
|
3582
|
+
points: svgPoints
|
|
3583
|
+
}
|
|
3584
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
3426
3585
|
"polyline",
|
|
3427
3586
|
{
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
stroke: "rgba(100, 180, 255, 0.8)",
|
|
3431
|
-
strokeWidth: 2,
|
|
3432
|
-
strokeDasharray: "6 4"
|
|
3587
|
+
className: "seatmap-editor__overlay-polyline",
|
|
3588
|
+
points: svgPoints
|
|
3433
3589
|
}
|
|
3434
|
-
),
|
|
3590
|
+
)),
|
|
3435
3591
|
mode === "polygon" && screenPoints.length >= 3 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
3436
3592
|
"line",
|
|
3437
3593
|
{
|
|
3594
|
+
className: `seatmap-editor__overlay-close-line ${closeable ? "seatmap-editor__overlay-close-line--active" : "seatmap-editor__overlay-close-line--inactive"}`,
|
|
3438
3595
|
x1: last.x,
|
|
3439
3596
|
y1: last.y,
|
|
3440
3597
|
x2: first.x,
|
|
3441
3598
|
y2: first.y,
|
|
3442
|
-
stroke: closeable ? "rgba(100, 255, 100, 0.8)" : "rgba(100, 180, 255, 0.3)",
|
|
3443
|
-
strokeWidth: closeable ? 2 : 1,
|
|
3444
3599
|
strokeDasharray: "4 4"
|
|
3445
3600
|
}
|
|
3446
3601
|
),
|
|
3447
3602
|
mode === "polygon" && screenPoints.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3448
3603
|
"circle",
|
|
3449
3604
|
{
|
|
3605
|
+
className: `seatmap-editor__overlay-point${i === 0 && closeable ? " seatmap-editor__overlay-point--first-active" : ""}`,
|
|
3450
3606
|
cx: p.x,
|
|
3451
3607
|
cy: p.y,
|
|
3452
|
-
r: i === 0 && closeable ? 8 : 4
|
|
3453
|
-
fill: i === 0 && closeable ? "rgba(100, 255, 100, 0.8)" : "rgba(100, 180, 255, 0.8)"
|
|
3608
|
+
r: i === 0 && closeable ? 8 : 4
|
|
3454
3609
|
},
|
|
3455
3610
|
i
|
|
3456
3611
|
)),
|
|
3457
3612
|
mode === "polygon" && points.length >= 2 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3458
3613
|
"text",
|
|
3459
3614
|
{
|
|
3615
|
+
className: "seatmap-editor__overlay-label-text",
|
|
3460
3616
|
x: (first.x + last.x) / 2,
|
|
3461
3617
|
y: (first.y + last.y) / 2 - 10,
|
|
3462
|
-
fill: "#e0e0e0",
|
|
3463
3618
|
fontSize: 12,
|
|
3464
|
-
fontFamily: "system-ui",
|
|
3465
|
-
textAnchor: "middle",
|
|
3466
3619
|
children: [
|
|
3467
3620
|
points.length,
|
|
3468
3621
|
" points ",
|
|
@@ -3487,35 +3640,23 @@ function DragPreviewOverlay({
|
|
|
3487
3640
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3488
3641
|
"svg",
|
|
3489
3642
|
{
|
|
3490
|
-
|
|
3491
|
-
position: "absolute",
|
|
3492
|
-
inset: 0,
|
|
3493
|
-
width: "100%",
|
|
3494
|
-
height: "100%",
|
|
3495
|
-
pointerEvents: "none",
|
|
3496
|
-
zIndex: 14
|
|
3497
|
-
},
|
|
3643
|
+
className: "seatmap-editor__overlay-svg seatmap-editor__overlay-svg--drag",
|
|
3498
3644
|
children: [
|
|
3499
3645
|
sectionScreenOutlines.filter((outline) => outline.length >= 3).map((outline, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3500
3646
|
"polygon",
|
|
3501
3647
|
{
|
|
3502
|
-
|
|
3503
|
-
|
|
3504
|
-
stroke: "rgba(110, 190, 255, 0.95)",
|
|
3505
|
-
strokeWidth: 2,
|
|
3506
|
-
strokeDasharray: "8 6"
|
|
3648
|
+
className: "seatmap-editor__overlay-drag-outline",
|
|
3649
|
+
points: outline.map((p) => `${p.x},${p.y}`).join(" ")
|
|
3507
3650
|
},
|
|
3508
3651
|
i
|
|
3509
3652
|
)),
|
|
3510
3653
|
seatScreenPoints.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
3511
3654
|
"circle",
|
|
3512
3655
|
{
|
|
3656
|
+
className: "seatmap-editor__overlay-drag-seat",
|
|
3513
3657
|
cx: p.x,
|
|
3514
3658
|
cy: p.y,
|
|
3515
|
-
r: 5
|
|
3516
|
-
fill: "rgba(110, 190, 255, 0.35)",
|
|
3517
|
-
stroke: "rgba(110, 190, 255, 0.95)",
|
|
3518
|
-
strokeWidth: 1.5
|
|
3659
|
+
r: 5
|
|
3519
3660
|
},
|
|
3520
3661
|
i
|
|
3521
3662
|
))
|
|
@@ -3541,6 +3682,8 @@ function EditorInner({
|
|
|
3541
3682
|
const backgroundResizeAnchorRef = react.useRef(null);
|
|
3542
3683
|
const backgroundMoveOffsetRef = react.useRef(null);
|
|
3543
3684
|
const historyRef = react.useRef(new seatmapCore.CommandHistory());
|
|
3685
|
+
const fitViewRafRef = react.useRef(0);
|
|
3686
|
+
const applyMotionSyncRafRef = react.useRef(0);
|
|
3544
3687
|
const [canUndo, setCanUndo] = react.useState(false);
|
|
3545
3688
|
const [canRedo, setCanRedo] = react.useState(false);
|
|
3546
3689
|
const panTool = react.useMemo(() => new PanTool(), []);
|
|
@@ -3584,17 +3727,209 @@ function EditorInner({
|
|
|
3584
3727
|
const sectionResizeReturnToAddSectionRef = react.useRef(false);
|
|
3585
3728
|
const [, setDragPreviewVersion] = react.useState(0);
|
|
3586
3729
|
const [sectionMode, setSectionMode] = react.useState("rectangle");
|
|
3730
|
+
const [sectionKind, setSectionKind] = react.useState("section");
|
|
3587
3731
|
const [sectionResizeEnabled, setSectionResizeEnabled] = react.useState(false);
|
|
3732
|
+
const [autoFocusNewSection, setAutoFocusNewSection] = react.useState(true);
|
|
3588
3733
|
const [gridEnabled, setGridEnabled] = react.useState(false);
|
|
3589
3734
|
const [isGridOptionsOpen, setIsGridOptionsOpen] = react.useState(false);
|
|
3590
3735
|
const [showCanvasGrid, setShowCanvasGrid] = react.useState(false);
|
|
3591
3736
|
const [canvasGridStyle, setCanvasGridStyle] = react.useState("solid");
|
|
3592
3737
|
const [showSectionGrid, setShowSectionGrid] = react.useState(true);
|
|
3593
3738
|
const [sectionGridStyle, setSectionGridStyle] = react.useState("dots");
|
|
3739
|
+
const [showHints, setShowHints] = react.useState(true);
|
|
3740
|
+
const [isEditorSettingsOpen, setIsEditorSettingsOpen] = react.useState(false);
|
|
3741
|
+
const motionSettings = react.useMemo(() => loadMotionSettings(), []);
|
|
3742
|
+
const [sectionDrawJelly, setSectionDrawJelly] = react.useState(motionSettings.sectionDrawJelly);
|
|
3743
|
+
const [fitViewJelly, setFitViewJelly] = react.useState(motionSettings.fitViewJelly);
|
|
3744
|
+
const [panInertiaJelly, setPanInertiaJelly] = react.useState(motionSettings.panInertiaJelly);
|
|
3745
|
+
const [pointerScrollZoomJelly, setPointerScrollZoomJelly] = react.useState(motionSettings.pointerScrollZoomJelly);
|
|
3746
|
+
const [useAdvancedMotion, setUseAdvancedMotion] = react.useState(motionSettings.useAdvancedMotion);
|
|
3747
|
+
const [sectionDrawDurationMs, setSectionDrawDurationMs] = react.useState(motionSettings.sectionDrawDurationMs);
|
|
3748
|
+
const [sectionDrawCenterPullPct, setSectionDrawCenterPullPct] = react.useState(motionSettings.sectionDrawCenterPullPct);
|
|
3749
|
+
const [sectionDrawZoomBoostPct, setSectionDrawZoomBoostPct] = react.useState(motionSettings.sectionDrawZoomBoostPct);
|
|
3750
|
+
const [sectionDrawOvershootPct, setSectionDrawOvershootPct] = react.useState(motionSettings.sectionDrawOvershootPct);
|
|
3751
|
+
const [fitViewDurationMs, setFitViewDurationMs] = react.useState(motionSettings.fitViewDurationMs);
|
|
3752
|
+
const [fitViewOvershootPct, setFitViewOvershootPct] = react.useState(motionSettings.fitViewOvershootPct);
|
|
3753
|
+
const [panInertiaCarryPct, setPanInertiaCarryPct] = react.useState(motionSettings.panInertiaCarryPct);
|
|
3754
|
+
const [panInertiaFrictionPct, setPanInertiaFrictionPct] = react.useState(motionSettings.panInertiaFrictionPct);
|
|
3755
|
+
const [panInertiaMinSpeedMilli, setPanInertiaMinSpeedMilli] = react.useState(motionSettings.panInertiaMinSpeedMilli);
|
|
3756
|
+
const [panVelocityBlendPct, setPanVelocityBlendPct] = react.useState(motionSettings.panVelocityBlendPct);
|
|
3757
|
+
const [panStopDeltaMilli, setPanStopDeltaMilli] = react.useState(motionSettings.panStopDeltaMilli);
|
|
3758
|
+
const [panReleaseIdleMs, setPanReleaseIdleMs] = react.useState(motionSettings.panReleaseIdleMs);
|
|
3759
|
+
const [pointerScrollZoomDurationMs, setPointerScrollZoomDurationMs] = react.useState(motionSettings.pointerScrollZoomDurationMs);
|
|
3760
|
+
const [pointerScrollZoomStrengthPct, setPointerScrollZoomStrengthPct] = react.useState(motionSettings.pointerScrollZoomStrengthPct);
|
|
3761
|
+
const [pointerScrollZoomDeltaDivisor, setPointerScrollZoomDeltaDivisor] = react.useState(motionSettings.pointerScrollZoomDeltaDivisor);
|
|
3594
3762
|
const [seatsPerRow, setSeatsPerRow] = react.useState(10);
|
|
3595
3763
|
const [rowsCount, setRowsCount] = react.useState(1);
|
|
3596
3764
|
const [rowOrientationDeg, setRowOrientationDeg] = react.useState(0);
|
|
3765
|
+
const [rowDirectionArrowMode, setRowDirectionArrowMode] = react.useState("row-direction");
|
|
3597
3766
|
const [rowPreviewPoint, setRowPreviewPoint] = react.useState(null);
|
|
3767
|
+
const [cursorScreenPoint, setCursorScreenPoint] = react.useState(null);
|
|
3768
|
+
react.useEffect(() => {
|
|
3769
|
+
if (typeof window === "undefined") return;
|
|
3770
|
+
const payload = {
|
|
3771
|
+
sectionDrawJelly,
|
|
3772
|
+
fitViewJelly,
|
|
3773
|
+
panInertiaJelly,
|
|
3774
|
+
pointerScrollZoomJelly,
|
|
3775
|
+
useAdvancedMotion,
|
|
3776
|
+
sectionDrawDurationMs,
|
|
3777
|
+
sectionDrawCenterPullPct,
|
|
3778
|
+
sectionDrawZoomBoostPct,
|
|
3779
|
+
sectionDrawOvershootPct,
|
|
3780
|
+
fitViewDurationMs,
|
|
3781
|
+
fitViewOvershootPct,
|
|
3782
|
+
panInertiaCarryPct,
|
|
3783
|
+
panInertiaFrictionPct,
|
|
3784
|
+
panInertiaMinSpeedMilli,
|
|
3785
|
+
panVelocityBlendPct,
|
|
3786
|
+
panStopDeltaMilli,
|
|
3787
|
+
panReleaseIdleMs,
|
|
3788
|
+
pointerScrollZoomDurationMs,
|
|
3789
|
+
pointerScrollZoomStrengthPct,
|
|
3790
|
+
pointerScrollZoomDeltaDivisor
|
|
3791
|
+
};
|
|
3792
|
+
window.localStorage.setItem(MOTION_SETTINGS_STORAGE_KEY, JSON.stringify(payload));
|
|
3793
|
+
}, [
|
|
3794
|
+
sectionDrawJelly,
|
|
3795
|
+
fitViewJelly,
|
|
3796
|
+
panInertiaJelly,
|
|
3797
|
+
pointerScrollZoomJelly,
|
|
3798
|
+
useAdvancedMotion,
|
|
3799
|
+
sectionDrawDurationMs,
|
|
3800
|
+
sectionDrawCenterPullPct,
|
|
3801
|
+
sectionDrawZoomBoostPct,
|
|
3802
|
+
sectionDrawOvershootPct,
|
|
3803
|
+
fitViewDurationMs,
|
|
3804
|
+
fitViewOvershootPct,
|
|
3805
|
+
panInertiaCarryPct,
|
|
3806
|
+
panInertiaFrictionPct,
|
|
3807
|
+
panInertiaMinSpeedMilli,
|
|
3808
|
+
panVelocityBlendPct,
|
|
3809
|
+
panStopDeltaMilli,
|
|
3810
|
+
panReleaseIdleMs,
|
|
3811
|
+
pointerScrollZoomDurationMs,
|
|
3812
|
+
pointerScrollZoomStrengthPct,
|
|
3813
|
+
pointerScrollZoomDeltaDivisor
|
|
3814
|
+
]);
|
|
3815
|
+
react.useEffect(() => {
|
|
3816
|
+
panTool.setInertiaOptions({
|
|
3817
|
+
panInertiaJelly,
|
|
3818
|
+
panInertiaCarry: useAdvancedMotion ? panInertiaCarryPct / 100 : void 0,
|
|
3819
|
+
panInertiaFriction: useAdvancedMotion ? panInertiaFrictionPct / 100 : void 0,
|
|
3820
|
+
panInertiaMinSpeed: useAdvancedMotion ? panInertiaMinSpeedMilli / 1e3 : void 0,
|
|
3821
|
+
panVelocityBlend: useAdvancedMotion ? panVelocityBlendPct / 100 : void 0,
|
|
3822
|
+
panStopDelta: useAdvancedMotion ? panStopDeltaMilli / 1e3 : void 0,
|
|
3823
|
+
panReleaseIdleMs: useAdvancedMotion ? panReleaseIdleMs : void 0
|
|
3824
|
+
});
|
|
3825
|
+
}, [
|
|
3826
|
+
panTool,
|
|
3827
|
+
panInertiaJelly,
|
|
3828
|
+
useAdvancedMotion,
|
|
3829
|
+
panInertiaCarryPct,
|
|
3830
|
+
panInertiaFrictionPct,
|
|
3831
|
+
panInertiaMinSpeedMilli,
|
|
3832
|
+
panVelocityBlendPct,
|
|
3833
|
+
panStopDeltaMilli,
|
|
3834
|
+
panReleaseIdleMs
|
|
3835
|
+
]);
|
|
3836
|
+
const handleResetMotionSettings = react.useCallback(() => {
|
|
3837
|
+
setSectionDrawJelly(DEFAULT_MOTION_SETTINGS.sectionDrawJelly);
|
|
3838
|
+
setFitViewJelly(DEFAULT_MOTION_SETTINGS.fitViewJelly);
|
|
3839
|
+
setPanInertiaJelly(DEFAULT_MOTION_SETTINGS.panInertiaJelly);
|
|
3840
|
+
setPointerScrollZoomJelly(DEFAULT_MOTION_SETTINGS.pointerScrollZoomJelly);
|
|
3841
|
+
setUseAdvancedMotion(DEFAULT_MOTION_SETTINGS.useAdvancedMotion);
|
|
3842
|
+
setSectionDrawDurationMs(DEFAULT_MOTION_SETTINGS.sectionDrawDurationMs);
|
|
3843
|
+
setSectionDrawCenterPullPct(DEFAULT_MOTION_SETTINGS.sectionDrawCenterPullPct);
|
|
3844
|
+
setSectionDrawZoomBoostPct(DEFAULT_MOTION_SETTINGS.sectionDrawZoomBoostPct);
|
|
3845
|
+
setSectionDrawOvershootPct(DEFAULT_MOTION_SETTINGS.sectionDrawOvershootPct);
|
|
3846
|
+
setFitViewDurationMs(DEFAULT_MOTION_SETTINGS.fitViewDurationMs);
|
|
3847
|
+
setFitViewOvershootPct(DEFAULT_MOTION_SETTINGS.fitViewOvershootPct);
|
|
3848
|
+
setPanInertiaCarryPct(DEFAULT_MOTION_SETTINGS.panInertiaCarryPct);
|
|
3849
|
+
setPanInertiaFrictionPct(DEFAULT_MOTION_SETTINGS.panInertiaFrictionPct);
|
|
3850
|
+
setPanInertiaMinSpeedMilli(DEFAULT_MOTION_SETTINGS.panInertiaMinSpeedMilli);
|
|
3851
|
+
setPanVelocityBlendPct(DEFAULT_MOTION_SETTINGS.panVelocityBlendPct);
|
|
3852
|
+
setPanStopDeltaMilli(DEFAULT_MOTION_SETTINGS.panStopDeltaMilli);
|
|
3853
|
+
setPanReleaseIdleMs(DEFAULT_MOTION_SETTINGS.panReleaseIdleMs);
|
|
3854
|
+
setPointerScrollZoomDurationMs(DEFAULT_MOTION_SETTINGS.pointerScrollZoomDurationMs);
|
|
3855
|
+
setPointerScrollZoomStrengthPct(DEFAULT_MOTION_SETTINGS.pointerScrollZoomStrengthPct);
|
|
3856
|
+
setPointerScrollZoomDeltaDivisor(DEFAULT_MOTION_SETTINGS.pointerScrollZoomDeltaDivisor);
|
|
3857
|
+
}, []);
|
|
3858
|
+
const animateBasicKnobValues = react.useCallback((targets) => {
|
|
3859
|
+
if (applyMotionSyncRafRef.current) {
|
|
3860
|
+
cancelAnimationFrame(applyMotionSyncRafRef.current);
|
|
3861
|
+
applyMotionSyncRafRef.current = 0;
|
|
3862
|
+
}
|
|
3863
|
+
const start = {
|
|
3864
|
+
section: sectionDrawJelly,
|
|
3865
|
+
fit: fitViewJelly,
|
|
3866
|
+
pan: panInertiaJelly,
|
|
3867
|
+
pointerZoom: pointerScrollZoomJelly
|
|
3868
|
+
};
|
|
3869
|
+
const startedAt = performance.now();
|
|
3870
|
+
const durationMs = 260;
|
|
3871
|
+
const easeOutCubicLocal = (t) => 1 - (1 - t) * (1 - t) * (1 - t);
|
|
3872
|
+
const tick = (now) => {
|
|
3873
|
+
const progress = Math.min(1, (now - startedAt) / durationMs);
|
|
3874
|
+
const eased = easeOutCubicLocal(progress);
|
|
3875
|
+
const nextSection = Math.round(start.section + (targets.section - start.section) * eased);
|
|
3876
|
+
const nextFit = Math.round(start.fit + (targets.fit - start.fit) * eased);
|
|
3877
|
+
const nextPan = Math.round(start.pan + (targets.pan - start.pan) * eased);
|
|
3878
|
+
const nextPointerZoom = Math.round(start.pointerZoom + (targets.pointerZoom - start.pointerZoom) * eased);
|
|
3879
|
+
setSectionDrawJelly(clampPercent(nextSection));
|
|
3880
|
+
setFitViewJelly(clampPercent(nextFit));
|
|
3881
|
+
setPanInertiaJelly(clampPercent(nextPan));
|
|
3882
|
+
setPointerScrollZoomJelly(clampPercent(nextPointerZoom));
|
|
3883
|
+
if (progress < 1) {
|
|
3884
|
+
applyMotionSyncRafRef.current = requestAnimationFrame(tick);
|
|
3885
|
+
return;
|
|
3886
|
+
}
|
|
3887
|
+
applyMotionSyncRafRef.current = 0;
|
|
3888
|
+
};
|
|
3889
|
+
applyMotionSyncRafRef.current = requestAnimationFrame(tick);
|
|
3890
|
+
}, [sectionDrawJelly, fitViewJelly, panInertiaJelly, pointerScrollZoomJelly]);
|
|
3891
|
+
const handleApplyAdvancedToBasic = react.useCallback(() => {
|
|
3892
|
+
const sectionFromZoomBoost = (clampRange(sectionDrawZoomBoostPct, 0, 50) / 100 - 0.01) / 0.08;
|
|
3893
|
+
const sectionFromCenterPull = (clampRange(sectionDrawCenterPullPct, 0, 100) / 100 - 0.12) / 0.22;
|
|
3894
|
+
const sectionFromDuration = (clampRange(sectionDrawDurationMs, 100, 3e3) - 380) / 520;
|
|
3895
|
+
const sectionFromOvershoot = (clampRange(sectionDrawOvershootPct, 0, 180) / 100 - 0.08) / 0.48;
|
|
3896
|
+
const nextSectionDrawJelly = clampPercent(
|
|
3897
|
+
Math.round((sectionFromZoomBoost + sectionFromCenterPull + sectionFromDuration + sectionFromOvershoot) / 4 * 100)
|
|
3898
|
+
);
|
|
3899
|
+
const fitFromDuration = (clampRange(fitViewDurationMs, 100, 3e3) - 360) / 620;
|
|
3900
|
+
const fitFromOvershoot = (clampRange(fitViewOvershootPct, 0, 180) / 100 - 0.2) / 0.9;
|
|
3901
|
+
const nextFitViewJelly = clampPercent(Math.round((fitFromDuration + fitFromOvershoot) / 2 * 100));
|
|
3902
|
+
const panFromCarry = (clampRange(panInertiaCarryPct, 0, 95) / 100 - 0.58) / 0.28;
|
|
3903
|
+
const panFromFriction = (clampRange(panInertiaFrictionPct, 70, 99) / 100 - 0.88) / 0.08;
|
|
3904
|
+
const panFromMinSpeed = (0.012 - clampRange(panInertiaMinSpeedMilli, 1, 50) / 1e3) / 4e-3;
|
|
3905
|
+
const nextPanInertiaJelly = clampPercent(Math.round((panFromCarry + panFromFriction + panFromMinSpeed) / 3 * 100));
|
|
3906
|
+
const zoomFromDuration = (clampRange(pointerScrollZoomDurationMs, 60, 600) - 90) / 220;
|
|
3907
|
+
const zoomFromSensitivity = (clampRange(pointerScrollZoomDeltaDivisor, 250, 1400) - 520) / 380;
|
|
3908
|
+
const zoomFromStrength = (0.33 - clampRange(pointerScrollZoomStrengthPct, 8, 55) / 100) / 0.14;
|
|
3909
|
+
const nextPointerScrollZoomJelly = clampPercent(
|
|
3910
|
+
Math.round((zoomFromDuration + zoomFromSensitivity + zoomFromStrength) / 3 * 100)
|
|
3911
|
+
);
|
|
3912
|
+
animateBasicKnobValues({
|
|
3913
|
+
section: nextSectionDrawJelly,
|
|
3914
|
+
fit: nextFitViewJelly,
|
|
3915
|
+
pan: nextPanInertiaJelly,
|
|
3916
|
+
pointerZoom: nextPointerScrollZoomJelly
|
|
3917
|
+
});
|
|
3918
|
+
}, [
|
|
3919
|
+
animateBasicKnobValues,
|
|
3920
|
+
sectionDrawZoomBoostPct,
|
|
3921
|
+
sectionDrawCenterPullPct,
|
|
3922
|
+
sectionDrawDurationMs,
|
|
3923
|
+
sectionDrawOvershootPct,
|
|
3924
|
+
fitViewDurationMs,
|
|
3925
|
+
fitViewOvershootPct,
|
|
3926
|
+
panInertiaCarryPct,
|
|
3927
|
+
panInertiaFrictionPct,
|
|
3928
|
+
panInertiaMinSpeedMilli,
|
|
3929
|
+
pointerScrollZoomDurationMs,
|
|
3930
|
+
pointerScrollZoomStrengthPct,
|
|
3931
|
+
pointerScrollZoomDeltaDivisor
|
|
3932
|
+
]);
|
|
3598
3933
|
const handleSeatsPerRowChange = react.useCallback(
|
|
3599
3934
|
(n) => {
|
|
3600
3935
|
setSeatsPerRow(n);
|
|
@@ -3624,8 +3959,20 @@ function EditorInner({
|
|
|
3624
3959
|
},
|
|
3625
3960
|
[handleRowOrientationChange, rowOrientationDeg]
|
|
3626
3961
|
);
|
|
3627
|
-
const
|
|
3628
|
-
(
|
|
3962
|
+
const rowOrientationKnobDeg = react.useMemo(
|
|
3963
|
+
() => rowDirectionArrowMode === "row-direction" ? ((rowOrientationDeg + 90) % 360 + 360) % 360 : rowOrientationDeg,
|
|
3964
|
+
[rowDirectionArrowMode, rowOrientationDeg]
|
|
3965
|
+
);
|
|
3966
|
+
const handleRowOrientationKnobChange = react.useCallback(
|
|
3967
|
+
(deg) => {
|
|
3968
|
+
const mapped = rowDirectionArrowMode === "row-direction" ? deg - 90 : deg;
|
|
3969
|
+
handleRowOrientationChange(mapped);
|
|
3970
|
+
},
|
|
3971
|
+
[rowDirectionArrowMode, handleRowOrientationChange]
|
|
3972
|
+
);
|
|
3973
|
+
const handleSectionToolVariantChange = react.useCallback(
|
|
3974
|
+
(kind, mode) => {
|
|
3975
|
+
setSectionKind(kind);
|
|
3629
3976
|
setSectionMode(mode);
|
|
3630
3977
|
},
|
|
3631
3978
|
[]
|
|
@@ -3633,6 +3980,72 @@ function EditorInner({
|
|
|
3633
3980
|
react.useEffect(() => {
|
|
3634
3981
|
addSectionTool.setMode(sectionMode);
|
|
3635
3982
|
}, [addSectionTool, sectionMode]);
|
|
3983
|
+
react.useEffect(() => {
|
|
3984
|
+
addSectionTool.setSectionKind(sectionKind);
|
|
3985
|
+
}, [addSectionTool, sectionKind]);
|
|
3986
|
+
const focusSectionGently = react.useCallback(
|
|
3987
|
+
(sectionId) => {
|
|
3988
|
+
const currentVenue = store.getState().venue;
|
|
3989
|
+
if (!currentVenue) return;
|
|
3990
|
+
const section = currentVenue.sections.find((entry) => entry.id === sectionId);
|
|
3991
|
+
if (!section) return;
|
|
3992
|
+
if (viewport.screenWidth <= 0 || viewport.screenHeight <= 0) return;
|
|
3993
|
+
const startX = viewport.x;
|
|
3994
|
+
const startY = viewport.y;
|
|
3995
|
+
const startZoom = viewport.zoom;
|
|
3996
|
+
const jelly = Math.max(0, Math.min(100, sectionDrawJelly)) / 100;
|
|
3997
|
+
const zoomBoost = useAdvancedMotion ? clampRange(sectionDrawZoomBoostPct, 0, 50) / 100 : 0.01 + jelly * 0.08;
|
|
3998
|
+
const targetZoom = Math.min(4, Math.max(0.05, startZoom * (1 + zoomBoost)));
|
|
3999
|
+
const centeredX = viewport.screenWidth / (2 * targetZoom) - section.position.x;
|
|
4000
|
+
const centeredY = viewport.screenHeight / (2 * targetZoom) - section.position.y;
|
|
4001
|
+
const centerPull = useAdvancedMotion ? clampRange(sectionDrawCenterPullPct, 0, 100) / 100 : 0.12 + jelly * 0.22;
|
|
4002
|
+
const targetX = startX + (centeredX - startX) * centerPull;
|
|
4003
|
+
const targetY = startY + (centeredY - startY) * centerPull;
|
|
4004
|
+
const durationMs = useAdvancedMotion ? clampRange(sectionDrawDurationMs, 100, 3e3) : 380 + jelly * 520;
|
|
4005
|
+
const overshoot = useAdvancedMotion ? clampRange(sectionDrawOvershootPct, 0, 180) / 100 : 0.08 + jelly * 0.48;
|
|
4006
|
+
const startedAt = performance.now();
|
|
4007
|
+
const animate = (now) => {
|
|
4008
|
+
const elapsed = now - startedAt;
|
|
4009
|
+
const progress = Math.min(1, elapsed / durationMs);
|
|
4010
|
+
const eased = progress >= 1 ? 1 : easeOutBack(progress, overshoot);
|
|
4011
|
+
viewport.x = startX + (targetX - startX) * eased;
|
|
4012
|
+
viewport.y = startY + (targetY - startY) * eased;
|
|
4013
|
+
viewport.setZoom(startZoom + (targetZoom - startZoom) * eased);
|
|
4014
|
+
if (progress < 1) {
|
|
4015
|
+
requestAnimationFrame(animate);
|
|
4016
|
+
}
|
|
4017
|
+
};
|
|
4018
|
+
requestAnimationFrame(animate);
|
|
4019
|
+
},
|
|
4020
|
+
[store, viewport, sectionDrawJelly, useAdvancedMotion, sectionDrawZoomBoostPct, sectionDrawCenterPullPct, sectionDrawDurationMs, sectionDrawOvershootPct]
|
|
4021
|
+
);
|
|
4022
|
+
react.useEffect(() => {
|
|
4023
|
+
addSectionTool.onSectionCreated = (sectionId) => {
|
|
4024
|
+
if (!autoFocusNewSection) return;
|
|
4025
|
+
focusSectionGently(sectionId);
|
|
4026
|
+
};
|
|
4027
|
+
return () => {
|
|
4028
|
+
addSectionTool.onSectionCreated = void 0;
|
|
4029
|
+
};
|
|
4030
|
+
}, [addSectionTool, autoFocusNewSection, focusSectionGently]);
|
|
4031
|
+
const sectionHintText = react.useMemo(() => {
|
|
4032
|
+
if (activeToolName === "select" && sectionResizeEnabled) {
|
|
4033
|
+
return "Resize mode: drag inside section to move it, drag corners to resize, drag sides to move edges, click a side to add a polygon point.";
|
|
4034
|
+
}
|
|
4035
|
+
if (activeToolName !== "add-section") return null;
|
|
4036
|
+
if (sectionMode === "rectangle") {
|
|
4037
|
+
if (addSectionTool.hasPendingDraft()) {
|
|
4038
|
+
return "Click opposite corner to finish. Esc to cancel.";
|
|
4039
|
+
}
|
|
4040
|
+
return "Click first corner to start rectangle.";
|
|
4041
|
+
}
|
|
4042
|
+
if (addSectionTool.hasPendingDraft()) {
|
|
4043
|
+
return "Click to add points. Click first point to close. Esc to cancel.";
|
|
4044
|
+
}
|
|
4045
|
+
return "Click to place first polygon point.";
|
|
4046
|
+
}, [activeToolName, sectionMode, addSectionTool, sectionResizeEnabled]);
|
|
4047
|
+
const rowPresetRows = [1, 2, 3, 4];
|
|
4048
|
+
const rowPresetSeats = [8, 10, 12, 16];
|
|
3636
4049
|
react.useEffect(() => {
|
|
3637
4050
|
selectTool.setSectionResizeEnabled(sectionResizeEnabled);
|
|
3638
4051
|
}, [selectTool, sectionResizeEnabled]);
|
|
@@ -3646,13 +4059,17 @@ function EditorInner({
|
|
|
3646
4059
|
if (name !== "pan") {
|
|
3647
4060
|
lastNonPanToolNameRef.current = name;
|
|
3648
4061
|
}
|
|
4062
|
+
if (name !== activeToolName) {
|
|
4063
|
+
setIsGridOptionsOpen(false);
|
|
4064
|
+
setIsEditorSettingsOpen(false);
|
|
4065
|
+
}
|
|
3649
4066
|
activeToolRef.current.onDeactivate();
|
|
3650
4067
|
const tool = toolMap[name] ?? selectTool;
|
|
3651
4068
|
tool.onActivate(viewport, store);
|
|
3652
4069
|
activeToolRef.current = tool;
|
|
3653
4070
|
setActiveToolName(name);
|
|
3654
4071
|
},
|
|
3655
|
-
[toolMap, selectTool, viewport, store]
|
|
4072
|
+
[activeToolName, toolMap, selectTool, viewport, store]
|
|
3656
4073
|
);
|
|
3657
4074
|
const handleToggleSectionResize = react.useCallback(
|
|
3658
4075
|
(fromAddSection = false) => {
|
|
@@ -3723,10 +4140,52 @@ function EditorInner({
|
|
|
3723
4140
|
};
|
|
3724
4141
|
input.click();
|
|
3725
4142
|
}, [store, spatialIndex, viewport]);
|
|
4143
|
+
const stopFitViewAnimation = react.useCallback(() => {
|
|
4144
|
+
if (fitViewRafRef.current) {
|
|
4145
|
+
cancelAnimationFrame(fitViewRafRef.current);
|
|
4146
|
+
fitViewRafRef.current = 0;
|
|
4147
|
+
}
|
|
4148
|
+
}, []);
|
|
4149
|
+
const animateFitView = react.useCallback(
|
|
4150
|
+
(bounds) => {
|
|
4151
|
+
const target = getViewportStateForFitBounds(viewport, bounds, 40);
|
|
4152
|
+
if (!target) return;
|
|
4153
|
+
stopFitViewAnimation();
|
|
4154
|
+
const startX = viewport.x;
|
|
4155
|
+
const startY = viewport.y;
|
|
4156
|
+
const startZoom = viewport.zoom;
|
|
4157
|
+
const jelly = Math.max(0, Math.min(100, fitViewJelly)) / 100;
|
|
4158
|
+
const durationMs = useAdvancedMotion ? clampRange(fitViewDurationMs, 100, 3e3) : 360 + jelly * 620;
|
|
4159
|
+
const overshoot = useAdvancedMotion ? clampRange(fitViewOvershootPct, 0, 180) / 100 : 0.2 + jelly * 0.9;
|
|
4160
|
+
const startedAt = performance.now();
|
|
4161
|
+
const animate = (now) => {
|
|
4162
|
+
const elapsed = now - startedAt;
|
|
4163
|
+
const progress = Math.min(1, elapsed / durationMs);
|
|
4164
|
+
const eased = progress >= 1 ? 1 : easeOutBack(progress, overshoot);
|
|
4165
|
+
viewport.x = startX + (target.x - startX) * eased;
|
|
4166
|
+
viewport.y = startY + (target.y - startY) * eased;
|
|
4167
|
+
viewport.setZoom(startZoom + (target.zoom - startZoom) * eased);
|
|
4168
|
+
if (progress < 1) {
|
|
4169
|
+
fitViewRafRef.current = requestAnimationFrame(animate);
|
|
4170
|
+
return;
|
|
4171
|
+
}
|
|
4172
|
+
fitViewRafRef.current = 0;
|
|
4173
|
+
};
|
|
4174
|
+
fitViewRafRef.current = requestAnimationFrame(animate);
|
|
4175
|
+
},
|
|
4176
|
+
[viewport, stopFitViewAnimation, fitViewJelly, useAdvancedMotion, fitViewDurationMs, fitViewOvershootPct]
|
|
4177
|
+
);
|
|
3726
4178
|
const handleFitView = react.useCallback(() => {
|
|
3727
4179
|
if (!venue) return;
|
|
3728
|
-
|
|
3729
|
-
}, [venue,
|
|
4180
|
+
animateFitView(seatmapCore.venueAABB(venue));
|
|
4181
|
+
}, [venue, animateFitView]);
|
|
4182
|
+
react.useEffect(() => () => stopFitViewAnimation(), [stopFitViewAnimation]);
|
|
4183
|
+
react.useEffect(() => () => {
|
|
4184
|
+
if (applyMotionSyncRafRef.current) {
|
|
4185
|
+
cancelAnimationFrame(applyMotionSyncRafRef.current);
|
|
4186
|
+
applyMotionSyncRafRef.current = 0;
|
|
4187
|
+
}
|
|
4188
|
+
}, []);
|
|
3730
4189
|
const handleUploadBackground = react.useCallback(() => {
|
|
3731
4190
|
const input = document.createElement("input");
|
|
3732
4191
|
input.type = "file";
|
|
@@ -4020,6 +4479,7 @@ function EditorInner({
|
|
|
4020
4479
|
}, [isBackgroundMoving, store, viewport]);
|
|
4021
4480
|
const renderBackgroundResizeOverlay = () => {
|
|
4022
4481
|
if (!venue?.backgroundImage) return null;
|
|
4482
|
+
if (activeToolName !== "select") return null;
|
|
4023
4483
|
const rectWorld = getBackgroundRectInWorld(venue);
|
|
4024
4484
|
const topLeft = viewport.worldToScreen(rectWorld.x, rectWorld.y);
|
|
4025
4485
|
const topRight = viewport.worldToScreen(rectWorld.x + rectWorld.width, rectWorld.y);
|
|
@@ -4039,19 +4499,16 @@ function EditorInner({
|
|
|
4039
4499
|
{ left: midTop.x, top: midTop.y, cursor: "ns-resize", handle: "n" },
|
|
4040
4500
|
{ left: midBottom.x, top: midBottom.y, cursor: "ns-resize", handle: "s" }
|
|
4041
4501
|
];
|
|
4042
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
4502
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__background-overlay", children: [
|
|
4043
4503
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4044
4504
|
"div",
|
|
4045
4505
|
{
|
|
4506
|
+
className: "seatmap-editor__background-frame",
|
|
4046
4507
|
style: {
|
|
4047
|
-
position: "absolute",
|
|
4048
4508
|
left: topLeft.x,
|
|
4049
4509
|
top: topLeft.y,
|
|
4050
4510
|
width: Math.max(1, topRight.x - topLeft.x),
|
|
4051
4511
|
height: Math.max(1, bottomLeft.y - topLeft.y),
|
|
4052
|
-
border: "1px dashed rgba(130, 190, 255, 0.9)",
|
|
4053
|
-
boxShadow: "0 0 0 1px rgba(30, 30, 50, 0.8) inset",
|
|
4054
|
-
pointerEvents: "auto",
|
|
4055
4512
|
cursor: isBackgroundMoving ? "grabbing" : "grab"
|
|
4056
4513
|
},
|
|
4057
4514
|
onPointerDown: (e) => {
|
|
@@ -4077,17 +4534,11 @@ function EditorInner({
|
|
|
4077
4534
|
handles.map((handle, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4078
4535
|
"div",
|
|
4079
4536
|
{
|
|
4537
|
+
className: "seatmap-editor__background-handle",
|
|
4080
4538
|
style: {
|
|
4081
|
-
position: "absolute",
|
|
4082
4539
|
left: handle.left - 5,
|
|
4083
4540
|
top: handle.top - 5,
|
|
4084
|
-
|
|
4085
|
-
height: 10,
|
|
4086
|
-
borderRadius: 2,
|
|
4087
|
-
background: "#82beff",
|
|
4088
|
-
border: "1px solid #1f2f5f",
|
|
4089
|
-
cursor: handle.cursor,
|
|
4090
|
-
pointerEvents: "auto"
|
|
4541
|
+
cursor: handle.cursor
|
|
4091
4542
|
},
|
|
4092
4543
|
onPointerDown: (e) => {
|
|
4093
4544
|
if (e.button !== 0) return;
|
|
@@ -4127,48 +4578,34 @@ function EditorInner({
|
|
|
4127
4578
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4128
4579
|
"svg",
|
|
4129
4580
|
{
|
|
4130
|
-
|
|
4131
|
-
position: "absolute",
|
|
4132
|
-
inset: 0,
|
|
4133
|
-
width: "100%",
|
|
4134
|
-
height: "100%",
|
|
4135
|
-
pointerEvents: "none",
|
|
4136
|
-
zIndex: 16
|
|
4137
|
-
},
|
|
4581
|
+
className: "seatmap-editor__overlay-svg seatmap-editor__overlay-svg--section-resize",
|
|
4138
4582
|
children: [
|
|
4139
4583
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4140
4584
|
"polygon",
|
|
4141
4585
|
{
|
|
4142
|
-
|
|
4143
|
-
|
|
4144
|
-
stroke: "rgba(255, 193, 110, 0.9)",
|
|
4145
|
-
strokeWidth: 1.5,
|
|
4146
|
-
strokeDasharray: "7 4"
|
|
4586
|
+
className: "seatmap-editor__overlay-section-outline",
|
|
4587
|
+
points: outlinePoints
|
|
4147
4588
|
}
|
|
4148
4589
|
),
|
|
4149
4590
|
sideMidpoints.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4150
4591
|
"rect",
|
|
4151
4592
|
{
|
|
4593
|
+
className: "seatmap-editor__overlay-section-side",
|
|
4152
4594
|
x: p.x - 5,
|
|
4153
4595
|
y: p.y - 5,
|
|
4154
4596
|
width: 10,
|
|
4155
4597
|
height: 10,
|
|
4156
|
-
rx: 2
|
|
4157
|
-
fill: "rgba(255, 193, 110, 0.9)",
|
|
4158
|
-
stroke: "rgba(45, 36, 20, 0.95)",
|
|
4159
|
-
strokeWidth: 1
|
|
4598
|
+
rx: 2
|
|
4160
4599
|
},
|
|
4161
4600
|
`side-${i}`
|
|
4162
4601
|
)),
|
|
4163
4602
|
corners.map((p, i) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
4164
4603
|
"circle",
|
|
4165
4604
|
{
|
|
4605
|
+
className: "seatmap-editor__overlay-section-corner",
|
|
4166
4606
|
cx: p.x,
|
|
4167
4607
|
cy: p.y,
|
|
4168
|
-
r: 5
|
|
4169
|
-
fill: "#ffd38a",
|
|
4170
|
-
stroke: "rgba(45, 36, 20, 0.95)",
|
|
4171
|
-
strokeWidth: 1.2
|
|
4608
|
+
r: 5
|
|
4172
4609
|
},
|
|
4173
4610
|
`corner-${i}`
|
|
4174
4611
|
)),
|
|
@@ -4176,25 +4613,20 @@ function EditorInner({
|
|
|
4176
4613
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4177
4614
|
"rect",
|
|
4178
4615
|
{
|
|
4616
|
+
className: "seatmap-editor__overlay-label-box",
|
|
4179
4617
|
x: hint.x - 88,
|
|
4180
4618
|
y: hint.y - 32,
|
|
4181
4619
|
width: 176,
|
|
4182
4620
|
height: 20,
|
|
4183
|
-
rx: 6
|
|
4184
|
-
fill: "rgba(15, 15, 25, 0.9)",
|
|
4185
|
-
stroke: "rgba(255, 193, 110, 0.65)",
|
|
4186
|
-
strokeWidth: 1
|
|
4621
|
+
rx: 6
|
|
4187
4622
|
}
|
|
4188
4623
|
),
|
|
4189
4624
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4190
4625
|
"text",
|
|
4191
4626
|
{
|
|
4627
|
+
className: "seatmap-editor__overlay-label-text",
|
|
4192
4628
|
x: hint.x,
|
|
4193
4629
|
y: hint.y - 18,
|
|
4194
|
-
fill: "#ffd38a",
|
|
4195
|
-
fontSize: 11,
|
|
4196
|
-
fontFamily: "system-ui",
|
|
4197
|
-
textAnchor: "middle",
|
|
4198
4630
|
children: resizeOverlay.mergeHint?.message
|
|
4199
4631
|
}
|
|
4200
4632
|
)
|
|
@@ -4207,42 +4639,37 @@ function EditorInner({
|
|
|
4207
4639
|
if (activeToolName !== "add-row" || !rowPreviewPoint || !venue) return null;
|
|
4208
4640
|
const preview = addRowTool.getPlacementPreview(rowPreviewPoint.x, rowPreviewPoint.y, venue);
|
|
4209
4641
|
if (!preview) return null;
|
|
4642
|
+
const displayAngleRad = rowDirectionArrowMode === "row-direction" ? preview.worldAngleRad + Math.PI / 2 : preview.worldAngleRad;
|
|
4210
4643
|
const origin = viewport.worldToScreen(preview.worldX, preview.worldY);
|
|
4211
4644
|
const lineLengthPx = 78;
|
|
4212
4645
|
const end = {
|
|
4213
|
-
x: origin.x + Math.cos(
|
|
4214
|
-
y: origin.y + Math.sin(
|
|
4646
|
+
x: origin.x + Math.cos(displayAngleRad) * lineLengthPx,
|
|
4647
|
+
y: origin.y + Math.sin(displayAngleRad) * lineLengthPx
|
|
4215
4648
|
};
|
|
4216
4649
|
const arrowSizePx = 11;
|
|
4217
4650
|
const leftWing = {
|
|
4218
|
-
x: end.x - Math.cos(
|
|
4219
|
-
y: end.y - Math.sin(
|
|
4651
|
+
x: end.x - Math.cos(displayAngleRad - Math.PI / 6) * arrowSizePx,
|
|
4652
|
+
y: end.y - Math.sin(displayAngleRad - Math.PI / 6) * arrowSizePx
|
|
4220
4653
|
};
|
|
4221
4654
|
const rightWing = {
|
|
4222
|
-
x: end.x - Math.cos(
|
|
4223
|
-
y: end.y - Math.sin(
|
|
4655
|
+
x: end.x - Math.cos(displayAngleRad + Math.PI / 6) * arrowSizePx,
|
|
4656
|
+
y: end.y - Math.sin(displayAngleRad + Math.PI / 6) * arrowSizePx
|
|
4224
4657
|
};
|
|
4225
|
-
const
|
|
4658
|
+
const displayAngleDeg = ((displayAngleRad * 180 / Math.PI + 90) % 360 + 360) % 360;
|
|
4659
|
+
const orientationLabel = rowDirectionArrowMode === "row-direction" ? "Row direction" : "Viewer direction";
|
|
4226
4660
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4227
4661
|
"svg",
|
|
4228
4662
|
{
|
|
4229
|
-
|
|
4230
|
-
position: "absolute",
|
|
4231
|
-
inset: 0,
|
|
4232
|
-
width: "100%",
|
|
4233
|
-
height: "100%",
|
|
4234
|
-
pointerEvents: "none",
|
|
4235
|
-
zIndex: 17
|
|
4236
|
-
},
|
|
4663
|
+
className: "seatmap-editor__overlay-svg seatmap-editor__overlay-svg--row-direction",
|
|
4237
4664
|
children: [
|
|
4238
4665
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4239
4666
|
"line",
|
|
4240
4667
|
{
|
|
4668
|
+
className: "seatmap-editor__overlay-row-direction-line",
|
|
4241
4669
|
x1: origin.x,
|
|
4242
4670
|
y1: origin.y,
|
|
4243
4671
|
x2: end.x,
|
|
4244
4672
|
y2: end.y,
|
|
4245
|
-
stroke: "rgba(255, 213, 122, 0.95)",
|
|
4246
4673
|
strokeWidth: 3,
|
|
4247
4674
|
strokeLinecap: "round"
|
|
4248
4675
|
}
|
|
@@ -4250,44 +4677,38 @@ function EditorInner({
|
|
|
4250
4677
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4251
4678
|
"polygon",
|
|
4252
4679
|
{
|
|
4253
|
-
|
|
4254
|
-
|
|
4680
|
+
className: "seatmap-editor__overlay-row-direction-arrow",
|
|
4681
|
+
points: `${end.x},${end.y} ${leftWing.x},${leftWing.y} ${rightWing.x},${rightWing.y}`
|
|
4255
4682
|
}
|
|
4256
4683
|
),
|
|
4257
4684
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4258
4685
|
"circle",
|
|
4259
4686
|
{
|
|
4687
|
+
className: "seatmap-editor__overlay-row-direction-origin",
|
|
4260
4688
|
cx: origin.x,
|
|
4261
4689
|
cy: origin.y,
|
|
4262
4690
|
r: 5,
|
|
4263
|
-
fill: "rgba(255, 213, 122, 0.28)",
|
|
4264
|
-
stroke: "rgba(255, 213, 122, 0.95)",
|
|
4265
4691
|
strokeWidth: 1.5
|
|
4266
4692
|
}
|
|
4267
4693
|
),
|
|
4268
4694
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4269
4695
|
"rect",
|
|
4270
4696
|
{
|
|
4697
|
+
className: "seatmap-editor__overlay-label-box",
|
|
4271
4698
|
x: origin.x + 10,
|
|
4272
4699
|
y: origin.y - 28,
|
|
4273
4700
|
width: 90,
|
|
4274
4701
|
height: 20,
|
|
4275
|
-
rx: 5
|
|
4276
|
-
fill: "rgba(15, 15, 25, 0.9)",
|
|
4277
|
-
stroke: "rgba(255, 213, 122, 0.65)",
|
|
4278
|
-
strokeWidth: 1
|
|
4702
|
+
rx: 5
|
|
4279
4703
|
}
|
|
4280
4704
|
),
|
|
4281
4705
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4282
4706
|
"text",
|
|
4283
4707
|
{
|
|
4708
|
+
className: "seatmap-editor__overlay-label-text",
|
|
4284
4709
|
x: origin.x + 55,
|
|
4285
4710
|
y: origin.y - 14,
|
|
4286
|
-
|
|
4287
|
-
fontSize: 11,
|
|
4288
|
-
fontFamily: "system-ui",
|
|
4289
|
-
textAnchor: "middle",
|
|
4290
|
-
children: `Row angle ${Math.round(worldAngleDeg)}deg`
|
|
4711
|
+
children: `${orientationLabel} ${Math.round(displayAngleDeg)}\xB0`
|
|
4291
4712
|
}
|
|
4292
4713
|
)
|
|
4293
4714
|
]
|
|
@@ -4306,719 +4727,808 @@ function EditorInner({
|
|
|
4306
4727
|
},
|
|
4307
4728
|
[venue, store]
|
|
4308
4729
|
);
|
|
4309
|
-
const
|
|
4310
|
-
const
|
|
4311
|
-
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4730
|
+
const handleDeleteSelectedObjects = react.useCallback(() => {
|
|
4731
|
+
const state = store.getState();
|
|
4732
|
+
const currentVenue = state.venue;
|
|
4733
|
+
if (!currentVenue) return;
|
|
4734
|
+
const selectedSeatIdSet = new Set(state.selectedSeatIds);
|
|
4735
|
+
const selectedSectionIdSet = new Set(state.selectedSectionIds);
|
|
4736
|
+
if (selectedSeatIdSet.size === 0 && selectedSectionIdSet.size === 0) return;
|
|
4737
|
+
const previousVenue = currentVenue;
|
|
4738
|
+
const nextVenue = {
|
|
4739
|
+
...currentVenue,
|
|
4740
|
+
sections: currentVenue.sections.filter((section) => !selectedSectionIdSet.has(section.id)).map((section) => ({
|
|
4741
|
+
...section,
|
|
4742
|
+
rows: section.rows.map((row) => ({
|
|
4743
|
+
...row,
|
|
4744
|
+
seats: row.seats.filter((seat) => !selectedSeatIdSet.has(seat.id))
|
|
4745
|
+
}))
|
|
4746
|
+
})),
|
|
4747
|
+
tables: currentVenue.tables.map((table) => ({
|
|
4748
|
+
...table,
|
|
4749
|
+
seats: table.seats.filter((seat) => !selectedSeatIdSet.has(seat.id))
|
|
4750
|
+
}))
|
|
4326
4751
|
};
|
|
4327
|
-
|
|
4328
|
-
"
|
|
4752
|
+
historyRef.current.execute({
|
|
4753
|
+
description: "Delete selected objects",
|
|
4754
|
+
execute: () => {
|
|
4755
|
+
store.getState().setVenue(nextVenue);
|
|
4756
|
+
store.getState().clearSelection();
|
|
4757
|
+
},
|
|
4758
|
+
undo: () => {
|
|
4759
|
+
store.getState().setVenue(previousVenue);
|
|
4760
|
+
}
|
|
4761
|
+
});
|
|
4762
|
+
}, [store]);
|
|
4763
|
+
const renderActiveToolOptionsOverlay = () => {
|
|
4764
|
+
const stopPointerPropagation = (e) => e.stopPropagation();
|
|
4765
|
+
const renderOverlay = (content) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__tool-options-overlay", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4766
|
+
"div",
|
|
4329
4767
|
{
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4336
|
-
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
|
|
4342
|
-
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
|
|
4346
|
-
|
|
4347
|
-
|
|
4348
|
-
|
|
4349
|
-
|
|
4350
|
-
|
|
4351
|
-
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
|
|
4768
|
+
className: "seatmap-editor__tool-options-shell",
|
|
4769
|
+
onPointerDown: stopPointerPropagation,
|
|
4770
|
+
onPointerMove: stopPointerPropagation,
|
|
4771
|
+
onPointerUp: stopPointerPropagation,
|
|
4772
|
+
children: content
|
|
4773
|
+
}
|
|
4774
|
+
) });
|
|
4775
|
+
const renderSwitch = (label, checked, onToggle) => /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "seatmap-editor__switch", children: [
|
|
4776
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: label }),
|
|
4777
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4778
|
+
"button",
|
|
4779
|
+
{
|
|
4780
|
+
type: "button",
|
|
4781
|
+
role: "switch",
|
|
4782
|
+
"aria-checked": checked,
|
|
4783
|
+
onClick: onToggle,
|
|
4784
|
+
className: `seatmap-editor__switch-track${checked ? " is-checked" : ""}`,
|
|
4785
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__switch-thumb" })
|
|
4786
|
+
}
|
|
4787
|
+
)
|
|
4788
|
+
] });
|
|
4789
|
+
const renderRange = (label, value, onChange2, hint, options) => {
|
|
4790
|
+
const min = options?.min ?? 0;
|
|
4791
|
+
const max = options?.max ?? 100;
|
|
4792
|
+
const step = options?.step ?? 1;
|
|
4793
|
+
const displayValue = options?.valueFormatter ? options.valueFormatter(value) : Math.round(value);
|
|
4794
|
+
const normalized = max > min ? (value - min) / (max - min) : 0;
|
|
4795
|
+
const clampedNormalized = clampRange(normalized, 0, 1);
|
|
4796
|
+
const isDial360 = options?.knobMode === "dial360";
|
|
4797
|
+
const knobFillStartDeg = options?.knobFillStartDeg ?? (isDial360 ? -90 : -130);
|
|
4798
|
+
const knobAngle = isDial360 ? knobFillStartDeg + clampedNormalized * 360 : -130 + clampedNormalized * 260;
|
|
4799
|
+
const knobFillPercent = isDial360 ? clampedNormalized * 100 : clampedNormalized * (260 / 360 * 100);
|
|
4800
|
+
const knobDisabled = Boolean(options?.disabled);
|
|
4801
|
+
const showKnob = Boolean(options?.displayAsKnob);
|
|
4802
|
+
const valuePlacement = options?.valuePlacement ?? "header";
|
|
4803
|
+
const compactKnobLayout = Boolean(options?.compactKnobLayout) && showKnob;
|
|
4804
|
+
const isLabelValueKnobLayout = showKnob && options?.knobLayout === "label-value-knob";
|
|
4805
|
+
const knobNamespace = options?.knobNamespace ?? "motion";
|
|
4806
|
+
const tooltipText = knobDisabled ? "Disabled while advanced overrides are enabled." : hint;
|
|
4807
|
+
const commitClampedValue = (raw) => {
|
|
4808
|
+
const clamped = clampRange(raw, min, max);
|
|
4809
|
+
const stepped = Math.round((clamped - min) / step) * step + min;
|
|
4810
|
+
onChange2(clampRange(stepped, min, max));
|
|
4811
|
+
};
|
|
4812
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4813
|
+
"label",
|
|
4814
|
+
{
|
|
4815
|
+
className: `seatmap-editor__motion-slider${compactKnobLayout ? " seatmap-editor__motion-slider--compact-knob" : ""}${isLabelValueKnobLayout ? " seatmap-editor__motion-slider--label-value-knob" : ""}${knobDisabled ? " is-disabled" : ""}`,
|
|
4816
|
+
children: [
|
|
4817
|
+
!isLabelValueKnobLayout && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__motion-slider-header", children: [
|
|
4818
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__label", title: label, children: label }),
|
|
4819
|
+
valuePlacement === "header" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__motion-slider-value", children: displayValue })
|
|
4820
|
+
] }),
|
|
4821
|
+
showKnob ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `seatmap-editor__motion-knob-row${isLabelValueKnobLayout ? " seatmap-editor__motion-knob-row--label-value-knob" : ""}`, children: [
|
|
4822
|
+
isLabelValueKnobLayout && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__label", title: label, children: label }),
|
|
4823
|
+
isLabelValueKnobLayout && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__motion-slider-value", children: displayValue }),
|
|
4824
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4825
|
+
"button",
|
|
4356
4826
|
{
|
|
4357
|
-
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4827
|
+
type: "button",
|
|
4828
|
+
className: "seatmap-editor__motion-knob",
|
|
4829
|
+
"aria-label": label,
|
|
4830
|
+
title: tooltipText,
|
|
4831
|
+
disabled: knobDisabled,
|
|
4832
|
+
onPointerDown: (e) => {
|
|
4833
|
+
if (knobDisabled) return;
|
|
4834
|
+
e.preventDefault();
|
|
4835
|
+
const pointerId = e.pointerId;
|
|
4836
|
+
const target = e.currentTarget;
|
|
4837
|
+
target.setPointerCapture(pointerId);
|
|
4838
|
+
const range = max - min;
|
|
4839
|
+
const getDialValue = (clientX, clientY) => {
|
|
4840
|
+
const rect = target.getBoundingClientRect();
|
|
4841
|
+
const centerX = rect.left + rect.width / 2;
|
|
4842
|
+
const centerY = rect.top + rect.height / 2;
|
|
4843
|
+
const angle = Math.atan2(clientY - centerY, clientX - centerX);
|
|
4844
|
+
const clockwiseFromUpDeg = ((angle * 180 / Math.PI + 90) % 360 + 360) % 360;
|
|
4845
|
+
return min + clockwiseFromUpDeg / 360 * range;
|
|
4846
|
+
};
|
|
4847
|
+
let handleMove;
|
|
4848
|
+
if (isDial360) {
|
|
4849
|
+
commitClampedValue(getDialValue(e.clientX, e.clientY));
|
|
4850
|
+
handleMove = (ev) => {
|
|
4851
|
+
commitClampedValue(getDialValue(ev.clientX, ev.clientY));
|
|
4852
|
+
};
|
|
4853
|
+
} else {
|
|
4854
|
+
const startY = e.clientY;
|
|
4855
|
+
const startValue = value;
|
|
4856
|
+
const dragHeightPx = 180;
|
|
4857
|
+
handleMove = (ev) => {
|
|
4858
|
+
const deltaY = startY - ev.clientY;
|
|
4859
|
+
commitClampedValue(startValue + deltaY / dragHeightPx * range);
|
|
4860
|
+
};
|
|
4861
|
+
}
|
|
4862
|
+
const handleUp = () => {
|
|
4863
|
+
target.removeEventListener("pointermove", handleMove);
|
|
4864
|
+
target.removeEventListener("pointerup", handleUp);
|
|
4865
|
+
target.removeEventListener("pointercancel", handleUp);
|
|
4866
|
+
};
|
|
4867
|
+
target.addEventListener("pointermove", handleMove);
|
|
4868
|
+
target.addEventListener("pointerup", handleUp);
|
|
4869
|
+
target.addEventListener("pointercancel", handleUp);
|
|
4870
|
+
},
|
|
4871
|
+
children: [
|
|
4872
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4873
|
+
"span",
|
|
4874
|
+
{
|
|
4875
|
+
className: `seatmap-editor__motion-knob-ring${knobNamespace === "tool" ? " seatmap-editor__motion-knob-ring--tool" : " seatmap-editor__motion-knob-ring--motion"}`,
|
|
4876
|
+
style: {
|
|
4877
|
+
[knobNamespace === "tool" ? "--tool-knob-fill" : "--motion-knob-fill"]: `${Math.round(knobFillPercent)}%`,
|
|
4878
|
+
[knobNamespace === "tool" ? "--tool-knob-start-angle" : "--motion-knob-start-angle"]: `${knobFillStartDeg}deg`
|
|
4879
|
+
}
|
|
4880
|
+
}
|
|
4881
|
+
),
|
|
4882
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4883
|
+
"span",
|
|
4884
|
+
{
|
|
4885
|
+
className: "seatmap-editor__motion-knob-indicator",
|
|
4886
|
+
style: { transform: `translate(-50%, -100%) rotate(${knobAngle}deg)` }
|
|
4887
|
+
}
|
|
4888
|
+
)
|
|
4889
|
+
]
|
|
4361
4890
|
}
|
|
4362
|
-
)
|
|
4891
|
+
),
|
|
4892
|
+
valuePlacement === "knob" && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__motion-slider-value seatmap-editor__motion-slider-value--knob", children: displayValue }),
|
|
4893
|
+
options?.knobRightContent && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__motion-knob-right-content", children: options.knobRightContent })
|
|
4894
|
+
] }) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
4895
|
+
"input",
|
|
4896
|
+
{
|
|
4897
|
+
type: "range",
|
|
4898
|
+
min,
|
|
4899
|
+
max,
|
|
4900
|
+
step,
|
|
4901
|
+
value,
|
|
4902
|
+
onChange: (e) => commitClampedValue(Number(e.target.value) || 0),
|
|
4903
|
+
disabled: knobDisabled,
|
|
4904
|
+
className: "seatmap-editor__panel-range",
|
|
4905
|
+
title: tooltipText
|
|
4906
|
+
}
|
|
4907
|
+
)
|
|
4908
|
+
]
|
|
4909
|
+
}
|
|
4910
|
+
);
|
|
4911
|
+
};
|
|
4912
|
+
const renderOptionCard = (title, body, className) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `seatmap-editor__option-card${className ? ` ${className}` : ""}`, children: [
|
|
4913
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__option-card-title seatmap-editor__option-card-title--group", children: title }),
|
|
4914
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__option-card-subdivider" }),
|
|
4915
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__option-card-body", children: body })
|
|
4916
|
+
] });
|
|
4917
|
+
const renderGridOptionsCard = () => renderOptionCard(
|
|
4918
|
+
"Grid options",
|
|
4919
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
4920
|
+
renderSwitch("Grid", gridEnabled, () => setGridEnabled((v) => !v)),
|
|
4921
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__option-card-divider" }),
|
|
4922
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__option-row", children: [
|
|
4923
|
+
renderSwitch("Canvas grid", showCanvasGrid, () => setShowCanvasGrid((v) => !v)),
|
|
4924
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4925
|
+
"select",
|
|
4926
|
+
{
|
|
4927
|
+
value: canvasGridStyle,
|
|
4928
|
+
onChange: (e) => setCanvasGridStyle(e.target.value),
|
|
4929
|
+
className: "seatmap-editor__select",
|
|
4930
|
+
disabled: !gridEnabled || !showCanvasGrid,
|
|
4931
|
+
children: [
|
|
4932
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "solid", children: "Solid" }),
|
|
4933
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "dashed", children: "Dashed" }),
|
|
4934
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "dotted", children: "Dotted" })
|
|
4935
|
+
]
|
|
4363
4936
|
}
|
|
4364
4937
|
)
|
|
4365
|
-
]
|
|
4366
|
-
|
|
4938
|
+
] }),
|
|
4939
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__option-row", children: [
|
|
4940
|
+
renderSwitch("Section grid", showSectionGrid, () => setShowSectionGrid((v) => !v)),
|
|
4941
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4942
|
+
"select",
|
|
4943
|
+
{
|
|
4944
|
+
value: sectionGridStyle,
|
|
4945
|
+
onChange: (e) => setSectionGridStyle(e.target.value),
|
|
4946
|
+
className: "seatmap-editor__select",
|
|
4947
|
+
disabled: !gridEnabled || !showSectionGrid,
|
|
4948
|
+
children: [
|
|
4949
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "dots", children: "Dots" }),
|
|
4950
|
+
/* @__PURE__ */ jsxRuntime.jsx("option", { value: "cross", children: "Cross" })
|
|
4951
|
+
]
|
|
4952
|
+
}
|
|
4953
|
+
)
|
|
4954
|
+
] })
|
|
4955
|
+
] })
|
|
4367
4956
|
);
|
|
4368
|
-
const
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
|
|
4374
|
-
|
|
4375
|
-
|
|
4376
|
-
|
|
4377
|
-
|
|
4378
|
-
|
|
4379
|
-
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4390
|
-
|
|
4391
|
-
|
|
4392
|
-
|
|
4393
|
-
|
|
4957
|
+
const renderMotionSettingsCard = () => renderOptionCard(
|
|
4958
|
+
"Editor motion",
|
|
4959
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__motion-layout", children: [
|
|
4960
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__motion-column seatmap-editor__motion-column--basic", children: [
|
|
4961
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__option-card-title", children: "Basic" }),
|
|
4962
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__motion-control-grid", children: [
|
|
4963
|
+
renderRange(
|
|
4964
|
+
"Section draw zoom jelly",
|
|
4965
|
+
sectionDrawJelly,
|
|
4966
|
+
setSectionDrawJelly,
|
|
4967
|
+
"Controls how floaty auto-focus feels after drawing a section.",
|
|
4968
|
+
{ disabled: useAdvancedMotion, displayAsKnob: true, knobLayout: "label-value-knob" }
|
|
4969
|
+
),
|
|
4970
|
+
renderRange(
|
|
4971
|
+
"Fit zoom jelly",
|
|
4972
|
+
fitViewJelly,
|
|
4973
|
+
setFitViewJelly,
|
|
4974
|
+
"Controls smoothness and duration of Fit action.",
|
|
4975
|
+
{ disabled: useAdvancedMotion, displayAsKnob: true, knobLayout: "label-value-knob" }
|
|
4976
|
+
),
|
|
4977
|
+
renderRange(
|
|
4978
|
+
"Canvas pan inertia",
|
|
4979
|
+
panInertiaJelly,
|
|
4980
|
+
setPanInertiaJelly,
|
|
4981
|
+
"Controls glide amount after you release a pan drag.",
|
|
4982
|
+
{ disabled: useAdvancedMotion, displayAsKnob: true, knobLayout: "label-value-knob" }
|
|
4983
|
+
),
|
|
4984
|
+
renderRange(
|
|
4985
|
+
"Pointer scroll zoom jelly",
|
|
4986
|
+
pointerScrollZoomJelly,
|
|
4987
|
+
setPointerScrollZoomJelly,
|
|
4988
|
+
"Controls how smooth pointer wheel zoom feels.",
|
|
4989
|
+
{ disabled: useAdvancedMotion, displayAsKnob: true, knobLayout: "label-value-knob" }
|
|
4990
|
+
)
|
|
4991
|
+
] }),
|
|
4992
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__option-row seatmap-editor__option-row--end seatmap-editor__motion-basic-actions", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
4993
|
+
"button",
|
|
4394
4994
|
{
|
|
4395
|
-
|
|
4396
|
-
|
|
4397
|
-
|
|
4398
|
-
|
|
4399
|
-
fontWeight: 600
|
|
4400
|
-
},
|
|
4401
|
-
children: "Grid options"
|
|
4995
|
+
type: "button",
|
|
4996
|
+
className: "seatmap-editor__panel-button seatmap-editor__panel-button--tiny",
|
|
4997
|
+
onClick: handleResetMotionSettings,
|
|
4998
|
+
children: "Reset to defaults"
|
|
4402
4999
|
}
|
|
4403
|
-
),
|
|
4404
|
-
renderSwitch("
|
|
4405
|
-
|
|
4406
|
-
|
|
4407
|
-
|
|
4408
|
-
|
|
4409
|
-
|
|
5000
|
+
) }),
|
|
5001
|
+
renderSwitch("Use advanced overrides", useAdvancedMotion, () => setUseAdvancedMotion((v) => !v))
|
|
5002
|
+
] }),
|
|
5003
|
+
useAdvancedMotion && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__motion-column seatmap-editor__motion-column--advanced", children: [
|
|
5004
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__motion-advanced-header", children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__option-card-title", children: "Advanced" }) }),
|
|
5005
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__option-card-title seatmap-editor__option-card-title--subtle", children: "Section" }),
|
|
5006
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__motion-control-grid is-knob-grid", children: [
|
|
5007
|
+
renderRange(
|
|
5008
|
+
"Draw duration",
|
|
5009
|
+
sectionDrawDurationMs,
|
|
5010
|
+
setSectionDrawDurationMs,
|
|
5011
|
+
"Animation duration in milliseconds.",
|
|
4410
5012
|
{
|
|
4411
|
-
|
|
4412
|
-
|
|
4413
|
-
|
|
4414
|
-
|
|
4415
|
-
|
|
4416
|
-
|
|
4417
|
-
|
|
4418
|
-
|
|
4419
|
-
|
|
5013
|
+
min: 100,
|
|
5014
|
+
max: 3e3,
|
|
5015
|
+
step: 10,
|
|
5016
|
+
valueFormatter: (n) => `${Math.round(n)}ms`,
|
|
5017
|
+
displayAsKnob: true,
|
|
5018
|
+
compactKnobLayout: true
|
|
5019
|
+
}
|
|
5020
|
+
),
|
|
5021
|
+
renderRange(
|
|
5022
|
+
"Center pull",
|
|
5023
|
+
sectionDrawCenterPullPct,
|
|
5024
|
+
setSectionDrawCenterPullPct,
|
|
5025
|
+
"How strongly section draw focus moves toward section center.",
|
|
5026
|
+
{
|
|
5027
|
+
min: 0,
|
|
5028
|
+
max: 100,
|
|
5029
|
+
step: 1,
|
|
5030
|
+
valueFormatter: (n) => `${Math.round(n)}%`,
|
|
5031
|
+
displayAsKnob: true,
|
|
5032
|
+
compactKnobLayout: true
|
|
5033
|
+
}
|
|
5034
|
+
),
|
|
5035
|
+
renderRange(
|
|
5036
|
+
"Zoom boost",
|
|
5037
|
+
sectionDrawZoomBoostPct,
|
|
5038
|
+
setSectionDrawZoomBoostPct,
|
|
5039
|
+
"Additional zoom applied during section draw focus.",
|
|
5040
|
+
{
|
|
5041
|
+
min: 0,
|
|
5042
|
+
max: 50,
|
|
5043
|
+
step: 1,
|
|
5044
|
+
valueFormatter: (n) => `${Math.round(n)}%`,
|
|
5045
|
+
displayAsKnob: true,
|
|
5046
|
+
compactKnobLayout: true
|
|
5047
|
+
}
|
|
5048
|
+
),
|
|
5049
|
+
renderRange(
|
|
5050
|
+
"Overshoot",
|
|
5051
|
+
sectionDrawOvershootPct,
|
|
5052
|
+
setSectionDrawOvershootPct,
|
|
5053
|
+
"Spring amount near the end of section auto-focus.",
|
|
5054
|
+
{
|
|
5055
|
+
min: 0,
|
|
5056
|
+
max: 180,
|
|
5057
|
+
step: 1,
|
|
5058
|
+
valueFormatter: (n) => `${Math.round(n)}%`,
|
|
5059
|
+
displayAsKnob: true,
|
|
5060
|
+
compactKnobLayout: true
|
|
4420
5061
|
}
|
|
4421
5062
|
)
|
|
4422
5063
|
] }),
|
|
4423
|
-
/* @__PURE__ */ jsxRuntime.
|
|
4424
|
-
|
|
4425
|
-
|
|
4426
|
-
|
|
5064
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__motion-group-divider" }),
|
|
5065
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__option-card-title seatmap-editor__option-card-title--subtle", children: "Fit" }),
|
|
5066
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__motion-control-grid is-knob-grid", children: [
|
|
5067
|
+
renderRange(
|
|
5068
|
+
"Duration",
|
|
5069
|
+
fitViewDurationMs,
|
|
5070
|
+
setFitViewDurationMs,
|
|
5071
|
+
"Animation duration for Fit action.",
|
|
4427
5072
|
{
|
|
4428
|
-
|
|
4429
|
-
|
|
4430
|
-
|
|
4431
|
-
|
|
4432
|
-
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
5073
|
+
min: 100,
|
|
5074
|
+
max: 3e3,
|
|
5075
|
+
step: 10,
|
|
5076
|
+
valueFormatter: (n) => `${Math.round(n)}ms`,
|
|
5077
|
+
displayAsKnob: true,
|
|
5078
|
+
compactKnobLayout: true
|
|
5079
|
+
}
|
|
5080
|
+
),
|
|
5081
|
+
renderRange(
|
|
5082
|
+
"Overshoot",
|
|
5083
|
+
fitViewOvershootPct,
|
|
5084
|
+
setFitViewOvershootPct,
|
|
5085
|
+
"Spring amount near the end of Fit movement.",
|
|
5086
|
+
{
|
|
5087
|
+
min: 0,
|
|
5088
|
+
max: 180,
|
|
5089
|
+
step: 1,
|
|
5090
|
+
valueFormatter: (n) => `${Math.round(n)}%`,
|
|
5091
|
+
displayAsKnob: true,
|
|
5092
|
+
compactKnobLayout: true
|
|
4436
5093
|
}
|
|
4437
5094
|
)
|
|
4438
|
-
] })
|
|
4439
|
-
|
|
4440
|
-
|
|
5095
|
+
] }),
|
|
5096
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__motion-group-divider" }),
|
|
5097
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__option-card-title seatmap-editor__option-card-title--subtle", children: "Pan" }),
|
|
5098
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__motion-control-grid is-knob-grid", children: [
|
|
5099
|
+
renderRange(
|
|
5100
|
+
"Inertia carry",
|
|
5101
|
+
panInertiaCarryPct,
|
|
5102
|
+
setPanInertiaCarryPct,
|
|
5103
|
+
"Velocity retained at pan release.",
|
|
5104
|
+
{
|
|
5105
|
+
min: 0,
|
|
5106
|
+
max: 95,
|
|
5107
|
+
step: 1,
|
|
5108
|
+
valueFormatter: (n) => `${Math.round(n)}%`,
|
|
5109
|
+
displayAsKnob: true,
|
|
5110
|
+
compactKnobLayout: true
|
|
5111
|
+
}
|
|
5112
|
+
),
|
|
5113
|
+
renderRange(
|
|
5114
|
+
"Inertia friction",
|
|
5115
|
+
panInertiaFrictionPct,
|
|
5116
|
+
setPanInertiaFrictionPct,
|
|
5117
|
+
"Per-frame damping (higher = longer glide).",
|
|
5118
|
+
{
|
|
5119
|
+
min: 70,
|
|
5120
|
+
max: 99,
|
|
5121
|
+
step: 1,
|
|
5122
|
+
valueFormatter: (n) => `${Math.round(n)}%`,
|
|
5123
|
+
displayAsKnob: true,
|
|
5124
|
+
compactKnobLayout: true
|
|
5125
|
+
}
|
|
5126
|
+
),
|
|
5127
|
+
renderRange(
|
|
5128
|
+
"Stop speed",
|
|
5129
|
+
panInertiaMinSpeedMilli,
|
|
5130
|
+
setPanInertiaMinSpeedMilli,
|
|
5131
|
+
"Stop threshold in px/ms x1000.",
|
|
5132
|
+
{
|
|
5133
|
+
min: 1,
|
|
5134
|
+
max: 50,
|
|
5135
|
+
step: 1,
|
|
5136
|
+
valueFormatter: (n) => `${Math.round(n)}`,
|
|
5137
|
+
displayAsKnob: true,
|
|
5138
|
+
compactKnobLayout: true
|
|
5139
|
+
}
|
|
5140
|
+
),
|
|
5141
|
+
renderRange(
|
|
5142
|
+
"Velocity blend",
|
|
5143
|
+
panVelocityBlendPct,
|
|
5144
|
+
setPanVelocityBlendPct,
|
|
5145
|
+
"How quickly release velocity follows latest drag samples.",
|
|
5146
|
+
{
|
|
5147
|
+
min: 5,
|
|
5148
|
+
max: 95,
|
|
5149
|
+
step: 1,
|
|
5150
|
+
valueFormatter: (n) => `${Math.round(n)}%`,
|
|
5151
|
+
displayAsKnob: true,
|
|
5152
|
+
compactKnobLayout: true
|
|
5153
|
+
}
|
|
5154
|
+
),
|
|
5155
|
+
renderRange(
|
|
5156
|
+
"Stop delta",
|
|
5157
|
+
panStopDeltaMilli,
|
|
5158
|
+
setPanStopDeltaMilli,
|
|
5159
|
+
"Treat movement below this px x1000 as stopped while dragging.",
|
|
5160
|
+
{
|
|
5161
|
+
min: 0,
|
|
5162
|
+
max: 4e3,
|
|
5163
|
+
step: 10,
|
|
5164
|
+
valueFormatter: (n) => `${Math.round(n)}`,
|
|
5165
|
+
displayAsKnob: true,
|
|
5166
|
+
compactKnobLayout: true
|
|
5167
|
+
}
|
|
5168
|
+
),
|
|
5169
|
+
renderRange(
|
|
5170
|
+
"Release idle",
|
|
5171
|
+
panReleaseIdleMs,
|
|
5172
|
+
setPanReleaseIdleMs,
|
|
5173
|
+
"If pointer pauses this long before release, inertia is dropped.",
|
|
5174
|
+
{
|
|
5175
|
+
min: 0,
|
|
5176
|
+
max: 400,
|
|
5177
|
+
step: 5,
|
|
5178
|
+
valueFormatter: (n) => `${Math.round(n)}ms`,
|
|
5179
|
+
displayAsKnob: true,
|
|
5180
|
+
compactKnobLayout: true
|
|
5181
|
+
}
|
|
5182
|
+
)
|
|
5183
|
+
] }),
|
|
5184
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__motion-group-divider" }),
|
|
5185
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__option-card-title seatmap-editor__option-card-title--subtle", children: "Pointer scroll zoom" }),
|
|
5186
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__motion-control-grid is-knob-grid", children: [
|
|
5187
|
+
renderRange(
|
|
5188
|
+
"Duration",
|
|
5189
|
+
pointerScrollZoomDurationMs,
|
|
5190
|
+
setPointerScrollZoomDurationMs,
|
|
5191
|
+
"Wheel zoom easing duration.",
|
|
5192
|
+
{
|
|
5193
|
+
min: 60,
|
|
5194
|
+
max: 600,
|
|
5195
|
+
step: 5,
|
|
5196
|
+
valueFormatter: (n) => `${Math.round(n)}ms`,
|
|
5197
|
+
displayAsKnob: true,
|
|
5198
|
+
compactKnobLayout: true
|
|
5199
|
+
}
|
|
5200
|
+
),
|
|
5201
|
+
renderRange(
|
|
5202
|
+
"Strength",
|
|
5203
|
+
pointerScrollZoomStrengthPct,
|
|
5204
|
+
setPointerScrollZoomStrengthPct,
|
|
5205
|
+
"Per-frame zoom blend amount (higher = snappier response).",
|
|
5206
|
+
{
|
|
5207
|
+
min: 8,
|
|
5208
|
+
max: 55,
|
|
5209
|
+
step: 1,
|
|
5210
|
+
valueFormatter: (n) => `${Math.round(n)}%`,
|
|
5211
|
+
displayAsKnob: true,
|
|
5212
|
+
compactKnobLayout: true
|
|
5213
|
+
}
|
|
5214
|
+
),
|
|
5215
|
+
renderRange(
|
|
5216
|
+
"Sensitivity",
|
|
5217
|
+
pointerScrollZoomDeltaDivisor,
|
|
5218
|
+
setPointerScrollZoomDeltaDivisor,
|
|
5219
|
+
"Wheel delta divisor (higher = less sensitive).",
|
|
5220
|
+
{
|
|
5221
|
+
min: 250,
|
|
5222
|
+
max: 1400,
|
|
5223
|
+
step: 10,
|
|
5224
|
+
valueFormatter: (n) => `${Math.round(n)}`,
|
|
5225
|
+
displayAsKnob: true,
|
|
5226
|
+
compactKnobLayout: true
|
|
5227
|
+
}
|
|
5228
|
+
)
|
|
5229
|
+
] }),
|
|
5230
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__motion-advanced-actions", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5231
|
+
"button",
|
|
5232
|
+
{
|
|
5233
|
+
type: "button",
|
|
5234
|
+
className: "seatmap-editor__panel-button seatmap-editor__panel-button--tiny",
|
|
5235
|
+
onClick: handleApplyAdvancedToBasic,
|
|
5236
|
+
title: "Recalculate basic knobs from advanced settings",
|
|
5237
|
+
children: "Apply to basic"
|
|
5238
|
+
}
|
|
5239
|
+
) })
|
|
5240
|
+
] })
|
|
5241
|
+
] }),
|
|
5242
|
+
`seatmap-editor__option-card--motion${useAdvancedMotion ? " is-advanced-open" : ""}`
|
|
4441
5243
|
);
|
|
4442
5244
|
if (activeToolName === "add-section") {
|
|
4443
|
-
return
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
5245
|
+
return renderOverlay(
|
|
5246
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5247
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__tool-options-title", children: "Tool Options" }),
|
|
5248
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__tool-options-divider" }),
|
|
5249
|
+
renderOptionCard("Section", /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__option-row", children: [
|
|
5250
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5251
|
+
"button",
|
|
5252
|
+
{
|
|
5253
|
+
onClick: () => handleSectionToolVariantChange("section", "rectangle"),
|
|
5254
|
+
className: `seatmap-editor__segmented-button${sectionKind === "section" && sectionMode === "rectangle" ? " is-active" : ""}`,
|
|
5255
|
+
children: "Rectangle"
|
|
5256
|
+
}
|
|
5257
|
+
),
|
|
5258
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5259
|
+
"button",
|
|
5260
|
+
{
|
|
5261
|
+
onClick: () => handleSectionToolVariantChange("section", "polygon"),
|
|
5262
|
+
className: `seatmap-editor__segmented-button${sectionKind === "section" && sectionMode === "polygon" ? " is-active" : ""}`,
|
|
5263
|
+
children: "Polygon"
|
|
5264
|
+
}
|
|
5265
|
+
)
|
|
5266
|
+
] })),
|
|
5267
|
+
renderOptionCard("Stage", /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__option-row", children: [
|
|
5268
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5269
|
+
"button",
|
|
5270
|
+
{
|
|
5271
|
+
onClick: () => handleSectionToolVariantChange("stage", "rectangle"),
|
|
5272
|
+
className: `seatmap-editor__segmented-button${sectionKind === "stage" && sectionMode === "rectangle" ? " is-active" : ""}`,
|
|
5273
|
+
children: "Rectangle"
|
|
5274
|
+
}
|
|
5275
|
+
),
|
|
5276
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5277
|
+
"button",
|
|
5278
|
+
{
|
|
5279
|
+
onClick: () => handleSectionToolVariantChange("stage", "polygon"),
|
|
5280
|
+
className: `seatmap-editor__segmented-button${sectionKind === "stage" && sectionMode === "polygon" ? " is-active" : ""}`,
|
|
5281
|
+
children: "Polygon"
|
|
5282
|
+
}
|
|
5283
|
+
)
|
|
5284
|
+
] })),
|
|
5285
|
+
renderOptionCard("Dancefloor", /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__option-row", children: [
|
|
5286
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5287
|
+
"button",
|
|
5288
|
+
{
|
|
5289
|
+
onClick: () => handleSectionToolVariantChange("dancefloor", "rectangle"),
|
|
5290
|
+
className: `seatmap-editor__segmented-button${sectionKind === "dancefloor" && sectionMode === "rectangle" ? " is-active" : ""}`,
|
|
5291
|
+
children: "Rectangle"
|
|
5292
|
+
}
|
|
5293
|
+
),
|
|
5294
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5295
|
+
"button",
|
|
5296
|
+
{
|
|
5297
|
+
onClick: () => handleSectionToolVariantChange("dancefloor", "polygon"),
|
|
5298
|
+
className: `seatmap-editor__segmented-button${sectionKind === "dancefloor" && sectionMode === "polygon" ? " is-active" : ""}`,
|
|
5299
|
+
children: "Polygon"
|
|
5300
|
+
}
|
|
5301
|
+
)
|
|
5302
|
+
] })),
|
|
5303
|
+
renderOptionCard("Section resize", /* @__PURE__ */ jsxRuntime.jsx(
|
|
5304
|
+
"button",
|
|
4456
5305
|
{
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
|
|
4461
|
-
|
|
4462
|
-
|
|
4463
|
-
|
|
4464
|
-
|
|
4465
|
-
|
|
4466
|
-
|
|
4467
|
-
|
|
4468
|
-
|
|
4469
|
-
|
|
4470
|
-
|
|
4471
|
-
|
|
5306
|
+
onClick: () => handleToggleSectionResize(true),
|
|
5307
|
+
className: `seatmap-editor__segmented-button${sectionResizeEnabled ? " is-active" : ""}`,
|
|
5308
|
+
title: "Enable section corner/side resizing",
|
|
5309
|
+
children: sectionResizeEnabled ? "Resize On" : "Resize Off"
|
|
5310
|
+
}
|
|
5311
|
+
)),
|
|
5312
|
+
renderOptionCard("Auto focus", renderSwitch(
|
|
5313
|
+
"Zoom to new section",
|
|
5314
|
+
autoFocusNewSection,
|
|
5315
|
+
() => setAutoFocusNewSection((current) => !current)
|
|
5316
|
+
)),
|
|
5317
|
+
isGridOptionsOpen && renderGridOptionsCard(),
|
|
5318
|
+
isEditorSettingsOpen && renderMotionSettingsCard()
|
|
5319
|
+
] })
|
|
5320
|
+
);
|
|
5321
|
+
}
|
|
5322
|
+
if (activeToolName === "select") {
|
|
5323
|
+
return renderOverlay(
|
|
5324
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5325
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__tool-options-title", children: "Tool Options" }),
|
|
5326
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__tool-options-divider" }),
|
|
5327
|
+
renderOptionCard("Section resize", /* @__PURE__ */ jsxRuntime.jsx(
|
|
5328
|
+
"button",
|
|
5329
|
+
{
|
|
5330
|
+
onClick: () => handleToggleSectionResize(false),
|
|
5331
|
+
className: `seatmap-editor__segmented-button${sectionResizeEnabled ? " is-active" : ""}`,
|
|
5332
|
+
children: sectionResizeEnabled ? "Resize On" : "Resize Off"
|
|
5333
|
+
}
|
|
5334
|
+
)),
|
|
5335
|
+
isGridOptionsOpen && renderGridOptionsCard(),
|
|
5336
|
+
isEditorSettingsOpen && renderMotionSettingsCard()
|
|
5337
|
+
] })
|
|
5338
|
+
);
|
|
5339
|
+
}
|
|
5340
|
+
if (activeToolName !== "add-row") {
|
|
5341
|
+
if (!isGridOptionsOpen && !isEditorSettingsOpen) return null;
|
|
5342
|
+
return renderOverlay(
|
|
5343
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5344
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__tool-options-title", children: "Tool Options" }),
|
|
5345
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__tool-options-divider" }),
|
|
5346
|
+
isGridOptionsOpen && renderGridOptionsCard(),
|
|
5347
|
+
isEditorSettingsOpen && renderMotionSettingsCard()
|
|
5348
|
+
] })
|
|
5349
|
+
);
|
|
5350
|
+
}
|
|
5351
|
+
return renderOverlay(
|
|
5352
|
+
/* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5353
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "seatmap-editor__tool-options-title", children: "Tool Options" }),
|
|
5354
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__tool-options-divider" }),
|
|
5355
|
+
renderOptionCard("Row layout", /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__row-layout-body", children: [
|
|
5356
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__row-layout-controls", children: [
|
|
5357
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "seatmap-editor__label seatmap-editor__option-row", children: [
|
|
5358
|
+
"Seats:",
|
|
5359
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__input-stepper", children: [
|
|
4472
5360
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4473
|
-
"
|
|
5361
|
+
"input",
|
|
4474
5362
|
{
|
|
4475
|
-
|
|
4476
|
-
|
|
4477
|
-
|
|
4478
|
-
|
|
4479
|
-
|
|
4480
|
-
|
|
4481
|
-
},
|
|
4482
|
-
children: "Tool Options"
|
|
5363
|
+
type: "number",
|
|
5364
|
+
min: 1,
|
|
5365
|
+
max: 100,
|
|
5366
|
+
value: seatsPerRow,
|
|
5367
|
+
onChange: (e) => handleSeatsPerRowChange(Math.max(1, Math.min(100, parseInt(e.target.value) || 1))),
|
|
5368
|
+
className: "seatmap-editor__input seatmap-editor__input--stepper"
|
|
4483
5369
|
}
|
|
4484
5370
|
),
|
|
4485
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4486
|
-
|
|
4487
|
-
"div",
|
|
5371
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5372
|
+
"button",
|
|
4488
5373
|
{
|
|
4489
|
-
|
|
4490
|
-
|
|
4491
|
-
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
padding: "8px 10px",
|
|
4495
|
-
border: "1px solid #3a3a5a",
|
|
4496
|
-
borderRadius: 6,
|
|
4497
|
-
background: "rgba(42, 42, 74, 0.65)"
|
|
4498
|
-
},
|
|
4499
|
-
children: [
|
|
4500
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4501
|
-
"span",
|
|
4502
|
-
{
|
|
4503
|
-
style: {
|
|
4504
|
-
color: "#c7c7df",
|
|
4505
|
-
fontSize: 12,
|
|
4506
|
-
fontFamily: "system-ui",
|
|
4507
|
-
fontWeight: 600
|
|
4508
|
-
},
|
|
4509
|
-
children: "Section shape"
|
|
4510
|
-
}
|
|
4511
|
-
),
|
|
4512
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6 }, children: [
|
|
4513
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4514
|
-
"button",
|
|
4515
|
-
{
|
|
4516
|
-
onClick: () => handleSectionModeChange("rectangle"),
|
|
4517
|
-
style: {
|
|
4518
|
-
padding: "4px 8px",
|
|
4519
|
-
borderRadius: 6,
|
|
4520
|
-
border: "1px solid #3a3a5a",
|
|
4521
|
-
background: sectionMode === "rectangle" ? "#4a4a7a" : "#2a2a4a",
|
|
4522
|
-
color: sectionMode === "rectangle" ? "#ffffff" : "#d0d0e0",
|
|
4523
|
-
cursor: "pointer",
|
|
4524
|
-
fontSize: 12,
|
|
4525
|
-
fontFamily: "system-ui",
|
|
4526
|
-
fontWeight: 600
|
|
4527
|
-
},
|
|
4528
|
-
children: "Rectangle"
|
|
4529
|
-
}
|
|
4530
|
-
),
|
|
4531
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4532
|
-
"button",
|
|
4533
|
-
{
|
|
4534
|
-
onClick: () => handleSectionModeChange("polygon"),
|
|
4535
|
-
style: {
|
|
4536
|
-
padding: "4px 8px",
|
|
4537
|
-
borderRadius: 6,
|
|
4538
|
-
border: "1px solid #3a3a5a",
|
|
4539
|
-
background: sectionMode === "polygon" ? "#4a4a7a" : "#2a2a4a",
|
|
4540
|
-
color: sectionMode === "polygon" ? "#ffffff" : "#d0d0e0",
|
|
4541
|
-
cursor: "pointer",
|
|
4542
|
-
fontSize: 12,
|
|
4543
|
-
fontFamily: "system-ui",
|
|
4544
|
-
fontWeight: 600
|
|
4545
|
-
},
|
|
4546
|
-
children: "Polygon"
|
|
4547
|
-
}
|
|
4548
|
-
)
|
|
4549
|
-
] })
|
|
4550
|
-
]
|
|
5374
|
+
type: "button",
|
|
5375
|
+
className: "seatmap-editor__stepper-button",
|
|
5376
|
+
"aria-label": "Increase seats per row",
|
|
5377
|
+
onClick: () => handleSeatsPerRowChange(seatsPerRow + 1),
|
|
5378
|
+
children: "+"
|
|
4551
5379
|
}
|
|
4552
5380
|
),
|
|
4553
|
-
/* @__PURE__ */ jsxRuntime.
|
|
4554
|
-
"
|
|
5381
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5382
|
+
"button",
|
|
4555
5383
|
{
|
|
4556
|
-
|
|
4557
|
-
|
|
4558
|
-
|
|
4559
|
-
|
|
4560
|
-
|
|
4561
|
-
padding: "8px 10px",
|
|
4562
|
-
border: "1px solid #3a3a5a",
|
|
4563
|
-
borderRadius: 6,
|
|
4564
|
-
background: "rgba(42, 42, 74, 0.65)"
|
|
4565
|
-
},
|
|
4566
|
-
children: [
|
|
4567
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4568
|
-
"span",
|
|
4569
|
-
{
|
|
4570
|
-
style: {
|
|
4571
|
-
color: "#c7c7df",
|
|
4572
|
-
fontSize: 12,
|
|
4573
|
-
fontFamily: "system-ui",
|
|
4574
|
-
fontWeight: 600
|
|
4575
|
-
},
|
|
4576
|
-
children: "Section resize"
|
|
4577
|
-
}
|
|
4578
|
-
),
|
|
4579
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4580
|
-
"button",
|
|
4581
|
-
{
|
|
4582
|
-
onClick: () => handleToggleSectionResize(true),
|
|
4583
|
-
style: {
|
|
4584
|
-
padding: "4px 8px",
|
|
4585
|
-
borderRadius: 6,
|
|
4586
|
-
border: "1px solid #3a3a5a",
|
|
4587
|
-
background: sectionResizeEnabled ? "#8a6a32" : "#2a2a4a",
|
|
4588
|
-
color: sectionResizeEnabled ? "#fff3d8" : "#d0d0e0",
|
|
4589
|
-
cursor: "pointer",
|
|
4590
|
-
fontSize: 12,
|
|
4591
|
-
fontFamily: "system-ui",
|
|
4592
|
-
fontWeight: 600
|
|
4593
|
-
},
|
|
4594
|
-
title: "Enable section corner/side resizing",
|
|
4595
|
-
children: sectionResizeEnabled ? "Resize On" : "Resize Off"
|
|
4596
|
-
}
|
|
4597
|
-
)
|
|
4598
|
-
]
|
|
5384
|
+
type: "button",
|
|
5385
|
+
className: "seatmap-editor__stepper-button",
|
|
5386
|
+
"aria-label": "Decrease seats per row",
|
|
5387
|
+
onClick: () => handleSeatsPerRowChange(seatsPerRow - 1),
|
|
5388
|
+
children: "-"
|
|
4599
5389
|
}
|
|
4600
|
-
)
|
|
4601
|
-
|
|
4602
|
-
|
|
4603
|
-
|
|
4604
|
-
|
|
4605
|
-
|
|
4606
|
-
);
|
|
4607
|
-
}
|
|
4608
|
-
if (activeToolName === "select") {
|
|
4609
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4610
|
-
"div",
|
|
4611
|
-
{
|
|
4612
|
-
style: {
|
|
4613
|
-
position: "absolute",
|
|
4614
|
-
top: 12,
|
|
4615
|
-
left: 12,
|
|
4616
|
-
right: 12,
|
|
4617
|
-
zIndex: 20,
|
|
4618
|
-
pointerEvents: "none"
|
|
4619
|
-
},
|
|
4620
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4621
|
-
"div",
|
|
4622
|
-
{
|
|
4623
|
-
style: {
|
|
4624
|
-
display: "flex",
|
|
4625
|
-
alignItems: "center",
|
|
4626
|
-
gap: 10,
|
|
4627
|
-
padding: "8px 12px",
|
|
4628
|
-
border: "1px solid #3a3a5a",
|
|
4629
|
-
borderRadius: 8,
|
|
4630
|
-
background: "rgba(21, 21, 40, 0.92)",
|
|
4631
|
-
backdropFilter: "blur(2px)",
|
|
4632
|
-
pointerEvents: "auto"
|
|
4633
|
-
},
|
|
4634
|
-
onPointerDown: (e) => e.stopPropagation(),
|
|
4635
|
-
onPointerMove: (e) => e.stopPropagation(),
|
|
4636
|
-
onPointerUp: (e) => e.stopPropagation(),
|
|
4637
|
-
children: [
|
|
5390
|
+
)
|
|
5391
|
+
] })
|
|
5392
|
+
] }),
|
|
5393
|
+
/* @__PURE__ */ jsxRuntime.jsxs("label", { className: "seatmap-editor__label seatmap-editor__option-row", children: [
|
|
5394
|
+
"Rows:",
|
|
5395
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__input-stepper", children: [
|
|
4638
5396
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4639
|
-
"
|
|
5397
|
+
"input",
|
|
4640
5398
|
{
|
|
4641
|
-
|
|
4642
|
-
|
|
4643
|
-
|
|
4644
|
-
|
|
4645
|
-
|
|
4646
|
-
|
|
4647
|
-
children: "Tool Options"
|
|
5399
|
+
type: "number",
|
|
5400
|
+
min: 1,
|
|
5401
|
+
max: 100,
|
|
5402
|
+
value: rowsCount,
|
|
5403
|
+
onChange: (e) => handleRowsCountChange(Math.max(1, Math.min(100, parseInt(e.target.value) || 1))),
|
|
5404
|
+
className: "seatmap-editor__input seatmap-editor__input--stepper"
|
|
4648
5405
|
}
|
|
4649
5406
|
),
|
|
4650
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4651
|
-
|
|
4652
|
-
"div",
|
|
5407
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5408
|
+
"button",
|
|
4653
5409
|
{
|
|
4654
|
-
|
|
4655
|
-
|
|
4656
|
-
|
|
4657
|
-
|
|
4658
|
-
|
|
4659
|
-
padding: "8px 10px",
|
|
4660
|
-
border: "1px solid #3a3a5a",
|
|
4661
|
-
borderRadius: 6,
|
|
4662
|
-
background: "rgba(42, 42, 74, 0.65)"
|
|
4663
|
-
},
|
|
4664
|
-
children: [
|
|
4665
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4666
|
-
"span",
|
|
4667
|
-
{
|
|
4668
|
-
style: {
|
|
4669
|
-
color: "#c7c7df",
|
|
4670
|
-
fontSize: 12,
|
|
4671
|
-
fontFamily: "system-ui",
|
|
4672
|
-
fontWeight: 600
|
|
4673
|
-
},
|
|
4674
|
-
children: "Section resize"
|
|
4675
|
-
}
|
|
4676
|
-
),
|
|
4677
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4678
|
-
"button",
|
|
4679
|
-
{
|
|
4680
|
-
onClick: () => handleToggleSectionResize(false),
|
|
4681
|
-
style: {
|
|
4682
|
-
padding: "4px 8px",
|
|
4683
|
-
borderRadius: 6,
|
|
4684
|
-
border: "1px solid #3a3a5a",
|
|
4685
|
-
background: sectionResizeEnabled ? "#8a6a32" : "#2a2a4a",
|
|
4686
|
-
color: sectionResizeEnabled ? "#fff3d8" : "#d0d0e0",
|
|
4687
|
-
cursor: "pointer",
|
|
4688
|
-
fontSize: 12,
|
|
4689
|
-
fontFamily: "system-ui",
|
|
4690
|
-
fontWeight: 600
|
|
4691
|
-
},
|
|
4692
|
-
children: sectionResizeEnabled ? "Resize On" : "Resize Off"
|
|
4693
|
-
}
|
|
4694
|
-
)
|
|
4695
|
-
]
|
|
5410
|
+
type: "button",
|
|
5411
|
+
className: "seatmap-editor__stepper-button",
|
|
5412
|
+
"aria-label": "Increase rows",
|
|
5413
|
+
onClick: () => handleRowsCountChange(rowsCount + 1),
|
|
5414
|
+
children: "+"
|
|
4696
5415
|
}
|
|
4697
5416
|
),
|
|
4698
|
-
isGridOptionsOpen && renderGridOptionsCard()
|
|
4699
|
-
]
|
|
4700
|
-
}
|
|
4701
|
-
)
|
|
4702
|
-
}
|
|
4703
|
-
);
|
|
4704
|
-
}
|
|
4705
|
-
if (activeToolName !== "add-row") {
|
|
4706
|
-
if (!isGridOptionsOpen) return null;
|
|
4707
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
4708
|
-
"div",
|
|
4709
|
-
{
|
|
4710
|
-
style: {
|
|
4711
|
-
position: "absolute",
|
|
4712
|
-
top: 12,
|
|
4713
|
-
left: 12,
|
|
4714
|
-
right: 12,
|
|
4715
|
-
zIndex: 20,
|
|
4716
|
-
pointerEvents: "none"
|
|
4717
|
-
},
|
|
4718
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4719
|
-
"div",
|
|
4720
|
-
{
|
|
4721
|
-
style: {
|
|
4722
|
-
display: "flex",
|
|
4723
|
-
alignItems: "center",
|
|
4724
|
-
gap: 10,
|
|
4725
|
-
padding: "8px 12px",
|
|
4726
|
-
border: "1px solid #3a3a5a",
|
|
4727
|
-
borderRadius: 8,
|
|
4728
|
-
background: "rgba(21, 21, 40, 0.92)",
|
|
4729
|
-
backdropFilter: "blur(2px)",
|
|
4730
|
-
pointerEvents: "auto"
|
|
4731
|
-
},
|
|
4732
|
-
onPointerDown: (e) => e.stopPropagation(),
|
|
4733
|
-
onPointerMove: (e) => e.stopPropagation(),
|
|
4734
|
-
onPointerUp: (e) => e.stopPropagation(),
|
|
4735
|
-
children: [
|
|
4736
5417
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4737
|
-
"
|
|
5418
|
+
"button",
|
|
4738
5419
|
{
|
|
4739
|
-
|
|
4740
|
-
|
|
4741
|
-
|
|
4742
|
-
|
|
4743
|
-
|
|
4744
|
-
},
|
|
4745
|
-
children: "Tool Options"
|
|
5420
|
+
type: "button",
|
|
5421
|
+
className: "seatmap-editor__stepper-button",
|
|
5422
|
+
"aria-label": "Decrease rows",
|
|
5423
|
+
onClick: () => handleRowsCountChange(rowsCount - 1),
|
|
5424
|
+
children: "-"
|
|
4746
5425
|
}
|
|
4747
|
-
)
|
|
4748
|
-
|
|
4749
|
-
|
|
4750
|
-
|
|
4751
|
-
|
|
4752
|
-
|
|
4753
|
-
|
|
4754
|
-
|
|
4755
|
-
|
|
4756
|
-
|
|
4757
|
-
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4762
|
-
left: 12,
|
|
4763
|
-
right: 12,
|
|
4764
|
-
zIndex: 20,
|
|
4765
|
-
pointerEvents: "none"
|
|
4766
|
-
},
|
|
4767
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4768
|
-
"div",
|
|
4769
|
-
{
|
|
4770
|
-
style: {
|
|
4771
|
-
display: "flex",
|
|
4772
|
-
alignItems: "center",
|
|
4773
|
-
gap: 10,
|
|
4774
|
-
padding: "8px 12px",
|
|
4775
|
-
border: "1px solid #3a3a5a",
|
|
4776
|
-
borderRadius: 8,
|
|
4777
|
-
background: "rgba(21, 21, 40, 0.92)",
|
|
4778
|
-
backdropFilter: "blur(2px)",
|
|
4779
|
-
pointerEvents: "auto"
|
|
4780
|
-
},
|
|
4781
|
-
onPointerDown: (e) => e.stopPropagation(),
|
|
4782
|
-
onPointerMove: (e) => e.stopPropagation(),
|
|
4783
|
-
onPointerUp: (e) => e.stopPropagation(),
|
|
4784
|
-
children: [
|
|
4785
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4786
|
-
"span",
|
|
4787
|
-
{
|
|
4788
|
-
style: {
|
|
4789
|
-
color: "#c7c7df",
|
|
4790
|
-
fontSize: 12,
|
|
4791
|
-
fontFamily: "system-ui",
|
|
4792
|
-
fontWeight: 600,
|
|
4793
|
-
letterSpacing: 0.2
|
|
4794
|
-
},
|
|
4795
|
-
children: "Tool Options"
|
|
4796
|
-
}
|
|
4797
|
-
),
|
|
4798
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { width: 1, height: 18, background: "#3a3a5a" } }),
|
|
4799
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4800
|
-
"div",
|
|
5426
|
+
)
|
|
5427
|
+
] })
|
|
5428
|
+
] }),
|
|
5429
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__option-card-title", children: [
|
|
5430
|
+
"Total seats to add: ",
|
|
5431
|
+
seatsPerRow * Math.max(1, rowsCount)
|
|
5432
|
+
] })
|
|
5433
|
+
] }),
|
|
5434
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__row-layout-separator" }),
|
|
5435
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__row-presets", children: /* @__PURE__ */ jsxRuntime.jsx("table", { className: "seatmap-editor__row-presets-table", children: /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: rowPresetRows.map((rowPreset) => /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
|
|
5436
|
+
/* @__PURE__ */ jsxRuntime.jsx("th", { scope: "row", className: "seatmap-editor__row-preset-row-label", children: rowPreset }),
|
|
5437
|
+
rowPresetSeats.map((seatPreset) => {
|
|
5438
|
+
const isActive = rowsCount === rowPreset && seatsPerRow === seatPreset;
|
|
5439
|
+
return /* @__PURE__ */ jsxRuntime.jsx("td", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5440
|
+
"button",
|
|
4801
5441
|
{
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
4807
|
-
padding: "8px 10px",
|
|
4808
|
-
border: "1px solid #3a3a5a",
|
|
4809
|
-
borderRadius: 6,
|
|
4810
|
-
background: "rgba(42, 42, 74, 0.65)"
|
|
5442
|
+
type: "button",
|
|
5443
|
+
className: `seatmap-editor__row-preset-button${isActive ? " is-active" : ""}`,
|
|
5444
|
+
onClick: () => {
|
|
5445
|
+
handleRowsCountChange(rowPreset);
|
|
5446
|
+
handleSeatsPerRowChange(seatPreset);
|
|
4811
5447
|
},
|
|
4812
|
-
children:
|
|
4813
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4814
|
-
"span",
|
|
4815
|
-
{
|
|
4816
|
-
style: {
|
|
4817
|
-
color: "#c7c7df",
|
|
4818
|
-
fontSize: 12,
|
|
4819
|
-
fontFamily: "system-ui",
|
|
4820
|
-
fontWeight: 600
|
|
4821
|
-
},
|
|
4822
|
-
children: "Row layout"
|
|
4823
|
-
}
|
|
4824
|
-
),
|
|
4825
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4826
|
-
"label",
|
|
4827
|
-
{
|
|
4828
|
-
style: {
|
|
4829
|
-
color: "#9e9e9e",
|
|
4830
|
-
fontSize: 12,
|
|
4831
|
-
fontFamily: "system-ui",
|
|
4832
|
-
display: "flex",
|
|
4833
|
-
alignItems: "center",
|
|
4834
|
-
gap: 6
|
|
4835
|
-
},
|
|
4836
|
-
children: [
|
|
4837
|
-
"Seats/row:",
|
|
4838
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4839
|
-
"input",
|
|
4840
|
-
{
|
|
4841
|
-
type: "number",
|
|
4842
|
-
min: 1,
|
|
4843
|
-
max: 100,
|
|
4844
|
-
value: seatsPerRow,
|
|
4845
|
-
onChange: (e) => handleSeatsPerRowChange(Math.max(1, Math.min(100, parseInt(e.target.value) || 1))),
|
|
4846
|
-
style: {
|
|
4847
|
-
width: 56,
|
|
4848
|
-
padding: "3px 6px",
|
|
4849
|
-
background: "#2a2a4a",
|
|
4850
|
-
border: "1px solid #3a3a5a",
|
|
4851
|
-
borderRadius: 4,
|
|
4852
|
-
color: "#e0e0e0",
|
|
4853
|
-
fontSize: 13,
|
|
4854
|
-
fontFamily: "system-ui"
|
|
4855
|
-
}
|
|
4856
|
-
}
|
|
4857
|
-
)
|
|
4858
|
-
]
|
|
4859
|
-
}
|
|
4860
|
-
),
|
|
4861
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4862
|
-
"label",
|
|
4863
|
-
{
|
|
4864
|
-
style: {
|
|
4865
|
-
color: "#9e9e9e",
|
|
4866
|
-
fontSize: 12,
|
|
4867
|
-
fontFamily: "system-ui",
|
|
4868
|
-
display: "flex",
|
|
4869
|
-
alignItems: "center",
|
|
4870
|
-
gap: 6
|
|
4871
|
-
},
|
|
4872
|
-
children: [
|
|
4873
|
-
"Rows:",
|
|
4874
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4875
|
-
"input",
|
|
4876
|
-
{
|
|
4877
|
-
type: "number",
|
|
4878
|
-
min: 1,
|
|
4879
|
-
max: 100,
|
|
4880
|
-
value: rowsCount,
|
|
4881
|
-
onChange: (e) => handleRowsCountChange(Math.max(1, Math.min(100, parseInt(e.target.value) || 1))),
|
|
4882
|
-
style: {
|
|
4883
|
-
width: 56,
|
|
4884
|
-
padding: "3px 6px",
|
|
4885
|
-
background: "#2a2a4a",
|
|
4886
|
-
border: "1px solid #3a3a5a",
|
|
4887
|
-
borderRadius: 4,
|
|
4888
|
-
color: "#e0e0e0",
|
|
4889
|
-
fontSize: 13,
|
|
4890
|
-
fontFamily: "system-ui"
|
|
4891
|
-
}
|
|
4892
|
-
}
|
|
4893
|
-
)
|
|
4894
|
-
]
|
|
4895
|
-
}
|
|
4896
|
-
),
|
|
4897
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4898
|
-
"div",
|
|
4899
|
-
{
|
|
4900
|
-
style: {
|
|
4901
|
-
color: "#c7c7df",
|
|
4902
|
-
fontSize: 12,
|
|
4903
|
-
fontFamily: "system-ui",
|
|
4904
|
-
fontWeight: 600
|
|
4905
|
-
},
|
|
4906
|
-
children: [
|
|
4907
|
-
"Total seats to add: ",
|
|
4908
|
-
seatsPerRow * Math.max(1, rowsCount)
|
|
4909
|
-
]
|
|
4910
|
-
}
|
|
4911
|
-
)
|
|
4912
|
-
]
|
|
5448
|
+
children: seatPreset
|
|
4913
5449
|
}
|
|
4914
|
-
),
|
|
4915
|
-
|
|
4916
|
-
|
|
5450
|
+
) }, `${rowPreset}-${seatPreset}`);
|
|
5451
|
+
})
|
|
5452
|
+
] }, `row-preset-${rowPreset}`)) }) }) })
|
|
5453
|
+
] })),
|
|
5454
|
+
renderOptionCard("Orientation", /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5455
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__option-row seatmap-editor__orientation-toggle", children: [
|
|
5456
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5457
|
+
"span",
|
|
5458
|
+
{
|
|
5459
|
+
className: `seatmap-editor__orientation-label${rowDirectionArrowMode === "viewer-direction" ? " is-active" : ""}`,
|
|
5460
|
+
children: "Viewer direction"
|
|
5461
|
+
}
|
|
5462
|
+
),
|
|
5463
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5464
|
+
"button",
|
|
5465
|
+
{
|
|
5466
|
+
type: "button",
|
|
5467
|
+
role: "switch",
|
|
5468
|
+
"aria-checked": rowDirectionArrowMode === "row-direction",
|
|
5469
|
+
onClick: () => setRowDirectionArrowMode(
|
|
5470
|
+
(mode) => mode === "row-direction" ? "viewer-direction" : "row-direction"
|
|
5471
|
+
),
|
|
5472
|
+
className: `seatmap-editor__switch-track seatmap-editor__switch-track--wide${rowDirectionArrowMode === "row-direction" ? " is-checked" : ""}`,
|
|
5473
|
+
title: "Toggle arrow orientation mode",
|
|
5474
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5475
|
+
"span",
|
|
5476
|
+
{
|
|
5477
|
+
className: "seatmap-editor__switch-thumb seatmap-editor__switch-thumb--wide",
|
|
5478
|
+
style: {
|
|
5479
|
+
transform: rowDirectionArrowMode === "row-direction" ? "translateX(22px)" : "translateX(0)"
|
|
5480
|
+
}
|
|
5481
|
+
}
|
|
5482
|
+
)
|
|
5483
|
+
}
|
|
5484
|
+
),
|
|
5485
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5486
|
+
"span",
|
|
5487
|
+
{
|
|
5488
|
+
className: `seatmap-editor__orientation-label${rowDirectionArrowMode === "row-direction" ? " is-active" : ""}`,
|
|
5489
|
+
children: "Row direction"
|
|
5490
|
+
}
|
|
5491
|
+
)
|
|
5492
|
+
] }),
|
|
5493
|
+
renderRange(
|
|
5494
|
+
"Orientation",
|
|
5495
|
+
rowOrientationKnobDeg,
|
|
5496
|
+
handleRowOrientationKnobChange,
|
|
5497
|
+
"Drag vertically on the knob to set row orientation.",
|
|
5498
|
+
{
|
|
5499
|
+
min: 0,
|
|
5500
|
+
max: 359,
|
|
5501
|
+
step: 1,
|
|
5502
|
+
valueFormatter: (n) => `${Math.round(n)}\xB0`,
|
|
5503
|
+
displayAsKnob: true,
|
|
5504
|
+
knobMode: "dial360",
|
|
5505
|
+
knobFillStartDeg: 0,
|
|
5506
|
+
knobNamespace: "tool",
|
|
5507
|
+
valuePlacement: "knob",
|
|
5508
|
+
knobRightContent: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5509
|
+
"button",
|
|
4917
5510
|
{
|
|
4918
|
-
|
|
4919
|
-
|
|
4920
|
-
|
|
4921
|
-
|
|
4922
|
-
gap: 8,
|
|
4923
|
-
padding: "8px 10px",
|
|
4924
|
-
border: "1px solid #3a3a5a",
|
|
4925
|
-
borderRadius: 6,
|
|
4926
|
-
background: "rgba(42, 42, 74, 0.65)"
|
|
4927
|
-
},
|
|
4928
|
-
children: [
|
|
4929
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4930
|
-
"span",
|
|
4931
|
-
{
|
|
4932
|
-
style: {
|
|
4933
|
-
color: "#c7c7df",
|
|
4934
|
-
fontSize: 12,
|
|
4935
|
-
fontFamily: "system-ui",
|
|
4936
|
-
fontWeight: 600
|
|
4937
|
-
},
|
|
4938
|
-
children: "Orientation"
|
|
4939
|
-
}
|
|
4940
|
-
),
|
|
4941
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4942
|
-
"label",
|
|
4943
|
-
{
|
|
4944
|
-
style: {
|
|
4945
|
-
color: "#9e9e9e",
|
|
4946
|
-
fontSize: 12,
|
|
4947
|
-
fontFamily: "system-ui",
|
|
4948
|
-
display: "flex",
|
|
4949
|
-
alignItems: "center",
|
|
4950
|
-
gap: 6
|
|
4951
|
-
},
|
|
4952
|
-
children: [
|
|
4953
|
-
"Orientation:",
|
|
4954
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4955
|
-
"input",
|
|
4956
|
-
{
|
|
4957
|
-
type: "number",
|
|
4958
|
-
min: 0,
|
|
4959
|
-
max: 359,
|
|
4960
|
-
value: rowOrientationDeg,
|
|
4961
|
-
onChange: (e) => handleRowOrientationChange(parseInt(e.target.value, 10) || 0),
|
|
4962
|
-
style: {
|
|
4963
|
-
width: 56,
|
|
4964
|
-
padding: "3px 6px",
|
|
4965
|
-
background: "#2a2a4a",
|
|
4966
|
-
border: "1px solid #3a3a5a",
|
|
4967
|
-
borderRadius: 4,
|
|
4968
|
-
color: "#e0e0e0",
|
|
4969
|
-
fontSize: 13,
|
|
4970
|
-
fontFamily: "system-ui"
|
|
4971
|
-
}
|
|
4972
|
-
}
|
|
4973
|
-
),
|
|
4974
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: "#9e9e9e" }, children: "deg" }),
|
|
4975
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4976
|
-
"button",
|
|
4977
|
-
{
|
|
4978
|
-
onClick: handleRotateRowOrientationQuarterTurn,
|
|
4979
|
-
style: {
|
|
4980
|
-
padding: "3px 6px",
|
|
4981
|
-
borderRadius: 4,
|
|
4982
|
-
border: "1px solid #3a3a5a",
|
|
4983
|
-
background: "#2a2a4a",
|
|
4984
|
-
color: "#d0d0e0",
|
|
4985
|
-
cursor: "pointer",
|
|
4986
|
-
fontSize: 12,
|
|
4987
|
-
fontFamily: "system-ui",
|
|
4988
|
-
fontWeight: 600
|
|
4989
|
-
},
|
|
4990
|
-
title: "Rotate row direction by 90 degrees",
|
|
4991
|
-
children: "+90deg"
|
|
4992
|
-
}
|
|
4993
|
-
)
|
|
4994
|
-
]
|
|
4995
|
-
}
|
|
4996
|
-
),
|
|
4997
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4998
|
-
"div",
|
|
4999
|
-
{
|
|
5000
|
-
style: {
|
|
5001
|
-
color: "#9e9e9e",
|
|
5002
|
-
fontSize: 11,
|
|
5003
|
-
fontFamily: "system-ui"
|
|
5004
|
-
},
|
|
5005
|
-
children: "0deg = up, 90deg = right"
|
|
5006
|
-
}
|
|
5007
|
-
)
|
|
5008
|
-
]
|
|
5511
|
+
onClick: handleRotateRowOrientationQuarterTurn,
|
|
5512
|
+
className: "seatmap-editor__segmented-button",
|
|
5513
|
+
title: "Rotate row direction by 90 degrees",
|
|
5514
|
+
children: "+90\xB0"
|
|
5009
5515
|
}
|
|
5010
|
-
)
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
}
|
|
5014
|
-
)
|
|
5015
|
-
|
|
5516
|
+
)
|
|
5517
|
+
}
|
|
5518
|
+
),
|
|
5519
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__hint-text", children: rowDirectionArrowMode === "row-direction" ? "Arrow: row direction (viewer +90\xB0)" : "Arrow: viewer direction (0\xB0 = up, 90\xB0 = right)" })
|
|
5520
|
+
] })),
|
|
5521
|
+
isGridOptionsOpen && renderGridOptionsCard(),
|
|
5522
|
+
isEditorSettingsOpen && renderMotionSettingsCard()
|
|
5523
|
+
] })
|
|
5016
5524
|
);
|
|
5017
5525
|
};
|
|
5018
5526
|
react.useEffect(() => {
|
|
5019
5527
|
const isTyping = () => {
|
|
5020
|
-
const
|
|
5021
|
-
|
|
5528
|
+
const activeElement = document.activeElement;
|
|
5529
|
+
if (!activeElement) return false;
|
|
5530
|
+
const tag = activeElement.tagName;
|
|
5531
|
+
return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || activeElement.isContentEditable;
|
|
5022
5532
|
};
|
|
5023
5533
|
const handler = (e) => {
|
|
5024
5534
|
if ((e.metaKey || e.ctrlKey) && e.key === "z") {
|
|
@@ -5028,8 +5538,18 @@ function EditorInner({
|
|
|
5028
5538
|
return;
|
|
5029
5539
|
}
|
|
5030
5540
|
if (isTyping()) return;
|
|
5031
|
-
if (e.key === "
|
|
5032
|
-
|
|
5541
|
+
if (e.key === "Delete" || e.key === "Backspace") {
|
|
5542
|
+
e.preventDefault();
|
|
5543
|
+
handleDeleteSelectedObjects();
|
|
5544
|
+
return;
|
|
5545
|
+
}
|
|
5546
|
+
if (e.key === "Escape" && activeToolName === "add-section") {
|
|
5547
|
+
e.preventDefault();
|
|
5548
|
+
addSectionTool.cancelDrawing();
|
|
5549
|
+
return;
|
|
5550
|
+
}
|
|
5551
|
+
if (e.key === "v" || e.key === "2") setActiveTool("select");
|
|
5552
|
+
if (e.key === "h" || e.key === "1") setActiveTool("pan");
|
|
5033
5553
|
if (e.key === "s" || e.key === "3") setActiveTool("add-section");
|
|
5034
5554
|
if (e.key === "r" || e.key === "4") setActiveTool("add-row");
|
|
5035
5555
|
if (e.key === "a" || e.key === "5") setActiveTool("add-seat");
|
|
@@ -5050,7 +5570,7 @@ function EditorInner({
|
|
|
5050
5570
|
window.removeEventListener("keydown", handler);
|
|
5051
5571
|
window.removeEventListener("keyup", upHandler);
|
|
5052
5572
|
};
|
|
5053
|
-
}, [setActiveTool, activeToolName]);
|
|
5573
|
+
}, [setActiveTool, activeToolName, handleDeleteSelectedObjects, addSectionTool]);
|
|
5054
5574
|
const toToolPointerEvent = react.useCallback(
|
|
5055
5575
|
(e) => {
|
|
5056
5576
|
const rect = e.currentTarget.getBoundingClientRect();
|
|
@@ -5097,6 +5617,11 @@ function EditorInner({
|
|
|
5097
5617
|
(e) => {
|
|
5098
5618
|
const toolEvent = toToolPointerEvent(e);
|
|
5099
5619
|
activeToolRef.current.onPointerMove(toolEvent, viewport, store);
|
|
5620
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
5621
|
+
setCursorScreenPoint({
|
|
5622
|
+
x: e.clientX - rect.left,
|
|
5623
|
+
y: e.clientY - rect.top
|
|
5624
|
+
});
|
|
5100
5625
|
if (activeToolName === "add-row") {
|
|
5101
5626
|
setRowPreviewPoint({ x: toolEvent.worldX, y: toolEvent.worldY });
|
|
5102
5627
|
}
|
|
@@ -5112,14 +5637,7 @@ function EditorInner({
|
|
|
5112
5637
|
(e) => handlePointerRelease(e),
|
|
5113
5638
|
[handlePointerRelease]
|
|
5114
5639
|
);
|
|
5115
|
-
|
|
5116
|
-
width: 260,
|
|
5117
|
-
background: "#1a1a2e",
|
|
5118
|
-
borderLeft: "1px solid #2a2a4a",
|
|
5119
|
-
overflowY: "auto",
|
|
5120
|
-
flexShrink: 0
|
|
5121
|
-
};
|
|
5122
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", width: "100%", height: "100%" }, children: [
|
|
5640
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor seatmap-editor--root", children: [
|
|
5123
5641
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5124
5642
|
Toolbar,
|
|
5125
5643
|
{
|
|
@@ -5134,19 +5652,25 @@ function EditorInner({
|
|
|
5134
5652
|
onRedo: () => historyRef.current.redo(),
|
|
5135
5653
|
onFitView: handleFitView,
|
|
5136
5654
|
onSave: handleSave,
|
|
5137
|
-
onLoad: handleLoad
|
|
5655
|
+
onLoad: handleLoad,
|
|
5656
|
+
showHints,
|
|
5657
|
+
onToggleHints: () => setShowHints((current) => !current),
|
|
5658
|
+
isEditorSettingsOpen,
|
|
5659
|
+
onToggleEditorSettings: () => setIsEditorSettingsOpen((current) => !current)
|
|
5138
5660
|
}
|
|
5139
5661
|
),
|
|
5140
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
5662
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__canvas-layout", children: [
|
|
5141
5663
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
5142
5664
|
"div",
|
|
5143
5665
|
{
|
|
5144
5666
|
ref: canvasAreaRef,
|
|
5145
|
-
|
|
5667
|
+
className: "seatmap-editor__canvas-area",
|
|
5668
|
+
style: { cursor: (toolMap[activeToolName] ?? selectTool).cursor },
|
|
5146
5669
|
onPointerDown: handleCanvasPointerDown,
|
|
5147
5670
|
onPointerMove: handleCanvasPointerMove,
|
|
5148
5671
|
onPointerUp: handleCanvasPointerUp,
|
|
5149
5672
|
onPointerCancel: handleCanvasPointerCancel,
|
|
5673
|
+
onPointerLeave: () => setCursorScreenPoint(null),
|
|
5150
5674
|
children: [
|
|
5151
5675
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5152
5676
|
seatmapReact.SeatmapCanvas,
|
|
@@ -5155,7 +5679,15 @@ function EditorInner({
|
|
|
5155
5679
|
showGridLines: gridEnabled && showCanvasGrid,
|
|
5156
5680
|
showSectionGridDots: gridEnabled && showSectionGrid,
|
|
5157
5681
|
canvasGridLineStyle: canvasGridStyle,
|
|
5158
|
-
sectionGridMarkerStyle: sectionGridStyle
|
|
5682
|
+
sectionGridMarkerStyle: sectionGridStyle,
|
|
5683
|
+
panInertiaJelly,
|
|
5684
|
+
panInertiaCarry: useAdvancedMotion ? panInertiaCarryPct / 100 : void 0,
|
|
5685
|
+
panInertiaFriction: useAdvancedMotion ? panInertiaFrictionPct / 100 : void 0,
|
|
5686
|
+
panInertiaMinSpeed: useAdvancedMotion ? panInertiaMinSpeedMilli / 1e3 : void 0,
|
|
5687
|
+
pointerScrollZoomJelly,
|
|
5688
|
+
pointerScrollZoomDurationMs: useAdvancedMotion ? pointerScrollZoomDurationMs : void 0,
|
|
5689
|
+
pointerScrollZoomStrengthPct: useAdvancedMotion ? pointerScrollZoomStrengthPct : void 0,
|
|
5690
|
+
pointerScrollZoomDeltaDivisor: useAdvancedMotion ? pointerScrollZoomDeltaDivisor : void 0
|
|
5159
5691
|
}
|
|
5160
5692
|
),
|
|
5161
5693
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -5178,11 +5710,22 @@ function EditorInner({
|
|
|
5178
5710
|
mode: sectionMode,
|
|
5179
5711
|
viewport
|
|
5180
5712
|
}
|
|
5713
|
+
),
|
|
5714
|
+
showHints && sectionHintText && cursorScreenPoint && /* @__PURE__ */ jsxRuntime.jsx(
|
|
5715
|
+
"div",
|
|
5716
|
+
{
|
|
5717
|
+
className: "seatmap-editor__hint-bubble",
|
|
5718
|
+
style: {
|
|
5719
|
+
left: cursorScreenPoint.x + 14,
|
|
5720
|
+
top: cursorScreenPoint.y + 14
|
|
5721
|
+
},
|
|
5722
|
+
children: sectionHintText
|
|
5723
|
+
}
|
|
5181
5724
|
)
|
|
5182
5725
|
]
|
|
5183
5726
|
}
|
|
5184
5727
|
),
|
|
5185
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", {
|
|
5728
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "seatmap-editor__sidebar", children: [
|
|
5186
5729
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5187
5730
|
PropertyPanel,
|
|
5188
5731
|
{
|
|
@@ -5199,7 +5742,7 @@ function EditorInner({
|
|
|
5199
5742
|
}
|
|
5200
5743
|
),
|
|
5201
5744
|
selectedSeatIds.size === 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5202
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
5745
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__sidebar-separator" }),
|
|
5203
5746
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5204
5747
|
LayerPanel,
|
|
5205
5748
|
{
|
|
@@ -5209,7 +5752,7 @@ function EditorInner({
|
|
|
5209
5752
|
onSelectSection: handleSelectSection
|
|
5210
5753
|
}
|
|
5211
5754
|
),
|
|
5212
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
5755
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__sidebar-separator" }),
|
|
5213
5756
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5214
5757
|
CategoryManager,
|
|
5215
5758
|
{
|
|
@@ -5221,7 +5764,7 @@ function EditorInner({
|
|
|
5221
5764
|
)
|
|
5222
5765
|
] }),
|
|
5223
5766
|
selectedSeatIds.size === 0 && selectedSectionIds.size === 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
5224
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", {
|
|
5767
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "seatmap-editor__sidebar-separator" }),
|
|
5225
5768
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5226
5769
|
StatusManager,
|
|
5227
5770
|
{
|
|
@@ -5236,7 +5779,7 @@ function EditorInner({
|
|
|
5236
5779
|
] });
|
|
5237
5780
|
}
|
|
5238
5781
|
function SeatmapEditor({ venue, onChange, onSave, fetchCategoryPrices, className }) {
|
|
5239
|
-
return /* @__PURE__ */ jsxRuntime.jsx(seatmapReact.SeatmapProvider, { venue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className
|
|
5782
|
+
return /* @__PURE__ */ jsxRuntime.jsx(seatmapReact.SeatmapProvider, { venue, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: className ? `seatmap-editor__host ${className}` : "seatmap-editor__host", children: /* @__PURE__ */ jsxRuntime.jsx(EditorInner, { onChange, onSave, fetchCategoryPrices }) }) });
|
|
5240
5783
|
}
|
|
5241
5784
|
var DrawGATool = class extends BaseTool {
|
|
5242
5785
|
constructor(history) {
|