@ericchen1990/pano-view 3.10.0 → 4.0.1

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.mjs CHANGED
@@ -9332,6 +9332,339 @@ function hotspotAction(hotspot, trigger) {
9332
9332
  return hotspot.events?.[trigger] ?? (trigger === "click" ? hotspot.action : void 0);
9333
9333
  }
9334
9334
 
9335
+ // src/overlay/align.ts
9336
+ var PANO_OVERLAY_ALIGNS = [
9337
+ "top",
9338
+ "bottom",
9339
+ "left",
9340
+ "right",
9341
+ "top-left",
9342
+ "top-right",
9343
+ "bottom-left",
9344
+ "bottom-right"
9345
+ ];
9346
+ var ALIGN_LIST = PANO_OVERLAY_ALIGNS.join(", ");
9347
+ var SAFE = {
9348
+ top: "var(--pano-safe-top)",
9349
+ right: "var(--pano-safe-right)",
9350
+ bottom: "var(--pano-safe-bottom)",
9351
+ left: "var(--pano-safe-left)"
9352
+ };
9353
+ function cssLength(value) {
9354
+ return typeof value === "number" ? `${value}px` : value;
9355
+ }
9356
+ function isOverlayCorner(align) {
9357
+ switch (align) {
9358
+ case "top-left":
9359
+ case "top-right":
9360
+ case "bottom-left":
9361
+ case "bottom-right":
9362
+ return true;
9363
+ case "top":
9364
+ case "bottom":
9365
+ case "left":
9366
+ case "right":
9367
+ return false;
9368
+ default: {
9369
+ const exhaustive = align;
9370
+ return exhaustive;
9371
+ }
9372
+ }
9373
+ }
9374
+ function resolveOverlayAxis(align, axis) {
9375
+ switch (align) {
9376
+ case "top":
9377
+ case "bottom":
9378
+ return "horizontal";
9379
+ case "left":
9380
+ case "right":
9381
+ return "vertical";
9382
+ case "top-left":
9383
+ case "top-right":
9384
+ case "bottom-left":
9385
+ case "bottom-right":
9386
+ return axis ?? "horizontal";
9387
+ default: {
9388
+ const exhaustive = align;
9389
+ return exhaustive;
9390
+ }
9391
+ }
9392
+ }
9393
+ function overlayCollapseEdge(align, axis) {
9394
+ switch (align) {
9395
+ case "top":
9396
+ return "top";
9397
+ case "bottom":
9398
+ return "bottom";
9399
+ case "left":
9400
+ return "left";
9401
+ case "right":
9402
+ return "right";
9403
+ case "top-left":
9404
+ return resolveOverlayAxis(align, axis) === "horizontal" ? "top" : "left";
9405
+ case "top-right":
9406
+ return resolveOverlayAxis(align, axis) === "horizontal" ? "top" : "right";
9407
+ case "bottom-left":
9408
+ return resolveOverlayAxis(align, axis) === "horizontal" ? "bottom" : "left";
9409
+ case "bottom-right":
9410
+ return resolveOverlayAxis(align, axis) === "horizontal" ? "bottom" : "right";
9411
+ default: {
9412
+ const exhaustive = align;
9413
+ return exhaustive;
9414
+ }
9415
+ }
9416
+ }
9417
+ function overlayChevron(edge, collapsed) {
9418
+ switch (edge) {
9419
+ case "top":
9420
+ return collapsed ? "down" : "up";
9421
+ case "bottom":
9422
+ return collapsed ? "up" : "down";
9423
+ case "left":
9424
+ return collapsed ? "right" : "left";
9425
+ case "right":
9426
+ return collapsed ? "left" : "right";
9427
+ default: {
9428
+ const exhaustive = edge;
9429
+ return exhaustive;
9430
+ }
9431
+ }
9432
+ }
9433
+ function overlayContentAlign(align, axis) {
9434
+ const resolved = resolveOverlayAxis(align, axis);
9435
+ if (resolved === "horizontal") {
9436
+ switch (align) {
9437
+ case "top-left":
9438
+ case "bottom-left":
9439
+ return "start";
9440
+ case "top-right":
9441
+ case "bottom-right":
9442
+ return "end";
9443
+ case "top":
9444
+ case "bottom":
9445
+ case "left":
9446
+ case "right":
9447
+ return "center";
9448
+ default: {
9449
+ const exhaustive = align;
9450
+ return exhaustive;
9451
+ }
9452
+ }
9453
+ }
9454
+ switch (align) {
9455
+ case "top-left":
9456
+ case "top-right":
9457
+ return "start";
9458
+ case "bottom-left":
9459
+ case "bottom-right":
9460
+ return "end";
9461
+ case "top":
9462
+ case "bottom":
9463
+ case "left":
9464
+ case "right":
9465
+ return "center";
9466
+ default: {
9467
+ const exhaustive = align;
9468
+ return exhaustive;
9469
+ }
9470
+ }
9471
+ }
9472
+ function resolveOverlayInset(inset, fallback) {
9473
+ const fallbackLength = cssLength(fallback);
9474
+ if (inset === void 0) {
9475
+ return {
9476
+ top: fallbackLength,
9477
+ right: fallbackLength,
9478
+ bottom: fallbackLength,
9479
+ left: fallbackLength
9480
+ };
9481
+ }
9482
+ if (typeof inset === "number" || typeof inset === "string") {
9483
+ const value = cssLength(inset);
9484
+ return { top: value, right: value, bottom: value, left: value };
9485
+ }
9486
+ return {
9487
+ top: cssLength(inset.top ?? fallback),
9488
+ right: cssLength(inset.right ?? fallback),
9489
+ bottom: cssLength(inset.bottom ?? fallback),
9490
+ left: cssLength(inset.left ?? fallback)
9491
+ };
9492
+ }
9493
+ function overlayDockInset(edge, inset) {
9494
+ switch (edge) {
9495
+ case "top":
9496
+ return inset.top;
9497
+ case "bottom":
9498
+ return inset.bottom;
9499
+ case "left":
9500
+ return inset.left;
9501
+ case "right":
9502
+ return inset.right;
9503
+ default: {
9504
+ const exhaustive = edge;
9505
+ return exhaustive;
9506
+ }
9507
+ }
9508
+ }
9509
+ function resolveOverlayAnchor(anchor, defaults) {
9510
+ const align = anchor?.align ?? defaults.align;
9511
+ const axis = resolveOverlayAxis(align, anchor?.axis);
9512
+ const inset = resolveOverlayInset(anchor?.inset, defaults.inset);
9513
+ const collapseEdge = overlayCollapseEdge(align, anchor?.axis);
9514
+ return {
9515
+ align,
9516
+ axis,
9517
+ inset,
9518
+ collapseEdge,
9519
+ contentAlign: overlayContentAlign(align, anchor?.axis),
9520
+ dockInset: overlayDockInset(collapseEdge, inset)
9521
+ };
9522
+ }
9523
+ function withSafe(inset, safeVariable) {
9524
+ return `calc(${inset} + ${safeVariable})`;
9525
+ }
9526
+ function maxBox(inset) {
9527
+ return {
9528
+ maxWidth: `calc(100% - ${inset.left} - ${inset.right} - ${SAFE.left} - ${SAFE.right})`,
9529
+ maxHeight: `calc(100% - ${inset.top} - ${inset.bottom} - ${SAFE.top} - ${SAFE.bottom})`
9530
+ };
9531
+ }
9532
+ function horizontalSpan(inset, width) {
9533
+ const left = withSafe(inset.left, SAFE.left);
9534
+ const right = withSafe(inset.right, SAFE.right);
9535
+ if (width === void 0) {
9536
+ return { left, right, width: void 0, marginInline: void 0 };
9537
+ }
9538
+ return { left, right, width, marginInline: "auto" };
9539
+ }
9540
+ function verticalSpan(inset, height) {
9541
+ const top = withSafe(inset.top, SAFE.top);
9542
+ const bottom = withSafe(inset.bottom, SAFE.bottom);
9543
+ if (height === void 0) {
9544
+ return { top, bottom, height: void 0, marginBlock: void 0 };
9545
+ }
9546
+ return { top, bottom, height, marginBlock: "auto" };
9547
+ }
9548
+ function overlayBoxStyle(align, inset, width, height) {
9549
+ const widthValue = width === void 0 ? void 0 : cssLength(width);
9550
+ const heightValue = height === void 0 ? void 0 : cssLength(height);
9551
+ const limits = maxBox(inset);
9552
+ switch (align) {
9553
+ case "top":
9554
+ return {
9555
+ ...horizontalSpan(inset, widthValue),
9556
+ top: withSafe(inset.top, SAFE.top),
9557
+ bottom: "auto",
9558
+ height: heightValue,
9559
+ ...limits
9560
+ };
9561
+ case "bottom":
9562
+ return {
9563
+ ...horizontalSpan(inset, widthValue),
9564
+ top: "auto",
9565
+ bottom: withSafe(inset.bottom, SAFE.bottom),
9566
+ height: heightValue,
9567
+ ...limits
9568
+ };
9569
+ case "left":
9570
+ return {
9571
+ ...verticalSpan(inset, heightValue),
9572
+ left: withSafe(inset.left, SAFE.left),
9573
+ right: "auto",
9574
+ width: widthValue,
9575
+ ...limits
9576
+ };
9577
+ case "right":
9578
+ return {
9579
+ ...verticalSpan(inset, heightValue),
9580
+ left: "auto",
9581
+ right: withSafe(inset.right, SAFE.right),
9582
+ width: widthValue,
9583
+ ...limits
9584
+ };
9585
+ case "top-left":
9586
+ return {
9587
+ top: withSafe(inset.top, SAFE.top),
9588
+ left: withSafe(inset.left, SAFE.left),
9589
+ right: "auto",
9590
+ bottom: "auto",
9591
+ width: widthValue,
9592
+ height: heightValue,
9593
+ ...limits
9594
+ };
9595
+ case "top-right":
9596
+ return {
9597
+ top: withSafe(inset.top, SAFE.top),
9598
+ right: withSafe(inset.right, SAFE.right),
9599
+ left: "auto",
9600
+ bottom: "auto",
9601
+ width: widthValue,
9602
+ height: heightValue,
9603
+ ...limits
9604
+ };
9605
+ case "bottom-left":
9606
+ return {
9607
+ bottom: withSafe(inset.bottom, SAFE.bottom),
9608
+ left: withSafe(inset.left, SAFE.left),
9609
+ top: "auto",
9610
+ right: "auto",
9611
+ width: widthValue,
9612
+ height: heightValue,
9613
+ ...limits
9614
+ };
9615
+ case "bottom-right":
9616
+ return {
9617
+ bottom: withSafe(inset.bottom, SAFE.bottom),
9618
+ right: withSafe(inset.right, SAFE.right),
9619
+ top: "auto",
9620
+ left: "auto",
9621
+ width: widthValue,
9622
+ height: heightValue,
9623
+ ...limits
9624
+ };
9625
+ default: {
9626
+ const exhaustive = align;
9627
+ return exhaustive;
9628
+ }
9629
+ }
9630
+ }
9631
+ function isRecord2(value) {
9632
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9633
+ }
9634
+ function parseInsetLength(value, field) {
9635
+ if (typeof value === "number" && Number.isFinite(value)) return value;
9636
+ if (typeof value === "string" && value.trim()) return value;
9637
+ throw new Error(`${field}: expected a finite number or a nonempty string`);
9638
+ }
9639
+ function parseInset(value, field) {
9640
+ if (typeof value === "number" || typeof value === "string") {
9641
+ return parseInsetLength(value, field);
9642
+ }
9643
+ if (!isRecord2(value)) throw new Error(`${field}: expected a number, string, or object`);
9644
+ const sides = ["top", "right", "bottom", "left"];
9645
+ const inset = {};
9646
+ for (const side of sides) {
9647
+ if (value[side] === void 0) continue;
9648
+ inset[side] = parseInsetLength(value[side], `${field}.${side}`);
9649
+ }
9650
+ return inset;
9651
+ }
9652
+ function parseOverlayAnchor(value, field) {
9653
+ if (!isRecord2(value)) throw new Error(`${field}: expected an object`);
9654
+ const align = value.align === void 0 ? void 0 : typeof value.align === "string" && PANO_OVERLAY_ALIGNS.includes(value.align) ? value.align : (() => {
9655
+ throw new Error(`${field}.align: expected ${ALIGN_LIST}`);
9656
+ })();
9657
+ const axis = value.axis === void 0 ? void 0 : value.axis === "horizontal" || value.axis === "vertical" ? value.axis : (() => {
9658
+ throw new Error(`${field}.axis: expected "horizontal" or "vertical"`);
9659
+ })();
9660
+ const inset = value.inset === void 0 ? void 0 : parseInset(value.inset, `${field}.inset`);
9661
+ return {
9662
+ ...align === void 0 ? {} : { align },
9663
+ ...axis === void 0 ? {} : { axis },
9664
+ ...inset === void 0 ? {} : { inset }
9665
+ };
9666
+ }
9667
+
9335
9668
  // src/project-scenes.tsx
