@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.cjs CHANGED
@@ -2270,6 +2270,7 @@ __export(index_exports, {
2270
2270
  MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY: () => MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY,
2271
2271
  MouseControls: () => MouseControls,
2272
2272
  PANO_FILTER_PRESETS: () => PANO_FILTER_PRESETS,
2273
+ PANO_OVERLAY_ALIGNS: () => PANO_OVERLAY_ALIGNS,
2273
2274
  PanoContextMenu: () => PanoContextMenu,
2274
2275
  PanoEvents: () => PanoEvents,
2275
2276
  PanoFilter: () => PanoFilter,
@@ -2305,9 +2306,13 @@ __export(index_exports, {
2305
2306
  headingFromVector: () => headingFromVector,
2306
2307
  horizontalFov: () => horizontalFov,
2307
2308
  imagePointToContainer: () => imagePointToContainer,
2309
+ isOverlayCorner: () => isOverlayCorner,
2308
2310
  normalizePanoPosition: () => normalizePanoPosition,
2309
2311
  normalizePanoYaw: () => normalizePanoYaw,
2310
2312
  panoPositionToVector3: () => panoPositionToVector3,
2313
+ parseOverlayAnchor: () => parseOverlayAnchor,
2314
+ resolveOverlayAnchor: () => resolveOverlayAnchor,
2315
+ resolveOverlayToggleIcon: () => resolveOverlayToggleIcon,
2311
2316
  resolvePanoContextMenuEntries: () => resolvePanoContextMenuEntries,
2312
2317
  sectorRotation: () => sectorRotation,
2313
2318
  unwrapPolygonVertices: () => unwrapPolygonVertices,
@@ -9348,6 +9353,339 @@ function hotspotAction(hotspot, trigger) {
9348
9353
  return hotspot.events?.[trigger] ?? (trigger === "click" ? hotspot.action : void 0);
9349
9354
  }
9350
9355
 
9356
+ // src/overlay/align.ts
9357
+ var PANO_OVERLAY_ALIGNS = [
9358
+ "top",
9359
+ "bottom",
9360
+ "left",
9361
+ "right",
9362
+ "top-left",
9363
+ "top-right",
9364
+ "bottom-left",
9365
+ "bottom-right"
9366
+ ];
9367
+ var ALIGN_LIST = PANO_OVERLAY_ALIGNS.join(", ");
9368
+ var SAFE = {
9369
+ top: "var(--pano-safe-top)",
9370
+ right: "var(--pano-safe-right)",
9371
+ bottom: "var(--pano-safe-bottom)",
9372
+ left: "var(--pano-safe-left)"
9373
+ };
9374
+ function cssLength(value) {
9375
+ return typeof value === "number" ? `${value}px` : value;
9376
+ }
9377
+ function isOverlayCorner(align) {
9378
+ switch (align) {
9379
+ case "top-left":
9380
+ case "top-right":
9381
+ case "bottom-left":
9382
+ case "bottom-right":
9383
+ return true;
9384
+ case "top":
9385
+ case "bottom":
9386
+ case "left":
9387
+ case "right":
9388
+ return false;
9389
+ default: {
9390
+ const exhaustive = align;
9391
+ return exhaustive;
9392
+ }
9393
+ }
9394
+ }
9395
+ function resolveOverlayAxis(align, axis) {
9396
+ switch (align) {
9397
+ case "top":
9398
+ case "bottom":
9399
+ return "horizontal";
9400
+ case "left":
9401
+ case "right":
9402
+ return "vertical";
9403
+ case "top-left":
9404
+ case "top-right":
9405
+ case "bottom-left":
9406
+ case "bottom-right":
9407
+ return axis ?? "horizontal";
9408
+ default: {
9409
+ const exhaustive = align;
9410
+ return exhaustive;
9411
+ }
9412
+ }
9413
+ }
9414
+ function overlayCollapseEdge(align, axis) {
9415
+ switch (align) {
9416
+ case "top":
9417
+ return "top";
9418
+ case "bottom":
9419
+ return "bottom";
9420
+ case "left":
9421
+ return "left";
9422
+ case "right":
9423
+ return "right";
9424
+ case "top-left":
9425
+ return resolveOverlayAxis(align, axis) === "horizontal" ? "top" : "left";
9426
+ case "top-right":
9427
+ return resolveOverlayAxis(align, axis) === "horizontal" ? "top" : "right";
9428
+ case "bottom-left":
9429
+ return resolveOverlayAxis(align, axis) === "horizontal" ? "bottom" : "left";
9430
+ case "bottom-right":
9431
+ return resolveOverlayAxis(align, axis) === "horizontal" ? "bottom" : "right";
9432
+ default: {
9433
+ const exhaustive = align;
9434
+ return exhaustive;
9435
+ }
9436
+ }
9437
+ }
9438
+ function overlayChevron(edge, collapsed) {
9439
+ switch (edge) {
9440
+ case "top":
9441
+ return collapsed ? "down" : "up";
9442
+ case "bottom":
9443
+ return collapsed ? "up" : "down";
9444
+ case "left":
9445
+ return collapsed ? "right" : "left";
9446
+ case "right":
9447
+ return collapsed ? "left" : "right";
9448
+ default: {
9449
+ const exhaustive = edge;
9450
+ return exhaustive;
9451
+ }
9452
+ }
9453
+ }
9454
+ function overlayContentAlign(align, axis) {
9455
+ const resolved = resolveOverlayAxis(align, axis);
9456
+ if (resolved === "horizontal") {
9457
+ switch (align) {
9458
+ case "top-left":
9459
+ case "bottom-left":
9460
+ return "start";
9461
+ case "top-right":
9462
+ case "bottom-right":
9463
+ return "end";
9464
+ case "top":
9465
+ case "bottom":
9466
+ case "left":
9467
+ case "right":
9468
+ return "center";
9469
+ default: {
9470
+ const exhaustive = align;
9471
+ return exhaustive;
9472
+ }
9473
+ }
9474
+ }
9475
+ switch (align) {
9476
+ case "top-left":
9477
+ case "top-right":
9478
+ return "start";
9479
+ case "bottom-left":
9480
+ case "bottom-right":
9481
+ return "end";
9482
+ case "top":
9483
+ case "bottom":
9484
+ case "left":
9485
+ case "right":
9486
+ return "center";
9487
+ default: {
9488
+ const exhaustive = align;
9489
+ return exhaustive;
9490
+ }
9491
+ }
9492
+ }
9493
+ function resolveOverlayInset(inset, fallback) {
9494
+ const fallbackLength = cssLength(fallback);
9495
+ if (inset === void 0) {
9496
+ return {
9497
+ top: fallbackLength,
9498
+ right: fallbackLength,
9499
+ bottom: fallbackLength,
9500
+ left: fallbackLength
9501
+ };
9502
+ }
9503
+ if (typeof inset === "number" || typeof inset === "string") {
9504
+ const value = cssLength(inset);
9505
+ return { top: value, right: value, bottom: value, left: value };
9506
+ }
9507
+ return {
9508
+ top: cssLength(inset.top ?? fallback),
9509
+ right: cssLength(inset.right ?? fallback),
9510
+ bottom: cssLength(inset.bottom ?? fallback),
9511
+ left: cssLength(inset.left ?? fallback)
9512
+ };
9513
+ }
9514
+ function overlayDockInset(edge, inset) {
9515
+ switch (edge) {
9516
+ case "top":
9517
+ return inset.top;
9518
+ case "bottom":
9519
+ return inset.bottom;
9520
+ case "left":
9521
+ return inset.left;
9522
+ case "right":
9523
+ return inset.right;
9524
+ default: {
9525
+ const exhaustive = edge;
9526
+ return exhaustive;
9527
+ }
9528
+ }
9529
+ }
9530
+ function resolveOverlayAnchor(anchor, defaults) {
9531
+ const align = anchor?.align ?? defaults.align;
9532
+ const axis = resolveOverlayAxis(align, anchor?.axis);
9533
+ const inset = resolveOverlayInset(anchor?.inset, defaults.inset);
9534
+ const collapseEdge = overlayCollapseEdge(align, anchor?.axis);
9535
+ return {
9536
+ align,
9537
+ axis,
9538
+ inset,
9539
+ collapseEdge,
9540
+ contentAlign: overlayContentAlign(align, anchor?.axis),
9541
+ dockInset: overlayDockInset(collapseEdge, inset)
9542
+ };
9543
+ }
9544
+ function withSafe(inset, safeVariable) {
9545
+ return `calc(${inset} + ${safeVariable})`;
9546
+ }
9547
+ function maxBox(inset) {
9548
+ return {
9549
+ maxWidth: `calc(100% - ${inset.left} - ${inset.right} - ${SAFE.left} - ${SAFE.right})`,
9550
+ maxHeight: `calc(100% - ${inset.top} - ${inset.bottom} - ${SAFE.top} - ${SAFE.bottom})`
9551
+ };
9552
+ }
9553
+ function horizontalSpan(inset, width) {
9554
+ const left = withSafe(inset.left, SAFE.left);
9555
+ const right = withSafe(inset.right, SAFE.right);
9556
+ if (width === void 0) {
9557
+ return { left, right, width: void 0, marginInline: void 0 };
9558
+ }
9559
+ return { left, right, width, marginInline: "auto" };
9560
+ }
9561
+ function verticalSpan(inset, height) {
9562
+ const top = withSafe(inset.top, SAFE.top);
9563
+ const bottom = withSafe(inset.bottom, SAFE.bottom);
9564
+ if (height === void 0) {
9565
+ return { top, bottom, height: void 0, marginBlock: void 0 };
9566
+ }
9567
+ return { top, bottom, height, marginBlock: "auto" };
9568
+ }
9569
+ function overlayBoxStyle(align, inset, width, height) {
9570
+ const widthValue = width === void 0 ? void 0 : cssLength(width);
9571
+ const heightValue = height === void 0 ? void 0 : cssLength(height);
9572
+ const limits = maxBox(inset);
9573
+ switch (align) {
9574
+ case "top":
9575
+ return {
9576
+ ...horizontalSpan(inset, widthValue),
9577
+ top: withSafe(inset.top, SAFE.top),
9578
+ bottom: "auto",
9579
+ height: heightValue,
9580
+ ...limits
9581
+ };
9582
+ case "bottom":
9583
+ return {
9584
+ ...horizontalSpan(inset, widthValue),
9585
+ top: "auto",
9586
+ bottom: withSafe(inset.bottom, SAFE.bottom),
9587
+ height: heightValue,
9588
+ ...limits
9589
+ };
9590
+ case "left":
9591
+ return {
9592
+ ...verticalSpan(inset, heightValue),
9593
+ left: withSafe(inset.left, SAFE.left),
9594
+ right: "auto",
9595
+ width: widthValue,
9596
+ ...limits
9597
+ };
9598
+ case "right":
9599
+ return {
9600
+ ...verticalSpan(inset, heightValue),
9601
+ left: "auto",
9602
+ right: withSafe(inset.right, SAFE.right),
9603
+ width: widthValue,
9604
+ ...limits
9605
+ };
9606
+ case "top-left":
9607
+ return {
9608
+ top: withSafe(inset.top, SAFE.top),
9609
+ left: withSafe(inset.left, SAFE.left),
9610
+ right: "auto",
9611
+ bottom: "auto",
9612
+ width: widthValue,
9613
+ height: heightValue,
9614
+ ...limits
9615
+ };
9616
+ case "top-right":
9617
+ return {
9618
+ top: withSafe(inset.top, SAFE.top),
9619
+ right: withSafe(inset.right, SAFE.right),
9620
+ left: "auto",
9621
+ bottom: "auto",
9622
+ width: widthValue,
9623
+ height: heightValue,
9624
+ ...limits
9625
+ };
9626
+ case "bottom-left":
9627
+ return {
9628
+ bottom: withSafe(inset.bottom, SAFE.bottom),
9629
+ left: withSafe(inset.left, SAFE.left),
9630
+ top: "auto",
9631
+ right: "auto",
9632
+ width: widthValue,
9633
+ height: heightValue,
9634
+ ...limits
9635
+ };
9636
+ case "bottom-right":
9637
+ return {
9638
+ bottom: withSafe(inset.bottom, SAFE.bottom),
9639
+ right: withSafe(inset.right, SAFE.right),
9640
+ top: "auto",
9641
+ left: "auto",
9642
+ width: widthValue,
9643
+ height: heightValue,
9644
+ ...limits
9645
+ };
9646
+ default: {
9647
+ const exhaustive = align;
9648
+ return exhaustive;
9649
+ }
9650
+ }
9651
+ }
9652
+ function isRecord2(value) {
9653
+ return typeof value === "object" && value !== null && !Array.isArray(value);
9654
+ }
9655
+ function parseInsetLength(value, field) {
9656
+ if (typeof value === "number" && Number.isFinite(value)) return value;
9657
+ if (typeof value === "string" && value.trim()) return value;
9658
+ throw new Error(`${field}: expected a finite number or a nonempty string`);
9659
+ }
9660
+ function parseInset(value, field) {
9661
+ if (typeof value === "number" || typeof value === "string") {
9662
+ return parseInsetLength(value, field);
9663
+ }
9664
+ if (!isRecord2(value)) throw new Error(`${field}: expected a number, string, or object`);
9665
+ const sides = ["top", "right", "bottom", "left"];
9666
+ const inset = {};
9667
+ for (const side of sides) {
9668
+ if (value[side] === void 0) continue;
9669
+ inset[side] = parseInsetLength(value[side], `${field}.${side}`);
9670
+ }
9671
+ return inset;
9672
+ }
9673
+ function parseOverlayAnchor(value, field) {
9674
+ if (!isRecord2(value)) throw new Error(`${field}: expected an object`);
9675
+ const align = value.align === void 0 ? void 0 : typeof value.align === "string" && PANO_OVERLAY_ALIGNS.includes(value.align) ? value.align : (() => {
9676
+ throw new Error(`${field}.align: expected ${ALIGN_LIST}`);
9677
+ })();
9678
+ const axis = value.axis === void 0 ? void 0 : value.axis === "horizontal" || value.axis === "vertical" ? value.axis : (() => {
9679
+ throw new Error(`${field}.axis: expected "horizontal" or "vertical"`);
9680
+ })();
9681
+ const inset = value.inset === void 0 ? void 0 : parseInset(value.inset, `${field}.inset`);
9682
+ return {
9683
+ ...align === void 0 ? {} : { align },
9684
+ ...axis === void 0 ? {} : { axis },
9685
+ ...inset === void 0 ? {} : { inset }
9686
+ };
9687
+ }
9688
+
9351
9689
  // src/project-scenes.tsx
9352
9690
  var import_react54 = require("react");
9353
9691
 
@@ -19051,6 +19389,13 @@ function pushSample(samples, sample, maxCount = 8) {
19051
19389
  return next;
19052
19390
  }
19053
19391
 
19392
+ // src/overlay/toggle.ts
19393
+ function resolveOverlayToggleIcon(icon, collapsed) {
19394
+ if (icon === void 0) return void 0;
19395
+ if (typeof icon === "function") return icon({ collapsed });
19396
+ return icon;
19397
+ }
19398
+
19054
19399
  // src/pano-scene-menu.tsx
19055
19400
  var import_jsx_runtime41 = require("react/jsx-runtime");
19056
19401
  var SCENE_MENU_DRAG_THRESHOLD_PX = 6;
@@ -19071,37 +19416,15 @@ var DEFAULT_PANO_SCENE_MENU_APPEARANCE = {
19071
19416
  controlBackground: "rgba(255, 255, 255, 0.12)"
19072
19417
  };
19073
19418
  var DEFAULT_INSET = 12;
19074
- function cssLength(value) {
19075
- return typeof value === "number" ? `${value}px` : value;
19076
- }
19077
19419
  function resolveAppearance2(appearance) {
19078
19420
  return {
19079
19421
  ...DEFAULT_PANO_SCENE_MENU_APPEARANCE,
19080
19422
  ...appearance
19081
19423
  };
19082
19424
  }
19083
- function resolveInset(inset) {
19084
- if (inset === void 0) {
19085
- const value = cssLength(DEFAULT_INSET);
19086
- return { top: value, right: value, bottom: value, left: value };
19087
- }
19088
- if (typeof inset === "number" || typeof inset === "string") {
19089
- const value = cssLength(inset);
19090
- return { top: value, right: value, bottom: value, left: value };
19091
- }
19092
- return {
19093
- top: cssLength(inset.top ?? DEFAULT_INSET),
19094
- right: cssLength(inset.right ?? DEFAULT_INSET),
19095
- bottom: cssLength(inset.bottom ?? DEFAULT_INSET),
19096
- left: cssLength(inset.left ?? DEFAULT_INSET)
19097
- };
19098
- }
19099
19425
  function itemLabel(item) {
19100
19426
  return item.title?.trim() || item.id;
19101
19427
  }
19102
- function isHorizontal(placement) {
19103
- return placement === "top" || placement === "bottom";
19104
- }
19105
19428
  var CHEVRON_PATHS = {
19106
19429
  // Keep glyphs optically centered in the 16×16 viewBox.
19107
19430
  up: "M3 11l5-6 5 6",
@@ -19115,40 +19438,6 @@ function arrowChevron(direction, orientation2) {
19115
19438
  }
19116
19439
  return orientation2 === "horizontal" ? "right" : "down";
19117
19440
  }
19118
- function toggleChevron(placement, expanded) {
19119
- switch (placement) {
19120
- case "bottom":
19121
- return expanded ? "down" : "up";
19122
- case "top":
19123
- return expanded ? "up" : "down";
19124
- case "left":
19125
- return expanded ? "left" : "right";
19126
- case "right":
19127
- return expanded ? "right" : "left";
19128
- default: {
19129
- const exhaustive = placement;
19130
- void exhaustive;
19131
- return "down";
19132
- }
19133
- }
19134
- }
19135
- function dockInset(placement, inset) {
19136
- switch (placement) {
19137
- case "top":
19138
- return inset.top;
19139
- case "bottom":
19140
- return inset.bottom;
19141
- case "left":
19142
- return inset.left;
19143
- case "right":
19144
- return inset.right;
19145
- default: {
19146
- const exhaustive = placement;
19147
- void exhaustive;
19148
- return inset.bottom;
19149
- }
19150
- }
19151
- }
19152
19441
  function Chevron({ arrow }) {
19153
19442
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
19154
19443
  "svg",
@@ -19171,6 +19460,7 @@ function ControlButton({
19171
19460
  disabled,
19172
19461
  label,
19173
19462
  onClick,
19463
+ style,
19174
19464
  children
19175
19465
  }) {
19176
19466
  return /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
@@ -19183,108 +19473,18 @@ function ControlButton({
19183
19473
  onPointerDown: (event) => {
19184
19474
  event.stopPropagation();
19185
19475
  },
19476
+ style,
19186
19477
  title: label,
19187
19478
  type: "button",
19188
19479
  children
19189
19480
  }
19190
19481
  );
19191
19482
  }
19192
- function horizontalAlignStyle(align, inset, widthValue) {
19193
- const left = `calc(${inset.left} + var(--pano-safe-left))`;
19194
- const right = `calc(${inset.right} + var(--pano-safe-right))`;
19195
- if (!widthValue) {
19196
- return { left, right, width: void 0, marginInline: void 0 };
19197
- }
19198
- switch (align) {
19199
- case "start":
19200
- return { left, right: "auto", width: widthValue, marginInline: void 0 };
19201
- case "end":
19202
- return { left: "auto", right, width: widthValue, marginInline: void 0 };
19203
- case "center":
19204
- return { left, right, width: widthValue, marginInline: "auto" };
19205
- default: {
19206
- const exhaustive = align;
19207
- void exhaustive;
19208
- return { left, right, width: widthValue, marginInline: "auto" };
19209
- }
19210
- }
19211
- }
19212
- function verticalAlignStyle(align, inset, heightValue) {
19213
- const top = `calc(${inset.top} + var(--pano-safe-top))`;
19214
- const bottom = `calc(${inset.bottom} + var(--pano-safe-bottom))`;
19215
- if (!heightValue) {
19216
- return { top, bottom, height: void 0, marginBlock: void 0 };
19217
- }
19218
- switch (align) {
19219
- case "start":
19220
- return { top, bottom: "auto", height: heightValue, marginBlock: void 0 };
19221
- case "end":
19222
- return { top: "auto", bottom, height: heightValue, marginBlock: void 0 };
19223
- case "center":
19224
- return { top, bottom, height: heightValue, marginBlock: "auto" };
19225
- default: {
19226
- const exhaustive = align;
19227
- void exhaustive;
19228
- return { top, bottom, height: heightValue, marginBlock: "auto" };
19229
- }
19230
- }
19231
- }
19232
- function placementBoxStyle(placement, inset, width, height, align) {
19233
- const widthValue = width === void 0 ? void 0 : cssLength(width);
19234
- const heightValue = height === void 0 ? void 0 : cssLength(height);
19235
- const maxWidth = `calc(100% - ${inset.left} - ${inset.right} - var(--pano-safe-left) - var(--pano-safe-right))`;
19236
- const maxHeight = `calc(100% - ${inset.top} - ${inset.bottom} - var(--pano-safe-top) - var(--pano-safe-bottom))`;
19237
- switch (placement) {
19238
- case "top":
19239
- return {
19240
- ...horizontalAlignStyle(align, inset, widthValue),
19241
- top: `calc(${inset.top} + var(--pano-safe-top))`,
19242
- bottom: "auto",
19243
- maxWidth,
19244
- height: heightValue,
19245
- maxHeight
19246
- };
19247
- case "bottom":
19248
- return {
19249
- ...horizontalAlignStyle(align, inset, widthValue),
19250
- top: "auto",
19251
- bottom: `calc(${inset.bottom} + var(--pano-safe-bottom))`,
19252
- maxWidth,
19253
- height: heightValue,
19254
- maxHeight
19255
- };
19256
- case "left":
19257
- return {
19258
- ...verticalAlignStyle(align, inset, heightValue),
19259
- right: "auto",
19260
- left: `calc(${inset.left} + var(--pano-safe-left))`,
19261
- width: widthValue,
19262
- maxWidth,
19263
- maxHeight
19264
- };
19265
- case "right":
19266
- return {
19267
- ...verticalAlignStyle(align, inset, heightValue),
19268
- right: `calc(${inset.right} + var(--pano-safe-right))`,
19269
- left: "auto",
19270
- width: widthValue,
19271
- maxWidth,
19272
- maxHeight
19273
- };
19274
- default: {
19275
- const exhaustive = placement;
19276
- void exhaustive;
19277
- return {};
19278
- }
19279
- }
19280
- }
19281
19483
  function PanoSceneMenu({
19282
19484
  items,
19283
19485
  activeId = null,
19284
19486
  onSelect,
19285
- placement = "bottom",
19286
- align = "center",
19287
- inset,
19487
+ anchor,
19288
19488
  width,
19289
19489
  height,
19290
19490
  collapsible = false,
@@ -19301,15 +19501,19 @@ function PanoSceneMenu({
19301
19501
  previousLabel = "Previous scene",
19302
19502
  nextLabel = "Next scene",
19303
19503
  expandLabel = "Show scenes",
19304
- collapseLabel = "Hide scenes"
19504
+ collapseLabel = "Hide scenes",
19505
+ toggle
19305
19506
  }) {
19306
19507
  const listId = (0, import_react63.useId)();
19307
19508
  const listRef = (0, import_react63.useRef)(null);
19308
19509
  const trackRef = (0, import_react63.useRef)(null);
19309
19510
  const itemRefs = (0, import_react63.useRef)(/* @__PURE__ */ new Map());
19310
19511
  const resolved = resolveAppearance2(appearance);
19311
- const resolvedInset = resolveInset(inset);
19312
- const horizontal = isHorizontal(placement);
19512
+ const resolvedAnchor = resolveOverlayAnchor(anchor, {
19513
+ align: "bottom",
19514
+ inset: DEFAULT_INSET
19515
+ });
19516
+ const horizontal = resolvedAnchor.axis === "horizontal";
19313
19517
  const customDrag = dragMotionProp !== false;
19314
19518
  const dragMotion = resolveDragMotion(
19315
19519
  dragMotionProp === false ? false : dragMotionProp
@@ -19482,15 +19686,15 @@ function PanoSceneMenu({
19482
19686
  "--pano-scene-menu-label-align": resolved.labelAlign,
19483
19687
  "--pano-scene-menu-font-size": cssLength(resolved.fontSize),
19484
19688
  "--pano-scene-menu-control-color": resolved.controlColor,
19485
- "--pano-scene-menu-control-background": resolved.controlBackground,
19486
- "--pano-scene-menu-dock-inset": dockInset(placement, resolvedInset)
19689
+ "--pano-scene-menu-control-background": resolved.controlBackground
19487
19690
  };
19488
19691
  return {
19489
19692
  ...vars,
19490
- ...placementBoxStyle(placement, resolvedInset, width, height, align),
19693
+ ["--pano-overlay-dock-inset"]: resolvedAnchor.dockInset,
19694
+ ...overlayBoxStyle(resolvedAnchor.align, resolvedAnchor.inset, width, height),
19491
19695
  ...style
19492
19696
  };
19493
- }, [align, height, placement, resolved, resolvedInset, style, width]);
19697
+ }, [height, resolved, resolvedAnchor, style, width]);
19494
19698
  const setExpanded = (next) => {
19495
19699
  if (!collapsible) {
19496
19700
  return;
@@ -19696,13 +19900,13 @@ function PanoSceneMenu({
19696
19900
  "div",
19697
19901
  {
19698
19902
  "aria-label": label,
19699
- className: cn("pano-scene-menu", className),
19700
- "data-align": align,
19903
+ className: cn("pano-overlay-anchor", "pano-scene-menu", className),
19904
+ "data-align": resolvedAnchor.contentAlign,
19905
+ "data-collapse-edge": resolvedAnchor.collapseEdge,
19906
+ "data-collapsed": collapsible && !expanded ? "true" : "false",
19701
19907
  "data-collapsible": collapsible ? "true" : void 0,
19702
19908
  "data-drag-motion": customDrag ? "custom" : "native",
19703
- "data-expanded": expanded ? "true" : "false",
19704
19909
  "data-orientation": orientation2,
19705
- "data-placement": placement,
19706
19910
  role: "group",
19707
19911
  style: rootStyle,
19708
19912
  children: [
@@ -19807,10 +20011,19 @@ function PanoSceneMenu({
19807
20011
  collapsible ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
19808
20012
  ControlButton,
19809
20013
  {
19810
- className: "pano-scene-menu-toggle",
20014
+ className: cn(
20015
+ "pano-scene-menu-toggle pano-overlay-anchor-toggle",
20016
+ toggle?.className
20017
+ ),
19811
20018
  label: expanded ? collapseLabel : expandLabel,
19812
20019
  onClick: () => setExpanded(!expanded),
19813
- children: /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(Chevron, { arrow: toggleChevron(placement, expanded) })
20020
+ style: toggle?.style,
20021
+ children: toggle?.icon === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime41.jsx)(
20022
+ Chevron,
20023
+ {
20024
+ arrow: overlayChevron(resolvedAnchor.collapseEdge, !expanded)
20025
+ }
20026
+ ) : resolveOverlayToggleIcon(toggle.icon, !expanded)
19814
20027
  }
19815
20028
  ) : null
19816
20029
  ]
@@ -21548,11 +21761,21 @@ var import_react68 = require("react");
21548
21761
  var import_jsx_runtime44 = require("react/jsx-runtime");
21549
21762
  var DEFAULT_DIALOG_WIDTH = 720;
21550
21763
  var DEFAULT_DIALOG_HEIGHT = 520;
21551
- function dockedBox(corner, offsetX, offsetY, width, height) {
21552
- const horizontal = corner === "top-right" || corner === "bottom-right" ? { left: "auto", right: offsetX } : { left: offsetX, right: "auto" };
21553
- const vertical = corner === "bottom-left" || corner === "bottom-right" ? { top: "auto", bottom: offsetY } : { top: offsetY, bottom: "auto" };
21554
- return { ...horizontal, ...vertical, width, height };
21764
+ var MAP_ANCHOR_DEFAULTS = { align: "bottom-left", inset: 16 };
21765
+ function mergeAnchor(base, override) {
21766
+ if (base === void 0 && override === void 0) return void 0;
21767
+ return {
21768
+ ...base,
21769
+ ...override,
21770
+ inset: override?.inset !== void 0 ? override.inset : base?.inset
21771
+ };
21555
21772
  }
21773
+ var CHEVRON_PATHS2 = {
21774
+ up: "M3 11l5-6 5 6",
21775
+ down: "M3 5l5 6 5-6",
21776
+ left: "M11 3 5 8l6 5",
21777
+ right: "M5 3l6 5-6 5"
21778
+ };
21556
21779
  function PanoMapDialogShell({
21557
21780
  open,
21558
21781
  onOpenChange,
@@ -21664,11 +21887,9 @@ function PanoMapOverlay({
21664
21887
  placementSceneId,
21665
21888
  presentationOverride,
21666
21889
  presentation: presentationModeProp,
21667
- corner: cornerProp,
21890
+ anchor: anchorProp,
21668
21891
  width: widthProp,
21669
21892
  height: heightProp,
21670
- offsetX: offsetXProp,
21671
- offsetY: offsetYProp,
21672
21893
  dialogWidth = DEFAULT_DIALOG_WIDTH,
21673
21894
  dialogHeight = DEFAULT_DIALOG_HEIGHT,
21674
21895
  interaction,
@@ -21682,23 +21903,22 @@ function PanoMapOverlay({
21682
21903
  title = "Map",
21683
21904
  className,
21684
21905
  style,
21906
+ toggle,
21685
21907
  editorSurface = false
21686
21908
  }) {
21687
21909
  const { size } = usePanoOverlay();
21688
21910
  const [open, setOpen] = (0, import_react68.useState)(false);
21689
21911
  const [collapsed, setCollapsed] = (0, import_react68.useState)(false);
21690
21912
  const basePresentation = presentationOverride ?? map.presentation ?? {
21691
- mode: "docked",
21692
- corner: "bottom-left",
21913
+ mode: "panel",
21693
21914
  width: 220,
21694
- height: 160,
21695
- offsetX: 16,
21696
- offsetY: 16
21915
+ height: 160
21697
21916
  };
21698
21917
  const presentationMode = presentationModeProp ?? basePresentation.mode;
21699
- const corner = cornerProp ?? basePresentation.corner ?? "bottom-left";
21700
- const offsetX = offsetXProp ?? basePresentation.offsetX ?? 16;
21701
- const offsetY = offsetYProp ?? basePresentation.offsetY ?? 16;
21918
+ const resolvedAnchor = resolveOverlayAnchor(
21919
+ mergeAnchor(basePresentation.anchor, anchorProp),
21920
+ MAP_ANCHOR_DEFAULTS
21921
+ );
21702
21922
  const width = widthProp ?? basePresentation.width ?? (presentationMode === "dialog" ? 48 : 220);
21703
21923
  const height = heightProp ?? basePresentation.height ?? (presentationMode === "dialog" ? 48 : 160);
21704
21924
  const resolvedMap = (0, import_react68.useMemo)(() => {
@@ -21730,15 +21950,15 @@ function PanoMapOverlay({
21730
21950
  }
21731
21951
  };
21732
21952
  const editorBox = (0, import_react68.useMemo)(() => {
21733
- const panelWidth2 = widthProp !== void 0 && Number.isFinite(widthProp) && widthProp > 0 ? widthProp : Math.min(420, Math.max(240, size.width * 0.45));
21734
- const panelHeight2 = heightProp !== void 0 && Number.isFinite(heightProp) && heightProp > 0 ? heightProp : Math.min(320, Math.max(180, size.height * 0.45));
21953
+ const panelWidth = widthProp !== void 0 && Number.isFinite(widthProp) && widthProp > 0 ? widthProp : Math.min(420, Math.max(240, size.width * 0.45));
21954
+ const panelHeight = heightProp !== void 0 && Number.isFinite(heightProp) && heightProp > 0 ? heightProp : Math.min(320, Math.max(180, size.height * 0.45));
21735
21955
  return {
21736
21956
  left: 16,
21737
21957
  right: "auto",
21738
21958
  top: "auto",
21739
21959
  bottom: 16,
21740
- width: panelWidth2,
21741
- height: panelHeight2
21960
+ width: panelWidth,
21961
+ height: panelHeight
21742
21962
  };
21743
21963
  }, [heightProp, size.height, size.width, widthProp]);
21744
21964
  if (editorSurface) {
@@ -21758,7 +21978,7 @@ function PanoMapOverlay({
21758
21978
  );
21759
21979
  }
21760
21980
  if (presentationMode === "dialog") {
21761
- const launcherBox = dockedBox(corner, offsetX, offsetY, width, height);
21981
+ const launcherBox = overlayBoxStyle(resolvedAnchor.align, resolvedAnchor.inset, width, height);
21762
21982
  return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)("div", { className: ["pano-map-overlay", className].filter(Boolean).join(" "), "data-pano-map-overlay": "", children: [
21763
21983
  /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
21764
21984
  "button",
@@ -21790,29 +22010,63 @@ function PanoMapOverlay({
21790
22010
  )
21791
22011
  ] });
21792
22012
  }
21793
- const panelWidth = collapsed ? Math.max(44, Math.min(width, 56)) : width;
21794
- const panelHeight = collapsed ? Math.max(44, Math.min(height, 56)) : height;
21795
- const box = dockedBox(corner, offsetX, offsetY, panelWidth, panelHeight);
21796
- return /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
22013
+ const collapseEnabled = showCollapseControl ?? map.controls?.collapse === true;
22014
+ const box = overlayBoxStyle(resolvedAnchor.align, resolvedAnchor.inset, width, height);
22015
+ const arrow = overlayChevron(resolvedAnchor.collapseEdge, collapsed);
22016
+ return /* @__PURE__ */ (0, import_jsx_runtime44.jsxs)(
21797
22017
  "div",
21798
22018
  {
21799
- className: ["pano-map-overlay", className].filter(Boolean).join(" "),
22019
+ className: ["pano-overlay-anchor", "pano-map-overlay", className].filter(Boolean).join(" "),
21800
22020
  style: {
21801
22021
  position: "absolute",
21802
- pointerEvents: "auto",
22022
+ pointerEvents: collapseEnabled && collapsed ? "none" : "auto",
22023
+ ["--pano-overlay-dock-inset"]: resolvedAnchor.dockInset,
22024
+ ["--pano-overlay-anchor-radius"]: "8px",
21803
22025
  ...box,
21804
22026
  ...style
21805
22027
  },
21806
22028
  "data-pano-map-overlay": "",
21807
- "data-presentation": "docked",
21808
- children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
21809
- PanoMap,
21810
- {
21811
- ...mapProps,
21812
- collapsed,
21813
- onCollapsedChange: setCollapsed
21814
- }
21815
- )
22029
+ "data-presentation": "panel",
22030
+ "data-collapse-edge": resolvedAnchor.collapseEdge,
22031
+ "data-collapsed": collapseEnabled && collapsed ? "true" : "false",
22032
+ children: [
22033
+ /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
22034
+ "div",
22035
+ {
22036
+ className: "pano-map-overlay-surface",
22037
+ "aria-hidden": collapseEnabled && collapsed ? true : void 0,
22038
+ ...collapseEnabled && collapsed ? { inert: true } : null,
22039
+ style: { height: "100%", width: "100%" },
22040
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(PanoMap, { ...mapProps, showCollapseControl: false })
22041
+ }
22042
+ ),
22043
+ collapseEnabled ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
22044
+ "button",
22045
+ {
22046
+ type: "button",
22047
+ className: ["pano-map-control", "pano-overlay-anchor-toggle", toggle?.className].filter(Boolean).join(" "),
22048
+ "aria-label": collapsed ? "Expand map" : "Collapse map",
22049
+ "aria-pressed": !collapsed,
22050
+ style: toggle?.style,
22051
+ onClick: () => setCollapsed((value) => !value),
22052
+ children: toggle?.icon === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime44.jsx)(
22053
+ "svg",
22054
+ {
22055
+ "aria-hidden": true,
22056
+ fill: "none",
22057
+ height: "16",
22058
+ stroke: "currentColor",
22059
+ strokeLinecap: "round",
22060
+ strokeLinejoin: "round",
22061
+ strokeWidth: "1.75",
22062
+ viewBox: "0 0 16 16",
22063
+ width: "16",
22064
+ children: /* @__PURE__ */ (0, import_jsx_runtime44.jsx)("path", { d: CHEVRON_PATHS2[arrow] })
22065
+ }
22066
+ ) : resolveOverlayToggleIcon(toggle.icon, collapsed)
22067
+ }
22068
+ ) : null
22069
+ ]
21816
22070
  }
21817
22071
  );
21818
22072
  }
@@ -21840,6 +22094,7 @@ function PanoMapOverlay({
21840
22094
  MIN_PANO_CONTEXT_MENU_BACKGROUND_OPACITY,
21841
22095
  MouseControls,
21842
22096
  PANO_FILTER_PRESETS,
22097
+ PANO_OVERLAY_ALIGNS,
21843
22098
  PanoContextMenu,
21844
22099
  PanoEvents,
21845
22100
  PanoFilter,
@@ -21875,9 +22130,13 @@ function PanoMapOverlay({
21875
22130
  headingFromVector,
21876
22131
  horizontalFov,
21877
22132
  imagePointToContainer,
22133
+ isOverlayCorner,
21878
22134
  normalizePanoPosition,
21879
22135
  normalizePanoYaw,
21880
22136
  panoPositionToVector3,
22137
+ parseOverlayAnchor,
22138
+ resolveOverlayAnchor,
22139
+ resolveOverlayToggleIcon,
21881
22140
  resolvePanoContextMenuEntries,
21882
22141
  sectorRotation,
21883
22142
  unwrapPolygonVertices,