@ericchen1990/pano-view 3.8.0 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -2254,6 +2254,7 @@ __export(index_exports, {
2254
2254
  DEFAULT_HOTSPOT_TOOLTIP_APPEARANCE: () => DEFAULT_HOTSPOT_TOOLTIP_APPEARANCE,
2255
2255
  DEFAULT_HOTSPOT_TOOLTIP_OFFSET: () => DEFAULT_HOTSPOT_TOOLTIP_OFFSET,
2256
2256
  DEFAULT_PANO_CURSORS: () => DEFAULT_PANO_CURSORS,
2257
+ DEFAULT_PANO_SCENE_MENU_APPEARANCE: () => DEFAULT_PANO_SCENE_MENU_APPEARANCE,
2257
2258
  DEFAULT_PANO_VIDEO_CAPTION_APPEARANCE: () => DEFAULT_PANO_VIDEO_CAPTION_APPEARANCE,
2258
2259
  DEFAULT_PANO_VIDEO_CONTROLS_APPEARANCE: () => DEFAULT_PANO_VIDEO_CONTROLS_APPEARANCE,
2259
2260
  DEFAULT_PANO_VIDEO_PLAYBACK_RATES: () => DEFAULT_PANO_VIDEO_PLAYBACK_RATES,
@@ -2274,6 +2275,7 @@ __export(index_exports, {
2274
2275
  PanoMap: () => PanoMap,
2275
2276
  PanoMapOverlay: () => PanoMapOverlay,
2276
2277
  PanoOverlayPortalProvider: () => PanoOverlayPortalProvider,
2278
+ PanoSceneMenu: () => PanoSceneMenu,
2277
2279
  PanoVideo: () => PanoVideo,
2278
2280
  PanoVideoControls: () => PanoVideoControls,
2279
2281
  PanoViewer: () => PanoViewer,
@@ -18240,8 +18242,908 @@ var WebVR = (0, import_react61.forwardRef)(function WebVR2({
18240
18242
  ] });
18241
18243
  });
18242
18244
 
18243
- // src/use-pano-viewer.ts
18245
+ // src/pano-scene-menu.tsx
18244
18246
  var import_react62 = require("react");
18247
+
18248
+ // src/drag-motion.ts
18249
+ var DRAG_INERTIA_DAMPING = 8;
18250
+ var DRAG_FRICTION_STOP = 24;
18251
+ var DRAG_SPRING_STRENGTH = 28;
18252
+ var DRAG_RUBBER_CONSTANT = 0.55;
18253
+ var DRAG_SPRING_SETTLE_DISTANCE = 0.5;
18254
+ var DRAG_SPRING_SETTLE_VELOCITY = 12;
18255
+ function resolveDragMotion(motion) {
18256
+ if (motion === false) {
18257
+ return { inertia: false, bounce: false };
18258
+ }
18259
+ return {
18260
+ inertia: motion?.inertia !== false,
18261
+ bounce: motion?.bounce !== false
18262
+ };
18263
+ }
18264
+ function clampAxis(value, min, max) {
18265
+ if (max < min) return min;
18266
+ return Math.min(max, Math.max(min, value));
18267
+ }
18268
+ function constrainDragPosition(raw, min, max, dimension, bounce) {
18269
+ if (!(Number.isFinite(raw) && Number.isFinite(min) && Number.isFinite(max))) {
18270
+ return 0;
18271
+ }
18272
+ if (raw >= min && raw <= max) {
18273
+ return raw;
18274
+ }
18275
+ if (!bounce) {
18276
+ return clampAxis(raw, min, max);
18277
+ }
18278
+ const size = Math.max(1, Math.abs(dimension) || 1);
18279
+ if (raw < min) {
18280
+ const overshoot2 = min - raw;
18281
+ return min - rubberBandDistance(overshoot2, size);
18282
+ }
18283
+ const overshoot = raw - max;
18284
+ return max + rubberBandDistance(overshoot, size);
18285
+ }
18286
+ function rubberBandDistance(overshoot, dimension, constant = DRAG_RUBBER_CONSTANT) {
18287
+ if (!(overshoot > 0)) return 0;
18288
+ const size = Math.max(1, Math.abs(dimension) || 1);
18289
+ return size * overshoot / (size + overshoot * constant);
18290
+ }
18291
+ function velocityFromSamples(samples, now) {
18292
+ if (samples.length < 2) return 0;
18293
+ const windowMs = 100;
18294
+ let oldest = samples[0];
18295
+ const newest = samples[samples.length - 1];
18296
+ const referenceTime = now ?? newest.t;
18297
+ for (let i = samples.length - 2; i >= 0; i -= 1) {
18298
+ const sample = samples[i];
18299
+ if (referenceTime - sample.t > windowMs) break;
18300
+ oldest = sample;
18301
+ }
18302
+ const dt = (newest.t - oldest.t) / 1e3;
18303
+ if (!(dt >= 1e-3)) return 0;
18304
+ return (newest.position - oldest.position) / dt;
18305
+ }
18306
+ function integrateAxis(state, deltaSeconds, options) {
18307
+ const dt = Math.max(0, deltaSeconds);
18308
+ if (!(dt > 0)) {
18309
+ return { ...state, settled: isAxisSettled(state, options) };
18310
+ }
18311
+ const damping = options.damping ?? DRAG_INERTIA_DAMPING;
18312
+ const frictionStop = options.frictionStop ?? DRAG_FRICTION_STOP;
18313
+ const spring = options.spring ?? DRAG_SPRING_STRENGTH;
18314
+ const { min, max } = options;
18315
+ let { position, velocity } = state;
18316
+ const pastMin = position < min;
18317
+ const pastMax = position > max;
18318
+ if (pastMin || pastMax) {
18319
+ if (!options.bounce) {
18320
+ position = clampAxis(position, min, max);
18321
+ return { position, velocity: 0, settled: true };
18322
+ }
18323
+ const edge = pastMin ? min : max;
18324
+ if (pastMin && velocity < 0) velocity = 0;
18325
+ if (pastMax && velocity > 0) velocity = 0;
18326
+ const displacement = edge - position;
18327
+ velocity += displacement * spring * dt;
18328
+ const decay = Math.exp(-damping * dt);
18329
+ velocity *= decay;
18330
+ position += velocity * dt;
18331
+ if (pastMin && position > min) {
18332
+ position = min;
18333
+ velocity = 0;
18334
+ }
18335
+ if (pastMax && position < max) {
18336
+ position = max;
18337
+ velocity = 0;
18338
+ }
18339
+ const settled = isAxisSettled({ position, velocity }, options);
18340
+ if (settled) {
18341
+ return { position: clampAxis(position, min, max), velocity: 0, settled: true };
18342
+ }
18343
+ return { position, velocity, settled: false };
18344
+ }
18345
+ if (!options.inertia || Math.abs(velocity) < frictionStop) {
18346
+ return { position, velocity: 0, settled: true };
18347
+ }
18348
+ position += velocity * dt;
18349
+ velocity *= Math.exp(-damping * dt);
18350
+ if (position < min || position > max) {
18351
+ if (!options.bounce) {
18352
+ position = clampAxis(position, min, max);
18353
+ return { position, velocity: 0, settled: true };
18354
+ }
18355
+ if (Math.abs(velocity) < frictionStop) {
18356
+ velocity = 0;
18357
+ }
18358
+ return { position, velocity, settled: false };
18359
+ }
18360
+ if (Math.abs(velocity) < frictionStop) {
18361
+ return { position, velocity: 0, settled: true };
18362
+ }
18363
+ return { position, velocity, settled: false };
18364
+ }
18365
+ function isAxisSettled(state, options) {
18366
+ const frictionStop = options.frictionStop ?? DRAG_FRICTION_STOP;
18367
+ const inside = state.position >= options.min - DRAG_SPRING_SETTLE_DISTANCE && state.position <= options.max + DRAG_SPRING_SETTLE_DISTANCE;
18368
+ const slow = Math.abs(state.velocity) < (options.frictionStop ?? DRAG_SPRING_SETTLE_VELOCITY);
18369
+ if (!inside) return false;
18370
+ return slow || Math.abs(state.velocity) < frictionStop;
18371
+ }
18372
+ function pushSample(samples, sample, maxCount = 8) {
18373
+ const next = samples.length >= maxCount ? samples.slice(samples.length - maxCount + 1) : samples.slice();
18374
+ next.push(sample);
18375
+ return next;
18376
+ }
18377
+
18378
+ // src/pano-scene-menu.tsx
18379
+ var import_jsx_runtime41 = require("react/jsx-runtime");
18380
+ var SCENE_MENU_DRAG_THRESHOLD_PX = 6;
18381
+ var DEFAULT_PANO_SCENE_MENU_APPEARANCE = {
18382
+ background: "rgba(16, 16, 16, 0.72)",
18383
+ padding: "8px 10px",
18384
+ itemWidth: 128,
18385
+ itemHeight: 80,
18386
+ gap: 8,
18387
+ borderRadius: 8,
18388
+ selectionColor: "#ffffff",
18389
+ selectionWidth: 2,
18390
+ labelBackground: "rgba(0, 0, 0, 0.55)",
18391
+ labelColor: "#ffffff",
18392
+ labelAlign: "start",
18393
+ fontSize: 12,
18394
+ controlColor: "#ffffff",
18395
+ controlBackground: "rgba(255, 255, 255, 0.12)"
18396
+ };
18397
+ var DEFAULT_INSET = 12;
18398
+ function cssLength(value) {
18399
+ return typeof value === "number" ? `${value}px` : value;
18400
+ }
18401
+ function resolveAppearance2(appearance) {
18402
+ return {
18403
+ ...DEFAULT_PANO_SCENE_MENU_APPEARANCE,
18404
+ ...appearance
18405
+ };
18406
+ }
18407
+ function resolveInset(inset) {
18408
+ if (inset === void 0) {
18409
+ const value = cssLength(DEFAULT_INSET);
18410
+ return { top: value, right: value, bottom: value, left: value };
18411
+ }
18412
+ if (typeof inset === "number" || typeof inset === "string") {
18413
+ const value = cssLength(inset);
18414
+ return { top: value, right: value, bottom: value, left: value };
18415
+ }
18416
+ return {
18417
+ top: cssLength(inset.top ?? DEFAULT_INSET),
18418
+ right: cssLength(inset.right ?? DEFAULT_INSET),
18419
+ bottom: cssLength(inset.bottom ?? DEFAULT_INSET),
18420
+ left: cssLength(inset.left ?? DEFAULT_INSET)
18421
+ };
18422
+ }
18423
+ function itemLabel(item) {
18424
+ return item.title?.trim() || item.id;
18425
+ }
18426
+ function isHorizontal(placement) {
18427
+ return placement === "top" || placement === "bottom";
18428
+ }
18429
+ var CHEVRON_PATHS = {
18430
+ // Keep glyphs optically centered in the 16×16 viewBox.
18431
+ up: "M3 11l5-6 5 6",
18432
+ down: "M3 5l5 6 5-6",
18433
+ left: "M11 3 5 8l6 5",
18434
+ right: "M5 3l6 5-6 5"
18435
+ };
18436
+ function arrowChevron(direction, orientation2) {
18437
+ if (direction === "prev") {
18438
+ return orientation2 === "horizontal" ? "left" : "up";
18439
+ }
18440
+ return orientation2 === "horizontal" ? "right" : "down";
18441
+ }
18442
+ function toggleChevron(placement, expanded) {
18443
+ switch (placement) {
18444
+ case "bottom":
18445
+ return expanded ? "down" : "up";
18446
+ case "top":
18447
+ return expanded ? "up" : "down";
18448
+ case "left":
18449
+ return expanded ? "left" : "right";
18450
+ case "right":
18451
+ return expanded ? "right" : "left";
18452
+ default: {
18453
+ const exhaustive = placement;
18454
+ void exhaustive;
18455
+ return "down";
18456
+ }
18457
+ }
18458
+ }
18459
+ function dockInset(placement, inset) {
18460
+ switch (placement) {
18461
+ case "top":
18462
+ return inset.top;
18463
+ case "bottom":
18464
+ return inset.bottom;
18465
+ case "left":
18466
+ return inset.left;
18467
+ case "right":
18468
+ return inset.right;
18469
+ default: {
18470
+ const exhaustive = placement;
18471
+ void exhaustive;
18472
+ return inset.bottom;
18473
+ }
18474
+ }
18475
+ }
18476
+ function Chevron({ arrow }) {
18477
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
18478
+ "svg",
18479
+ {
18480
+ "aria-hidden": true,
18481
+ fill: "none",
18482
+ height: "16",
18483
+ stroke: "currentColor",
18484
+ strokeLinecap: "round",
18485
+ strokeLinejoin: "round",
18486
+ strokeWidth: "1.75",
18487
+ viewBox: "0 0 16 16",
18488
+ width: "16",
18489
+ children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("path", { d: CHEVRON_PATHS[arrow] })
18490
+ }
18491
+ );
18492
+ }
18493
+ function ControlButton({
18494
+ className,
18495
+ disabled,
18496
+ label,
18497
+ onClick,
18498
+ children
18499
+ }) {
18500
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
18501
+ "button",
18502
+ {
18503
+ "aria-label": label,
18504
+ className: cn("pano-scene-menu-control", className),
18505
+ disabled,
18506
+ onClick,
18507
+ onPointerDown: (event) => {
18508
+ event.stopPropagation();
18509
+ },
18510
+ title: label,
18511
+ type: "button",
18512
+ children
18513
+ }
18514
+ );
18515
+ }
18516
+ function horizontalAlignStyle(align, inset, widthValue) {
18517
+ const left = `calc(${inset.left} + var(--pano-safe-left))`;
18518
+ const right = `calc(${inset.right} + var(--pano-safe-right))`;
18519
+ if (!widthValue) {
18520
+ return { left, right, width: void 0, marginInline: void 0 };
18521
+ }
18522
+ switch (align) {
18523
+ case "start":
18524
+ return { left, right: "auto", width: widthValue, marginInline: void 0 };
18525
+ case "end":
18526
+ return { left: "auto", right, width: widthValue, marginInline: void 0 };
18527
+ case "center":
18528
+ return { left, right, width: widthValue, marginInline: "auto" };
18529
+ default: {
18530
+ const exhaustive = align;
18531
+ void exhaustive;
18532
+ return { left, right, width: widthValue, marginInline: "auto" };
18533
+ }
18534
+ }
18535
+ }
18536
+ function verticalAlignStyle(align, inset, heightValue) {
18537
+ const top = `calc(${inset.top} + var(--pano-safe-top))`;
18538
+ const bottom = `calc(${inset.bottom} + var(--pano-safe-bottom))`;
18539
+ if (!heightValue) {
18540
+ return { top, bottom, height: void 0, marginBlock: void 0 };
18541
+ }
18542
+ switch (align) {
18543
+ case "start":
18544
+ return { top, bottom: "auto", height: heightValue, marginBlock: void 0 };
18545
+ case "end":
18546
+ return { top: "auto", bottom, height: heightValue, marginBlock: void 0 };
18547
+ case "center":
18548
+ return { top, bottom, height: heightValue, marginBlock: "auto" };
18549
+ default: {
18550
+ const exhaustive = align;
18551
+ void exhaustive;
18552
+ return { top, bottom, height: heightValue, marginBlock: "auto" };
18553
+ }
18554
+ }
18555
+ }
18556
+ function placementBoxStyle(placement, inset, width, height, align) {
18557
+ const widthValue = width === void 0 ? void 0 : cssLength(width);
18558
+ const heightValue = height === void 0 ? void 0 : cssLength(height);
18559
+ const maxWidth = `calc(100% - ${inset.left} - ${inset.right} - var(--pano-safe-left) - var(--pano-safe-right))`;
18560
+ const maxHeight = `calc(100% - ${inset.top} - ${inset.bottom} - var(--pano-safe-top) - var(--pano-safe-bottom))`;
18561
+ switch (placement) {
18562
+ case "top":
18563
+ return {
18564
+ ...horizontalAlignStyle(align, inset, widthValue),
18565
+ top: `calc(${inset.top} + var(--pano-safe-top))`,
18566
+ bottom: "auto",
18567
+ maxWidth,
18568
+ height: heightValue,
18569
+ maxHeight
18570
+ };
18571
+ case "bottom":
18572
+ return {
18573
+ ...horizontalAlignStyle(align, inset, widthValue),
18574
+ top: "auto",
18575
+ bottom: `calc(${inset.bottom} + var(--pano-safe-bottom))`,
18576
+ maxWidth,
18577
+ height: heightValue,
18578
+ maxHeight
18579
+ };
18580
+ case "left":
18581
+ return {
18582
+ ...verticalAlignStyle(align, inset, heightValue),
18583
+ right: "auto",
18584
+ left: `calc(${inset.left} + var(--pano-safe-left))`,
18585
+ width: widthValue,
18586
+ maxWidth,
18587
+ maxHeight
18588
+ };
18589
+ case "right":
18590
+ return {
18591
+ ...verticalAlignStyle(align, inset, heightValue),
18592
+ right: `calc(${inset.right} + var(--pano-safe-right))`,
18593
+ left: "auto",
18594
+ width: widthValue,
18595
+ maxWidth,
18596
+ maxHeight
18597
+ };
18598
+ default: {
18599
+ const exhaustive = placement;
18600
+ void exhaustive;
18601
+ return {};
18602
+ }
18603
+ }
18604
+ }
18605
+ function PanoSceneMenu({
18606
+ items,
18607
+ activeId = null,
18608
+ onSelect,
18609
+ placement = "bottom",
18610
+ align = "center",
18611
+ inset,
18612
+ width,
18613
+ height,
18614
+ collapsible = false,
18615
+ defaultExpanded = true,
18616
+ expanded: expandedProp,
18617
+ onExpandedChange,
18618
+ showArrows = false,
18619
+ loop = false,
18620
+ dragMotion: dragMotionProp,
18621
+ appearance,
18622
+ className,
18623
+ style,
18624
+ label = "Scenes",
18625
+ previousLabel = "Previous scene",
18626
+ nextLabel = "Next scene",
18627
+ expandLabel = "Show scenes",
18628
+ collapseLabel = "Hide scenes"
18629
+ }) {
18630
+ const listId = (0, import_react62.useId)();
18631
+ const listRef = (0, import_react62.useRef)(null);
18632
+ const trackRef = (0, import_react62.useRef)(null);
18633
+ const itemRefs = (0, import_react62.useRef)(/* @__PURE__ */ new Map());
18634
+ const resolved = resolveAppearance2(appearance);
18635
+ const resolvedInset = resolveInset(inset);
18636
+ const horizontal = isHorizontal(placement);
18637
+ const customDrag = dragMotionProp !== false;
18638
+ const dragMotion = resolveDragMotion(
18639
+ dragMotionProp === false ? false : dragMotionProp
18640
+ );
18641
+ const isExpandedControlled = expandedProp !== void 0;
18642
+ const [internalExpanded, setInternalExpanded] = (0, import_react62.useState)(defaultExpanded);
18643
+ const expanded = collapsible ? isExpandedControlled ? expandedProp : internalExpanded : true;
18644
+ const [offset, setOffset] = (0, import_react62.useState)(0);
18645
+ const [viewportSize, setViewportSize] = (0, import_react62.useState)(0);
18646
+ const [contentSize, setContentSize] = (0, import_react62.useState)(0);
18647
+ const offsetRef = (0, import_react62.useRef)(0);
18648
+ const dragRef = (0, import_react62.useRef)(null);
18649
+ const animRef = (0, import_react62.useRef)(null);
18650
+ const maxOffset = Math.max(0, contentSize - viewportSize);
18651
+ offsetRef.current = offset;
18652
+ const activeIndex = (0, import_react62.useMemo)(() => {
18653
+ if (activeId == null) {
18654
+ return -1;
18655
+ }
18656
+ return items.findIndex((item) => item.id === activeId);
18657
+ }, [activeId, items]);
18658
+ const cancelScrollAnimation = (0, import_react62.useCallback)(() => {
18659
+ const anim = animRef.current;
18660
+ if (anim?.raf != null) {
18661
+ cancelAnimationFrame(anim.raf);
18662
+ }
18663
+ animRef.current = null;
18664
+ }, []);
18665
+ const clampOffset = (0, import_react62.useCallback)(
18666
+ (value) => Math.min(maxOffset, Math.max(0, value)),
18667
+ [maxOffset]
18668
+ );
18669
+ const startScrollAnimation = (0, import_react62.useCallback)(
18670
+ (velocity) => {
18671
+ cancelScrollAnimation();
18672
+ const current = offsetRef.current;
18673
+ const needsSpring = dragMotion.bounce && (current < 0 || current > maxOffset);
18674
+ const nextVelocity = dragMotion.inertia ? velocity : 0;
18675
+ if (!needsSpring && Math.abs(nextVelocity) < 1) {
18676
+ setOffset(clampOffset(current));
18677
+ return;
18678
+ }
18679
+ if (!dragMotion.inertia && !dragMotion.bounce) {
18680
+ setOffset(clampOffset(current));
18681
+ return;
18682
+ }
18683
+ const anim = {
18684
+ raf: null,
18685
+ lastTime: performance.now(),
18686
+ position: current,
18687
+ velocity: nextVelocity
18688
+ };
18689
+ animRef.current = anim;
18690
+ const tick = (now) => {
18691
+ if (animRef.current !== anim) return;
18692
+ const dt = Math.min(0.064, Math.max(0, (now - anim.lastTime) / 1e3));
18693
+ anim.lastTime = now;
18694
+ const next = integrateAxis(
18695
+ { position: anim.position, velocity: anim.velocity },
18696
+ dt,
18697
+ {
18698
+ min: 0,
18699
+ max: maxOffset,
18700
+ inertia: dragMotion.inertia,
18701
+ bounce: dragMotion.bounce
18702
+ }
18703
+ );
18704
+ anim.position = next.position;
18705
+ anim.velocity = next.velocity;
18706
+ setOffset(next.position);
18707
+ if (next.settled) {
18708
+ setOffset(clampOffset(next.position));
18709
+ animRef.current = null;
18710
+ return;
18711
+ }
18712
+ anim.raf = requestAnimationFrame(tick);
18713
+ };
18714
+ anim.raf = requestAnimationFrame(tick);
18715
+ },
18716
+ [
18717
+ cancelScrollAnimation,
18718
+ clampOffset,
18719
+ dragMotion.bounce,
18720
+ dragMotion.inertia,
18721
+ maxOffset
18722
+ ]
18723
+ );
18724
+ (0, import_react62.useEffect)(() => () => cancelScrollAnimation(), [cancelScrollAnimation]);
18725
+ (0, import_react62.useLayoutEffect)(() => {
18726
+ if (!customDrag || !expanded) return;
18727
+ const list = listRef.current;
18728
+ const track = trackRef.current;
18729
+ if (!list || !track) return;
18730
+ const measure = () => {
18731
+ const nextViewport = horizontal ? list.clientWidth : list.clientHeight;
18732
+ const nextContent = horizontal ? track.scrollWidth : track.scrollHeight;
18733
+ setViewportSize(
18734
+ (previous) => previous === nextViewport ? previous : nextViewport
18735
+ );
18736
+ setContentSize(
18737
+ (previous) => previous === nextContent ? previous : nextContent
18738
+ );
18739
+ };
18740
+ measure();
18741
+ const observer = new ResizeObserver(measure);
18742
+ observer.observe(list);
18743
+ observer.observe(track);
18744
+ return () => observer.disconnect();
18745
+ }, [customDrag, expanded, horizontal, items]);
18746
+ (0, import_react62.useEffect)(() => {
18747
+ if (!customDrag) return;
18748
+ if (dragRef.current || animRef.current) return;
18749
+ setOffset((previous) => Math.min(maxOffset, Math.max(0, previous)));
18750
+ }, [customDrag, maxOffset]);
18751
+ (0, import_react62.useEffect)(() => {
18752
+ if (!expanded || activeId == null) {
18753
+ return;
18754
+ }
18755
+ const node = itemRefs.current.get(activeId);
18756
+ if (!node) return;
18757
+ if (!customDrag) {
18758
+ node.scrollIntoView({
18759
+ behavior: "smooth",
18760
+ block: "nearest",
18761
+ inline: "nearest"
18762
+ });
18763
+ return;
18764
+ }
18765
+ const list = listRef.current;
18766
+ if (!list) return;
18767
+ const listRect = list.getBoundingClientRect();
18768
+ const itemRect = node.getBoundingClientRect();
18769
+ if (horizontal) {
18770
+ const itemStart2 = itemRect.left - listRect.left + offsetRef.current;
18771
+ const itemEnd2 = itemStart2 + itemRect.width;
18772
+ let next2 = offsetRef.current;
18773
+ if (itemStart2 < next2) next2 = itemStart2;
18774
+ else if (itemEnd2 > next2 + listRect.width) next2 = itemEnd2 - listRect.width;
18775
+ cancelScrollAnimation();
18776
+ setOffset(clampOffset(next2));
18777
+ return;
18778
+ }
18779
+ const itemStart = itemRect.top - listRect.top + offsetRef.current;
18780
+ const itemEnd = itemStart + itemRect.height;
18781
+ let next = offsetRef.current;
18782
+ if (itemStart < next) next = itemStart;
18783
+ else if (itemEnd > next + listRect.height) next = itemEnd - listRect.height;
18784
+ cancelScrollAnimation();
18785
+ setOffset(clampOffset(next));
18786
+ }, [
18787
+ activeId,
18788
+ cancelScrollAnimation,
18789
+ clampOffset,
18790
+ customDrag,
18791
+ expanded,
18792
+ horizontal
18793
+ ]);
18794
+ const rootStyle = (0, import_react62.useMemo)(() => {
18795
+ const vars = {
18796
+ "--pano-scene-menu-background": resolved.background,
18797
+ "--pano-scene-menu-padding": cssLength(resolved.padding),
18798
+ "--pano-scene-menu-item-width": cssLength(resolved.itemWidth),
18799
+ "--pano-scene-menu-item-height": cssLength(resolved.itemHeight),
18800
+ "--pano-scene-menu-gap": cssLength(resolved.gap),
18801
+ "--pano-scene-menu-radius": cssLength(resolved.borderRadius),
18802
+ "--pano-scene-menu-selection-color": resolved.selectionColor,
18803
+ "--pano-scene-menu-selection-width": cssLength(resolved.selectionWidth),
18804
+ "--pano-scene-menu-label-background": resolved.labelBackground,
18805
+ "--pano-scene-menu-label-color": resolved.labelColor,
18806
+ "--pano-scene-menu-label-align": resolved.labelAlign,
18807
+ "--pano-scene-menu-font-size": cssLength(resolved.fontSize),
18808
+ "--pano-scene-menu-control-color": resolved.controlColor,
18809
+ "--pano-scene-menu-control-background": resolved.controlBackground,
18810
+ "--pano-scene-menu-dock-inset": dockInset(placement, resolvedInset)
18811
+ };
18812
+ return {
18813
+ ...vars,
18814
+ ...placementBoxStyle(placement, resolvedInset, width, height, align),
18815
+ ...style
18816
+ };
18817
+ }, [align, height, placement, resolved, resolvedInset, style, width]);
18818
+ const setExpanded = (next) => {
18819
+ if (!collapsible) {
18820
+ return;
18821
+ }
18822
+ if (!isExpandedControlled) {
18823
+ setInternalExpanded(next);
18824
+ }
18825
+ onExpandedChange?.(next);
18826
+ };
18827
+ const selectAt = (index) => {
18828
+ const item = items[index];
18829
+ if (!item) {
18830
+ return;
18831
+ }
18832
+ onSelect?.(item.id);
18833
+ itemRefs.current.get(item.id)?.focus({ preventScroll: true });
18834
+ };
18835
+ const selectRelative = (delta) => {
18836
+ if (items.length === 0) {
18837
+ return;
18838
+ }
18839
+ const current = activeIndex >= 0 ? activeIndex : 0;
18840
+ if (loop) {
18841
+ const next = ((current + delta) % items.length + items.length) % items.length;
18842
+ selectAt(next);
18843
+ return;
18844
+ }
18845
+ selectAt(Math.max(0, Math.min(items.length - 1, current + delta)));
18846
+ };
18847
+ const onKeyDown = (event) => {
18848
+ if (items.length === 0 || !expanded) {
18849
+ return;
18850
+ }
18851
+ const current = activeIndex >= 0 ? activeIndex : Math.max(
18852
+ 0,
18853
+ items.findIndex(
18854
+ (item) => itemRefs.current.get(item.id) === event.target
18855
+ )
18856
+ );
18857
+ const backward = horizontal ? event.key === "ArrowLeft" : event.key === "ArrowUp";
18858
+ const forward = horizontal ? event.key === "ArrowRight" : event.key === "ArrowDown";
18859
+ if (backward) {
18860
+ event.preventDefault();
18861
+ if (loop) {
18862
+ selectAt((current - 1 + items.length) % items.length);
18863
+ } else {
18864
+ selectAt(Math.max(0, current - 1));
18865
+ }
18866
+ return;
18867
+ }
18868
+ if (forward) {
18869
+ event.preventDefault();
18870
+ if (loop) {
18871
+ selectAt((current + 1) % items.length);
18872
+ } else {
18873
+ selectAt(Math.min(items.length - 1, current + 1));
18874
+ }
18875
+ return;
18876
+ }
18877
+ switch (event.key) {
18878
+ case "Home": {
18879
+ event.preventDefault();
18880
+ selectAt(0);
18881
+ break;
18882
+ }
18883
+ case "End": {
18884
+ event.preventDefault();
18885
+ selectAt(items.length - 1);
18886
+ break;
18887
+ }
18888
+ default:
18889
+ break;
18890
+ }
18891
+ };
18892
+ const onWheel = (event) => {
18893
+ const list = listRef.current;
18894
+ if (!list) {
18895
+ return;
18896
+ }
18897
+ if (!customDrag) {
18898
+ if (horizontal) {
18899
+ const canScroll2 = list.scrollWidth > list.clientWidth + 1;
18900
+ if (!canScroll2 || Math.abs(event.deltaY) <= Math.abs(event.deltaX)) {
18901
+ return;
18902
+ }
18903
+ event.preventDefault();
18904
+ event.stopPropagation();
18905
+ list.scrollLeft += event.deltaY;
18906
+ return;
18907
+ }
18908
+ const canScroll = list.scrollHeight > list.clientHeight + 1;
18909
+ if (!canScroll || Math.abs(event.deltaX) <= Math.abs(event.deltaY)) {
18910
+ return;
18911
+ }
18912
+ event.preventDefault();
18913
+ event.stopPropagation();
18914
+ list.scrollTop += event.deltaX;
18915
+ return;
18916
+ }
18917
+ if (horizontal) {
18918
+ if (maxOffset <= 0 || Math.abs(event.deltaY) <= Math.abs(event.deltaX)) {
18919
+ return;
18920
+ }
18921
+ event.preventDefault();
18922
+ event.stopPropagation();
18923
+ cancelScrollAnimation();
18924
+ setOffset((previous) => clampOffset(previous + event.deltaY));
18925
+ return;
18926
+ }
18927
+ if (maxOffset <= 0 || Math.abs(event.deltaX) <= Math.abs(event.deltaY)) {
18928
+ return;
18929
+ }
18930
+ event.preventDefault();
18931
+ event.stopPropagation();
18932
+ cancelScrollAnimation();
18933
+ setOffset((previous) => clampOffset(previous + event.deltaX));
18934
+ };
18935
+ const axisPoint = (event) => horizontal ? event.clientX : event.clientY;
18936
+ const endSuppressClickRef = (0, import_react62.useRef)(false);
18937
+ const beginListDrag = (event, captureTarget) => {
18938
+ if (!customDrag || event.button !== 0) return;
18939
+ cancelScrollAnimation();
18940
+ const now = performance.now();
18941
+ const origin = axisPoint(event);
18942
+ dragRef.current = {
18943
+ pointerId: event.pointerId,
18944
+ origin,
18945
+ startOffset: offsetRef.current,
18946
+ samples: [{ t: now, position: offsetRef.current }],
18947
+ moved: false,
18948
+ suppressClick: false
18949
+ };
18950
+ try {
18951
+ captureTarget.setPointerCapture(event.pointerId);
18952
+ } catch {
18953
+ }
18954
+ };
18955
+ const onListPointerMove = (event) => {
18956
+ const drag = dragRef.current;
18957
+ if (!drag || drag.pointerId !== event.pointerId) return;
18958
+ const point = axisPoint(event);
18959
+ const delta = point - drag.origin;
18960
+ if (!drag.moved && Math.abs(delta) >= SCENE_MENU_DRAG_THRESHOLD_PX) {
18961
+ drag.moved = true;
18962
+ drag.suppressClick = true;
18963
+ }
18964
+ if (!drag.moved) return;
18965
+ const raw = drag.startOffset - delta;
18966
+ const now = performance.now();
18967
+ drag.samples = pushSample(drag.samples, { t: now, position: raw });
18968
+ setOffset(
18969
+ constrainDragPosition(
18970
+ raw,
18971
+ 0,
18972
+ maxOffset,
18973
+ Math.max(1, viewportSize),
18974
+ dragMotion.bounce
18975
+ )
18976
+ );
18977
+ event.preventDefault();
18978
+ };
18979
+ const finishPointerWithClickGuard = (event) => {
18980
+ const drag = dragRef.current;
18981
+ if (!drag || drag.pointerId !== event.pointerId) return;
18982
+ const suppress = drag.suppressClick;
18983
+ dragRef.current = null;
18984
+ try {
18985
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
18986
+ event.currentTarget.releasePointerCapture(event.pointerId);
18987
+ }
18988
+ } catch {
18989
+ }
18990
+ if (suppress) {
18991
+ endSuppressClickRef.current = true;
18992
+ queueMicrotask(() => {
18993
+ endSuppressClickRef.current = false;
18994
+ });
18995
+ }
18996
+ if (!drag.moved) return;
18997
+ if (event.type === "pointercancel") {
18998
+ setOffset(clampOffset(offsetRef.current));
18999
+ return;
19000
+ }
19001
+ startScrollAnimation(velocityFromSamples(drag.samples));
19002
+ };
19003
+ const onItemClick = (event, id) => {
19004
+ if (endSuppressClickRef.current) {
19005
+ event.preventDefault();
19006
+ event.stopPropagation();
19007
+ return;
19008
+ }
19009
+ onSelect?.(id);
19010
+ };
19011
+ const canStep = items.length > 1;
19012
+ const canGoPrevious = canStep && (loop || activeIndex !== 0);
19013
+ const canGoNext = canStep && (loop || activeIndex !== items.length - 1);
19014
+ const orientation2 = horizontal ? "horizontal" : "vertical";
19015
+ const trackStyle = customDrag ? {
19016
+ transform: horizontal ? `translate3d(${-offset}px, 0, 0)` : `translate3d(0, ${-offset}px, 0)`,
19017
+ willChange: "transform"
19018
+ } : void 0;
19019
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
19020
+ "div",
19021
+ {
19022
+ "aria-label": label,
19023
+ className: cn("pano-scene-menu", className),
19024
+ "data-align": align,
19025
+ "data-collapsible": collapsible ? "true" : void 0,
19026
+ "data-drag-motion": customDrag ? "custom" : "native",
19027
+ "data-expanded": expanded ? "true" : "false",
19028
+ "data-orientation": orientation2,
19029
+ "data-placement": placement,
19030
+ role: "group",
19031
+ style: rootStyle,
19032
+ children: [
19033
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
19034
+ "div",
19035
+ {
19036
+ "aria-hidden": expanded ? void 0 : true,
19037
+ className: "pano-scene-menu-body",
19038
+ ...!expanded ? { inert: true } : null,
19039
+ children: [
19040
+ showArrows ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
19041
+ ControlButton,
19042
+ {
19043
+ className: "pano-scene-menu-arrow",
19044
+ disabled: !canGoPrevious || !expanded,
19045
+ label: previousLabel,
19046
+ onClick: () => selectRelative(-1),
19047
+ children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Chevron, { arrow: arrowChevron("prev", orientation2) })
19048
+ }
19049
+ ) : null,
19050
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
19051
+ "div",
19052
+ {
19053
+ "aria-label": label,
19054
+ className: "pano-scene-menu-list",
19055
+ id: listId,
19056
+ onKeyDown,
19057
+ onPointerCancel: customDrag ? finishPointerWithClickGuard : void 0,
19058
+ onPointerDown: customDrag ? (event) => beginListDrag(event, event.currentTarget) : void 0,
19059
+ onPointerMove: customDrag ? onListPointerMove : void 0,
19060
+ onPointerUp: customDrag ? finishPointerWithClickGuard : void 0,
19061
+ onWheel,
19062
+ ref: listRef,
19063
+ role: "radiogroup",
19064
+ children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
19065
+ "div",
19066
+ {
19067
+ className: "pano-scene-menu-track",
19068
+ ref: trackRef,
19069
+ style: trackStyle,
19070
+ children: items.map((item) => {
19071
+ const name = itemLabel(item);
19072
+ const selected = item.id === activeId;
19073
+ return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
19074
+ "button",
19075
+ {
19076
+ "aria-checked": selected,
19077
+ className: "pano-scene-menu-item",
19078
+ "data-active": selected ? "true" : void 0,
19079
+ onClick: (event) => onItemClick(event, item.id),
19080
+ onPointerDown: (event) => {
19081
+ event.stopPropagation();
19082
+ const list = listRef.current;
19083
+ if (customDrag && list) {
19084
+ beginListDrag(event, list);
19085
+ }
19086
+ },
19087
+ ref: (node) => {
19088
+ if (node) {
19089
+ itemRefs.current.set(item.id, node);
19090
+ } else {
19091
+ itemRefs.current.delete(item.id);
19092
+ }
19093
+ },
19094
+ role: "radio",
19095
+ tabIndex: selected || activeId == null && item === items[0] ? 0 : -1,
19096
+ title: name,
19097
+ type: "button",
19098
+ children: /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)("span", { className: "pano-scene-menu-thumb", children: [
19099
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
19100
+ "img",
19101
+ {
19102
+ alt: "",
19103
+ decoding: "async",
19104
+ draggable: false,
19105
+ src: item.thumbnailUrl
19106
+ }
19107
+ ),
19108
+ /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("span", { className: "pano-scene-menu-label", children: name })
19109
+ ] })
19110
+ },
19111
+ item.id
19112
+ );
19113
+ })
19114
+ }
19115
+ )
19116
+ }
19117
+ ),
19118
+ showArrows ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
19119
+ ControlButton,
19120
+ {
19121
+ className: "pano-scene-menu-arrow",
19122
+ disabled: !canGoNext || !expanded,
19123
+ label: nextLabel,
19124
+ onClick: () => selectRelative(1),
19125
+ children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Chevron, { arrow: arrowChevron("next", orientation2) })
19126
+ }
19127
+ ) : null
19128
+ ]
19129
+ }
19130
+ ),
19131
+ collapsible ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
19132
+ ControlButton,
19133
+ {
19134
+ className: "pano-scene-menu-toggle",
19135
+ label: expanded ? collapseLabel : expandLabel,
19136
+ onClick: () => setExpanded(!expanded),
19137
+ children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Chevron, { arrow: toggleChevron(placement, expanded) })
19138
+ }
19139
+ ) : null
19140
+ ]
19141
+ }
19142
+ );
19143
+ }
19144
+
19145
+ // src/use-pano-viewer.ts
19146
+ var import_react63 = require("react");
18245
19147
  function createPanoViewerHandle(ref) {
18246
19148
  return {
18247
19149
  getView: () => ref.current?.getView() ?? { yaw: 0, pitch: 0, fov: 75 },
@@ -18323,16 +19225,16 @@ function createPanoViewerHandle(ref) {
18323
19225
  };
18324
19226
  }
18325
19227
  function usePanoViewer() {
18326
- const ref = (0, import_react62.useRef)(null);
18327
- const handle = (0, import_react62.useMemo)(() => createPanoViewerHandle(ref), []);
18328
- return (0, import_react62.useMemo)(() => ({ ref, ...handle }), [handle]);
19228
+ const ref = (0, import_react63.useRef)(null);
19229
+ const handle = (0, import_react63.useMemo)(() => createPanoViewerHandle(ref), []);
19230
+ return (0, import_react63.useMemo)(() => ({ ref, ...handle }), [handle]);
18329
19231
  }
18330
19232
 
18331
19233
  // src/video/pano-video.tsx
18332
19234
  var import_fiber31 = require("@react-three/fiber");
18333
- var import_react63 = require("react");
19235
+ var import_react64 = require("react");
18334
19236
  var import_three40 = require("three");
18335
- var import_jsx_runtime41 = require("react/jsx-runtime");
19237
+ var import_jsx_runtime42 = require("react/jsx-runtime");
18336
19238
  var EMPTY_TRACKS = [];
18337
19239
  var EMPTY_RATES = DEFAULT_PANO_VIDEO_PLAYBACK_RATES;
18338
19240
  function createEmptySnapshot() {
@@ -18417,10 +19319,10 @@ function applyEquirectangularLayout(texture, anisotropy) {
18417
19319
  }
18418
19320
  function usePosterTexture2(poster, onError) {
18419
19321
  const gl = (0, import_fiber31.useThree)((state) => state.gl);
18420
- const [texture, setTexture] = (0, import_react63.useState)(null);
18421
- const onErrorRef = (0, import_react63.useRef)(onError);
19322
+ const [texture, setTexture] = (0, import_react64.useState)(null);
19323
+ const onErrorRef = (0, import_react64.useRef)(onError);
18422
19324
  onErrorRef.current = onError;
18423
- (0, import_react63.useEffect)(() => {
19325
+ (0, import_react64.useEffect)(() => {
18424
19326
  let active = true;
18425
19327
  setTexture(null);
18426
19328
  if (!poster) {
@@ -18457,7 +19359,7 @@ function usePosterTexture2(poster, onError) {
18457
19359
  active = false;
18458
19360
  };
18459
19361
  }, [gl, poster]);
18460
- (0, import_react63.useEffect)(
19362
+ (0, import_react64.useEffect)(
18461
19363
  () => () => {
18462
19364
  texture?.dispose();
18463
19365
  },
@@ -18516,37 +19418,37 @@ function PanoVideo({
18516
19418
  }) {
18517
19419
  usePanoMediaActivationIntent(autoPlay);
18518
19420
  const gl = (0, import_fiber31.useThree)((state) => state.gl);
18519
- const host = (0, import_react63.useContext)(PanoVideoHostContext);
18520
- const variants = (0, import_react63.useMemo)(
19421
+ const host = (0, import_react64.useContext)(PanoVideoHostContext);
19422
+ const variants = (0, import_react64.useMemo)(
18521
19423
  () => resolvePanoVideoVariants({ poster, src, variants: variantsProp }),
18522
19424
  [poster, src, variantsProp]
18523
19425
  );
18524
19426
  const tracks = tracksProp ?? EMPTY_TRACKS;
18525
19427
  const captionsEnabled = captions !== false;
18526
- const captionAppearance = (0, import_react63.useMemo)(
19428
+ const captionAppearance = (0, import_react64.useMemo)(
18527
19429
  () => resolvePanoVideoCaptionAppearance(
18528
19430
  typeof captions === "object" ? captions : void 0
18529
19431
  ),
18530
19432
  [captions]
18531
19433
  );
18532
- const [variantId, setVariantId] = (0, import_react63.useState)(
19434
+ const [variantId, setVariantId] = (0, import_react64.useState)(
18533
19435
  () => resolvePanoVideoVariantId(variants, defaultVariantId)
18534
19436
  );
18535
- const [trackId, setTrackId] = (0, import_react63.useState)(
19437
+ const [trackId, setTrackId] = (0, import_react64.useState)(
18536
19438
  () => captionsEnabled ? defaultPanoVideoTrackId(tracks) : null
18537
19439
  );
18538
- const [ready, setReady] = (0, import_react63.useState)(false);
18539
- const autoPlayRef = (0, import_react63.useRef)(autoPlay);
19440
+ const [ready, setReady] = (0, import_react64.useState)(false);
19441
+ const autoPlayRef = (0, import_react64.useRef)(autoPlay);
18540
19442
  autoPlayRef.current = autoPlay;
18541
- const blockedRef = (0, import_react63.useRef)(false);
18542
- const captionAppearanceOverrideRef = (0, import_react63.useRef)(
19443
+ const blockedRef = (0, import_react64.useRef)(false);
19444
+ const captionAppearanceOverrideRef = (0, import_react64.useRef)(
18543
19445
  {}
18544
19446
  );
18545
- const mediaCapabilitiesRef = (0, import_react63.useRef)({
19447
+ const mediaCapabilitiesRef = (0, import_react64.useRef)({
18546
19448
  volumeAdjustable: true,
18547
19449
  playbackRates: [...DEFAULT_PANO_VIDEO_PLAYBACK_RATES]
18548
19450
  });
18549
- const storeRef = (0, import_react63.useRef)(null);
19451
+ const storeRef = (0, import_react64.useRef)(null);
18550
19452
  if (!storeRef.current) {
18551
19453
  storeRef.current = {
18552
19454
  snapshot: {
@@ -18556,7 +19458,7 @@ function PanoVideo({
18556
19458
  listeners: /* @__PURE__ */ new Set()
18557
19459
  };
18558
19460
  }
18559
- const actionsRef = (0, import_react63.useRef)({
19461
+ const actionsRef = (0, import_react64.useRef)({
18560
19462
  play: async () => {
18561
19463
  },
18562
19464
  pause: () => {
@@ -18576,19 +19478,19 @@ function PanoVideo({
18576
19478
  setCaptionAppearance: (_appearance) => {
18577
19479
  }
18578
19480
  });
18579
- const onLoadRef = (0, import_react63.useRef)(onLoad);
18580
- const onErrorRef = (0, import_react63.useRef)(onError);
18581
- const onPlaybackStateChangeRef = (0, import_react63.useRef)(onPlaybackStateChange);
18582
- const onEndedRef = (0, import_react63.useRef)(onEnded);
18583
- const onVariantChangeRef = (0, import_react63.useRef)(onVariantChange);
18584
- const onTrackChangeRef = (0, import_react63.useRef)(onTrackChange);
19481
+ const onLoadRef = (0, import_react64.useRef)(onLoad);
19482
+ const onErrorRef = (0, import_react64.useRef)(onError);
19483
+ const onPlaybackStateChangeRef = (0, import_react64.useRef)(onPlaybackStateChange);
19484
+ const onEndedRef = (0, import_react64.useRef)(onEnded);
19485
+ const onVariantChangeRef = (0, import_react64.useRef)(onVariantChange);
19486
+ const onTrackChangeRef = (0, import_react64.useRef)(onTrackChange);
18585
19487
  onLoadRef.current = onLoad;
18586
19488
  onErrorRef.current = onError;
18587
19489
  onPlaybackStateChangeRef.current = onPlaybackStateChange;
18588
19490
  onEndedRef.current = onEnded;
18589
19491
  onVariantChangeRef.current = onVariantChange;
18590
19492
  onTrackChangeRef.current = onTrackChange;
18591
- const resource = (0, import_react63.useMemo)(() => {
19493
+ const resource = (0, import_react64.useMemo)(() => {
18592
19494
  if (typeof document === "undefined") {
18593
19495
  return null;
18594
19496
  }
@@ -18603,7 +19505,7 @@ function PanoVideo({
18603
19505
  texture.generateMipmaps = false;
18604
19506
  return { video, texture };
18605
19507
  }, []);
18606
- (0, import_react63.useEffect)(
19508
+ (0, import_react64.useEffect)(
18607
19509
  () => () => {
18608
19510
  if (!resource) {
18609
19511
  return;
@@ -18618,13 +19520,13 @@ function PanoVideo({
18618
19520
  },
18619
19521
  [resource]
18620
19522
  );
18621
- (0, import_react63.useEffect)(() => {
19523
+ (0, import_react64.useEffect)(() => {
18622
19524
  if (!resource) {
18623
19525
  return;
18624
19526
  }
18625
19527
  resource.texture.anisotropy = gl.capabilities.getMaxAnisotropy();
18626
19528
  }, [gl, resource]);
18627
- (0, import_react63.useLayoutEffect)(() => {
19529
+ (0, import_react64.useLayoutEffect)(() => {
18628
19530
  if (!resource) {
18629
19531
  return;
18630
19532
  }
@@ -18640,7 +19542,7 @@ function PanoVideo({
18640
19542
  activeVariant?.poster ?? poster,
18641
19543
  onError
18642
19544
  );
18643
- const emitSnapshot = (0, import_react63.useCallback)(
19545
+ const emitSnapshot = (0, import_react64.useCallback)(
18644
19546
  (patch) => {
18645
19547
  const store = storeRef.current;
18646
19548
  const video = resource?.video;
@@ -18692,7 +19594,7 @@ function PanoVideo({
18692
19594
  captionAppearance
18693
19595
  ]
18694
19596
  );
18695
- const controller = (0, import_react63.useMemo)(
19597
+ const controller = (0, import_react64.useMemo)(
18696
19598
  () => ({
18697
19599
  subscribe: (onStoreChange) => {
18698
19600
  const store = storeRef.current;
@@ -18744,7 +19646,7 @@ function PanoVideo({
18744
19646
  }),
18745
19647
  []
18746
19648
  );
18747
- (0, import_react63.useLayoutEffect)(() => {
19649
+ (0, import_react64.useLayoutEffect)(() => {
18748
19650
  if (!host || variants.length === 0) {
18749
19651
  return;
18750
19652
  }
@@ -18757,7 +19659,7 @@ function PanoVideo({
18757
19659
  notifyPanoVideoHost(host);
18758
19660
  };
18759
19661
  }, [captionsEnabled, controller, controls, host, variants.length]);
18760
- (0, import_react63.useLayoutEffect)(() => {
19662
+ (0, import_react64.useLayoutEffect)(() => {
18761
19663
  if (!resource) {
18762
19664
  return;
18763
19665
  }
@@ -18770,10 +19672,10 @@ function PanoVideo({
18770
19672
  };
18771
19673
  emitSnapshot();
18772
19674
  }, [emitSnapshot, playbackRates, resource]);
18773
- (0, import_react63.useEffect)(() => {
19675
+ (0, import_react64.useEffect)(() => {
18774
19676
  emitSnapshot();
18775
19677
  }, [emitSnapshot]);
18776
- (0, import_react63.useEffect)(() => {
19678
+ (0, import_react64.useEffect)(() => {
18777
19679
  if (!resource || !activeVariant || !mediaKey) {
18778
19680
  return;
18779
19681
  }
@@ -18830,7 +19732,7 @@ function PanoVideo({
18830
19732
  video.removeEventListener("error", handleError);
18831
19733
  };
18832
19734
  }, [mediaKey, resource]);
18833
- (0, import_react63.useEffect)(() => {
19735
+ (0, import_react64.useEffect)(() => {
18834
19736
  if (!resource) {
18835
19737
  return;
18836
19738
  }
@@ -18922,7 +19824,7 @@ function PanoVideo({
18922
19824
  }
18923
19825
  };
18924
19826
  }, [emitSnapshot, resource]);
18925
- (0, import_react63.useLayoutEffect)(() => {
19827
+ (0, import_react64.useLayoutEffect)(() => {
18926
19828
  if (!resource) {
18927
19829
  return;
18928
19830
  }
@@ -18935,7 +19837,7 @@ function PanoVideo({
18935
19837
  applyInlineVideoPlayback(video, playsInline);
18936
19838
  video.preload = preload;
18937
19839
  }, [loop, muted, playsInline, preload, resource, volume]);
18938
- (0, import_react63.useEffect)(() => {
19840
+ (0, import_react64.useEffect)(() => {
18939
19841
  if (!resource) {
18940
19842
  return;
18941
19843
  }
@@ -18985,7 +19887,7 @@ function PanoVideo({
18985
19887
  video.removeEventListener("ratechange", handleVolume);
18986
19888
  };
18987
19889
  }, [emitSnapshot, resource]);
18988
- (0, import_react63.useEffect)(() => {
19890
+ (0, import_react64.useEffect)(() => {
18989
19891
  if (!resource) {
18990
19892
  return;
18991
19893
  }
@@ -19020,7 +19922,7 @@ function PanoVideo({
19020
19922
  video.textTracks.removeEventListener("change", applyModes);
19021
19923
  };
19022
19924
  }, [emitSnapshot, resource, trackId, tracks]);
19023
- (0, import_react63.useEffect)(() => {
19925
+ (0, import_react64.useEffect)(() => {
19024
19926
  if (!captionsEnabled) {
19025
19927
  setTrackId(null);
19026
19928
  return;
@@ -19031,15 +19933,15 @@ function PanoVideo({
19031
19933
  if (variants.length === 0) {
19032
19934
  return null;
19033
19935
  }
19034
- return /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(import_jsx_runtime41.Fragment, { children: [
19035
- activeTexture ? /* @__PURE__ */ (0, import_jsx_runtime41.jsxs)(
19936
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
19937
+ activeTexture ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
19036
19938
  "mesh",
19037
19939
  {
19038
19940
  rotation: [0, import_three40.MathUtils.degToRad(90 + yawOffset2), 0],
19039
19941
  visible,
19040
19942
  children: [
19041
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
19042
- /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
19943
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("sphereGeometry", { args: [DEFAULT_PANORAMA_RADIUS, 128, 64] }),
19944
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
19043
19945
  PanoBasicMaterial,
19044
19946
  {
19045
19947
  map: activeTexture,
@@ -19050,12 +19952,12 @@ function PanoVideo({
19050
19952
  ]
19051
19953
  }
19052
19954
  ) : null,
19053
- captionsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(PanoVideoCaptions, { appearance: typeof captions === "object" ? captions : void 0 }) : null
19955
+ captionsEnabled ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(PanoVideoCaptions, { appearance: typeof captions === "object" ? captions : void 0 }) : null
19054
19956
  ] });
19055
19957
  }
19056
19958
 
19057
19959
  // src/map/pano-map.tsx
19058
- var import_react65 = require("react");
19960
+ var import_react66 = require("react");
19059
19961
 
19060
19962
  // src/map/geometry.ts
19061
19963
  function sectorRotation(heading, yaw) {
@@ -19115,14 +20017,14 @@ function normalizeDegrees(value) {
19115
20017
  }
19116
20018
 
19117
20019
  // src/map/use-map-view.ts
19118
- var import_react64 = require("react");
20020
+ var import_react65 = require("react");
19119
20021
  var DEFAULT_VIEW2 = { yaw: 0, pitch: 0, fov: 75 };
19120
20022
  function useMapView(controlled) {
19121
- const eventBus = (0, import_react64.useContext)(PanoEventBusContext);
19122
- const [view, setView] = (0, import_react64.useState)(controlled ?? DEFAULT_VIEW2);
19123
- const controlledRef = (0, import_react64.useRef)(controlled);
20023
+ const eventBus = (0, import_react65.useContext)(PanoEventBusContext);
20024
+ const [view, setView] = (0, import_react65.useState)(controlled ?? DEFAULT_VIEW2);
20025
+ const controlledRef = (0, import_react65.useRef)(controlled);
19124
20026
  controlledRef.current = controlled;
19125
- (0, import_react64.useEffect)(() => {
20027
+ (0, import_react65.useEffect)(() => {
19126
20028
  if (controlled !== void 0) {
19127
20029
  setView(controlled);
19128
20030
  return;
@@ -19137,7 +20039,7 @@ function useMapView(controlled) {
19137
20039
  }
19138
20040
 
19139
20041
  // src/map/pano-map.tsx
19140
- var import_jsx_runtime42 = require("react/jsx-runtime");
20042
+ var import_jsx_runtime43 = require("react/jsx-runtime");
19141
20043
  var DEFAULT_ICON_SIZE2 = 28;
19142
20044
  var DEFAULT_SECTOR_COLOR = "#ffffff";
19143
20045
  var DEFAULT_SECTOR_OPACITY = 0.55;
@@ -19176,8 +20078,8 @@ function SectorShape({
19176
20078
  ...gradient.stops.map((stop) => stop.opacity ?? 1),
19177
20079
  Number.EPSILON
19178
20080
  ) : 1;
19179
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("g", { ref: transformRef, transform: `rotate(${rotation})`, style: { pointerEvents: "none" }, children: [
19180
- gradient ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("defs", { children: gradient.type === "radial" ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("radialGradient", { id: gradientId, cx: "0", cy: "0", r: radius, gradientUnits: "userSpaceOnUse", children: gradient.stops.map((stop, index) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20081
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("g", { ref: transformRef, transform: `rotate(${rotation})`, style: { pointerEvents: "none" }, children: [
20082
+ gradient ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("defs", { children: gradient.type === "radial" ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("radialGradient", { id: gradientId, cx: "0", cy: "0", r: radius, gradientUnits: "userSpaceOnUse", children: gradient.stops.map((stop, index) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19181
20083
  "stop",
19182
20084
  {
19183
20085
  offset: `${stop.offset * 100}%`,
@@ -19185,7 +20087,7 @@ function SectorShape({
19185
20087
  stopOpacity: (stop.opacity ?? 1) / gradientStrength
19186
20088
  },
19187
20089
  `${stop.offset}:${index}`
19188
- )) }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20090
+ )) }) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19189
20091
  "linearGradient",
19190
20092
  {
19191
20093
  id: gradientId,
@@ -19194,7 +20096,7 @@ function SectorShape({
19194
20096
  x2: "0",
19195
20097
  y2: -radius,
19196
20098
  gradientUnits: "userSpaceOnUse",
19197
- children: gradient.stops.map((stop, index) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20099
+ children: gradient.stops.map((stop, index) => /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19198
20100
  "stop",
19199
20101
  {
19200
20102
  offset: `${stop.offset * 100}%`,
@@ -19205,7 +20107,7 @@ function SectorShape({
19205
20107
  ))
19206
20108
  }
19207
20109
  ) }) : null,
19208
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20110
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19209
20111
  "path",
19210
20112
  {
19211
20113
  d: path,
@@ -19225,7 +20127,7 @@ function SectorShape({
19225
20127
  ] });
19226
20128
  }
19227
20129
  function DefaultSpotIcon({ active, size }) {
19228
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
20130
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
19229
20131
  "svg",
19230
20132
  {
19231
20133
  width: size,
@@ -19234,7 +20136,7 @@ function DefaultSpotIcon({ active, size }) {
19234
20136
  "aria-hidden": "true",
19235
20137
  style: { display: "block" },
19236
20138
  children: [
19237
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20139
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19238
20140
  "rect",
19239
20141
  {
19240
20142
  x: "3",
@@ -19247,8 +20149,8 @@ function DefaultSpotIcon({ active, size }) {
19247
20149
  strokeWidth: "2"
19248
20150
  }
19249
20151
  ),
19250
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("circle", { cx: "16", cy: "16", r: "5", fill: "none", stroke: "rgba(255,255,255,0.92)", strokeWidth: "2" }),
19251
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("circle", { cx: "16", cy: "16", r: "2", fill: active ? "#ef4444" : "rgba(255,255,255,0.85)" })
20152
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("circle", { cx: "16", cy: "16", r: "5", fill: "none", stroke: "rgba(255,255,255,0.92)", strokeWidth: "2" }),
20153
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("circle", { cx: "16", cy: "16", r: "2", fill: active ? "#ef4444" : "rgba(255,255,255,0.85)" })
19252
20154
  ]
19253
20155
  }
19254
20156
  );
@@ -19269,38 +20171,51 @@ function PanoMap({
19269
20171
  showCollapseControl,
19270
20172
  collapsed: controlledCollapsed,
19271
20173
  onCollapsedChange,
19272
- renderChrome
20174
+ renderChrome,
20175
+ initialScale
19273
20176
  }) {
19274
- const reactId = (0, import_react65.useId)().replace(/:/g, "");
20177
+ const reactId = (0, import_react66.useId)().replace(/:/g, "");
19275
20178
  const view = useMapView(controlledView);
19276
- const rootRef = (0, import_react65.useRef)(null);
19277
- const containerRef = (0, import_react65.useRef)(null);
19278
- const sectorNodeRef = (0, import_react65.useRef)(null);
19279
- const yawRef = (0, import_react65.useRef)(view.yaw);
19280
- const [size, setSize] = (0, import_react65.useState)({ width: 0, height: 0 });
19281
- const [imageSize2, setImageSize] = (0, import_react65.useState)({ width: 0, height: 0 });
19282
- const [scale, setScale] = (0, import_react65.useState)(1);
19283
- const [pan, setPan] = (0, import_react65.useState)({ x: 0, y: 0 });
19284
- const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react65.useState)(false);
19285
- const dragRef = (0, import_react65.useRef)(null);
20179
+ const rootRef = (0, import_react66.useRef)(null);
20180
+ const containerRef = (0, import_react66.useRef)(null);
20181
+ const sectorNodeRef = (0, import_react66.useRef)(null);
20182
+ const yawRef = (0, import_react66.useRef)(view.yaw);
20183
+ const [size, setSize] = (0, import_react66.useState)({ width: 0, height: 0 });
20184
+ const [imageSize2, setImageSize] = (0, import_react66.useState)({ width: 0, height: 0 });
20185
+ const [scale, setScale] = (0, import_react66.useState)(1);
20186
+ const [pan, setPan] = (0, import_react66.useState)({ x: 0, y: 0 });
20187
+ const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react66.useState)(false);
20188
+ const dragRef = (0, import_react66.useRef)(null);
20189
+ const panAnimRef = (0, import_react66.useRef)(null);
20190
+ const panRef = (0, import_react66.useRef)(pan);
20191
+ const scaleRef = (0, import_react66.useRef)(scale);
20192
+ const initialViewKeyRef = (0, import_react66.useRef)(null);
19286
20193
  const spots = map.spots;
19287
20194
  const iconSize = map.iconSize ?? DEFAULT_ICON_SIZE2;
19288
20195
  const interaction = map.interaction ?? "fixed";
19289
20196
  const gestureZoom = interaction === "zoom";
20197
+ const dragMotion = resolveDragMotion(map.dragMotion);
19290
20198
  const maxScale = Math.max(MIN_SCALE, map.maxScale ?? DEFAULT_MAX_SCALE);
20199
+ const resolvedInitialScale = Math.min(
20200
+ maxScale,
20201
+ Math.max(
20202
+ MIN_SCALE,
20203
+ Number.isFinite(initialScale) ? initialScale : MIN_SCALE
20204
+ )
20205
+ );
19291
20206
  const zoomControls = gestureZoom && (showZoomControls ?? map.controls?.zoom === true);
19292
20207
  const collapseControl = showCollapseControl ?? map.controls?.collapse === true;
19293
20208
  const collapsed = controlledCollapsed ?? uncontrolledCollapsed;
19294
20209
  const editing = mode === "edit";
19295
- const fit = (0, import_react65.useMemo)(
20210
+ const fit = (0, import_react66.useMemo)(
19296
20211
  () => containRect(size.width, size.height, imageSize2.width, imageSize2.height),
19297
20212
  [size, imageSize2]
19298
20213
  );
19299
- const viewport = (0, import_react65.useMemo)(
20214
+ const viewport = (0, import_react66.useMemo)(
19300
20215
  () => ({ fit, scale, panX: pan.x, panY: pan.y }),
19301
20216
  [fit, scale, pan]
19302
20217
  );
19303
- (0, import_react65.useLayoutEffect)(() => {
20218
+ (0, import_react66.useLayoutEffect)(() => {
19304
20219
  if (collapsed) return;
19305
20220
  const node = containerRef.current;
19306
20221
  if (!node) return;
@@ -19317,7 +20232,7 @@ function PanoMap({
19317
20232
  observer.observe(node);
19318
20233
  return () => observer.disconnect();
19319
20234
  }, [collapsed]);
19320
- (0, import_react65.useEffect)(() => {
20235
+ (0, import_react66.useEffect)(() => {
19321
20236
  yawRef.current = view.yaw;
19322
20237
  const node = sectorNodeRef.current;
19323
20238
  if (!node) return;
@@ -19332,24 +20247,108 @@ function PanoMap({
19332
20247
  onCollapsedChange?.(next);
19333
20248
  if (controlledCollapsed === void 0) setUncontrolledCollapsed(next);
19334
20249
  };
19335
- const clampPan = (0, import_react65.useCallback)(
19336
- (nextScale, nextPan) => {
20250
+ panRef.current = pan;
20251
+ scaleRef.current = scale;
20252
+ const panBoundsFor = (0, import_react66.useCallback)(
20253
+ (nextScale) => {
19337
20254
  const width = fit.width * nextScale;
19338
20255
  const height = fit.height * nextScale;
19339
- if (!(fit.width > 0) || !(fit.height > 0)) return nextPan;
19340
- const minX = Math.min(0, size.width - (fit.left + width));
19341
- const maxX = Math.max(0, -fit.left);
19342
- const minY = Math.min(0, size.height - (fit.top + height));
19343
- const maxY = Math.max(0, -fit.top);
20256
+ if (!(fit.width > 0) || !(fit.height > 0)) {
20257
+ return { minX: 0, maxX: 0, minY: 0, maxY: 0 };
20258
+ }
19344
20259
  return {
19345
- x: Math.min(maxX, Math.max(minX, nextPan.x)),
19346
- y: Math.min(maxY, Math.max(minY, nextPan.y))
20260
+ minX: Math.min(0, size.width - (fit.left + width)),
20261
+ maxX: Math.max(0, -fit.left),
20262
+ minY: Math.min(0, size.height - (fit.top + height)),
20263
+ maxY: Math.max(0, -fit.top)
19347
20264
  };
19348
20265
  },
19349
20266
  [fit, size]
19350
20267
  );
19351
- (0, import_react65.useEffect)(() => {
20268
+ const clampPan = (0, import_react66.useCallback)(
20269
+ (nextScale, nextPan) => {
20270
+ const bounds = panBoundsFor(nextScale);
20271
+ return {
20272
+ x: Math.min(bounds.maxX, Math.max(bounds.minX, nextPan.x)),
20273
+ y: Math.min(bounds.maxY, Math.max(bounds.minY, nextPan.y))
20274
+ };
20275
+ },
20276
+ [panBoundsFor]
20277
+ );
20278
+ const cancelPanAnimation = (0, import_react66.useCallback)(() => {
20279
+ const anim = panAnimRef.current;
20280
+ if (anim?.raf != null) {
20281
+ cancelAnimationFrame(anim.raf);
20282
+ }
20283
+ panAnimRef.current = null;
20284
+ }, []);
20285
+ const startPanAnimation = (0, import_react66.useCallback)(
20286
+ (velocity) => {
20287
+ cancelPanAnimation();
20288
+ const current = panRef.current;
20289
+ const currentScale = scaleRef.current;
20290
+ const bounds = panBoundsFor(currentScale);
20291
+ const needsSpring = dragMotion.bounce && (current.x < bounds.minX || current.x > bounds.maxX || current.y < bounds.minY || current.y > bounds.maxY);
20292
+ const vx = dragMotion.inertia ? velocity.x : 0;
20293
+ const vy = dragMotion.inertia ? velocity.y : 0;
20294
+ if (!needsSpring && Math.abs(vx) < 1 && Math.abs(vy) < 1) {
20295
+ setPan(clampPan(currentScale, current));
20296
+ return;
20297
+ }
20298
+ if (!dragMotion.inertia && !dragMotion.bounce) {
20299
+ setPan(clampPan(currentScale, current));
20300
+ return;
20301
+ }
20302
+ const anim = {
20303
+ raf: null,
20304
+ lastTime: performance.now(),
20305
+ x: { position: current.x, velocity: vx },
20306
+ y: { position: current.y, velocity: vy }
20307
+ };
20308
+ panAnimRef.current = anim;
20309
+ const tick = (now) => {
20310
+ if (panAnimRef.current !== anim) return;
20311
+ const dt = Math.min(0.064, Math.max(0, (now - anim.lastTime) / 1e3));
20312
+ anim.lastTime = now;
20313
+ const liveBounds = panBoundsFor(scaleRef.current);
20314
+ const nextX = integrateAxis(anim.x, dt, {
20315
+ min: liveBounds.minX,
20316
+ max: liveBounds.maxX,
20317
+ inertia: dragMotion.inertia,
20318
+ bounce: dragMotion.bounce
20319
+ });
20320
+ const nextY = integrateAxis(anim.y, dt, {
20321
+ min: liveBounds.minY,
20322
+ max: liveBounds.maxY,
20323
+ inertia: dragMotion.inertia,
20324
+ bounce: dragMotion.bounce
20325
+ });
20326
+ anim.x = { position: nextX.position, velocity: nextX.velocity };
20327
+ anim.y = { position: nextY.position, velocity: nextY.velocity };
20328
+ setPan({ x: nextX.position, y: nextY.position });
20329
+ if (nextX.settled && nextY.settled) {
20330
+ setPan(
20331
+ clampPan(scaleRef.current, {
20332
+ x: nextX.position,
20333
+ y: nextY.position
20334
+ })
20335
+ );
20336
+ panAnimRef.current = null;
20337
+ return;
20338
+ }
20339
+ anim.raf = requestAnimationFrame(tick);
20340
+ };
20341
+ anim.raf = requestAnimationFrame(tick);
20342
+ },
20343
+ [cancelPanAnimation, clampPan, dragMotion.bounce, dragMotion.inertia, panBoundsFor]
20344
+ );
20345
+ (0, import_react66.useEffect)(() => () => cancelPanAnimation(), [cancelPanAnimation]);
20346
+ (0, import_react66.useEffect)(() => {
20347
+ if (collapsed) cancelPanAnimation();
20348
+ }, [cancelPanAnimation, collapsed]);
20349
+ (0, import_react66.useEffect)(() => {
19352
20350
  if (!gestureZoom) {
20351
+ initialViewKeyRef.current = null;
19353
20352
  setScale(1);
19354
20353
  setPan({ x: 0, y: 0 });
19355
20354
  return;
@@ -19362,9 +20361,37 @@ function PanoMap({
19362
20361
  return next;
19363
20362
  });
19364
20363
  }, [clampPan, gestureZoom, maxScale]);
19365
- const applyZoom = (0, import_react65.useCallback)(
20364
+ (0, import_react66.useEffect)(() => {
20365
+ if (!gestureZoom) return;
20366
+ if (!(fit.width > 0) || !(fit.height > 0)) return;
20367
+ if (!(size.width > 0) || !(size.height > 0)) return;
20368
+ const key = `${interaction}:${initialScale ?? MIN_SCALE}`;
20369
+ if (initialViewKeyRef.current === key) return;
20370
+ initialViewKeyRef.current = key;
20371
+ const pivot = { x: size.width / 2, y: size.height / 2 };
20372
+ const nextPan = {
20373
+ x: pivot.x - (pivot.x - fit.left) * resolvedInitialScale - fit.left,
20374
+ y: pivot.y - (pivot.y - fit.top) * resolvedInitialScale - fit.top
20375
+ };
20376
+ setScale(resolvedInitialScale);
20377
+ setPan(clampPan(resolvedInitialScale, nextPan));
20378
+ }, [
20379
+ clampPan,
20380
+ fit.height,
20381
+ fit.left,
20382
+ fit.top,
20383
+ fit.width,
20384
+ gestureZoom,
20385
+ initialScale,
20386
+ interaction,
20387
+ resolvedInitialScale,
20388
+ size.height,
20389
+ size.width
20390
+ ]);
20391
+ const applyZoom = (0, import_react66.useCallback)(
19366
20392
  (nextScale, origin) => {
19367
20393
  if (!gestureZoom) return;
20394
+ cancelPanAnimation();
19368
20395
  const clamped = Math.min(maxScale, Math.max(MIN_SCALE, nextScale));
19369
20396
  const pivot = origin ?? { x: size.width / 2, y: size.height / 2 };
19370
20397
  const ratio = clamped / scale;
@@ -19376,6 +20403,7 @@ function PanoMap({
19376
20403
  setPan(clampPan(clamped, nextPan));
19377
20404
  },
19378
20405
  [
20406
+ cancelPanAnimation,
19379
20407
  clampPan,
19380
20408
  fit.left,
19381
20409
  fit.top,
@@ -19388,7 +20416,7 @@ function PanoMap({
19388
20416
  size.width
19389
20417
  ]
19390
20418
  );
19391
- (0, import_react65.useEffect)(() => {
20419
+ (0, import_react66.useEffect)(() => {
19392
20420
  const root = rootRef.current;
19393
20421
  if (!root) return;
19394
20422
  const onWheel = (event) => {
@@ -19440,15 +20468,22 @@ function PanoMap({
19440
20468
  return;
19441
20469
  }
19442
20470
  if (gestureZoom && scale > 1) {
20471
+ cancelPanAnimation();
20472
+ const now = performance.now();
19443
20473
  dragRef.current = {
19444
20474
  kind: "pan",
19445
20475
  pointerId: event.pointerId,
19446
20476
  originX: point.x,
19447
20477
  originY: point.y,
19448
20478
  panX: pan.x,
19449
- panY: pan.y
20479
+ panY: pan.y,
20480
+ samplesX: [{ t: now, position: pan.x }],
20481
+ samplesY: [{ t: now, position: pan.y }]
19450
20482
  };
19451
- event.currentTarget.setPointerCapture(event.pointerId);
20483
+ try {
20484
+ event.currentTarget.setPointerCapture(event.pointerId);
20485
+ } catch {
20486
+ }
19452
20487
  event.preventDefault();
19453
20488
  event.stopPropagation();
19454
20489
  }
@@ -19459,12 +20494,28 @@ function PanoMap({
19459
20494
  const point = localPoint(event);
19460
20495
  if (!point) return;
19461
20496
  if (drag.kind === "pan") {
19462
- setPan(
19463
- clampPan(scale, {
19464
- x: drag.panX + (point.x - drag.originX),
19465
- y: drag.panY + (point.y - drag.originY)
19466
- })
19467
- );
20497
+ const rawX = drag.panX + (point.x - drag.originX);
20498
+ const rawY = drag.panY + (point.y - drag.originY);
20499
+ const bounds = panBoundsFor(scale);
20500
+ const now = performance.now();
20501
+ drag.samplesX = pushSample(drag.samplesX, { t: now, position: rawX });
20502
+ drag.samplesY = pushSample(drag.samplesY, { t: now, position: rawY });
20503
+ setPan({
20504
+ x: constrainDragPosition(
20505
+ rawX,
20506
+ bounds.minX,
20507
+ bounds.maxX,
20508
+ size.width,
20509
+ dragMotion.bounce
20510
+ ),
20511
+ y: constrainDragPosition(
20512
+ rawY,
20513
+ bounds.minY,
20514
+ bounds.maxY,
20515
+ size.height,
20516
+ dragMotion.bounce
20517
+ )
20518
+ });
19468
20519
  event.preventDefault();
19469
20520
  event.stopPropagation();
19470
20521
  return;
@@ -19505,11 +20556,24 @@ function PanoMap({
19505
20556
  event.stopPropagation();
19506
20557
  };
19507
20558
  const endDrag = (event) => {
19508
- if (dragRef.current?.pointerId !== event.pointerId) return;
20559
+ const drag = dragRef.current;
20560
+ if (drag?.pointerId !== event.pointerId) return;
19509
20561
  dragRef.current = null;
19510
- if (event.currentTarget.hasPointerCapture(event.pointerId)) {
19511
- event.currentTarget.releasePointerCapture(event.pointerId);
20562
+ try {
20563
+ if (event.currentTarget.hasPointerCapture(event.pointerId)) {
20564
+ event.currentTarget.releasePointerCapture(event.pointerId);
20565
+ }
20566
+ } catch {
20567
+ }
20568
+ if (drag.kind !== "pan") return;
20569
+ if (event.type === "pointercancel") {
20570
+ setPan(clampPan(scale, panRef.current));
20571
+ return;
19512
20572
  }
20573
+ startPanAnimation({
20574
+ x: velocityFromSamples(drag.samplesX),
20575
+ y: velocityFromSamples(drag.samplesY)
20576
+ });
19513
20577
  };
19514
20578
  const sectorAngle = (() => {
19515
20579
  if (!map.sector) return 0;
@@ -19519,8 +20583,8 @@ function PanoMap({
19519
20583
  })();
19520
20584
  const activeSpot = spots.find((spot) => spot.sceneId === activeSceneId);
19521
20585
  const selectedSpot = spots.find((spot) => spot.id === selectedSpotId);
19522
- const controls = /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "pano-map-controls", style: { pointerEvents: "auto" }, children: [
19523
- collapseControl ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20586
+ const controls = /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: "pano-map-controls", style: { pointerEvents: "auto" }, children: [
20587
+ collapseControl ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19524
20588
  "button",
19525
20589
  {
19526
20590
  type: "button",
@@ -19536,8 +20600,8 @@ function PanoMap({
19536
20600
  children: collapsed ? "\u25A3" : "\u25A2"
19537
20601
  }
19538
20602
  ) : null,
19539
- zoomControls && !collapsed ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_jsx_runtime42.Fragment, { children: [
19540
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20603
+ zoomControls && !collapsed ? /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
20604
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19541
20605
  "button",
19542
20606
  {
19543
20607
  type: "button",
@@ -19552,7 +20616,7 @@ function PanoMap({
19552
20616
  children: "+"
19553
20617
  }
19554
20618
  ),
19555
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20619
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19556
20620
  "button",
19557
20621
  {
19558
20622
  type: "button",
@@ -19575,7 +20639,7 @@ function PanoMap({
19575
20639
  (previous) => previous.width === image.naturalWidth && previous.height === image.naturalHeight ? previous : { width: image.naturalWidth, height: image.naturalHeight }
19576
20640
  );
19577
20641
  };
19578
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
20642
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
19579
20643
  "div",
19580
20644
  {
19581
20645
  ref: rootRef,
@@ -19593,8 +20657,8 @@ function PanoMap({
19593
20657
  "data-mode": mode,
19594
20658
  "data-collapsed": collapsed ? "true" : void 0,
19595
20659
  children: [
19596
- renderChrome ? renderChrome(controls) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("div", { className: "pano-map-chrome", children: controls }),
19597
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
20660
+ renderChrome ? renderChrome(controls) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "pano-map-chrome", children: controls }),
20661
+ /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
19598
20662
  "div",
19599
20663
  {
19600
20664
  ref: containerRef,
@@ -19611,7 +20675,7 @@ function PanoMap({
19611
20675
  onPointerUp: endDrag,
19612
20676
  onPointerCancel: endDrag,
19613
20677
  children: [
19614
- fit.width > 0 && fit.height > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20678
+ fit.width > 0 && fit.height > 0 ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19615
20679
  "div",
19616
20680
  {
19617
20681
  className: "pano-map-image-layer",
@@ -19623,7 +20687,7 @@ function PanoMap({
19623
20687
  height: fit.height * scale,
19624
20688
  transformOrigin: "0 0"
19625
20689
  },
19626
- children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20690
+ children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19627
20691
  "img",
19628
20692
  {
19629
20693
  src: map.image,
@@ -19645,7 +20709,7 @@ function PanoMap({
19645
20709
  }
19646
20710
  )
19647
20711
  }
19648
- ) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20712
+ ) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19649
20713
  "img",
19650
20714
  {
19651
20715
  src: map.image,
@@ -19659,7 +20723,7 @@ function PanoMap({
19659
20723
  }
19660
20724
  }
19661
20725
  ),
19662
- activeSpot && map.sector ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20726
+ activeSpot && map.sector ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19663
20727
  "svg",
19664
20728
  {
19665
20729
  className: "pano-map-sector-layer",
@@ -19671,11 +20735,11 @@ function PanoMap({
19671
20735
  overflow: "visible",
19672
20736
  pointerEvents: "none"
19673
20737
  },
19674
- children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20738
+ children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19675
20739
  "g",
19676
20740
  {
19677
20741
  transform: `translate(${imagePointToContainer(activeSpot, viewport).x} ${imagePointToContainer(activeSpot, viewport).y})`,
19678
- children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20742
+ children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19679
20743
  SectorShape,
19680
20744
  {
19681
20745
  sector: map.sector,
@@ -19707,7 +20771,7 @@ function PanoMap({
19707
20771
  const point = imagePointToContainer(spot, viewport);
19708
20772
  const active = spot.sceneId === activeSceneId;
19709
20773
  const selected = spot.id === selectedSpotId;
19710
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20774
+ return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19711
20775
  "button",
19712
20776
  {
19713
20777
  type: "button",
@@ -19752,7 +20816,7 @@ function PanoMap({
19752
20816
  if (editing) return;
19753
20817
  onSelectScene?.(spot.sceneId, spot);
19754
20818
  },
19755
- children: spot.icon ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
20819
+ children: spot.icon ? /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
19756
20820
  "img",
19757
20821
  {
19758
20822
  src: spot.icon,
@@ -19760,7 +20824,7 @@ function PanoMap({
19760
20824
  draggable: false,
19761
20825
  style: { width: "100%", height: "100%", display: "block", pointerEvents: "none" }
19762
20826
  }
19763
- ) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(DefaultSpotIcon, { active, size: iconSize })
20827
+ ) : /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(DefaultSpotIcon, { active, size: iconSize })
19764
20828
  },
19765
20829
  spot.id
19766
20830
  );
@@ -19774,8 +20838,10 @@ function PanoMap({
19774
20838
  }
19775
20839
 
19776
20840
  // src/map/pano-map-overlay.tsx
19777
- var import_react66 = require("react");
19778
- var import_jsx_runtime43 = require("react/jsx-runtime");
20841
+ var import_react67 = require("react");
20842
+ var import_jsx_runtime44 = require("react/jsx-runtime");
20843
+ var DEFAULT_DIALOG_WIDTH = 720;
20844
+ var DEFAULT_DIALOG_HEIGHT = 520;
19779
20845
  function dockedBox(corner, offsetX, offsetY, width, height) {
19780
20846
  const horizontal = corner === "top-right" || corner === "bottom-right" ? { left: "auto", right: offsetX } : { left: offsetX, right: "auto" };
19781
20847
  const vertical = corner === "bottom-left" || corner === "bottom-right" ? { top: "auto", bottom: offsetY } : { top: offsetY, bottom: "auto" };
@@ -19786,14 +20852,16 @@ function PanoMapDialogShell({
19786
20852
  onOpenChange,
19787
20853
  title,
19788
20854
  closeLabel,
20855
+ dialogWidth,
20856
+ dialogHeight,
19789
20857
  children
19790
20858
  }) {
19791
20859
  usePanoModal(open);
19792
20860
  const { size } = usePanoOverlay();
19793
- const titleId = (0, import_react66.useId)();
19794
- const panelRef = (0, import_react66.useRef)(null);
19795
- const previouslyFocused = (0, import_react66.useRef)(null);
19796
- (0, import_react66.useEffect)(() => {
20861
+ const titleId = (0, import_react67.useId)();
20862
+ const panelRef = (0, import_react67.useRef)(null);
20863
+ const previouslyFocused = (0, import_react67.useRef)(null);
20864
+ (0, import_react67.useEffect)(() => {
19797
20865
  if (!open) return;
19798
20866
  const active = panelRef.current?.ownerDocument.activeElement;
19799
20867
  previouslyFocused.current = active instanceof HTMLElement ? active : null;
@@ -19815,12 +20883,14 @@ function PanoMapDialogShell({
19815
20883
  };
19816
20884
  }, [open, onOpenChange]);
19817
20885
  if (!open) return null;
19818
- const width = Math.min(720, Math.max(0, size.width - 32));
19819
- const height = Math.min(520, Math.max(0, size.height - 32));
20886
+ const preferredWidth = Number.isFinite(dialogWidth) && dialogWidth > 0 ? dialogWidth : DEFAULT_DIALOG_WIDTH;
20887
+ const preferredHeight = Number.isFinite(dialogHeight) && dialogHeight > 0 ? dialogHeight : DEFAULT_DIALOG_HEIGHT;
20888
+ const width = Math.min(preferredWidth, Math.max(0, size.width - 32));
20889
+ const height = Math.min(preferredHeight, Math.max(0, size.height - 32));
19820
20890
  const left = Math.max(0, (size.width - width) / 2);
19821
20891
  const top = Math.max(0, (size.height - height) / 2);
19822
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(import_jsx_runtime43.Fragment, { children: [
19823
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
20892
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(import_jsx_runtime44.Fragment, { children: [
20893
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
19824
20894
  "div",
19825
20895
  {
19826
20896
  className: "pano-map-dialog-backdrop",
@@ -19833,7 +20903,7 @@ function PanoMapDialogShell({
19833
20903
  onClick: () => onOpenChange(false)
19834
20904
  }
19835
20905
  ),
19836
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)(
20906
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
19837
20907
  "div",
19838
20908
  {
19839
20909
  ref: panelRef,
@@ -19856,9 +20926,9 @@ function PanoMapDialogShell({
19856
20926
  zIndex: 1
19857
20927
  },
19858
20928
  children: [
19859
- /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("header", { className: "pano-map-dialog-header", style: { flexShrink: 0 }, children: [
19860
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("h2", { id: titleId, className: "pano-map-dialog-title", children: title }),
19861
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
20929
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("header", { className: "pano-map-dialog-header", style: { flexShrink: 0 }, children: [
20930
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("h2", { id: titleId, className: "pano-map-dialog-title", children: title }),
20931
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
19862
20932
  "button",
19863
20933
  {
19864
20934
  type: "button",
@@ -19866,11 +20936,11 @@ function PanoMapDialogShell({
19866
20936
  "data-pano-map-dialog-close": "",
19867
20937
  "aria-label": closeLabel,
19868
20938
  onClick: () => onOpenChange(false),
19869
- children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("svg", { "aria-hidden": "true", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("path", { d: "m4 4 8 8M12 4l-8 8" }) })
20939
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("svg", { "aria-hidden": "true", width: "16", height: "16", viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("path", { d: "m4 4 8 8M12 4l-8 8" }) })
19870
20940
  }
19871
20941
  )
19872
20942
  ] }),
19873
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { className: "pano-map-dialog-body", style: { minHeight: 0, flex: 1 }, children })
20943
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { className: "pano-map-dialog-body", style: { minHeight: 0, flex: 1 }, children })
19874
20944
  ]
19875
20945
  }
19876
20946
  )
@@ -19887,6 +20957,20 @@ function PanoMapOverlay({
19887
20957
  onEditChange,
19888
20958
  placementSceneId,
19889
20959
  presentationOverride,
20960
+ presentation: presentationModeProp,
20961
+ corner: cornerProp,
20962
+ width: widthProp,
20963
+ height: heightProp,
20964
+ offsetX: offsetXProp,
20965
+ offsetY: offsetYProp,
20966
+ dialogWidth = DEFAULT_DIALOG_WIDTH,
20967
+ dialogHeight = DEFAULT_DIALOG_HEIGHT,
20968
+ interaction,
20969
+ maxScale,
20970
+ initialScale,
20971
+ showZoomControls,
20972
+ showCollapseControl,
20973
+ iconSize,
19890
20974
  openLabel = "Map",
19891
20975
  closeLabel = "Close",
19892
20976
  title = "Map",
@@ -19895,9 +20979,9 @@ function PanoMapOverlay({
19895
20979
  editorSurface = false
19896
20980
  }) {
19897
20981
  const { size } = usePanoOverlay();
19898
- const [open, setOpen] = (0, import_react66.useState)(false);
19899
- const [collapsed, setCollapsed] = (0, import_react66.useState)(false);
19900
- const presentation = presentationOverride ?? map.presentation ?? {
20982
+ const [open, setOpen] = (0, import_react67.useState)(false);
20983
+ const [collapsed, setCollapsed] = (0, import_react67.useState)(false);
20984
+ const basePresentation = presentationOverride ?? map.presentation ?? {
19901
20985
  mode: "docked",
19902
20986
  corner: "bottom-left",
19903
20987
  width: 220,
@@ -19905,13 +20989,25 @@ function PanoMapOverlay({
19905
20989
  offsetX: 16,
19906
20990
  offsetY: 16
19907
20991
  };
19908
- const corner = presentation.corner ?? "bottom-left";
19909
- const offsetX = presentation.offsetX ?? 16;
19910
- const offsetY = presentation.offsetY ?? 16;
19911
- const width = presentation.width ?? (presentation.mode === "dialog" ? 48 : 220);
19912
- const height = presentation.height ?? (presentation.mode === "dialog" ? 48 : 160);
20992
+ const presentationMode = presentationModeProp ?? basePresentation.mode;
20993
+ const corner = cornerProp ?? basePresentation.corner ?? "bottom-left";
20994
+ const offsetX = offsetXProp ?? basePresentation.offsetX ?? 16;
20995
+ const offsetY = offsetYProp ?? basePresentation.offsetY ?? 16;
20996
+ const width = widthProp ?? basePresentation.width ?? (presentationMode === "dialog" ? 48 : 220);
20997
+ const height = heightProp ?? basePresentation.height ?? (presentationMode === "dialog" ? 48 : 160);
20998
+ const resolvedMap = (0, import_react67.useMemo)(() => {
20999
+ if (interaction === void 0 && maxScale === void 0 && iconSize === void 0) {
21000
+ return map;
21001
+ }
21002
+ return {
21003
+ ...map,
21004
+ ...interaction === void 0 ? {} : { interaction },
21005
+ ...maxScale === void 0 ? {} : { maxScale },
21006
+ ...iconSize === void 0 ? {} : { iconSize }
21007
+ };
21008
+ }, [iconSize, interaction, map, maxScale]);
19913
21009
  const mapProps = {
19914
- map,
21010
+ map: resolvedMap,
19915
21011
  activeSceneId,
19916
21012
  view,
19917
21013
  mode,
@@ -19919,14 +21015,17 @@ function PanoMapOverlay({
19919
21015
  onSelectedSpotIdChange,
19920
21016
  onEditChange,
19921
21017
  placementSceneId,
21018
+ initialScale,
21019
+ showZoomControls,
21020
+ showCollapseControl,
19922
21021
  onSelectScene: (sceneId, spot) => {
19923
21022
  onSelectScene?.(sceneId, spot);
19924
- if (presentation.mode === "dialog") setOpen(false);
21023
+ if (presentationMode === "dialog") setOpen(false);
19925
21024
  }
19926
21025
  };
19927
- const editorBox = (0, import_react66.useMemo)(() => {
19928
- const panelWidth2 = Math.min(420, Math.max(240, size.width * 0.45));
19929
- const panelHeight2 = Math.min(320, Math.max(180, size.height * 0.45));
21026
+ const editorBox = (0, import_react67.useMemo)(() => {
21027
+ const panelWidth2 = widthProp !== void 0 && Number.isFinite(widthProp) && widthProp > 0 ? widthProp : Math.min(420, Math.max(240, size.width * 0.45));
21028
+ const panelHeight2 = heightProp !== void 0 && Number.isFinite(heightProp) && heightProp > 0 ? heightProp : Math.min(320, Math.max(180, size.height * 0.45));
19930
21029
  return {
19931
21030
  left: 16,
19932
21031
  right: "auto",
@@ -19935,9 +21034,9 @@ function PanoMapOverlay({
19935
21034
  width: panelWidth2,
19936
21035
  height: panelHeight2
19937
21036
  };
19938
- }, [size.height, size.width]);
21037
+ }, [heightProp, size.height, size.width, widthProp]);
19939
21038
  if (editorSurface) {
19940
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
21039
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
19941
21040
  "div",
19942
21041
  {
19943
21042
  className: ["pano-map-overlay", "pano-map-overlay-editor", className].filter(Boolean).join(" "),
@@ -19948,14 +21047,14 @@ function PanoMapOverlay({
19948
21047
  ...style
19949
21048
  },
19950
21049
  "data-pano-map-overlay": "",
19951
- children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PanoMap, { ...mapProps, showCollapseControl: false })
21050
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(PanoMap, { ...mapProps, showCollapseControl: false })
19952
21051
  }
19953
21052
  );
19954
21053
  }
19955
- if (presentation.mode === "dialog") {
21054
+ if (presentationMode === "dialog") {
19956
21055
  const launcherBox = dockedBox(corner, offsetX, offsetY, width, height);
19957
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsxs)("div", { className: ["pano-map-overlay", className].filter(Boolean).join(" "), "data-pano-map-overlay": "", children: [
19958
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
21056
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: ["pano-map-overlay", className].filter(Boolean).join(" "), "data-pano-map-overlay": "", children: [
21057
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
19959
21058
  "button",
19960
21059
  {
19961
21060
  type: "button",
@@ -19971,14 +21070,16 @@ function PanoMapOverlay({
19971
21070
  children: openLabel
19972
21071
  }
19973
21072
  ),
19974
- /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
21073
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
19975
21074
  PanoMapDialogShell,
19976
21075
  {
19977
21076
  open,
19978
21077
  onOpenChange: setOpen,
19979
21078
  title,
19980
21079
  closeLabel,
19981
- children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)("div", { style: { height: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(PanoMap, { ...mapProps, showCollapseControl: false }) })
21080
+ dialogWidth,
21081
+ dialogHeight,
21082
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("div", { style: { height: "100%" }, children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(PanoMap, { ...mapProps, showCollapseControl: false }) })
19982
21083
  }
19983
21084
  )
19984
21085
  ] });
@@ -19986,7 +21087,7 @@ function PanoMapOverlay({
19986
21087
  const panelWidth = collapsed ? Math.max(44, Math.min(width, 56)) : width;
19987
21088
  const panelHeight = collapsed ? Math.max(44, Math.min(height, 56)) : height;
19988
21089
  const box = dockedBox(corner, offsetX, offsetY, panelWidth, panelHeight);
19989
- return /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
21090
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
19990
21091
  "div",
19991
21092
  {
19992
21093
  className: ["pano-map-overlay", className].filter(Boolean).join(" "),
@@ -19998,7 +21099,7 @@ function PanoMapOverlay({
19998
21099
  },
19999
21100
  "data-pano-map-overlay": "",
20000
21101
  "data-presentation": "docked",
20001
- children: /* @__PURE__ */ (0, import_jsx_runtime43.jsx)(
21102
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
20002
21103
  PanoMap,
20003
21104
  {
20004
21105
  ...mapProps,
@@ -20017,6 +21118,7 @@ function PanoMapOverlay({
20017
21118
  DEFAULT_HOTSPOT_TOOLTIP_APPEARANCE,
20018
21119
  DEFAULT_HOTSPOT_TOOLTIP_OFFSET,
20019
21120
  DEFAULT_PANO_CURSORS,
21121
+ DEFAULT_PANO_SCENE_MENU_APPEARANCE,
20020
21122
  DEFAULT_PANO_VIDEO_CAPTION_APPEARANCE,
20021
21123
  DEFAULT_PANO_VIDEO_CONTROLS_APPEARANCE,
20022
21124
  DEFAULT_PANO_VIDEO_PLAYBACK_RATES,
@@ -20037,6 +21139,7 @@ function PanoMapOverlay({
20037
21139
  PanoMap,
20038
21140
  PanoMapOverlay,
20039
21141
  PanoOverlayPortalProvider,
21142
+ PanoSceneMenu,
20040
21143
  PanoVideo,
20041
21144
  PanoVideoControls,
20042
21145
  PanoViewer,