sa2kit 1.6.89 → 1.6.91
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/{booking-473Db8Bo.d.mts → booking-BH7HM0D0.d.mts} +1 -0
- package/dist/{booking-473Db8Bo.d.ts → booking-BH7HM0D0.d.ts} +1 -0
- package/dist/{bookingAdminService-DqQ7hEGw.d.ts → bookingAdminService-nr1vOp6I.d.ts} +1 -1
- package/dist/{bookingAdminService-SBX4JA_U.d.mts → bookingAdminService-pvk2MY1r.d.mts} +1 -1
- package/dist/{client-Bkn6mRI7.d.ts → client-UDQ7uMFA.d.ts} +1 -1
- package/dist/{client-exYn2Qla.d.mts → client-jOToHJEx.d.mts} +1 -1
- package/dist/festivalCard/index.js +803 -212
- package/dist/festivalCard/index.js.map +1 -1
- package/dist/festivalCard/index.mjs +784 -193
- package/dist/festivalCard/index.mjs.map +1 -1
- package/dist/festivalCard/miniapp/index.js +162 -21
- package/dist/festivalCard/miniapp/index.js.map +1 -1
- package/dist/festivalCard/miniapp/index.mjs +153 -12
- package/dist/festivalCard/miniapp/index.mjs.map +1 -1
- package/dist/festivalCard/web/index.d.mts +17 -3
- package/dist/festivalCard/web/index.d.ts +17 -3
- package/dist/festivalCard/web/index.js +803 -212
- package/dist/festivalCard/web/index.js.map +1 -1
- package/dist/festivalCard/web/index.mjs +784 -193
- package/dist/festivalCard/web/index.mjs.map +1 -1
- package/dist/{index-z15F7afa.d.mts → index-Bs06cHTn.d.mts} +2 -2
- package/dist/{index-BJpxvH7X.d.ts → index-C-oNM7Gv.d.ts} +1 -1
- package/dist/{index-XTV6IU-M.d.ts → index-CUab5EBV.d.ts} +2 -2
- package/dist/{index-Cum2EknK.d.mts → index-CYDb3AKs.d.mts} +1 -1
- package/dist/{index-DyxLpkmm.d.mts → index-DBB4ad0S.d.mts} +2 -2
- package/dist/{index-CdTIsNsy.d.ts → index-DBHwbXrv.d.ts} +2 -2
- package/dist/index.js +575 -170
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +575 -170
- package/dist/index.mjs.map +1 -1
- package/dist/showmasterpiece/core.d.mts +3 -3
- package/dist/showmasterpiece/core.d.ts +3 -3
- package/dist/showmasterpiece/db.d.mts +2 -0
- package/dist/showmasterpiece/db.d.ts +2 -0
- package/dist/showmasterpiece/db.js +4 -2
- package/dist/showmasterpiece/db.js.map +1 -1
- package/dist/showmasterpiece/db.mjs +4 -2
- package/dist/showmasterpiece/db.mjs.map +1 -1
- package/dist/showmasterpiece/index.js +18 -2
- package/dist/showmasterpiece/index.js.map +1 -1
- package/dist/showmasterpiece/index.mjs +18 -2
- package/dist/showmasterpiece/index.mjs.map +1 -1
- package/dist/showmasterpiece/logic/index.d.mts +2 -2
- package/dist/showmasterpiece/logic/index.d.ts +2 -2
- package/dist/showmasterpiece/server/index.js +4 -2
- package/dist/showmasterpiece/server/index.js.map +1 -1
- package/dist/showmasterpiece/server/index.mjs +4 -2
- package/dist/showmasterpiece/server/index.mjs.map +1 -1
- package/dist/showmasterpiece/service/api/index.d.mts +1 -1
- package/dist/showmasterpiece/service/api/index.d.ts +1 -1
- package/dist/showmasterpiece/service/client-business/index.d.mts +3 -3
- package/dist/showmasterpiece/service/client-business/index.d.ts +3 -3
- package/dist/showmasterpiece/service/index.d.mts +6 -6
- package/dist/showmasterpiece/service/index.d.ts +6 -6
- package/dist/showmasterpiece/service/miniapp/index.d.mts +2 -2
- package/dist/showmasterpiece/service/miniapp/index.d.ts +2 -2
- package/dist/showmasterpiece/service/web/index.d.mts +4 -4
- package/dist/showmasterpiece/service/web/index.d.ts +4 -4
- package/dist/showmasterpiece/ui/miniapp/index.d.mts +2 -2
- package/dist/showmasterpiece/ui/miniapp/index.d.ts +2 -2
- package/dist/showmasterpiece/ui/miniapp/index.js +4 -3
- package/dist/showmasterpiece/ui/miniapp/index.js.map +1 -1
- package/dist/showmasterpiece/ui/miniapp/index.mjs +4 -3
- package/dist/showmasterpiece/ui/miniapp/index.mjs.map +1 -1
- package/dist/showmasterpiece/ui/web/index.js +18 -2
- package/dist/showmasterpiece/ui/web/index.js.map +1 -1
- package/dist/showmasterpiece/ui/web/index.mjs +18 -2
- package/dist/showmasterpiece/ui/web/index.mjs.map +1 -1
- package/dist/showmasterpiece/web/index.js +18 -2
- package/dist/showmasterpiece/web/index.js.map +1 -1
- package/dist/showmasterpiece/web/index.mjs +18 -2
- package/dist/showmasterpiece/web/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var React4 = require('react');
|
|
4
|
+
var reactDom = require('react-dom');
|
|
5
|
+
var clsx = require('clsx');
|
|
4
6
|
|
|
5
7
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
6
8
|
|
|
7
|
-
var
|
|
9
|
+
var React4__default = /*#__PURE__*/_interopDefault(React4);
|
|
8
10
|
|
|
9
11
|
// src/festivalCard/core/defaults.ts
|
|
10
12
|
var DEFAULT_FESTIVAL_CARD_CONFIG = {
|
|
@@ -182,12 +184,12 @@ var resizeFestivalCardPages = (config, pageCount) => {
|
|
|
182
184
|
};
|
|
183
185
|
};
|
|
184
186
|
var useFestivalCardConfig = (options) => {
|
|
185
|
-
const [config, setConfig] =
|
|
187
|
+
const [config, setConfig] = React4.useState(
|
|
186
188
|
() => normalizeFestivalCardConfig(options?.initialConfig || DEFAULT_FESTIVAL_CARD_CONFIG)
|
|
187
189
|
);
|
|
188
|
-
const [loading, setLoading] =
|
|
189
|
-
const [saving, setSaving] =
|
|
190
|
-
|
|
190
|
+
const [loading, setLoading] = React4.useState(Boolean(options?.fetchConfig));
|
|
191
|
+
const [saving, setSaving] = React4.useState(false);
|
|
192
|
+
React4.useEffect(() => {
|
|
191
193
|
const fetchConfig = options?.fetchConfig;
|
|
192
194
|
if (!fetchConfig) return;
|
|
193
195
|
let active = true;
|
|
@@ -201,7 +203,7 @@ var useFestivalCardConfig = (options) => {
|
|
|
201
203
|
active = false;
|
|
202
204
|
};
|
|
203
205
|
}, [options?.fetchConfig]);
|
|
204
|
-
const save =
|
|
206
|
+
const save = React4.useCallback(async () => {
|
|
205
207
|
const onSave = options?.onSave;
|
|
206
208
|
if (!onSave) return;
|
|
207
209
|
setSaving(true);
|
|
@@ -211,7 +213,7 @@ var useFestivalCardConfig = (options) => {
|
|
|
211
213
|
setSaving(false);
|
|
212
214
|
}
|
|
213
215
|
}, [config, options?.onSave]);
|
|
214
|
-
return
|
|
216
|
+
return React4.useMemo(
|
|
215
217
|
() => ({
|
|
216
218
|
config,
|
|
217
219
|
setConfig,
|
|
@@ -222,6 +224,190 @@ var useFestivalCardConfig = (options) => {
|
|
|
222
224
|
[config, loading, save, saving]
|
|
223
225
|
);
|
|
224
226
|
};
|
|
227
|
+
var FloatingMenu = ({
|
|
228
|
+
trigger,
|
|
229
|
+
menu,
|
|
230
|
+
initialPosition = { x: 20, y: 20 },
|
|
231
|
+
defaultOpen = false,
|
|
232
|
+
className = "",
|
|
233
|
+
menuClassName = "",
|
|
234
|
+
triggerClassName = "",
|
|
235
|
+
zIndex = 1e3
|
|
236
|
+
}) => {
|
|
237
|
+
const [position, setPosition] = React4.useState(initialPosition);
|
|
238
|
+
const [isMenuOpen, setIsMenuOpen] = React4.useState(defaultOpen);
|
|
239
|
+
const [menuDirection, setMenuDirection] = React4.useState("right");
|
|
240
|
+
const [isDragging, setIsDragging] = React4.useState(false);
|
|
241
|
+
const [dragOffset, setDragOffset] = React4.useState({ x: 0, y: 0 });
|
|
242
|
+
const containerRef = React4.useRef(null);
|
|
243
|
+
const [mounted, setMounted] = React4.useState(false);
|
|
244
|
+
const [hasDragged, setHasDragged] = React4.useState(false);
|
|
245
|
+
const dragTimerRef = React4.useRef(null);
|
|
246
|
+
const mouseDownPosRef = React4.useRef(null);
|
|
247
|
+
React4.useEffect(() => {
|
|
248
|
+
setMounted(true);
|
|
249
|
+
return () => setMounted(false);
|
|
250
|
+
}, []);
|
|
251
|
+
React4.useEffect(() => {
|
|
252
|
+
if (!mounted || !containerRef.current) return;
|
|
253
|
+
const updateMenuDirection = () => {
|
|
254
|
+
const rect = containerRef.current?.getBoundingClientRect();
|
|
255
|
+
if (!rect) return;
|
|
256
|
+
const windowWidth = window.innerWidth;
|
|
257
|
+
const middlePoint = windowWidth / 2;
|
|
258
|
+
setMenuDirection(rect.left < middlePoint ? "right" : "left");
|
|
259
|
+
};
|
|
260
|
+
updateMenuDirection();
|
|
261
|
+
window.addEventListener("resize", updateMenuDirection);
|
|
262
|
+
window.addEventListener("scroll", updateMenuDirection);
|
|
263
|
+
return () => {
|
|
264
|
+
window.removeEventListener("resize", updateMenuDirection);
|
|
265
|
+
window.removeEventListener("scroll", updateMenuDirection);
|
|
266
|
+
};
|
|
267
|
+
}, [mounted]);
|
|
268
|
+
const handleMouseDown = (e) => {
|
|
269
|
+
if (!containerRef.current) return;
|
|
270
|
+
e.stopPropagation();
|
|
271
|
+
mouseDownPosRef.current = { x: e.clientX, y: e.clientY };
|
|
272
|
+
const rect = containerRef.current.getBoundingClientRect();
|
|
273
|
+
setDragOffset({
|
|
274
|
+
x: e.clientX - rect.left,
|
|
275
|
+
y: e.clientY - rect.top
|
|
276
|
+
});
|
|
277
|
+
setHasDragged(false);
|
|
278
|
+
setIsDragging(true);
|
|
279
|
+
};
|
|
280
|
+
React4.useEffect(() => {
|
|
281
|
+
if (!isDragging) return;
|
|
282
|
+
const handleMouseMove = (e) => {
|
|
283
|
+
if (mouseDownPosRef.current) {
|
|
284
|
+
const dx = Math.abs(e.clientX - mouseDownPosRef.current.x);
|
|
285
|
+
const dy = Math.abs(e.clientY - mouseDownPosRef.current.y);
|
|
286
|
+
if (dx > 3 || dy > 3) {
|
|
287
|
+
setHasDragged(true);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const newX = e.clientX - dragOffset.x;
|
|
291
|
+
const newY = e.clientY - dragOffset.y;
|
|
292
|
+
const windowWidth = window.innerWidth;
|
|
293
|
+
const windowHeight = window.innerHeight;
|
|
294
|
+
setPosition({
|
|
295
|
+
x: Math.min(Math.max(newX, 0), windowWidth - 50),
|
|
296
|
+
y: Math.min(Math.max(newY, 0), windowHeight - 50)
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
const handleMouseUp = () => {
|
|
300
|
+
setIsDragging(false);
|
|
301
|
+
mouseDownPosRef.current = null;
|
|
302
|
+
if (dragTimerRef.current) {
|
|
303
|
+
window.clearTimeout(dragTimerRef.current);
|
|
304
|
+
}
|
|
305
|
+
dragTimerRef.current = window.setTimeout(() => {
|
|
306
|
+
setHasDragged(false);
|
|
307
|
+
}, 300);
|
|
308
|
+
};
|
|
309
|
+
document.addEventListener("mousemove", handleMouseMove);
|
|
310
|
+
document.addEventListener("mouseup", handleMouseUp);
|
|
311
|
+
return () => {
|
|
312
|
+
document.removeEventListener("mousemove", handleMouseMove);
|
|
313
|
+
document.removeEventListener("mouseup", handleMouseUp);
|
|
314
|
+
};
|
|
315
|
+
}, [isDragging, dragOffset]);
|
|
316
|
+
React4.useEffect(() => {
|
|
317
|
+
return () => {
|
|
318
|
+
if (dragTimerRef.current) {
|
|
319
|
+
window.clearTimeout(dragTimerRef.current);
|
|
320
|
+
}
|
|
321
|
+
};
|
|
322
|
+
}, []);
|
|
323
|
+
const toggleMenu = (e) => {
|
|
324
|
+
e.stopPropagation();
|
|
325
|
+
if (hasDragged) {
|
|
326
|
+
return;
|
|
327
|
+
}
|
|
328
|
+
setIsMenuOpen(!isMenuOpen);
|
|
329
|
+
};
|
|
330
|
+
React4.useEffect(() => {
|
|
331
|
+
if (!isMenuOpen) return;
|
|
332
|
+
const handleClickOutside = (e) => {
|
|
333
|
+
if (containerRef.current && !containerRef.current.contains(e.target)) {
|
|
334
|
+
setIsMenuOpen(false);
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
338
|
+
return () => {
|
|
339
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
340
|
+
};
|
|
341
|
+
}, [isMenuOpen]);
|
|
342
|
+
React4.useEffect(() => {
|
|
343
|
+
if (!mounted) return;
|
|
344
|
+
const checkBoundaries = () => {
|
|
345
|
+
const windowWidth = window.innerWidth;
|
|
346
|
+
const windowHeight = window.innerHeight;
|
|
347
|
+
setPosition((prev) => {
|
|
348
|
+
const newX = Math.min(Math.max(prev.x, 0), windowWidth - 50);
|
|
349
|
+
const newY = Math.min(Math.max(prev.y, 0), windowHeight - 50);
|
|
350
|
+
if (newX !== prev.x || newY !== prev.y) {
|
|
351
|
+
return { x: newX, y: newY };
|
|
352
|
+
}
|
|
353
|
+
return prev;
|
|
354
|
+
});
|
|
355
|
+
};
|
|
356
|
+
window.addEventListener("resize", checkBoundaries);
|
|
357
|
+
return () => {
|
|
358
|
+
window.removeEventListener("resize", checkBoundaries);
|
|
359
|
+
};
|
|
360
|
+
}, [mounted]);
|
|
361
|
+
if (!mounted) return null;
|
|
362
|
+
return reactDom.createPortal(
|
|
363
|
+
/* @__PURE__ */ React4__default.default.createElement(
|
|
364
|
+
"div",
|
|
365
|
+
{
|
|
366
|
+
ref: containerRef,
|
|
367
|
+
className: clsx.clsx("fixed select-none box-border", className),
|
|
368
|
+
style: {
|
|
369
|
+
left: position.x + "px",
|
|
370
|
+
top: position.y + "px",
|
|
371
|
+
zIndex
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
/* @__PURE__ */ React4__default.default.createElement(
|
|
375
|
+
"div",
|
|
376
|
+
{
|
|
377
|
+
className: clsx.clsx(
|
|
378
|
+
"flex items-center justify-center w-12 h-12 md:w-12 md:h-12 bg-white rounded-full shadow-md hover:shadow-lg cursor-grab active:cursor-grabbing transition-all duration-200 hover:scale-105 active:scale-95",
|
|
379
|
+
triggerClassName
|
|
380
|
+
),
|
|
381
|
+
onMouseDown: handleMouseDown,
|
|
382
|
+
onClick: toggleMenu
|
|
383
|
+
},
|
|
384
|
+
trigger
|
|
385
|
+
),
|
|
386
|
+
isMenuOpen && /* @__PURE__ */ React4__default.default.createElement(
|
|
387
|
+
"div",
|
|
388
|
+
{
|
|
389
|
+
className: clsx.clsx(
|
|
390
|
+
"absolute top-0 bg-white rounded-lg shadow-xl p-3 min-w-[200px] md:min-w-[200px] max-w-[300px] z-[1000] transition-all duration-200",
|
|
391
|
+
isMenuOpen ? "opacity-100 scale-100" : "opacity-0 scale-95",
|
|
392
|
+
menuDirection === "left" ? "right-[calc(100%+10px)]" : "left-[calc(100%+10px)]",
|
|
393
|
+
menuClassName
|
|
394
|
+
),
|
|
395
|
+
onClick: (e) => e.stopPropagation(),
|
|
396
|
+
onMouseDown: (e) => e.stopPropagation(),
|
|
397
|
+
onMouseUp: (e) => e.stopPropagation(),
|
|
398
|
+
onTouchStart: (e) => e.stopPropagation(),
|
|
399
|
+
onTouchMove: (e) => e.stopPropagation(),
|
|
400
|
+
onTouchEnd: (e) => e.stopPropagation(),
|
|
401
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
402
|
+
onPointerUp: (e) => e.stopPropagation()
|
|
403
|
+
},
|
|
404
|
+
menu
|
|
405
|
+
)
|
|
406
|
+
),
|
|
407
|
+
document.body
|
|
408
|
+
);
|
|
409
|
+
};
|
|
410
|
+
var FloatingMenu_default = FloatingMenu;
|
|
225
411
|
var elementStyle = (element) => ({
|
|
226
412
|
position: "absolute",
|
|
227
413
|
zIndex: 2,
|
|
@@ -233,7 +419,7 @@ var elementStyle = (element) => ({
|
|
|
233
419
|
});
|
|
234
420
|
var renderElement = (element) => {
|
|
235
421
|
if (element.type === "text") {
|
|
236
|
-
return /* @__PURE__ */
|
|
422
|
+
return /* @__PURE__ */ React4__default.default.createElement(
|
|
237
423
|
"div",
|
|
238
424
|
{
|
|
239
425
|
key: element.id,
|
|
@@ -251,7 +437,7 @@ var renderElement = (element) => {
|
|
|
251
437
|
element.content
|
|
252
438
|
);
|
|
253
439
|
}
|
|
254
|
-
return /* @__PURE__ */
|
|
440
|
+
return /* @__PURE__ */ React4__default.default.createElement(
|
|
255
441
|
"img",
|
|
256
442
|
{
|
|
257
443
|
key: element.id,
|
|
@@ -267,15 +453,92 @@ var renderElement = (element) => {
|
|
|
267
453
|
}
|
|
268
454
|
);
|
|
269
455
|
};
|
|
270
|
-
var
|
|
271
|
-
|
|
272
|
-
|
|
456
|
+
var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
|
|
457
|
+
var FestivalCardPageRenderer = ({
|
|
458
|
+
page,
|
|
459
|
+
editable = false,
|
|
460
|
+
selectedElementId = null,
|
|
461
|
+
onElementSelect,
|
|
462
|
+
onElementChange
|
|
463
|
+
}) => {
|
|
464
|
+
const [draggingElementId, setDraggingElementId] = React4.useState(null);
|
|
465
|
+
const [resizingElementId, setResizingElementId] = React4.useState(null);
|
|
466
|
+
const stageRef = React4.useRef(null);
|
|
467
|
+
const interactionRef = React4.useRef(null);
|
|
468
|
+
const backgroundElement = React4.useMemo(
|
|
469
|
+
() => page.elements.find(
|
|
470
|
+
(element) => element.type === "image" && Boolean(element.isBackground)
|
|
471
|
+
),
|
|
472
|
+
[page]
|
|
273
473
|
);
|
|
274
|
-
const foregroundElements =
|
|
275
|
-
|
|
474
|
+
const foregroundElements = React4.useMemo(
|
|
475
|
+
() => page.elements.filter((element) => !(element.type === "image" && element.isBackground)),
|
|
476
|
+
[page]
|
|
477
|
+
);
|
|
478
|
+
const updateElementByPointer = (element, interaction, clientX, clientY) => {
|
|
479
|
+
if (!onElementChange || interaction.rect.width <= 0 || interaction.rect.height <= 0) return;
|
|
480
|
+
const xPercent = clamp((clientX - interaction.rect.left) / interaction.rect.width * 100, 0, 100);
|
|
481
|
+
const yPercent = clamp((clientY - interaction.rect.top) / interaction.rect.height * 100, 0, 100);
|
|
482
|
+
if (interaction.mode === "move") {
|
|
483
|
+
onElementChange(element.id, { x: xPercent, y: yPercent });
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const nextWidth = clamp(Math.abs(xPercent - element.x) * 2, 4, 100);
|
|
487
|
+
if (element.type === "image") {
|
|
488
|
+
const nextHeight = clamp(Math.abs(yPercent - element.y) * 2, 4, 100);
|
|
489
|
+
onElementChange(element.id, { width: nextWidth, height: nextHeight });
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
onElementChange(element.id, { width: nextWidth });
|
|
493
|
+
};
|
|
494
|
+
const beginInteraction = (event, elementId, mode) => {
|
|
495
|
+
if (!editable || !stageRef.current) return;
|
|
496
|
+
event.preventDefault();
|
|
497
|
+
event.stopPropagation();
|
|
498
|
+
const rect = stageRef.current.getBoundingClientRect();
|
|
499
|
+
interactionRef.current = {
|
|
500
|
+
pointerId: event.pointerId,
|
|
501
|
+
elementId,
|
|
502
|
+
mode,
|
|
503
|
+
rect
|
|
504
|
+
};
|
|
505
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
506
|
+
onElementSelect?.(elementId);
|
|
507
|
+
if (mode === "move") {
|
|
508
|
+
setDraggingElementId(elementId);
|
|
509
|
+
setResizingElementId(null);
|
|
510
|
+
} else {
|
|
511
|
+
setResizingElementId(elementId);
|
|
512
|
+
setDraggingElementId(null);
|
|
513
|
+
}
|
|
514
|
+
const element = foregroundElements.find((item) => item.id === elementId);
|
|
515
|
+
if (element) {
|
|
516
|
+
updateElementByPointer(element, interactionRef.current, event.clientX, event.clientY);
|
|
517
|
+
}
|
|
518
|
+
};
|
|
519
|
+
const handlePointerMove = (event) => {
|
|
520
|
+
const interaction = interactionRef.current;
|
|
521
|
+
if (!interaction || interaction.pointerId !== event.pointerId) return;
|
|
522
|
+
const element = foregroundElements.find((item) => item.id === interaction.elementId);
|
|
523
|
+
if (!element) return;
|
|
524
|
+
updateElementByPointer(element, interaction, event.clientX, event.clientY);
|
|
525
|
+
};
|
|
526
|
+
const endInteraction = (event) => {
|
|
527
|
+
const interaction = interactionRef.current;
|
|
528
|
+
if (!interaction || interaction.pointerId !== event.pointerId) return;
|
|
529
|
+
interactionRef.current = null;
|
|
530
|
+
setDraggingElementId(null);
|
|
531
|
+
setResizingElementId(null);
|
|
532
|
+
};
|
|
533
|
+
return /* @__PURE__ */ React4__default.default.createElement(
|
|
276
534
|
"div",
|
|
277
535
|
{
|
|
278
|
-
|
|
536
|
+
ref: stageRef,
|
|
537
|
+
onPointerMove: editable ? handlePointerMove : void 0,
|
|
538
|
+
onPointerUp: editable ? endInteraction : void 0,
|
|
539
|
+
onPointerCancel: editable ? endInteraction : void 0,
|
|
540
|
+
onClick: editable ? () => onElementSelect?.(null) : void 0,
|
|
541
|
+
className: `relative h-full w-full overflow-hidden rounded-2xl ${editable ? "touch-none" : ""}`,
|
|
279
542
|
style: {
|
|
280
543
|
backgroundColor: page.background?.color || "#0f172a",
|
|
281
544
|
backgroundImage: backgroundElement ? `url(${backgroundElement.src})` : page.background?.image ? `url(${page.background.image})` : void 0,
|
|
@@ -283,19 +546,251 @@ var FestivalCardPageRenderer = ({ page }) => {
|
|
|
283
546
|
backgroundPosition: "center"
|
|
284
547
|
}
|
|
285
548
|
},
|
|
286
|
-
/* @__PURE__ */
|
|
287
|
-
foregroundElements.map(
|
|
549
|
+
/* @__PURE__ */ React4__default.default.createElement("div", { className: "absolute inset-0 bg-slate-950/20" }),
|
|
550
|
+
foregroundElements.map((element) => {
|
|
551
|
+
if (!editable) {
|
|
552
|
+
return renderElement(element);
|
|
553
|
+
}
|
|
554
|
+
const isSelected = selectedElementId === element.id;
|
|
555
|
+
const isDragging = draggingElementId === element.id;
|
|
556
|
+
const isResizing = resizingElementId === element.id;
|
|
557
|
+
return /* @__PURE__ */ React4__default.default.createElement(
|
|
558
|
+
"div",
|
|
559
|
+
{
|
|
560
|
+
key: element.id,
|
|
561
|
+
role: "button",
|
|
562
|
+
tabIndex: 0,
|
|
563
|
+
onClick: (event) => {
|
|
564
|
+
event.stopPropagation();
|
|
565
|
+
onElementSelect?.(element.id);
|
|
566
|
+
},
|
|
567
|
+
onPointerDown: (event) => beginInteraction(event, element.id, "move"),
|
|
568
|
+
className: `absolute select-none touch-none rounded-md ${isDragging ? "cursor-grabbing" : isResizing ? "cursor-se-resize" : "cursor-grab"} ${isSelected ? "ring-2 ring-sky-300" : "ring-1 ring-white/40"}`,
|
|
569
|
+
style: {
|
|
570
|
+
...elementStyle(element),
|
|
571
|
+
zIndex: isSelected ? 4 : 2
|
|
572
|
+
}
|
|
573
|
+
},
|
|
574
|
+
element.type === "text" ? /* @__PURE__ */ React4__default.default.createElement(
|
|
575
|
+
"div",
|
|
576
|
+
{
|
|
577
|
+
className: "rounded-md bg-black/20 px-2 py-1",
|
|
578
|
+
style: {
|
|
579
|
+
color: element.color || "#f8fafc",
|
|
580
|
+
fontSize: element.fontSize || 18,
|
|
581
|
+
fontWeight: element.fontWeight || 500,
|
|
582
|
+
fontFamily: element.fontFamily || "inherit",
|
|
583
|
+
textAlign: element.align || "left",
|
|
584
|
+
lineHeight: 1.45,
|
|
585
|
+
whiteSpace: "pre-wrap"
|
|
586
|
+
}
|
|
587
|
+
},
|
|
588
|
+
element.content
|
|
589
|
+
) : /* @__PURE__ */ React4__default.default.createElement(
|
|
590
|
+
"img",
|
|
591
|
+
{
|
|
592
|
+
src: element.src,
|
|
593
|
+
alt: element.alt || "festival-card-image",
|
|
594
|
+
draggable: false,
|
|
595
|
+
className: "pointer-events-none h-full w-full",
|
|
596
|
+
style: {
|
|
597
|
+
objectFit: element.fit || "cover",
|
|
598
|
+
borderRadius: element.borderRadius || 0,
|
|
599
|
+
overflow: "hidden",
|
|
600
|
+
boxShadow: "0 12px 30px rgba(2, 6, 23, 0.32)"
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
),
|
|
604
|
+
/* @__PURE__ */ React4__default.default.createElement(
|
|
605
|
+
"button",
|
|
606
|
+
{
|
|
607
|
+
type: "button",
|
|
608
|
+
"aria-label": "resize",
|
|
609
|
+
onPointerDown: (event) => beginInteraction(event, element.id, "resize"),
|
|
610
|
+
className: "absolute -bottom-2 -right-2 h-4 w-4 rounded-full border border-white bg-sky-500 shadow"
|
|
611
|
+
}
|
|
612
|
+
)
|
|
613
|
+
);
|
|
614
|
+
})
|
|
288
615
|
);
|
|
289
616
|
};
|
|
290
617
|
|
|
291
618
|
// src/festivalCard/components/FestivalCardBook3D.tsx
|
|
292
|
-
var
|
|
293
|
-
const
|
|
294
|
-
|
|
619
|
+
var loadImage = (src) => new Promise((resolve, reject) => {
|
|
620
|
+
const image = new window.Image();
|
|
621
|
+
image.crossOrigin = "anonymous";
|
|
622
|
+
image.decoding = "async";
|
|
623
|
+
image.onload = () => resolve(image);
|
|
624
|
+
image.onerror = () => reject(new Error(`\u56FE\u7247\u52A0\u8F7D\u5931\u8D25: ${src}`));
|
|
625
|
+
image.src = src;
|
|
626
|
+
});
|
|
627
|
+
var drawImageWithFit = (ctx, image, left, top, width, height, fit) => {
|
|
628
|
+
const imageRatio = image.width / image.height;
|
|
629
|
+
const boxRatio = width / height;
|
|
630
|
+
let drawWidth = width;
|
|
631
|
+
let drawHeight = height;
|
|
632
|
+
let offsetX = left;
|
|
633
|
+
let offsetY = top;
|
|
634
|
+
if (fit === "cover") {
|
|
635
|
+
if (imageRatio > boxRatio) {
|
|
636
|
+
drawHeight = height;
|
|
637
|
+
drawWidth = height * imageRatio;
|
|
638
|
+
offsetX = left - (drawWidth - width) / 2;
|
|
639
|
+
} else {
|
|
640
|
+
drawWidth = width;
|
|
641
|
+
drawHeight = width / imageRatio;
|
|
642
|
+
offsetY = top - (drawHeight - height) / 2;
|
|
643
|
+
}
|
|
644
|
+
} else if (imageRatio > boxRatio) {
|
|
645
|
+
drawWidth = width;
|
|
646
|
+
drawHeight = width / imageRatio;
|
|
647
|
+
offsetY = top + (height - drawHeight) / 2;
|
|
648
|
+
} else {
|
|
649
|
+
drawHeight = height;
|
|
650
|
+
drawWidth = height * imageRatio;
|
|
651
|
+
offsetX = left + (width - drawWidth) / 2;
|
|
652
|
+
}
|
|
653
|
+
ctx.drawImage(image, offsetX, offsetY, drawWidth, drawHeight);
|
|
654
|
+
};
|
|
655
|
+
var withRoundedClip = (ctx, left, top, width, height, radius, draw) => {
|
|
656
|
+
const safeRadius = Math.max(0, Math.min(radius, Math.min(width, height) / 2));
|
|
657
|
+
if (safeRadius <= 0) {
|
|
658
|
+
draw();
|
|
659
|
+
return;
|
|
660
|
+
}
|
|
661
|
+
ctx.save();
|
|
662
|
+
ctx.beginPath();
|
|
663
|
+
ctx.moveTo(left + safeRadius, top);
|
|
664
|
+
ctx.lineTo(left + width - safeRadius, top);
|
|
665
|
+
ctx.quadraticCurveTo(left + width, top, left + width, top + safeRadius);
|
|
666
|
+
ctx.lineTo(left + width, top + height - safeRadius);
|
|
667
|
+
ctx.quadraticCurveTo(left + width, top + height, left + width - safeRadius, top + height);
|
|
668
|
+
ctx.lineTo(left + safeRadius, top + height);
|
|
669
|
+
ctx.quadraticCurveTo(left, top + height, left, top + height - safeRadius);
|
|
670
|
+
ctx.lineTo(left, top + safeRadius);
|
|
671
|
+
ctx.quadraticCurveTo(left, top, left + safeRadius, top);
|
|
672
|
+
ctx.closePath();
|
|
673
|
+
ctx.clip();
|
|
674
|
+
draw();
|
|
675
|
+
ctx.restore();
|
|
676
|
+
};
|
|
677
|
+
var drawMultilineText = (ctx, text, left, top, maxWidth, lineHeight) => {
|
|
678
|
+
const paragraphs = text.split("\n");
|
|
679
|
+
let currentY = top;
|
|
680
|
+
paragraphs.forEach((paragraph, index) => {
|
|
681
|
+
const words = paragraph.split("");
|
|
682
|
+
let line = "";
|
|
683
|
+
for (const word of words) {
|
|
684
|
+
const testLine = line + word;
|
|
685
|
+
if (ctx.measureText(testLine).width > maxWidth && line) {
|
|
686
|
+
ctx.fillText(line, left, currentY);
|
|
687
|
+
line = word;
|
|
688
|
+
currentY += lineHeight;
|
|
689
|
+
} else {
|
|
690
|
+
line = testLine;
|
|
691
|
+
}
|
|
692
|
+
}
|
|
693
|
+
ctx.fillText(line, left, currentY);
|
|
694
|
+
currentY += lineHeight;
|
|
695
|
+
if (index < paragraphs.length - 1) {
|
|
696
|
+
currentY += lineHeight * 0.2;
|
|
697
|
+
}
|
|
698
|
+
});
|
|
699
|
+
};
|
|
700
|
+
var exportPageToPng = async (page, fileName) => {
|
|
701
|
+
const width = 1080;
|
|
702
|
+
const height = 1440;
|
|
703
|
+
const canvas = document.createElement("canvas");
|
|
704
|
+
canvas.width = width;
|
|
705
|
+
canvas.height = height;
|
|
706
|
+
const ctx = canvas.getContext("2d");
|
|
707
|
+
if (!ctx) throw new Error("\u65E0\u6CD5\u521B\u5EFA Canvas \u4E0A\u4E0B\u6587");
|
|
708
|
+
ctx.fillStyle = page.background?.color || "#0f172a";
|
|
709
|
+
ctx.fillRect(0, 0, width, height);
|
|
710
|
+
const backgroundElement = page.elements.find(
|
|
711
|
+
(element) => element.type === "image" && Boolean(element.isBackground)
|
|
712
|
+
);
|
|
713
|
+
const backgroundImageSrc = backgroundElement?.src || page.background?.image;
|
|
714
|
+
if (backgroundImageSrc) {
|
|
715
|
+
const image = await loadImage(backgroundImageSrc);
|
|
716
|
+
drawImageWithFit(ctx, image, 0, 0, width, height, "cover");
|
|
717
|
+
}
|
|
718
|
+
const foregroundElements = page.elements.filter((element) => !(element.type === "image" && element.isBackground));
|
|
719
|
+
for (const element of foregroundElements) {
|
|
720
|
+
const elementWidth = width * (element.width ?? 70) / 100;
|
|
721
|
+
const elementHeight = element.height ? height * element.height / 100 : void 0;
|
|
722
|
+
const centerX = width * element.x / 100;
|
|
723
|
+
const centerY = height * element.y / 100;
|
|
724
|
+
const left = centerX - elementWidth / 2;
|
|
725
|
+
if (element.type === "image") {
|
|
726
|
+
const image = await loadImage(element.src);
|
|
727
|
+
const drawHeight = elementHeight ?? elementWidth;
|
|
728
|
+
const boxTop = centerY - drawHeight / 2;
|
|
729
|
+
withRoundedClip(ctx, left, boxTop, elementWidth, drawHeight, element.borderRadius ?? 0, () => {
|
|
730
|
+
drawImageWithFit(ctx, image, left, boxTop, elementWidth, drawHeight, element.fit || "cover");
|
|
731
|
+
});
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
const fontSize = (element.fontSize || 18) * 1.5;
|
|
735
|
+
ctx.fillStyle = element.color || "#f8fafc";
|
|
736
|
+
ctx.font = `${element.fontWeight || 500} ${fontSize}px ${element.fontFamily || "sans-serif"}`;
|
|
737
|
+
ctx.textBaseline = "top";
|
|
738
|
+
ctx.textAlign = element.align || "left";
|
|
739
|
+
const textX = element.align === "center" ? centerX : element.align === "right" ? left + elementWidth : left;
|
|
740
|
+
drawMultilineText(ctx, element.content || "", textX, centerY - fontSize * 0.72, elementWidth, fontSize * 1.45);
|
|
741
|
+
}
|
|
742
|
+
const blob = await new Promise((resolve) => canvas.toBlob(resolve, "image/png"));
|
|
743
|
+
if (!blob) throw new Error("\u5BFC\u51FA\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5");
|
|
744
|
+
const url = URL.createObjectURL(blob);
|
|
745
|
+
const anchor = document.createElement("a");
|
|
746
|
+
anchor.href = url;
|
|
747
|
+
anchor.download = fileName;
|
|
748
|
+
anchor.click();
|
|
749
|
+
URL.revokeObjectURL(url);
|
|
750
|
+
};
|
|
751
|
+
var FestivalCardBook3D = ({
|
|
752
|
+
config,
|
|
753
|
+
className,
|
|
754
|
+
editable = false,
|
|
755
|
+
enableExportImage = !editable,
|
|
756
|
+
currentPage: currentPageProp,
|
|
757
|
+
onCurrentPageChange,
|
|
758
|
+
selectedElementId = null,
|
|
759
|
+
onSelectedElementChange,
|
|
760
|
+
onElementChange
|
|
761
|
+
}) => {
|
|
762
|
+
const [internalCurrentPage, setInternalCurrentPage] = React4.useState(0);
|
|
763
|
+
const [exporting, setExporting] = React4.useState(false);
|
|
764
|
+
const normalized = React4.useMemo(() => normalizeFestivalCardConfig(config), [config]);
|
|
295
765
|
const pages = normalized.pages;
|
|
766
|
+
const currentPage = typeof currentPageProp === "number" ? currentPageProp : internalCurrentPage;
|
|
767
|
+
const setCurrentPage = (updater) => {
|
|
768
|
+
const prev = currentPage;
|
|
769
|
+
const nextValue = typeof updater === "function" ? updater(prev) : updater;
|
|
770
|
+
if (typeof currentPageProp === "number") {
|
|
771
|
+
onCurrentPageChange?.(nextValue);
|
|
772
|
+
return;
|
|
773
|
+
}
|
|
774
|
+
setInternalCurrentPage(nextValue);
|
|
775
|
+
onCurrentPageChange?.(nextValue);
|
|
776
|
+
};
|
|
296
777
|
const canPrev = currentPage > 0;
|
|
297
778
|
const canNext = currentPage < pages.length - 1;
|
|
298
|
-
|
|
779
|
+
const currentPageData = pages[currentPage];
|
|
780
|
+
const handleExportCurrentPage = async () => {
|
|
781
|
+
if (!currentPageData || exporting) return;
|
|
782
|
+
setExporting(true);
|
|
783
|
+
try {
|
|
784
|
+
const base = normalized.id || "festival-card";
|
|
785
|
+
const fileName = `${base}-page-${currentPage + 1}.png`;
|
|
786
|
+
await exportPageToPng(currentPageData, fileName);
|
|
787
|
+
} catch (error) {
|
|
788
|
+
window.alert(error.message || "\u5BFC\u51FA\u56FE\u7247\u5931\u8D25");
|
|
789
|
+
} finally {
|
|
790
|
+
setExporting(false);
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
return /* @__PURE__ */ React4__default.default.createElement("div", { className }, /* @__PURE__ */ React4__default.default.createElement("div", { className: "w-full min-h-screen px-0 py-4" }, /* @__PURE__ */ React4__default.default.createElement("div", { className: "mx-auto w-full text-center text-slate-100" }, /* @__PURE__ */ React4__default.default.createElement("h3", { className: "mb-3 text-lg font-semibold" }, normalized.coverTitle || "Festival Card")), /* @__PURE__ */ React4__default.default.createElement("div", { className: "mx-auto w-full" }, /* @__PURE__ */ React4__default.default.createElement("div", { className: "relative h-[calc(100vh-170px)] min-h-[460px]" }, pages.map((page, index) => /* @__PURE__ */ React4__default.default.createElement(
|
|
299
794
|
"div",
|
|
300
795
|
{
|
|
301
796
|
key: page.id,
|
|
@@ -305,8 +800,17 @@ var FestivalCardBook3D = ({ config, className }) => {
|
|
|
305
800
|
pointerEvents: index === currentPage ? "auto" : "none"
|
|
306
801
|
}
|
|
307
802
|
},
|
|
308
|
-
/* @__PURE__ */
|
|
309
|
-
|
|
803
|
+
/* @__PURE__ */ React4__default.default.createElement(
|
|
804
|
+
FestivalCardPageRenderer,
|
|
805
|
+
{
|
|
806
|
+
page,
|
|
807
|
+
editable: editable && index === currentPage,
|
|
808
|
+
selectedElementId,
|
|
809
|
+
onElementSelect: onSelectedElementChange,
|
|
810
|
+
onElementChange: (elementId, patch) => onElementChange?.(index, elementId, patch)
|
|
811
|
+
}
|
|
812
|
+
)
|
|
813
|
+
)))), /* @__PURE__ */ React4__default.default.createElement("div", { className: "mt-4 flex justify-center gap-3" }, /* @__PURE__ */ React4__default.default.createElement(
|
|
310
814
|
"button",
|
|
311
815
|
{
|
|
312
816
|
type: "button",
|
|
@@ -315,7 +819,7 @@ var FestivalCardBook3D = ({ config, className }) => {
|
|
|
315
819
|
className: "rounded-full bg-white px-5 py-2 text-sm font-medium text-slate-900 disabled:cursor-not-allowed disabled:opacity-45"
|
|
316
820
|
},
|
|
317
821
|
"\u4E0A\u4E00\u9875"
|
|
318
|
-
), /* @__PURE__ */
|
|
822
|
+
), /* @__PURE__ */ React4__default.default.createElement(
|
|
319
823
|
"button",
|
|
320
824
|
{
|
|
321
825
|
type: "button",
|
|
@@ -324,7 +828,7 @@ var FestivalCardBook3D = ({ config, className }) => {
|
|
|
324
828
|
className: "rounded-full bg-sky-300 px-5 py-2 text-sm font-medium text-slate-900 disabled:cursor-not-allowed disabled:opacity-45"
|
|
325
829
|
},
|
|
326
830
|
"\u4E0B\u4E00\u9875"
|
|
327
|
-
))), normalized.backgroundMusic?.src ? /* @__PURE__ */
|
|
831
|
+
))), normalized.backgroundMusic?.src ? /* @__PURE__ */ React4__default.default.createElement(
|
|
328
832
|
"audio",
|
|
329
833
|
{
|
|
330
834
|
src: normalized.backgroundMusic.src,
|
|
@@ -333,6 +837,24 @@ var FestivalCardBook3D = ({ config, className }) => {
|
|
|
333
837
|
controls: true,
|
|
334
838
|
className: "mt-3 w-full"
|
|
335
839
|
}
|
|
840
|
+
) : null, enableExportImage ? /* @__PURE__ */ React4__default.default.createElement(
|
|
841
|
+
FloatingMenu_default,
|
|
842
|
+
{
|
|
843
|
+
initialPosition: { x: 24, y: 120 },
|
|
844
|
+
trigger: /* @__PURE__ */ React4__default.default.createElement("div", { className: "text-lg leading-none text-slate-700", "aria-hidden": true }, "\u2301"),
|
|
845
|
+
menu: /* @__PURE__ */ React4__default.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React4__default.default.createElement("div", { className: "text-xs font-semibold tracking-wide text-slate-500" }, "\u8D3A\u5361\u5DE5\u5177"), /* @__PURE__ */ React4__default.default.createElement(
|
|
846
|
+
"button",
|
|
847
|
+
{
|
|
848
|
+
type: "button",
|
|
849
|
+
onClick: () => void handleExportCurrentPage(),
|
|
850
|
+
disabled: exporting,
|
|
851
|
+
className: "rounded-lg bg-sky-600 px-3 py-2 text-left text-sm font-medium text-white disabled:opacity-60"
|
|
852
|
+
},
|
|
853
|
+
exporting ? "\u5BFC\u51FA\u4E2D..." : `\u5BFC\u51FA\u7B2C ${currentPage + 1} \u9875 PNG`
|
|
854
|
+
)),
|
|
855
|
+
triggerClassName: "bg-white/95 backdrop-blur",
|
|
856
|
+
menuClassName: "bg-white/95 backdrop-blur"
|
|
857
|
+
}
|
|
336
858
|
) : null);
|
|
337
859
|
};
|
|
338
860
|
var createTextElement = (pageIndex) => ({
|
|
@@ -359,11 +881,25 @@ var createImageElement = (pageIndex) => ({
|
|
|
359
881
|
fit: "cover",
|
|
360
882
|
borderRadius: 12
|
|
361
883
|
});
|
|
362
|
-
var FestivalCardConfigEditor = ({
|
|
363
|
-
|
|
884
|
+
var FestivalCardConfigEditor = ({
|
|
885
|
+
value,
|
|
886
|
+
onChange,
|
|
887
|
+
activePageIndex: activePageIndexProp,
|
|
888
|
+
onActivePageIndexChange,
|
|
889
|
+
selectedElementId
|
|
890
|
+
}) => {
|
|
891
|
+
const [internalActivePageIndex, setInternalActivePageIndex] = React4.useState(0);
|
|
892
|
+
const activePageIndex = activePageIndexProp ?? internalActivePageIndex;
|
|
893
|
+
const setActivePageIndex = (index) => {
|
|
894
|
+
if (typeof activePageIndexProp === "number") {
|
|
895
|
+
onActivePageIndexChange?.(index);
|
|
896
|
+
return;
|
|
897
|
+
}
|
|
898
|
+
setInternalActivePageIndex(index);
|
|
899
|
+
};
|
|
364
900
|
const page = value.pages[activePageIndex];
|
|
365
901
|
const canEditPage = Boolean(page);
|
|
366
|
-
const pageOptions =
|
|
902
|
+
const pageOptions = React4.useMemo(() => value.pages.map((_, index) => index), [value.pages]);
|
|
367
903
|
const handlePageCountChange = (nextRaw) => {
|
|
368
904
|
const next = Number.isFinite(nextRaw) ? Math.max(1, Math.min(12, Math.floor(nextRaw))) : value.pages.length;
|
|
369
905
|
const resized = resizeFestivalCardPages(value, next);
|
|
@@ -401,7 +937,7 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
|
|
|
401
937
|
});
|
|
402
938
|
};
|
|
403
939
|
const numberFieldClassName = "w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100";
|
|
404
|
-
return /* @__PURE__ */
|
|
940
|
+
return /* @__PURE__ */ React4__default.default.createElement("div", { className: "rounded-2xl border border-slate-200 bg-white p-4 text-slate-900 shadow-sm" }, /* @__PURE__ */ React4__default.default.createElement("div", { className: "grid gap-3" }, /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React4__default.default.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u9875\u9762\u6570\u91CF"), /* @__PURE__ */ React4__default.default.createElement(
|
|
405
941
|
"input",
|
|
406
942
|
{
|
|
407
943
|
type: "number",
|
|
@@ -411,7 +947,7 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
|
|
|
411
947
|
onChange: (event) => handlePageCountChange(Number(event.target.value)),
|
|
412
948
|
className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
|
|
413
949
|
}
|
|
414
|
-
)), /* @__PURE__ */
|
|
950
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React4__default.default.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u80CC\u666F\u97F3\u4E50 URL"), /* @__PURE__ */ React4__default.default.createElement(
|
|
415
951
|
"input",
|
|
416
952
|
{
|
|
417
953
|
type: "url",
|
|
@@ -425,7 +961,7 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
|
|
|
425
961
|
}),
|
|
426
962
|
className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
|
|
427
963
|
}
|
|
428
|
-
)), /* @__PURE__ */
|
|
964
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React4__default.default.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u9875\u9762\u6807\u9898"), /* @__PURE__ */ React4__default.default.createElement(
|
|
429
965
|
"input",
|
|
430
966
|
{
|
|
431
967
|
type: "text",
|
|
@@ -433,7 +969,7 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
|
|
|
433
969
|
onChange: (event) => updatePage({ title: event.target.value }),
|
|
434
970
|
className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
|
|
435
971
|
}
|
|
436
|
-
)), /* @__PURE__ */
|
|
972
|
+
)), /* @__PURE__ */ React4__default.default.createElement("div", { className: "grid gap-2 sm:grid-cols-2" }, /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React4__default.default.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u9875\u9762\u80CC\u666F\u8272"), /* @__PURE__ */ React4__default.default.createElement(
|
|
437
973
|
"input",
|
|
438
974
|
{
|
|
439
975
|
type: "color",
|
|
@@ -446,7 +982,7 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
|
|
|
446
982
|
}),
|
|
447
983
|
className: "h-10 w-full rounded-lg border border-slate-300 bg-white p-1"
|
|
448
984
|
}
|
|
449
|
-
)), /* @__PURE__ */
|
|
985
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React4__default.default.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u9875\u9762\u80CC\u666F\u56FE URL"), /* @__PURE__ */ React4__default.default.createElement(
|
|
450
986
|
"input",
|
|
451
987
|
{
|
|
452
988
|
type: "url",
|
|
@@ -459,15 +995,15 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
|
|
|
459
995
|
}),
|
|
460
996
|
className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
|
|
461
997
|
}
|
|
462
|
-
))), /* @__PURE__ */
|
|
998
|
+
))), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1.5" }, /* @__PURE__ */ React4__default.default.createElement("span", { className: "text-sm font-medium text-slate-700" }, "\u7F16\u8F91\u9875\u9762"), /* @__PURE__ */ React4__default.default.createElement(
|
|
463
999
|
"select",
|
|
464
1000
|
{
|
|
465
1001
|
value: activePageIndex,
|
|
466
1002
|
onChange: (event) => setActivePageIndex(Number(event.target.value)),
|
|
467
1003
|
className: "rounded-lg border border-slate-300 bg-white px-3 py-2 text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
|
|
468
1004
|
},
|
|
469
|
-
pageOptions.map((index) => /* @__PURE__ */
|
|
470
|
-
))), canEditPage ? /* @__PURE__ */
|
|
1005
|
+
pageOptions.map((index) => /* @__PURE__ */ React4__default.default.createElement("option", { key: index, value: index }, "\u7B2C ", index + 1, " \u9875"))
|
|
1006
|
+
))), canEditPage ? /* @__PURE__ */ React4__default.default.createElement("div", { className: "mt-4" }, /* @__PURE__ */ React4__default.default.createElement("div", { className: "mb-3 flex gap-2" }, /* @__PURE__ */ React4__default.default.createElement(
|
|
471
1007
|
"button",
|
|
472
1008
|
{
|
|
473
1009
|
type: "button",
|
|
@@ -480,7 +1016,7 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
|
|
|
480
1016
|
className: "rounded-lg bg-slate-900 px-3 py-2 text-sm font-medium text-white"
|
|
481
1017
|
},
|
|
482
1018
|
"+ \u6587\u5B57"
|
|
483
|
-
), /* @__PURE__ */
|
|
1019
|
+
), /* @__PURE__ */ React4__default.default.createElement(
|
|
484
1020
|
"button",
|
|
485
1021
|
{
|
|
486
1022
|
type: "button",
|
|
@@ -493,166 +1029,174 @@ var FestivalCardConfigEditor = ({ value, onChange }) => {
|
|
|
493
1029
|
className: "rounded-lg bg-sky-600 px-3 py-2 text-sm font-medium text-white"
|
|
494
1030
|
},
|
|
495
1031
|
"+ \u56FE\u7247"
|
|
496
|
-
)), /* @__PURE__ */
|
|
497
|
-
"
|
|
498
|
-
{
|
|
499
|
-
type: "button",
|
|
500
|
-
onClick: () => removeElement(element.id),
|
|
501
|
-
className: "rounded-md border border-rose-300 bg-rose-50 px-2 py-1 text-xs font-medium text-rose-700"
|
|
502
|
-
},
|
|
503
|
-
"\u5220\u9664"
|
|
504
|
-
)), element.type === "text" ? /* @__PURE__ */ React3__default.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React3__default.default.createElement(
|
|
505
|
-
"textarea",
|
|
506
|
-
{
|
|
507
|
-
value: element.content,
|
|
508
|
-
onChange: (event) => updateElement(element.id, { content: event.target.value }),
|
|
509
|
-
rows: 3,
|
|
510
|
-
className: "w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
|
|
511
|
-
}
|
|
512
|
-
), /* @__PURE__ */ React3__default.default.createElement("div", { className: "grid gap-2 sm:grid-cols-2" }, /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "X(%)", /* @__PURE__ */ React3__default.default.createElement(
|
|
513
|
-
"input",
|
|
514
|
-
{
|
|
515
|
-
type: "number",
|
|
516
|
-
value: element.x,
|
|
517
|
-
onChange: (event) => updateElement(element.id, { x: Number(event.target.value) }),
|
|
518
|
-
className: numberFieldClassName
|
|
519
|
-
}
|
|
520
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "Y(%)", /* @__PURE__ */ React3__default.default.createElement(
|
|
521
|
-
"input",
|
|
522
|
-
{
|
|
523
|
-
type: "number",
|
|
524
|
-
value: element.y,
|
|
525
|
-
onChange: (event) => updateElement(element.id, { y: Number(event.target.value) }),
|
|
526
|
-
className: numberFieldClassName
|
|
527
|
-
}
|
|
528
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5BBD\u5EA6(%)", /* @__PURE__ */ React3__default.default.createElement(
|
|
529
|
-
"input",
|
|
530
|
-
{
|
|
531
|
-
type: "number",
|
|
532
|
-
value: element.width ?? 70,
|
|
533
|
-
onChange: (event) => updateElement(element.id, { width: Number(event.target.value) }),
|
|
534
|
-
className: numberFieldClassName
|
|
535
|
-
}
|
|
536
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5B57\u53F7(px)", /* @__PURE__ */ React3__default.default.createElement(
|
|
537
|
-
"input",
|
|
538
|
-
{
|
|
539
|
-
type: "number",
|
|
540
|
-
value: element.fontSize ?? 18,
|
|
541
|
-
onChange: (event) => updateElement(element.id, { fontSize: Number(event.target.value) }),
|
|
542
|
-
className: numberFieldClassName
|
|
543
|
-
}
|
|
544
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5B57\u91CD", /* @__PURE__ */ React3__default.default.createElement(
|
|
545
|
-
"input",
|
|
546
|
-
{
|
|
547
|
-
type: "number",
|
|
548
|
-
min: 100,
|
|
549
|
-
max: 900,
|
|
550
|
-
step: 100,
|
|
551
|
-
value: element.fontWeight ?? 500,
|
|
552
|
-
onChange: (event) => updateElement(element.id, { fontWeight: Number(event.target.value) }),
|
|
553
|
-
className: numberFieldClassName
|
|
554
|
-
}
|
|
555
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5BF9\u9F50", /* @__PURE__ */ React3__default.default.createElement(
|
|
556
|
-
"select",
|
|
557
|
-
{
|
|
558
|
-
value: element.align || "left",
|
|
559
|
-
onChange: (event) => updateElement(element.id, { align: event.target.value }),
|
|
560
|
-
className: numberFieldClassName
|
|
561
|
-
},
|
|
562
|
-
/* @__PURE__ */ React3__default.default.createElement("option", { value: "left" }, "left"),
|
|
563
|
-
/* @__PURE__ */ React3__default.default.createElement("option", { value: "center" }, "center"),
|
|
564
|
-
/* @__PURE__ */ React3__default.default.createElement("option", { value: "right" }, "right")
|
|
565
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600 sm:col-span-2" }, "\u5B57\u4F53", /* @__PURE__ */ React3__default.default.createElement(
|
|
566
|
-
"input",
|
|
567
|
-
{
|
|
568
|
-
type: "text",
|
|
569
|
-
value: element.fontFamily || "",
|
|
570
|
-
onChange: (event) => updateElement(element.id, { fontFamily: event.target.value }),
|
|
571
|
-
placeholder: "inherit / serif / sans-serif / PingFang SC",
|
|
572
|
-
className: numberFieldClassName
|
|
573
|
-
}
|
|
574
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600 sm:col-span-2" }, "\u6587\u5B57\u989C\u8272", /* @__PURE__ */ React3__default.default.createElement("div", { className: "grid grid-cols-[64px_1fr] gap-2" }, /* @__PURE__ */ React3__default.default.createElement(
|
|
575
|
-
"input",
|
|
576
|
-
{
|
|
577
|
-
type: "color",
|
|
578
|
-
value: element.color || "#ffffff",
|
|
579
|
-
onChange: (event) => updateElement(element.id, { color: event.target.value }),
|
|
580
|
-
className: "h-10 rounded-lg border border-slate-300 bg-white p-1"
|
|
581
|
-
}
|
|
582
|
-
), /* @__PURE__ */ React3__default.default.createElement(
|
|
583
|
-
"input",
|
|
584
|
-
{
|
|
585
|
-
type: "text",
|
|
586
|
-
value: element.color || "#ffffff",
|
|
587
|
-
onChange: (event) => updateElement(element.id, { color: event.target.value }),
|
|
588
|
-
className: numberFieldClassName
|
|
589
|
-
}
|
|
590
|
-
))))) : /* @__PURE__ */ React3__default.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React3__default.default.createElement(
|
|
591
|
-
"input",
|
|
592
|
-
{
|
|
593
|
-
type: "url",
|
|
594
|
-
value: element.src,
|
|
595
|
-
onChange: (event) => updateElement(element.id, { src: event.target.value }),
|
|
596
|
-
className: "w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
|
|
597
|
-
}
|
|
598
|
-
), /* @__PURE__ */ React3__default.default.createElement("div", { className: "grid gap-2 sm:grid-cols-2" }, /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "X(%)", /* @__PURE__ */ React3__default.default.createElement(
|
|
599
|
-
"input",
|
|
600
|
-
{
|
|
601
|
-
type: "number",
|
|
602
|
-
value: element.x,
|
|
603
|
-
onChange: (event) => updateElement(element.id, { x: Number(event.target.value) }),
|
|
604
|
-
className: numberFieldClassName
|
|
605
|
-
}
|
|
606
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "Y(%)", /* @__PURE__ */ React3__default.default.createElement(
|
|
607
|
-
"input",
|
|
608
|
-
{
|
|
609
|
-
type: "number",
|
|
610
|
-
value: element.y,
|
|
611
|
-
onChange: (event) => updateElement(element.id, { y: Number(event.target.value) }),
|
|
612
|
-
className: numberFieldClassName
|
|
613
|
-
}
|
|
614
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5BBD\u5EA6(%)", /* @__PURE__ */ React3__default.default.createElement(
|
|
615
|
-
"input",
|
|
616
|
-
{
|
|
617
|
-
type: "number",
|
|
618
|
-
value: element.width ?? 60,
|
|
619
|
-
onChange: (event) => updateElement(element.id, { width: Number(event.target.value) }),
|
|
620
|
-
className: numberFieldClassName
|
|
621
|
-
}
|
|
622
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u9AD8\u5EA6(%)", /* @__PURE__ */ React3__default.default.createElement(
|
|
623
|
-
"input",
|
|
624
|
-
{
|
|
625
|
-
type: "number",
|
|
626
|
-
value: element.height ?? 40,
|
|
627
|
-
onChange: (event) => updateElement(element.id, { height: Number(event.target.value) }),
|
|
628
|
-
className: numberFieldClassName
|
|
629
|
-
}
|
|
630
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5706\u89D2(px)", /* @__PURE__ */ React3__default.default.createElement(
|
|
631
|
-
"input",
|
|
632
|
-
{
|
|
633
|
-
type: "number",
|
|
634
|
-
value: element.borderRadius ?? 0,
|
|
635
|
-
onChange: (event) => updateElement(element.id, { borderRadius: Number(event.target.value) }),
|
|
636
|
-
className: numberFieldClassName
|
|
637
|
-
}
|
|
638
|
-
)), /* @__PURE__ */ React3__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u586B\u5145", /* @__PURE__ */ React3__default.default.createElement(
|
|
639
|
-
"select",
|
|
1032
|
+
)), /* @__PURE__ */ React4__default.default.createElement("div", { className: "grid max-h-[340px] gap-2.5 overflow-auto pr-1" }, (page?.elements ?? []).map((element) => /* @__PURE__ */ React4__default.default.createElement(
|
|
1033
|
+
"div",
|
|
640
1034
|
{
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
className: numberFieldClassName
|
|
1035
|
+
key: element.id,
|
|
1036
|
+
className: `rounded-xl border bg-slate-50 p-3 ${selectedElementId === element.id ? "border-sky-400 ring-2 ring-sky-100" : "border-slate-200"}`
|
|
644
1037
|
},
|
|
645
|
-
/* @__PURE__ */
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
}
|
|
655
|
-
|
|
1038
|
+
/* @__PURE__ */ React4__default.default.createElement("div", { className: "mb-2 flex items-center justify-between" }, /* @__PURE__ */ React4__default.default.createElement("div", { className: "text-xs font-semibold tracking-wide text-slate-500" }, element.type.toUpperCase()), /* @__PURE__ */ React4__default.default.createElement(
|
|
1039
|
+
"button",
|
|
1040
|
+
{
|
|
1041
|
+
type: "button",
|
|
1042
|
+
onClick: () => removeElement(element.id),
|
|
1043
|
+
className: "rounded-md border border-rose-300 bg-rose-50 px-2 py-1 text-xs font-medium text-rose-700"
|
|
1044
|
+
},
|
|
1045
|
+
"\u5220\u9664"
|
|
1046
|
+
)),
|
|
1047
|
+
element.type === "text" ? /* @__PURE__ */ React4__default.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React4__default.default.createElement(
|
|
1048
|
+
"textarea",
|
|
1049
|
+
{
|
|
1050
|
+
value: element.content,
|
|
1051
|
+
onChange: (event) => updateElement(element.id, { content: event.target.value }),
|
|
1052
|
+
rows: 3,
|
|
1053
|
+
className: "w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
|
|
1054
|
+
}
|
|
1055
|
+
), /* @__PURE__ */ React4__default.default.createElement("div", { className: "grid gap-2 sm:grid-cols-2" }, /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "X(%)", /* @__PURE__ */ React4__default.default.createElement(
|
|
1056
|
+
"input",
|
|
1057
|
+
{
|
|
1058
|
+
type: "number",
|
|
1059
|
+
value: element.x,
|
|
1060
|
+
onChange: (event) => updateElement(element.id, { x: Number(event.target.value) }),
|
|
1061
|
+
className: numberFieldClassName
|
|
1062
|
+
}
|
|
1063
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "Y(%)", /* @__PURE__ */ React4__default.default.createElement(
|
|
1064
|
+
"input",
|
|
1065
|
+
{
|
|
1066
|
+
type: "number",
|
|
1067
|
+
value: element.y,
|
|
1068
|
+
onChange: (event) => updateElement(element.id, { y: Number(event.target.value) }),
|
|
1069
|
+
className: numberFieldClassName
|
|
1070
|
+
}
|
|
1071
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5BBD\u5EA6(%)", /* @__PURE__ */ React4__default.default.createElement(
|
|
1072
|
+
"input",
|
|
1073
|
+
{
|
|
1074
|
+
type: "number",
|
|
1075
|
+
value: element.width ?? 70,
|
|
1076
|
+
onChange: (event) => updateElement(element.id, { width: Number(event.target.value) }),
|
|
1077
|
+
className: numberFieldClassName
|
|
1078
|
+
}
|
|
1079
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5B57\u53F7(px)", /* @__PURE__ */ React4__default.default.createElement(
|
|
1080
|
+
"input",
|
|
1081
|
+
{
|
|
1082
|
+
type: "number",
|
|
1083
|
+
value: element.fontSize ?? 18,
|
|
1084
|
+
onChange: (event) => updateElement(element.id, { fontSize: Number(event.target.value) }),
|
|
1085
|
+
className: numberFieldClassName
|
|
1086
|
+
}
|
|
1087
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5B57\u91CD", /* @__PURE__ */ React4__default.default.createElement(
|
|
1088
|
+
"input",
|
|
1089
|
+
{
|
|
1090
|
+
type: "number",
|
|
1091
|
+
min: 100,
|
|
1092
|
+
max: 900,
|
|
1093
|
+
step: 100,
|
|
1094
|
+
value: element.fontWeight ?? 500,
|
|
1095
|
+
onChange: (event) => updateElement(element.id, { fontWeight: Number(event.target.value) }),
|
|
1096
|
+
className: numberFieldClassName
|
|
1097
|
+
}
|
|
1098
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5BF9\u9F50", /* @__PURE__ */ React4__default.default.createElement(
|
|
1099
|
+
"select",
|
|
1100
|
+
{
|
|
1101
|
+
value: element.align || "left",
|
|
1102
|
+
onChange: (event) => updateElement(element.id, { align: event.target.value }),
|
|
1103
|
+
className: numberFieldClassName
|
|
1104
|
+
},
|
|
1105
|
+
/* @__PURE__ */ React4__default.default.createElement("option", { value: "left" }, "left"),
|
|
1106
|
+
/* @__PURE__ */ React4__default.default.createElement("option", { value: "center" }, "center"),
|
|
1107
|
+
/* @__PURE__ */ React4__default.default.createElement("option", { value: "right" }, "right")
|
|
1108
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600 sm:col-span-2" }, "\u5B57\u4F53", /* @__PURE__ */ React4__default.default.createElement(
|
|
1109
|
+
"input",
|
|
1110
|
+
{
|
|
1111
|
+
type: "text",
|
|
1112
|
+
value: element.fontFamily || "",
|
|
1113
|
+
onChange: (event) => updateElement(element.id, { fontFamily: event.target.value }),
|
|
1114
|
+
placeholder: "inherit / serif / sans-serif / PingFang SC",
|
|
1115
|
+
className: numberFieldClassName
|
|
1116
|
+
}
|
|
1117
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600 sm:col-span-2" }, "\u6587\u5B57\u989C\u8272", /* @__PURE__ */ React4__default.default.createElement("div", { className: "grid grid-cols-[64px_1fr] gap-2" }, /* @__PURE__ */ React4__default.default.createElement(
|
|
1118
|
+
"input",
|
|
1119
|
+
{
|
|
1120
|
+
type: "color",
|
|
1121
|
+
value: element.color || "#ffffff",
|
|
1122
|
+
onChange: (event) => updateElement(element.id, { color: event.target.value }),
|
|
1123
|
+
className: "h-10 rounded-lg border border-slate-300 bg-white p-1"
|
|
1124
|
+
}
|
|
1125
|
+
), /* @__PURE__ */ React4__default.default.createElement(
|
|
1126
|
+
"input",
|
|
1127
|
+
{
|
|
1128
|
+
type: "text",
|
|
1129
|
+
value: element.color || "#ffffff",
|
|
1130
|
+
onChange: (event) => updateElement(element.id, { color: event.target.value }),
|
|
1131
|
+
className: numberFieldClassName
|
|
1132
|
+
}
|
|
1133
|
+
))))) : /* @__PURE__ */ React4__default.default.createElement("div", { className: "grid gap-2" }, /* @__PURE__ */ React4__default.default.createElement(
|
|
1134
|
+
"input",
|
|
1135
|
+
{
|
|
1136
|
+
type: "url",
|
|
1137
|
+
value: element.src,
|
|
1138
|
+
onChange: (event) => updateElement(element.id, { src: event.target.value }),
|
|
1139
|
+
className: "w-full rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
|
|
1140
|
+
}
|
|
1141
|
+
), /* @__PURE__ */ React4__default.default.createElement("div", { className: "grid gap-2 sm:grid-cols-2" }, /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "X(%)", /* @__PURE__ */ React4__default.default.createElement(
|
|
1142
|
+
"input",
|
|
1143
|
+
{
|
|
1144
|
+
type: "number",
|
|
1145
|
+
value: element.x,
|
|
1146
|
+
onChange: (event) => updateElement(element.id, { x: Number(event.target.value) }),
|
|
1147
|
+
className: numberFieldClassName
|
|
1148
|
+
}
|
|
1149
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "Y(%)", /* @__PURE__ */ React4__default.default.createElement(
|
|
1150
|
+
"input",
|
|
1151
|
+
{
|
|
1152
|
+
type: "number",
|
|
1153
|
+
value: element.y,
|
|
1154
|
+
onChange: (event) => updateElement(element.id, { y: Number(event.target.value) }),
|
|
1155
|
+
className: numberFieldClassName
|
|
1156
|
+
}
|
|
1157
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5BBD\u5EA6(%)", /* @__PURE__ */ React4__default.default.createElement(
|
|
1158
|
+
"input",
|
|
1159
|
+
{
|
|
1160
|
+
type: "number",
|
|
1161
|
+
value: element.width ?? 60,
|
|
1162
|
+
onChange: (event) => updateElement(element.id, { width: Number(event.target.value) }),
|
|
1163
|
+
className: numberFieldClassName
|
|
1164
|
+
}
|
|
1165
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u9AD8\u5EA6(%)", /* @__PURE__ */ React4__default.default.createElement(
|
|
1166
|
+
"input",
|
|
1167
|
+
{
|
|
1168
|
+
type: "number",
|
|
1169
|
+
value: element.height ?? 40,
|
|
1170
|
+
onChange: (event) => updateElement(element.id, { height: Number(event.target.value) }),
|
|
1171
|
+
className: numberFieldClassName
|
|
1172
|
+
}
|
|
1173
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u5706\u89D2(px)", /* @__PURE__ */ React4__default.default.createElement(
|
|
1174
|
+
"input",
|
|
1175
|
+
{
|
|
1176
|
+
type: "number",
|
|
1177
|
+
value: element.borderRadius ?? 0,
|
|
1178
|
+
onChange: (event) => updateElement(element.id, { borderRadius: Number(event.target.value) }),
|
|
1179
|
+
className: numberFieldClassName
|
|
1180
|
+
}
|
|
1181
|
+
)), /* @__PURE__ */ React4__default.default.createElement("label", { className: "grid gap-1 text-xs text-slate-600" }, "\u586B\u5145", /* @__PURE__ */ React4__default.default.createElement(
|
|
1182
|
+
"select",
|
|
1183
|
+
{
|
|
1184
|
+
value: element.fit || "cover",
|
|
1185
|
+
onChange: (event) => updateElement(element.id, { fit: event.target.value }),
|
|
1186
|
+
className: numberFieldClassName
|
|
1187
|
+
},
|
|
1188
|
+
/* @__PURE__ */ React4__default.default.createElement("option", { value: "cover" }, "cover"),
|
|
1189
|
+
/* @__PURE__ */ React4__default.default.createElement("option", { value: "contain" }, "contain")
|
|
1190
|
+
))), /* @__PURE__ */ React4__default.default.createElement("label", { className: "inline-flex items-center gap-2 text-sm text-slate-700" }, /* @__PURE__ */ React4__default.default.createElement(
|
|
1191
|
+
"input",
|
|
1192
|
+
{
|
|
1193
|
+
type: "checkbox",
|
|
1194
|
+
checked: Boolean(element.isBackground),
|
|
1195
|
+
onChange: (event) => updateElement(element.id, { isBackground: event.target.checked }),
|
|
1196
|
+
className: "h-4 w-4 rounded border-slate-300 text-sky-600"
|
|
1197
|
+
}
|
|
1198
|
+
), "\u4F5C\u4E3A\u672C\u9875\u80CC\u666F\u56FE"))
|
|
1199
|
+
)))) : null);
|
|
656
1200
|
};
|
|
657
1201
|
|
|
658
1202
|
// src/festivalCard/components/FestivalCardStudio.tsx
|
|
@@ -662,8 +1206,55 @@ var FestivalCardStudio = ({ initialConfig, fetchConfig, onSave }) => {
|
|
|
662
1206
|
fetchConfig,
|
|
663
1207
|
onSave
|
|
664
1208
|
});
|
|
665
|
-
|
|
666
|
-
|
|
1209
|
+
const [activePageIndex, setActivePageIndex] = React4.useState(0);
|
|
1210
|
+
const [selectedElementId, setSelectedElementId] = React4.useState(null);
|
|
1211
|
+
React4.useEffect(() => {
|
|
1212
|
+
if (config.pages.length === 0) return;
|
|
1213
|
+
if (activePageIndex <= config.pages.length - 1) return;
|
|
1214
|
+
setActivePageIndex(config.pages.length - 1);
|
|
1215
|
+
}, [activePageIndex, config.pages.length]);
|
|
1216
|
+
const updateElementByPreview = (pageIndex, elementId, patch) => {
|
|
1217
|
+
setConfig((prev) => ({
|
|
1218
|
+
...prev,
|
|
1219
|
+
pages: prev.pages.map(
|
|
1220
|
+
(page, index) => index === pageIndex ? {
|
|
1221
|
+
...page,
|
|
1222
|
+
elements: page.elements.map(
|
|
1223
|
+
(element) => element.id === elementId ? { ...element, ...patch } : element
|
|
1224
|
+
)
|
|
1225
|
+
} : page
|
|
1226
|
+
)
|
|
1227
|
+
}));
|
|
1228
|
+
};
|
|
1229
|
+
if (loading) return /* @__PURE__ */ React4__default.default.createElement("div", null, "\u52A0\u8F7D\u4E2D...");
|
|
1230
|
+
return /* @__PURE__ */ React4__default.default.createElement("div", { className: "grid items-start gap-4 lg:grid-cols-[1.45fr_1fr]" }, /* @__PURE__ */ React4__default.default.createElement(
|
|
1231
|
+
FestivalCardBook3D,
|
|
1232
|
+
{
|
|
1233
|
+
config,
|
|
1234
|
+
className: "h-full",
|
|
1235
|
+
editable: true,
|
|
1236
|
+
currentPage: activePageIndex,
|
|
1237
|
+
onCurrentPageChange: (index) => {
|
|
1238
|
+
setActivePageIndex(index);
|
|
1239
|
+
setSelectedElementId(null);
|
|
1240
|
+
},
|
|
1241
|
+
selectedElementId,
|
|
1242
|
+
onSelectedElementChange: setSelectedElementId,
|
|
1243
|
+
onElementChange: updateElementByPreview
|
|
1244
|
+
}
|
|
1245
|
+
), /* @__PURE__ */ React4__default.default.createElement("div", { className: "lg:sticky lg:top-4" }, /* @__PURE__ */ React4__default.default.createElement(
|
|
1246
|
+
FestivalCardConfigEditor,
|
|
1247
|
+
{
|
|
1248
|
+
value: config,
|
|
1249
|
+
onChange: setConfig,
|
|
1250
|
+
activePageIndex,
|
|
1251
|
+
onActivePageIndexChange: (index) => {
|
|
1252
|
+
setActivePageIndex(index);
|
|
1253
|
+
setSelectedElementId(null);
|
|
1254
|
+
},
|
|
1255
|
+
selectedElementId
|
|
1256
|
+
}
|
|
1257
|
+
), onSave ? /* @__PURE__ */ React4__default.default.createElement(
|
|
667
1258
|
"button",
|
|
668
1259
|
{
|
|
669
1260
|
type: "button",
|
|
@@ -681,8 +1272,8 @@ var FestivalCardConfigPage = ({
|
|
|
681
1272
|
cardId,
|
|
682
1273
|
mainPagePath = "/festivalCard"
|
|
683
1274
|
}) => {
|
|
684
|
-
const [list, setList] =
|
|
685
|
-
const [selectedId, setSelectedId] =
|
|
1275
|
+
const [list, setList] = React4.useState([]);
|
|
1276
|
+
const [selectedId, setSelectedId] = React4.useState(cardId || "default-festival-card");
|
|
686
1277
|
const parseListResponse2 = (data) => {
|
|
687
1278
|
if (!data || typeof data !== "object") return [];
|
|
688
1279
|
const payload = data.data;
|
|
@@ -695,7 +1286,7 @@ var FestivalCardConfigPage = ({
|
|
|
695
1286
|
if (!payload || typeof payload !== "object") return normalizeFestivalCardConfig();
|
|
696
1287
|
return normalizeFestivalCardConfig(payload);
|
|
697
1288
|
};
|
|
698
|
-
const reloadList =
|
|
1289
|
+
const reloadList = React4.useCallback(async () => {
|
|
699
1290
|
try {
|
|
700
1291
|
const response = await fetch(apiBase, { cache: "no-store" });
|
|
701
1292
|
if (!response.ok) throw new Error(`\u52A0\u8F7D\u5361\u7247\u5217\u8868\u5931\u8D25: ${response.status}`);
|
|
@@ -718,7 +1309,7 @@ var FestivalCardConfigPage = ({
|
|
|
718
1309
|
window.alert(error.message || "\u52A0\u8F7D\u5361\u7247\u5217\u8868\u5931\u8D25");
|
|
719
1310
|
}
|
|
720
1311
|
}, [apiBase]);
|
|
721
|
-
|
|
1312
|
+
React4.useEffect(() => {
|
|
722
1313
|
void reloadList();
|
|
723
1314
|
}, [reloadList]);
|
|
724
1315
|
const fetchConfig = async () => {
|
|
@@ -768,16 +1359,16 @@ var FestivalCardConfigPage = ({
|
|
|
768
1359
|
window.alert(error.message || "\u521B\u5EFA\u5931\u8D25");
|
|
769
1360
|
}
|
|
770
1361
|
};
|
|
771
|
-
const mainLink =
|
|
772
|
-
return /* @__PURE__ */
|
|
1362
|
+
const mainLink = React4.useMemo(() => `${mainPagePath}?cardId=${encodeURIComponent(selectedId)}`, [mainPagePath, selectedId]);
|
|
1363
|
+
return /* @__PURE__ */ React4__default.default.createElement("div", { className: "grid gap-4" }, /* @__PURE__ */ React4__default.default.createElement("div", { className: "flex flex-wrap items-center gap-2 rounded-xl border border-slate-800 bg-slate-900/50 p-3" }, /* @__PURE__ */ React4__default.default.createElement(
|
|
773
1364
|
"select",
|
|
774
1365
|
{
|
|
775
1366
|
value: selectedId,
|
|
776
1367
|
onChange: (event) => setSelectedId(event.target.value),
|
|
777
1368
|
className: "min-w-[240px] rounded-lg border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 outline-none focus:border-sky-400 focus:ring-2 focus:ring-sky-100"
|
|
778
1369
|
},
|
|
779
|
-
list.map((item) => /* @__PURE__ */
|
|
780
|
-
), /* @__PURE__ */
|
|
1370
|
+
list.map((item) => /* @__PURE__ */ React4__default.default.createElement("option", { key: item.id, value: item.id }, item.name || item.id))
|
|
1371
|
+
), /* @__PURE__ */ React4__default.default.createElement("button", { type: "button", onClick: () => void createNew(), className: "rounded-lg bg-slate-900 px-3 py-2 text-sm font-medium text-white" }, "\u65B0\u5EFA\u5361\u7247"), /* @__PURE__ */ React4__default.default.createElement("a", { href: mainLink, className: "rounded-lg border border-sky-200 bg-sky-50 px-3 py-2 text-sm text-sky-700" }, "\u6253\u5F00\u4E3B\u9875\u9762")), /* @__PURE__ */ React4__default.default.createElement(FestivalCardStudio, { fetchConfig, onSave: saveConfig }));
|
|
781
1372
|
};
|
|
782
1373
|
var isSummary = (value) => {
|
|
783
1374
|
if (!value || typeof value !== "object") return false;
|
|
@@ -800,10 +1391,10 @@ var FestivalCardManagedPage = ({
|
|
|
800
1391
|
apiBase = "/api/festivalCard",
|
|
801
1392
|
cardId
|
|
802
1393
|
}) => {
|
|
803
|
-
const [currentCardId, setCurrentCardId] =
|
|
804
|
-
const [config, setConfig] =
|
|
805
|
-
const [loading, setLoading] =
|
|
806
|
-
|
|
1394
|
+
const [currentCardId, setCurrentCardId] = React4.useState(cardId || "");
|
|
1395
|
+
const [config, setConfig] = React4.useState(null);
|
|
1396
|
+
const [loading, setLoading] = React4.useState(true);
|
|
1397
|
+
React4.useEffect(() => {
|
|
807
1398
|
const fetchList = async () => {
|
|
808
1399
|
const response = await fetch(apiBase, { cache: "no-store" });
|
|
809
1400
|
const data = await response.json();
|
|
@@ -815,12 +1406,12 @@ var FestivalCardManagedPage = ({
|
|
|
815
1406
|
};
|
|
816
1407
|
void fetchList();
|
|
817
1408
|
}, [apiBase, currentCardId]);
|
|
818
|
-
|
|
1409
|
+
React4.useEffect(() => {
|
|
819
1410
|
if (!currentCardId) return;
|
|
820
1411
|
setLoading(true);
|
|
821
1412
|
void fetch(`${apiBase}/${encodeURIComponent(currentCardId)}`, { cache: "no-store" }).then((res) => res.json()).then((data) => setConfig(parseConfigResponse(data))).finally(() => setLoading(false));
|
|
822
1413
|
}, [apiBase, currentCardId]);
|
|
823
|
-
return /* @__PURE__ */
|
|
1414
|
+
return /* @__PURE__ */ React4__default.default.createElement("div", null, loading || !config ? /* @__PURE__ */ React4__default.default.createElement("div", { className: "py-12 text-center text-slate-400" }, "\u52A0\u8F7D\u4E2D...") : /* @__PURE__ */ React4__default.default.createElement(FestivalCardBook3D, { config }));
|
|
824
1415
|
};
|
|
825
1416
|
|
|
826
1417
|
// src/festivalCard/server/db.ts
|