9336
9669
  import {
9337
9670
  useContext as useContext22,
@@ -19232,6 +19565,13 @@ function pushSample(samples, sample, maxCount = 8) {
19232
19565
  return next;
19233
19566
  }
19234
19567
 
19568
+ // src/overlay/toggle.ts
19569
+ function resolveOverlayToggleIcon(icon, collapsed) {
19570
+ if (icon === void 0) return void 0;
19571
+ if (typeof icon === "function") return icon({ collapsed });
19572
+ return icon;
19573
+ }
19574
+
19235
19575
  // src/pano-scene-menu.tsx
19236
19576
  import { jsx as jsx41, jsxs as jsxs22 } from "react/jsx-runtime";
19237
19577
  var SCENE_MENU_DRAG_THRESHOLD_PX = 6;
@@ -19252,37 +19592,15 @@ var DEFAULT_PANO_SCENE_MENU_APPEARANCE = {
19252
19592
  controlBackground: "rgba(255, 255, 255, 0.12)"
19253
19593
  };
19254
19594
  var DEFAULT_INSET = 12;
19255
- function cssLength(value) {
19256
- return typeof value === "number" ? `${value}px` : value;
19257
- }
19258
19595
  function resolveAppearance2(appearance) {
19259
19596
  return {
19260
19597
  ...DEFAULT_PANO_SCENE_MENU_APPEARANCE,
19261
19598
  ...appearance
19262
19599
  };
19263
19600
  }
19264
- function resolveInset(inset) {
19265
- if (inset === void 0) {
19266
- const value = cssLength(DEFAULT_INSET);
19267
- return { top: value, right: value, bottom: value, left: value };
19268
- }
19269
- if (typeof inset === "number" || typeof inset === "string") {
19270
- const value = cssLength(inset);
19271
- return { top: value, right: value, bottom: value, left: value };
19272
- }
19273
- return {
19274
- top: cssLength(inset.top ?? DEFAULT_INSET),
19275
- right: cssLength(inset.right ?? DEFAULT_INSET),
19276
- bottom: cssLength(inset.bottom ?? DEFAULT_INSET),
19277
- left: cssLength(inset.left ?? DEFAULT_INSET)
19278
- };
19279
- }
19280
19601
  function itemLabel(item) {
19281
19602
  return item.title?.trim() || item.id;
19282
19603
  }
19283
- function isHorizontal(placement) {
19284
- return placement === "top" || placement === "bottom";
19285
- }
19286
19604
  var CHEVRON_PATHS = {
19287
19605
  // Keep glyphs optically centered in the 16×16 viewBox.
19288
19606
  up: "M3 11l5-6 5 6",
@@ -19296,40 +19614,6 @@ function arrowChevron(direction, orientation2) {
19296
19614
  }
19297
19615
  return orientation2 === "horizontal" ? "right" : "down";
19298
19616
  }
19299
- function toggleChevron(placement, expanded) {
19300
- switch (placement) {
19301
- case "bottom":
19302
- return expanded ? "down" : "up";
19303
- case "top":
19304
- return expanded ? "up" : "down";
19305
- case "left":
19306
- return expanded ? "left" : "right";
19307
- case "right":
19308
- return expanded ? "right" : "left";
19309
- default: {
19310
- const exhaustive = placement;
19311
- void exhaustive;
19312
- return "down";
19313
- }
19314
- }
19315
- }
19316
- function dockInset(placement, inset) {
19317
- switch (placement) {
19318
- case "top":
19319
- return inset.top;
19320
- case "bottom":
19321
- return inset.bottom;
19322
- case "left":
19323
- return inset.left;
19324
- case "right":
19325
- return inset.right;
19326
- default: {
19327
- const exhaustive = placement;
19328
- void exhaustive;
19329
- return inset.bottom;
19330
- }
19331
- }
19332
- }
19333
19617
  function Chevron({ arrow }) {
19334
19618
  return /* @__PURE__ */ jsx41(
19335
19619
  "svg",
@@ -19352,6 +19636,7 @@ function ControlButton({
19352
19636
  disabled,
19353
19637
  label,
19354
19638
  onClick,
19639
+ style,
19355
19640
  children
19356
19641
  }) {
19357
19642
  return /* @__PURE__ */ jsx41(
@@ -19364,108 +19649,18 @@ function ControlButton({
19364
19649
  onPointerDown: (event) => {
19365
19650
  event.stopPropagation();
19366
19651
  },
19652
+ style,
19367
19653
  title: label,
19368
19654
  type: "button",
19369
19655
  children
19370
19656
  }
19371
19657
  );
19372
19658
  }
19373
- function horizontalAlignStyle(align, inset, widthValue) {
19374
- const left = `calc(${inset.left} + var(--pano-safe-left))`;
19375
- const right = `calc(${inset.right} + var(--pano-safe-right))`;
19376
- if (!widthValue) {
19377
- return { left, right, width: void 0, marginInline: void 0 };
19378
- }
19379
- switch (align) {
19380
- case "start":
19381
- return { left, right: "auto", width: widthValue, marginInline: void 0 };
19382
- case "end":
19383
- return { left: "auto", right, width: widthValue, marginInline: void 0 };
19384
- case "center":
19385
- return { left, right, width: widthValue, marginInline: "auto" };
19386
- default: {
19387
- const exhaustive = align;
19388
- void exhaustive;
19389
- return { left, right, width: widthValue, marginInline: "auto" };
19390
- }
19391
- }
19392
- }
19393
- function verticalAlignStyle(align, inset, heightValue) {
19394
- const top = `calc(${inset.top} + var(--pano-safe-top))`;
19395
- const bottom = `calc(${inset.bottom} + var(--pano-safe-bottom))`;
19396
- if (!heightValue) {
19397
- return { top, bottom, height: void 0, marginBlock: void 0 };
19398
- }
19399
- switch (align) {
19400
- case "start":
19401
- return { top, bottom: "auto", height: heightValue, marginBlock: void 0 };
19402
- case "end":
19403
- return { top: "auto", bottom, height: heightValue, marginBlock: void 0 };
19404
- case "center":
19405
- return { top, bottom, height: heightValue, marginBlock: "auto" };
19406
- default: {
19407
- const exhaustive = align;
19408
- void exhaustive;
19409
- return { top, bottom, height: heightValue, marginBlock: "auto" };
19410
- }
19411
- }
19412
- }
19413
- function placementBoxStyle(placement, inset, width, height, align) {
19414
- const widthValue = width === void 0 ? void 0 : cssLength(width);
19415
- const heightValue = height === void 0 ? void 0 : cssLength(height);
19416
- const maxWidth = `calc(100% - ${inset.left} - ${inset.right} - var(--pano-safe-left) - var(--pano-safe-right))`;
19417
- const maxHeight = `calc(100% - ${inset.top} - ${inset.bottom} - var(--pano-safe-top) - var(--pano-safe-bottom))`;
19418
- switch (placement) {
19419
- case "top":
19420
- return {
19421
- ...horizontalAlignStyle(align, inset, widthValue),
19422
- top: `calc(${inset.top} + var(--pano-safe-top))`,
19423
- bottom: "auto",
19424
- maxWidth,
19425
- height: heightValue,
19426
- maxHeight
19427
- };
19428
- case "bottom":
19429
- return {
19430
- ...horizontalAlignStyle(align, inset, widthValue),
19431
- top: "auto",
19432
- bottom: `calc(${inset.bottom} + var(--pano-safe-bottom))`,
19433
- maxWidth,
19434
- height: heightValue,
19435
- maxHeight
19436
- };
19437
- case "left":
19438
- return {
19439
- ...verticalAlignStyle(align, inset, heightValue),
19440
- right: "auto",
19441
- left: `calc(${inset.left} + var(--pano-safe-left))`,
19442
- width: widthValue,
19443
- maxWidth,
19444
- maxHeight
19445
- };
19446
- case "right":
19447
- return {
19448
- ...verticalAlignStyle(align, inset, heightValue),
19449
- right: `calc(${inset.right} + var(--pano-safe-right))`,
19450
- left: "auto",
19451
- width: widthValue,
19452
- maxWidth,
19453
- maxHeight
19454
- };
19455
- default: {
19456
- const exhaustive = placement;
19457
- void exhaustive;
19458
- return {};
19459
- }
19460
- }
19461
- }
19462
19659
  function PanoSceneMenu({
19463
19660
  items,
19464
19661
  activeId = null,
19465
19662
  onSelect,
19466
- placement = "bottom",
19467
- align = "center",
19468
- inset,
19663
+ anchor,
19469
19664
  width,
19470
19665
  height,
19471
19666
  collapsible = false,
@@ -19482,15 +19677,19 @@ function PanoSceneMenu({
19482
19677
  previousLabel = "Previous scene",
19483
19678
  nextLabel = "Next scene",
19484
19679
  expandLabel = "Show scenes",
19485
- collapseLabel = "Hide scenes"
19680
+ collapseLabel = "Hide scenes",
19681
+ toggle
19486
19682
  }) {
19487
19683
  const listId = useId3();
19488
19684
  const listRef = useRef41(null);
19489
19685
  const trackRef = useRef41(null);
19490
19686
  const itemRefs = useRef41(/* @__PURE__ */ new Map());
19491
19687
  const resolved = resolveAppearance2(appearance);
19492
- const resolvedInset = resolveInset(inset);
19493
- const horizontal = isHorizontal(placement);
19688
+ const resolvedAnchor = resolveOverlayAnchor(anchor, {
19689
+ align: "bottom",
19690
+ inset: DEFAULT_INSET
19691
+ });
19692
+ const horizontal = resolvedAnchor.axis === "horizontal";
19494
19693
  const customDrag = dragMotionProp !== false;
19495
19694
  const dragMotion = resolveDragMotion(
19496
19695
  dragMotionProp === false ? false : dragMotionProp
@@ -19663,15 +19862,15 @@ function PanoSceneMenu({
19663
19862
  "--pano-scene-menu-label-align": resolved.labelAlign,
19664
19863
  "--pano-scene-menu-font-size": cssLength(resolved.fontSize),
19665
19864
  "--pano-scene-menu-control-color": resolved.controlColor,
19666
- "--pano-scene-menu-control-background": resolved.controlBackground,
19667
- "--pano-scene-menu-dock-inset": dockInset(placement, resolvedInset)
19865
+ "--pano-scene-menu-control-background": resolved.controlBackground
19668
19866
  };
19669
19867
  return {
19670
19868
  ...vars,
19671
- ...placementBoxStyle(placement, resolvedInset, width, height, align),
19869
+ ["--pano-overlay-dock-inset"]: resolvedAnchor.dockInset,
19870
+ ...overlayBoxStyle(resolvedAnchor.align, resolvedAnchor.inset, width, height),
19672
19871
  ...style
19673
19872
  };
19674
- }, [align, height, placement, resolved, resolvedInset, style, width]);
19873
+ }, [height, resolved, resolvedAnchor, style, width]);
19675
19874
  const setExpanded = (next) => {
19676
19875
  if (!collapsible) {
19677
19876
  return;
@@ -19877,13 +20076,13 @@ function PanoSceneMenu({
19877
20076
  "div",
19878
20077
  {
19879
20078
  "aria-label": label,
19880
- className: cn("pano-scene-menu", className),
19881
- "data-align": align,
20079
+ className: cn("pano-overlay-anchor", "pano-scene-menu", className),
20080
+ "data-align": resolvedAnchor.contentAlign,
20081
+ "data-collapse-edge": resolvedAnchor.collapseEdge,
20082
+ "data-collapsed": collapsible && !expanded ? "true" : "false",
19882
20083
  "data-collapsible": collapsible ? "true" : void 0,
19883
20084
  "data-drag-motion": customDrag ? "custom" : "native",
19884
- "data-expanded": expanded ? "true" : "false",
19885
20085
  "data-orientation": orientation2,
19886
- "data-placement": placement,
19887
20086
  role: "group",
19888
20087
  style: rootStyle,
19889
20088
  children: [
@@ -19988,10 +20187,19 @@ function PanoSceneMenu({
19988
20187
  collapsible ? /* @__PURE__ */ jsx41(
19989
20188
  ControlButton,
19990
20189
  {
19991
- className: "pano-scene-menu-toggle",
20190
+ className: cn(
20191
+ "pano-scene-menu-toggle pano-overlay-anchor-toggle",
20192
+ toggle?.className
20193
+ ),
19992
20194
  label: expanded ? collapseLabel : expandLabel,
19993
20195
  onClick: () => setExpanded(!expanded),
19994
- children: /* @__PURE__ */ jsx41(Chevron, { arrow: toggleChevron(placement, expanded) })
20196
+ style: toggle?.style,
20197
+ children: toggle?.icon === void 0 ? /* @__PURE__ */ jsx41(
20198
+ Chevron,
20199
+ {
20200
+ arrow: overlayChevron(resolvedAnchor.collapseEdge, !expanded)
20201
+ }
20202
+ ) : resolveOverlayToggleIcon(toggle.icon, !expanded)
19995
20203
  }
19996
20204
  ) : null
19997
20205
  ]
@@ -21760,11 +21968,21 @@ import {
21760
21968
  import { Fragment as Fragment11, jsx as jsx44, jsxs as jsxs25 } from "react/jsx-runtime";
21761
21969
  var DEFAULT_DIALOG_WIDTH = 720;
21762
21970
  var DEFAULT_DIALOG_HEIGHT = 520;
21763
- function dockedBox(corner, offsetX, offsetY, width, height) {
21764
- const horizontal = corner === "top-right" || corner === "bottom-right" ? { left: "auto", right: offsetX } : { left: offsetX, right: "auto" };
21765
- const vertical = corner === "bottom-left" || corner === "bottom-right" ? { top: "auto", bottom: offsetY } : { top: offsetY, bottom: "auto" };
21766
- return { ...horizontal, ...vertical, width, height };
21971
+ var MAP_ANCHOR_DEFAULTS = { align: "bottom-left", inset: 16 };
21972
+ function mergeAnchor(base, override) {
21973
+ if (base === void 0 && override === void 0) return void 0;
21974
+ return {
21975
+ ...base,
21976
+ ...override,
21977
+ inset: override?.inset !== void 0 ? override.inset : base?.inset
21978
+ };
21767
21979
  }
21980
+ var CHEVRON_PATHS2 = {
21981
+ up: "M3 11l5-6 5 6",
21982
+ down: "M3 5l5 6 5-6",
21983
+ left: "M11 3 5 8l6 5",
21984
+ right: "M5 3l6 5-6 5"
21985
+ };
21768
21986
  function PanoMapDialogShell({
21769
21987
  open,
21770
21988
  onOpenChange,
@@ -21876,11 +22094,9 @@ function PanoMapOverlay({
21876
22094
  placementSceneId,
21877
22095
  presentationOverride,
21878
22096
  presentation: presentationModeProp,
21879
- corner: cornerProp,
22097
+ anchor: anchorProp,
21880
22098
  width: widthProp,
21881
22099
  height: heightProp,
21882
- offsetX: offsetXProp,
21883
- offsetY: offsetYProp,
21884
22100
  dialogWidth = DEFAULT_DIALOG_WIDTH,
21885
22101
  dialogHeight = DEFAULT_DIALOG_HEIGHT,
21886
22102
  interaction,
@@ -21894,23 +22110,22 @@ function PanoMapOverlay({
21894
22110
  title = "Map",
21895
22111
  className,
21896
22112
  style,
22113
+ toggle,
21897
22114
  editorSurface = false
21898
22115
  }) {
21899
22116
  const { size } = usePanoOverlay();
21900
22117
  const [open, setOpen] = useState29(false);
21901
22118
  const [collapsed, setCollapsed] = useState29(false);
21902
22119
  const basePresentation = presentationOverride ?? map.presentation ?? {
21903
- mode: "docked",
21904
- corner: "bottom-left",
22120
+ mode: "panel",
21905
22121
  width: 220,
21906
- height: 160,
21907
- offsetX: 16,
21908
- offsetY: 16
22122
+ height: 160
21909
22123
  };
21910
22124
  const presentationMode = presentationModeProp ?? basePresentation.mode;
21911
- const corner = cornerProp ?? basePresentation.corner ?? "bottom-left";
21912
- const offsetX = offsetXProp ?? basePresentation.offsetX ?? 16;
21913
- const offsetY = offsetYProp ?? basePresentation.offsetY ?? 16;
22125
+ const resolvedAnchor = resolveOverlayAnchor(
22126
+ mergeAnchor(basePresentation.anchor, anchorProp),
22127
+ MAP_ANCHOR_DEFAULTS
22128
+ );
21914
22129
  const width = widthProp ?? basePresentation.width ?? (presentationMode === "dialog" ? 48 : 220);
21915
22130
  const height = heightProp ?? basePresentation.height ?? (presentationMode === "dialog" ? 48 : 160);
21916
22131
  const resolvedMap = useMemo31(() => {
@@ -21942,15 +22157,15 @@ function PanoMapOverlay({
21942
22157
  }
21943
22158
  };
21944
22159
  const editorBox = useMemo31(() => {
21945
- const panelWidth2 = widthProp !== void 0 && Number.isFinite(widthProp) && widthProp > 0 ? widthProp : Math.min(420, Math.max(240, size.width * 0.45));
21946
- const panelHeight2 = heightProp !== void 0 && Number.isFinite(heightProp) && heightProp > 0 ? heightProp : Math.min(320, Math.max(180, size.height * 0.45));
22160
+ const panelWidth = widthProp !== void 0 && Number.isFinite(widthProp) && widthProp > 0 ? widthProp : Math.min(420, Math.max(240, size.width * 0.45));
22161
+ const panelHeight = heightProp !== void 0 && Number.isFinite(heightProp) && heightProp > 0 ? heightProp : Math.min(320, Math.max(180, size.height * 0.45));
21947
22162
  return {
21948
22163
  left: 16,
21949
22164
  right: "auto",
21950
22165
  top: "auto",
21951
22166
  bottom: 16,
21952
- width: panelWidth2,
21953
- height: panelHeight2
22167
+ width: panelWidth,
22168
+ height: panelHeight
21954
22169
  };
21955
22170
  }, [heightProp, size.height, size.width, widthProp]);
21956
22171
  if (editorSurface) {
@@ -21970,7 +22185,7 @@ function PanoMapOverlay({
21970
22185
  );
21971
22186
  }
21972
22187
  if (presentationMode === "dialog") {
21973
- const launcherBox = dockedBox(corner, offsetX, offsetY, width, height);
22188
+ const launcherBox = overlayBoxStyle(resolvedAnchor.align, resolvedAnchor.inset, width, height);
21974
22189
  return /* @__PURE__ */ jsxs25("div", { className: ["pano-map-overlay", className].filter(Boolean).join(" "), "data-pano-map-overlay": "", children: [
21975
22190
  /* @__PURE__ */ jsx44(
21976
22191
  "button",
@@ -22002,29 +22217,63 @@ function PanoMapOverlay({
22002
22217
  )
22003
22218
  ] });
22004
22219
  }
22005
- const panelWidth = collapsed ? Math.max(44, Math.min(width, 56)) : width;
22006
- const panelHeight = collapsed ? Math.max(44, Math.min(height, 56)) : height;
22007
- const box = dockedBox(corner, offsetX, offsetY, panelWidth, panelHeight);
22008
- return /* @__PURE__ */ jsx44(
22220
+ const collapseEnabled = showCollapseControl ?? map.controls?.collapse === true;
22221
+ const box = overlayBoxStyle(resolvedAnchor.align, resolvedAnchor.inset, width, height);
22222
+ const arrow = overlayChevron(resolvedAnchor.collapseEdge, collapsed);
22223
+ return /* @__PURE__ */ jsxs25(
22009
22224
  "div",
22010
22225
  {
22011
- className: ["pano-map-overlay", className].filter(Boolean).join(" "),
22226
+ className: ["pano-overlay-anchor", "pano-map-overlay", className].filter(Boolean).join(" "),
22012
22227
  style: {
22013
22228
  position: "absolute",
22014
- pointerEvents: "auto",
22229
+ pointerEvents: collapseEnabled && collapsed ? "none" : "auto",
22230
+ ["--pano-overlay-dock-inset"]: resolvedAnchor.dockInset,
22231
+ ["--pano-overlay-anchor-radius"]: "8px",
22015
22232
  ...box,
22016
22233
  ...style
22017
22234
  },
22018
22235
  "data-pano-map-overlay": "",
22019
- "data-presentation": "docked",
22020
- children: /* @__PURE__ */ jsx44(
22021
- PanoMap,
22022
- {
22023
- ...mapProps,
22024
- collapsed,
22025
- onCollapsedChange: setCollapsed
22026
- }
22027
- )
22236
+ "data-presentation": "panel",
22237
+ "data-collapse-edge": resolvedAnchor.collapseEdge,
22238
+ "data-collapsed": collapseEnabled && collapsed ? "true" : "false",
22239
+ children: [
22240
+ /* @__PURE__ */ jsx44(
22241
+ "div",
22242
+ {
22243
+ className: "pano-map-overlay-surface",
22244
+ "aria-hidden": collapseEnabled && collapsed ? true : void 0,
22245
+ ...collapseEnabled && collapsed ? { inert: true } : null,
22246
+ style: { height: "100%", width: "100%" },
22247
+ children: /* @__PURE__ */ jsx44(PanoMap, { ...mapProps, showCollapseControl: false })
22248
+ }
22249
+ ),
22250
+ collapseEnabled ? /* @__PURE__ */ jsx44(
22251
+ "button",
22252
+ {
22253
+ type: "button",
22254
+ className: ["pano-map-control", "pano-overlay-anchor-toggle", toggle?.className].filter(Boolean).join(" "),
22255
+ "aria-label": collapsed ? "Expand map" : "Collapse map",
22256
+ "aria-pressed": !collapsed,
22257
+ style: toggle?.style,
22258
+ onClick: () => setCollapsed((value) => !value),
22259
+ children: toggle?.icon === void 0 ? /* @__PURE__ */ jsx44(
22260
+ "svg",
22261
+ {
22262
+ "aria-hidden": true,
22263
+ fill: "none",
22264
+ height: "16",
22265
+ stroke: "currentColor",
22266
+ strokeLinecap: "round",
22267
+ strokeLinejoin: "round",
22268
+ strokeWidth: "1.75",
22269
+ viewBox: "0 0 16 16",
22270
+ width: "16",
22271
+ children: /* @__PURE__ */ jsx44("path", { d: CHEVRON_PATHS2[arrow] })
22272
+ }
22273
+ ) : resolveOverlayToggleIcon(toggle.icon, collapsed)
22274
+ }
22275
+ ) : null
22276
+ ]
22028
22277
  }
22029
22278
  );
22030
22279
  }
@@ -22051,6 +22300,7 @@ export {
22051
22300
  MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY,
22052
22301
  MouseControls,
22053
22302
  PANO_FILTER_PRESETS,
22303
+ PANO_OVERLAY_ALIGNS,
22054
22304
  PanoContextMenu,
22055
22305
  PanoEvents,
22056
22306
  PanoFilter,
@@ -22086,9 +22336,13 @@ export {
22086
22336
  headingFromVector,
22087
22337
  horizontalFov,
22088
22338
  imagePointToContainer,
22339
+ isOverlayCorner,
22089
22340
  normalizePanoPosition,
22090
22341
  normalizePanoYaw,
22091
22342
  panoPositionToVector3,
22343
+ parseOverlayAnchor,
22344
+ resolveOverlayAnchor,
22345
+ resolveOverlayToggleIcon,
22092
22346
  resolvePanoContextMenuEntries,
22093
22347
  sectorRotation,
22094
22348
  unwrapPolygonVertices,