@ericchen1990/pano-view 3.7.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/README.md +675 -71
- package/README.zh-CN.md +558 -42
- package/dist/data.cjs +239 -7
- package/dist/data.cjs.map +1 -1
- package/dist/data.d.mts +120 -2
- package/dist/data.d.ts +120 -2
- package/dist/data.mjs +239 -7
- package/dist/data.mjs.map +1 -1
- package/dist/index.cjs +2385 -310
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +335 -2
- package/dist/index.d.ts +335 -2
- package/dist/index.mjs +2393 -306
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +399 -1
- package/dist/ui.cjs +1 -1
- package/dist/ui.cjs.map +1 -1
- package/dist/ui.mjs +1 -1
- package/dist/ui.mjs.map +1 -1
- package/package.json +1 -1
package/dist/data.d.mts
CHANGED
|
@@ -209,7 +209,7 @@ type SphereTileScene = Omit<SphereTileProps, "loadMode" | "visible" | "onReady"
|
|
|
209
209
|
|
|
210
210
|
type AssetUrlContext = {
|
|
211
211
|
defaultUrl: string;
|
|
212
|
-
kind: "tile" | "preview" | "hotspot";
|
|
212
|
+
kind: "tile" | "preview" | "hotspot" | "map";
|
|
213
213
|
sceneId: string;
|
|
214
214
|
hotspotId?: string;
|
|
215
215
|
};
|
|
@@ -531,6 +531,122 @@ type ProjectHotspotDefinition = HotspotDefinition & {
|
|
|
531
531
|
};
|
|
532
532
|
declare function resolveHotspotAssets(hotspot: ProjectHotspotDefinition, baseUrl: string, sceneId: string, resolver?: AssetUrlResolver): ProjectHotspotDefinition;
|
|
533
533
|
|
|
534
|
+
/**
|
|
535
|
+
* Shared drag inertia and edge bounce for map pan and scene-menu scrolling.
|
|
536
|
+
* Axis motion is pure; hosts own pointer capture and rAF loops.
|
|
537
|
+
*/
|
|
538
|
+
type PanoDragMotion = {
|
|
539
|
+
/** Continue sliding after release from pointer velocity. Defaults to true. */
|
|
540
|
+
inertia?: boolean;
|
|
541
|
+
/** Rubber-band past edges while dragging; spring back on release. Defaults to true. */
|
|
542
|
+
bounce?: boolean;
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
type PanoMapCorner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
546
|
+
type PanoMapGradientStop = {
|
|
547
|
+
offset: number;
|
|
548
|
+
color: string;
|
|
549
|
+
opacity?: number;
|
|
550
|
+
};
|
|
551
|
+
type PanoMapSectorGradient = {
|
|
552
|
+
type: "radial" | "linear";
|
|
553
|
+
stops: PanoMapGradientStop[];
|
|
554
|
+
};
|
|
555
|
+
type PanoMapSectorDefinition = {
|
|
556
|
+
/** Sector opening angle in degrees. */
|
|
557
|
+
angle: number;
|
|
558
|
+
/** Sector radius in CSS pixels; does not scale with the floor image. */
|
|
559
|
+
radius: number;
|
|
560
|
+
/** When true, sector angle follows the current horizontal FOV. */
|
|
561
|
+
followFov?: boolean;
|
|
562
|
+
/** Solid fill color when `gradient` is omitted. */
|
|
563
|
+
color?: string;
|
|
564
|
+
/**
|
|
565
|
+
* Overall fill opacity in [0, 1].
|
|
566
|
+
* Applies in both solid and gradient modes; gradient stop opacities only
|
|
567
|
+
* describe the relative fade shape and are normalized against this value.
|
|
568
|
+
*/
|
|
569
|
+
opacity?: number;
|
|
570
|
+
/** Optional sector outline color. */
|
|
571
|
+
strokeColor?: string;
|
|
572
|
+
/** Outline width in CSS pixels. */
|
|
573
|
+
strokeWidth?: number;
|
|
574
|
+
/** Outline opacity in [0, 1]. Defaults to 1 when stroke is set. */
|
|
575
|
+
strokeOpacity?: number;
|
|
576
|
+
/**
|
|
577
|
+
* Optional fill gradient. When omitted, the sector uses a solid `color`.
|
|
578
|
+
* Stop colors are independent of `color` unless the author keeps them in sync.
|
|
579
|
+
*/
|
|
580
|
+
gradient?: PanoMapSectorGradient;
|
|
581
|
+
};
|
|
582
|
+
type PanoMapSpotDefinition = {
|
|
583
|
+
id: string;
|
|
584
|
+
sceneId: string;
|
|
585
|
+
/** Normalized image X in [0, 1], origin at the top-left. */
|
|
586
|
+
x: number;
|
|
587
|
+
/** Normalized image Y in [0, 1], origin at the top-left. */
|
|
588
|
+
y: number;
|
|
589
|
+
/**
|
|
590
|
+
* Clockwise degrees from image up when viewer yaw is 0.
|
|
591
|
+
* Runtime sector rotation is `heading + yaw`.
|
|
592
|
+
*/
|
|
593
|
+
heading: number;
|
|
594
|
+
icon?: string;
|
|
595
|
+
};
|
|
596
|
+
type PanoMapControlsDefinition = {
|
|
597
|
+
/** Show expand/collapse on a docked panel. */
|
|
598
|
+
collapse?: boolean;
|
|
599
|
+
/** Show zoom-in and zoom-out buttons. */
|
|
600
|
+
zoom?: boolean;
|
|
601
|
+
};
|
|
602
|
+
type PanoMapDockedPresentation = {
|
|
603
|
+
mode: "docked";
|
|
604
|
+
corner?: PanoMapCorner;
|
|
605
|
+
width?: number;
|
|
606
|
+
height?: number;
|
|
607
|
+
offsetX?: number;
|
|
608
|
+
offsetY?: number;
|
|
609
|
+
};
|
|
610
|
+
type PanoMapDialogPresentation = {
|
|
611
|
+
mode: "dialog";
|
|
612
|
+
corner?: PanoMapCorner;
|
|
613
|
+
offsetX?: number;
|
|
614
|
+
offsetY?: number;
|
|
615
|
+
width?: number;
|
|
616
|
+
height?: number;
|
|
617
|
+
};
|
|
618
|
+
type PanoMapPresentation = PanoMapDockedPresentation | PanoMapDialogPresentation;
|
|
619
|
+
type PanoMapDefinition = {
|
|
620
|
+
image: string;
|
|
621
|
+
/**
|
|
622
|
+
* Pointer interaction with the floor image.
|
|
623
|
+
* - `fixed`: fit-only; no wheel/pan zoom (zoom buttons are also suppressed).
|
|
624
|
+
* - `zoom`: wheel/pan zoom and optional zoom buttons.
|
|
625
|
+
*/
|
|
626
|
+
interaction?: "fixed" | "zoom";
|
|
627
|
+
presentation?: PanoMapPresentation;
|
|
628
|
+
controls?: PanoMapControlsDefinition;
|
|
629
|
+
/** Spot icon edge length in CSS pixels. */
|
|
630
|
+
iconSize?: number;
|
|
631
|
+
/**
|
|
632
|
+
* Maximum zoom multiplier relative to the contain-fit scale.
|
|
633
|
+
* Only applies when `interaction` is `"zoom"`. Defaults to 8.
|
|
634
|
+
*/
|
|
635
|
+
maxScale?: number;
|
|
636
|
+
/**
|
|
637
|
+
* Pan inertia and edge bounce when `interaction` is `"zoom"`.
|
|
638
|
+
* Omit for both on; set `false` or flag fields to disable.
|
|
639
|
+
*/
|
|
640
|
+
dragMotion?: PanoDragMotion | false;
|
|
641
|
+
spots: PanoMapSpotDefinition[];
|
|
642
|
+
sector?: PanoMapSectorDefinition;
|
|
643
|
+
};
|
|
644
|
+
/** Parse an optional project floor plan. Scene IDs must already be collected. */
|
|
645
|
+
declare function parsePanoMap(value: unknown, sceneIds: Set<string>, options?: {
|
|
646
|
+
draft?: boolean;
|
|
647
|
+
}): PanoMapDefinition | undefined;
|
|
648
|
+
declare function resolvePanoMapAssets(map: PanoMapDefinition, baseUrl: string, resolveAssetUrl?: AssetUrlResolver): PanoMapDefinition;
|
|
649
|
+
|
|
534
650
|
type PanoProjectSceneDefinition = {
|
|
535
651
|
id: string;
|
|
536
652
|
title?: string;
|
|
@@ -553,6 +669,7 @@ type PanoProjectDefinition = {
|
|
|
553
669
|
initialSceneId?: string;
|
|
554
670
|
scenes: PanoProjectSceneDefinition[];
|
|
555
671
|
popups?: ProjectPopupDefinition[];
|
|
672
|
+
map?: PanoMapDefinition;
|
|
556
673
|
};
|
|
557
674
|
type LoadedProjectScene = {
|
|
558
675
|
scene: TileScene | SphereTileScene;
|
|
@@ -565,6 +682,7 @@ type LoadedPanoProject = {
|
|
|
565
682
|
initialSceneId: string;
|
|
566
683
|
scenes: LoadedProjectScene[];
|
|
567
684
|
popups?: ProjectPopupDefinition[];
|
|
685
|
+
map?: PanoMapDefinition;
|
|
568
686
|
};
|
|
569
687
|
type LoadPanoProjectOptions = JsonRequestOptions & {
|
|
570
688
|
/** Directory used for project-relative references. Required for object input. */
|
|
@@ -578,4 +696,4 @@ declare function parsePanoProject(value: unknown, options?: {
|
|
|
578
696
|
}): PanoProjectDefinition;
|
|
579
697
|
declare function loadPanoProject(source: string | PanoProjectDefinition, options?: LoadPanoProjectOptions): Promise<LoadedPanoProject>;
|
|
580
698
|
|
|
581
|
-
export { type AssetUrlContext, type AssetUrlResolver, type CubemapViewerConfig, type JsonObject, type JsonRequestOptions, type JsonValue, type LoadPanoProjectOptions, type LoadSceneFromJsonOptions, type LoadedPanoProject, type LoadedProjectScene, PanoDataError, type PanoDataErrorCode, type PanoProjectDefinition, type PanoProjectSceneDefinition, type PanoViewLimits, type PopupAssetUrlContext, type PopupAssetUrlResolver, type ProjectCustomHotspotAction, type ProjectHotspotAction, type ProjectHotspotDefinition, type ProjectHotspotTrigger, type ProjectPopupBlock, type ProjectPopupDefinition, type SceneFromViewerConfigOptions, type SphereViewerConfig, type ViewerConfig, type ViewerDocument, hotspotAction, loadPanoProject, loadSceneFromJson, parsePanoProject, projectHotspotTriggers, resolveHotspotAssets as resolveProjectHotspotAssets, resolveProjectPopupAssets, sceneFromViewerConfig, setHotspotAction };
|
|
699
|
+
export { type AssetUrlContext, type AssetUrlResolver, type CubemapViewerConfig, type JsonObject, type JsonRequestOptions, type JsonValue, type LoadPanoProjectOptions, type LoadSceneFromJsonOptions, type LoadedPanoProject, type LoadedProjectScene, PanoDataError, type PanoDataErrorCode, type PanoDragMotion, type PanoMapControlsDefinition, type PanoMapCorner, type PanoMapDefinition, type PanoMapDialogPresentation, type PanoMapDockedPresentation, type PanoMapGradientStop, type PanoMapPresentation, type PanoMapSectorDefinition, type PanoMapSectorGradient, type PanoMapSpotDefinition, type PanoProjectDefinition, type PanoProjectSceneDefinition, type PanoViewLimits, type PopupAssetUrlContext, type PopupAssetUrlResolver, type ProjectCustomHotspotAction, type ProjectHotspotAction, type ProjectHotspotDefinition, type ProjectHotspotTrigger, type ProjectPopupBlock, type ProjectPopupDefinition, type SceneFromViewerConfigOptions, type SphereViewerConfig, type ViewerConfig, type ViewerDocument, hotspotAction, loadPanoProject, loadSceneFromJson, parsePanoMap, parsePanoProject, projectHotspotTriggers, resolvePanoMapAssets, resolveHotspotAssets as resolveProjectHotspotAssets, resolveProjectPopupAssets, sceneFromViewerConfig, setHotspotAction };
|
package/dist/data.d.ts
CHANGED
|
@@ -209,7 +209,7 @@ type SphereTileScene = Omit<SphereTileProps, "loadMode" | "visible" | "onReady"
|
|
|
209
209
|
|
|
210
210
|
type AssetUrlContext = {
|
|
211
211
|
defaultUrl: string;
|
|
212
|
-
kind: "tile" | "preview" | "hotspot";
|
|
212
|
+
kind: "tile" | "preview" | "hotspot" | "map";
|
|
213
213
|
sceneId: string;
|
|
214
214
|
hotspotId?: string;
|
|
215
215
|
};
|
|
@@ -531,6 +531,122 @@ type ProjectHotspotDefinition = HotspotDefinition & {
|
|
|
531
531
|
};
|
|
532
532
|
declare function resolveHotspotAssets(hotspot: ProjectHotspotDefinition, baseUrl: string, sceneId: string, resolver?: AssetUrlResolver): ProjectHotspotDefinition;
|
|
533
533
|
|
|
534
|
+
/**
|
|
535
|
+
* Shared drag inertia and edge bounce for map pan and scene-menu scrolling.
|
|
536
|
+
* Axis motion is pure; hosts own pointer capture and rAF loops.
|
|
537
|
+
*/
|
|
538
|
+
type PanoDragMotion = {
|
|
539
|
+
/** Continue sliding after release from pointer velocity. Defaults to true. */
|
|
540
|
+
inertia?: boolean;
|
|
541
|
+
/** Rubber-band past edges while dragging; spring back on release. Defaults to true. */
|
|
542
|
+
bounce?: boolean;
|
|
543
|
+
};
|
|
544
|
+
|
|
545
|
+
type PanoMapCorner = "top-left" | "top-right" | "bottom-left" | "bottom-right";
|
|
546
|
+
type PanoMapGradientStop = {
|
|
547
|
+
offset: number;
|
|
548
|
+
color: string;
|
|
549
|
+
opacity?: number;
|
|
550
|
+
};
|
|
551
|
+
type PanoMapSectorGradient = {
|
|
552
|
+
type: "radial" | "linear";
|
|
553
|
+
stops: PanoMapGradientStop[];
|
|
554
|
+
};
|
|
555
|
+
type PanoMapSectorDefinition = {
|
|
556
|
+
/** Sector opening angle in degrees. */
|
|
557
|
+
angle: number;
|
|
558
|
+
/** Sector radius in CSS pixels; does not scale with the floor image. */
|
|
559
|
+
radius: number;
|
|
560
|
+
/** When true, sector angle follows the current horizontal FOV. */
|
|
561
|
+
followFov?: boolean;
|
|
562
|
+
/** Solid fill color when `gradient` is omitted. */
|
|
563
|
+
color?: string;
|
|
564
|
+
/**
|
|
565
|
+
* Overall fill opacity in [0, 1].
|
|
566
|
+
* Applies in both solid and gradient modes; gradient stop opacities only
|
|
567
|
+
* describe the relative fade shape and are normalized against this value.
|
|
568
|
+
*/
|
|
569
|
+
opacity?: number;
|
|
570
|
+
/** Optional sector outline color. */
|
|
571
|
+
strokeColor?: string;
|
|
572
|
+
/** Outline width in CSS pixels. */
|
|
573
|
+
strokeWidth?: number;
|
|
574
|
+
/** Outline opacity in [0, 1]. Defaults to 1 when stroke is set. */
|
|
575
|
+
strokeOpacity?: number;
|
|
576
|
+
/**
|
|
577
|
+
* Optional fill gradient. When omitted, the sector uses a solid `color`.
|
|
578
|
+
* Stop colors are independent of `color` unless the author keeps them in sync.
|
|
579
|
+
*/
|
|
580
|
+
gradient?: PanoMapSectorGradient;
|
|
581
|
+
};
|
|
582
|
+
type PanoMapSpotDefinition = {
|
|
583
|
+
id: string;
|
|
584
|
+
sceneId: string;
|
|
585
|
+
/** Normalized image X in [0, 1], origin at the top-left. */
|
|
586
|
+
x: number;
|
|
587
|
+
/** Normalized image Y in [0, 1], origin at the top-left. */
|
|
588
|
+
y: number;
|
|
589
|
+
/**
|
|
590
|
+
* Clockwise degrees from image up when viewer yaw is 0.
|
|
591
|
+
* Runtime sector rotation is `heading + yaw`.
|
|
592
|
+
*/
|
|
593
|
+
heading: number;
|
|
594
|
+
icon?: string;
|
|
595
|
+
};
|
|
596
|
+
type PanoMapControlsDefinition = {
|
|
597
|
+
/** Show expand/collapse on a docked panel. */
|
|
598
|
+
collapse?: boolean;
|
|
599
|
+
/** Show zoom-in and zoom-out buttons. */
|
|
600
|
+
zoom?: boolean;
|
|
601
|
+
};
|
|
602
|
+
type PanoMapDockedPresentation = {
|
|
603
|
+
mode: "docked";
|
|
604
|
+
corner?: PanoMapCorner;
|
|
605
|
+
width?: number;
|
|
606
|
+
height?: number;
|
|
607
|
+
offsetX?: number;
|
|
608
|
+
offsetY?: number;
|
|
609
|
+
};
|
|
610
|
+
type PanoMapDialogPresentation = {
|
|
611
|
+
mode: "dialog";
|
|
612
|
+
corner?: PanoMapCorner;
|
|
613
|
+
offsetX?: number;
|
|
614
|
+
offsetY?: number;
|
|
615
|
+
width?: number;
|
|
616
|
+
height?: number;
|
|
617
|
+
};
|
|
618
|
+
type PanoMapPresentation = PanoMapDockedPresentation | PanoMapDialogPresentation;
|
|
619
|
+
type PanoMapDefinition = {
|
|
620
|
+
image: string;
|
|
621
|
+
/**
|
|
622
|
+
* Pointer interaction with the floor image.
|
|
623
|
+
* - `fixed`: fit-only; no wheel/pan zoom (zoom buttons are also suppressed).
|
|
624
|
+
* - `zoom`: wheel/pan zoom and optional zoom buttons.
|
|
625
|
+
*/
|
|
626
|
+
interaction?: "fixed" | "zoom";
|
|
627
|
+
presentation?: PanoMapPresentation;
|
|
628
|
+
controls?: PanoMapControlsDefinition;
|
|
629
|
+
/** Spot icon edge length in CSS pixels. */
|
|
630
|
+
iconSize?: number;
|
|
631
|
+
/**
|
|
632
|
+
* Maximum zoom multiplier relative to the contain-fit scale.
|
|
633
|
+
* Only applies when `interaction` is `"zoom"`. Defaults to 8.
|
|
634
|
+
*/
|
|
635
|
+
maxScale?: number;
|
|
636
|
+
/**
|
|
637
|
+
* Pan inertia and edge bounce when `interaction` is `"zoom"`.
|
|
638
|
+
* Omit for both on; set `false` or flag fields to disable.
|
|
639
|
+
*/
|
|
640
|
+
dragMotion?: PanoDragMotion | false;
|
|
641
|
+
spots: PanoMapSpotDefinition[];
|
|
642
|
+
sector?: PanoMapSectorDefinition;
|
|
643
|
+
};
|
|
644
|
+
/** Parse an optional project floor plan. Scene IDs must already be collected. */
|
|
645
|
+
declare function parsePanoMap(value: unknown, sceneIds: Set<string>, options?: {
|
|
646
|
+
draft?: boolean;
|
|
647
|
+
}): PanoMapDefinition | undefined;
|
|
648
|
+
declare function resolvePanoMapAssets(map: PanoMapDefinition, baseUrl: string, resolveAssetUrl?: AssetUrlResolver): PanoMapDefinition;
|
|
649
|
+
|
|
534
650
|
type PanoProjectSceneDefinition = {
|
|
535
651
|
id: string;
|
|
536
652
|
title?: string;
|
|
@@ -553,6 +669,7 @@ type PanoProjectDefinition = {
|
|
|
553
669
|
initialSceneId?: string;
|
|
554
670
|
scenes: PanoProjectSceneDefinition[];
|
|
555
671
|
popups?: ProjectPopupDefinition[];
|
|
672
|
+
map?: PanoMapDefinition;
|
|
556
673
|
};
|
|
557
674
|
type LoadedProjectScene = {
|
|
558
675
|
scene: TileScene | SphereTileScene;
|
|
@@ -565,6 +682,7 @@ type LoadedPanoProject = {
|
|
|
565
682
|
initialSceneId: string;
|
|
566
683
|
scenes: LoadedProjectScene[];
|
|
567
684
|
popups?: ProjectPopupDefinition[];
|
|
685
|
+
map?: PanoMapDefinition;
|
|
568
686
|
};
|
|
569
687
|
type LoadPanoProjectOptions = JsonRequestOptions & {
|
|
570
688
|
/** Directory used for project-relative references. Required for object input. */
|
|
@@ -578,4 +696,4 @@ declare function parsePanoProject(value: unknown, options?: {
|
|
|
578
696
|
}): PanoProjectDefinition;
|
|
579
697
|
declare function loadPanoProject(source: string | PanoProjectDefinition, options?: LoadPanoProjectOptions): Promise<LoadedPanoProject>;
|
|
580
698
|
|
|
581
|
-
export { type AssetUrlContext, type AssetUrlResolver, type CubemapViewerConfig, type JsonObject, type JsonRequestOptions, type JsonValue, type LoadPanoProjectOptions, type LoadSceneFromJsonOptions, type LoadedPanoProject, type LoadedProjectScene, PanoDataError, type PanoDataErrorCode, type PanoProjectDefinition, type PanoProjectSceneDefinition, type PanoViewLimits, type PopupAssetUrlContext, type PopupAssetUrlResolver, type ProjectCustomHotspotAction, type ProjectHotspotAction, type ProjectHotspotDefinition, type ProjectHotspotTrigger, type ProjectPopupBlock, type ProjectPopupDefinition, type SceneFromViewerConfigOptions, type SphereViewerConfig, type ViewerConfig, type ViewerDocument, hotspotAction, loadPanoProject, loadSceneFromJson, parsePanoProject, projectHotspotTriggers, resolveHotspotAssets as resolveProjectHotspotAssets, resolveProjectPopupAssets, sceneFromViewerConfig, setHotspotAction };
|
|
699
|
+
export { type AssetUrlContext, type AssetUrlResolver, type CubemapViewerConfig, type JsonObject, type JsonRequestOptions, type JsonValue, type LoadPanoProjectOptions, type LoadSceneFromJsonOptions, type LoadedPanoProject, type LoadedProjectScene, PanoDataError, type PanoDataErrorCode, type PanoDragMotion, type PanoMapControlsDefinition, type PanoMapCorner, type PanoMapDefinition, type PanoMapDialogPresentation, type PanoMapDockedPresentation, type PanoMapGradientStop, type PanoMapPresentation, type PanoMapSectorDefinition, type PanoMapSectorGradient, type PanoMapSpotDefinition, type PanoProjectDefinition, type PanoProjectSceneDefinition, type PanoViewLimits, type PopupAssetUrlContext, type PopupAssetUrlResolver, type ProjectCustomHotspotAction, type ProjectHotspotAction, type ProjectHotspotDefinition, type ProjectHotspotTrigger, type ProjectPopupBlock, type ProjectPopupDefinition, type SceneFromViewerConfigOptions, type SphereViewerConfig, type ViewerConfig, type ViewerDocument, hotspotAction, loadPanoProject, loadSceneFromJson, parsePanoMap, parsePanoProject, projectHotspotTriggers, resolvePanoMapAssets, resolveHotspotAssets as resolveProjectHotspotAssets, resolveProjectPopupAssets, sceneFromViewerConfig, setHotspotAction };
|
package/dist/data.mjs
CHANGED
|
@@ -809,10 +809,231 @@ function resolveHotspotAssets(hotspot, baseUrl, sceneId, resolver) {
|
|
|
809
809
|
return result;
|
|
810
810
|
}
|
|
811
811
|
|
|
812
|
-
// src/data/
|
|
812
|
+
// src/data/map.ts
|
|
813
|
+
var CORNERS = /* @__PURE__ */ new Set([
|
|
814
|
+
"top-left",
|
|
815
|
+
"top-right",
|
|
816
|
+
"bottom-left",
|
|
817
|
+
"bottom-right"
|
|
818
|
+
]);
|
|
819
|
+
function optionalNumber(record, key, options = {}) {
|
|
820
|
+
const value = record[key];
|
|
821
|
+
if (value === void 0) return void 0;
|
|
822
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
823
|
+
throw new Error(`${key}: expected a finite number`);
|
|
824
|
+
if (options.min !== void 0 && value < options.min)
|
|
825
|
+
throw new Error(`${key}: expected a number >= ${options.min}`);
|
|
826
|
+
if (options.max !== void 0 && value > options.max)
|
|
827
|
+
throw new Error(`${key}: expected a number <= ${options.max}`);
|
|
828
|
+
if (options.exclusiveMin !== void 0 && value <= options.exclusiveMin)
|
|
829
|
+
throw new Error(`${key}: expected a number > ${options.exclusiveMin}`);
|
|
830
|
+
if (options.exclusiveMax !== void 0 && value >= options.exclusiveMax)
|
|
831
|
+
throw new Error(`${key}: expected a number < ${options.exclusiveMax}`);
|
|
832
|
+
return value;
|
|
833
|
+
}
|
|
834
|
+
function requiredNumber(record, key, options = {}) {
|
|
835
|
+
const value = optionalNumber(record, key, options);
|
|
836
|
+
if (value === void 0) throw new Error(`${key}: expected a finite number`);
|
|
837
|
+
return value;
|
|
838
|
+
}
|
|
813
839
|
function optionalString(record, key) {
|
|
814
840
|
return record[key] === void 0 ? void 0 : requiredString(record[key], key);
|
|
815
841
|
}
|
|
842
|
+
function parseGradientStop(value, field) {
|
|
843
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object`);
|
|
844
|
+
const offset = requiredNumber(value, "offset", { min: 0, max: 1 });
|
|
845
|
+
const color = requiredString(value.color, `${field}.color`);
|
|
846
|
+
const opacity = optionalNumber(value, "opacity", { min: 0, max: 1 });
|
|
847
|
+
return {
|
|
848
|
+
offset,
|
|
849
|
+
color,
|
|
850
|
+
...opacity === void 0 ? {} : { opacity }
|
|
851
|
+
};
|
|
852
|
+
}
|
|
853
|
+
function parseSector(value, field) {
|
|
854
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object`);
|
|
855
|
+
const angle = requiredNumber(value, "angle", { exclusiveMin: 0, exclusiveMax: 360 });
|
|
856
|
+
const radius = requiredNumber(value, "radius", { exclusiveMin: 0 });
|
|
857
|
+
const followFov = value.followFov === void 0 ? void 0 : value.followFov === true ? true : value.followFov === false ? false : (() => {
|
|
858
|
+
throw new Error(`${field}.followFov: expected a boolean`);
|
|
859
|
+
})();
|
|
860
|
+
const color = optionalString(value, "color");
|
|
861
|
+
const opacity = optionalNumber(value, "opacity", { min: 0, max: 1 });
|
|
862
|
+
const strokeColor = optionalString(value, "strokeColor");
|
|
863
|
+
const strokeWidth = optionalNumber(value, "strokeWidth", { min: 0 });
|
|
864
|
+
const strokeOpacity = optionalNumber(value, "strokeOpacity", { min: 0, max: 1 });
|
|
865
|
+
let gradient;
|
|
866
|
+
if (value.gradient !== void 0) {
|
|
867
|
+
if (!isRecord(value.gradient))
|
|
868
|
+
throw new Error(`${field}.gradient: expected an object`);
|
|
869
|
+
const type = value.gradient.type;
|
|
870
|
+
if (type !== "radial" && type !== "linear")
|
|
871
|
+
throw new Error(`${field}.gradient.type: expected "radial" or "linear"`);
|
|
872
|
+
if (!Array.isArray(value.gradient.stops) || value.gradient.stops.length < 2)
|
|
873
|
+
throw new Error(`${field}.gradient.stops: expected at least two stops`);
|
|
874
|
+
gradient = {
|
|
875
|
+
type,
|
|
876
|
+
stops: value.gradient.stops.map(
|
|
877
|
+
(stop, index) => parseGradientStop(stop, `${field}.gradient.stops[${index}]`)
|
|
878
|
+
)
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
return {
|
|
882
|
+
angle,
|
|
883
|
+
radius,
|
|
884
|
+
...followFov === void 0 ? {} : { followFov },
|
|
885
|
+
...color === void 0 ? {} : { color },
|
|
886
|
+
...opacity === void 0 ? {} : { opacity },
|
|
887
|
+
...strokeColor === void 0 ? {} : { strokeColor },
|
|
888
|
+
...strokeWidth === void 0 ? {} : { strokeWidth },
|
|
889
|
+
...strokeOpacity === void 0 ? {} : { strokeOpacity },
|
|
890
|
+
...gradient === void 0 ? {} : { gradient }
|
|
891
|
+
};
|
|
892
|
+
}
|
|
893
|
+
function parsePresentation(value, field) {
|
|
894
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object`);
|
|
895
|
+
const mode = value.mode;
|
|
896
|
+
if (mode !== "docked" && mode !== "dialog")
|
|
897
|
+
throw new Error(`${field}.mode: expected "docked" or "dialog"`);
|
|
898
|
+
const cornerValue = value.corner;
|
|
899
|
+
const corner = cornerValue === void 0 ? void 0 : typeof cornerValue === "string" && CORNERS.has(cornerValue) ? cornerValue : (() => {
|
|
900
|
+
throw new Error(
|
|
901
|
+
`${field}.corner: expected top-left, top-right, bottom-left, or bottom-right`
|
|
902
|
+
);
|
|
903
|
+
})();
|
|
904
|
+
const width = optionalNumber(value, "width", { exclusiveMin: 0 });
|
|
905
|
+
const height = optionalNumber(value, "height", { exclusiveMin: 0 });
|
|
906
|
+
const offsetX = optionalNumber(value, "offsetX", { min: 0 });
|
|
907
|
+
const offsetY = optionalNumber(value, "offsetY", { min: 0 });
|
|
908
|
+
return {
|
|
909
|
+
mode,
|
|
910
|
+
...corner === void 0 ? {} : { corner },
|
|
911
|
+
...width === void 0 ? {} : { width },
|
|
912
|
+
...height === void 0 ? {} : { height },
|
|
913
|
+
...offsetX === void 0 ? {} : { offsetX },
|
|
914
|
+
...offsetY === void 0 ? {} : { offsetY }
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
function parseOptionalBoolean(value, field) {
|
|
918
|
+
if (value === void 0) return void 0;
|
|
919
|
+
if (value === true) return true;
|
|
920
|
+
if (value === false) return false;
|
|
921
|
+
throw new Error(`${field}: expected a boolean`);
|
|
922
|
+
}
|
|
923
|
+
function parseDragMotion(value, field) {
|
|
924
|
+
if (value === false) return false;
|
|
925
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object or false`);
|
|
926
|
+
const inertia = parseOptionalBoolean(value.inertia, `${field}.inertia`);
|
|
927
|
+
const bounce = parseOptionalBoolean(value.bounce, `${field}.bounce`);
|
|
928
|
+
const motion = {
|
|
929
|
+
...inertia === void 0 ? {} : { inertia },
|
|
930
|
+
...bounce === void 0 ? {} : { bounce }
|
|
931
|
+
};
|
|
932
|
+
return motion;
|
|
933
|
+
}
|
|
934
|
+
function parseControls(value, field) {
|
|
935
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object`);
|
|
936
|
+
const collapse = value.collapse === void 0 ? void 0 : value.collapse === true ? true : value.collapse === false ? false : (() => {
|
|
937
|
+
throw new Error(`${field}.collapse: expected a boolean`);
|
|
938
|
+
})();
|
|
939
|
+
const zoom = value.zoom === void 0 ? void 0 : value.zoom === true ? true : value.zoom === false ? false : (() => {
|
|
940
|
+
throw new Error(`${field}.zoom: expected a boolean`);
|
|
941
|
+
})();
|
|
942
|
+
return {
|
|
943
|
+
...collapse === void 0 ? {} : { collapse },
|
|
944
|
+
...zoom === void 0 ? {} : { zoom }
|
|
945
|
+
};
|
|
946
|
+
}
|
|
947
|
+
function parseSpot(value, field, sceneIds) {
|
|
948
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object`);
|
|
949
|
+
const id = requiredString(value.id, `${field}.id`);
|
|
950
|
+
const sceneId = requiredString(value.sceneId, `${field}.sceneId`);
|
|
951
|
+
if (!sceneIds.has(sceneId))
|
|
952
|
+
throw new Error(`${field}.sceneId: scene does not exist`);
|
|
953
|
+
const x = requiredNumber(value, "x", { min: 0, max: 1 });
|
|
954
|
+
const y = requiredNumber(value, "y", { min: 0, max: 1 });
|
|
955
|
+
const heading = requiredNumber(value, "heading");
|
|
956
|
+
const icon = optionalString(value, "icon");
|
|
957
|
+
if (icon) resolveResource("https://map.invalid/", icon);
|
|
958
|
+
return {
|
|
959
|
+
id,
|
|
960
|
+
sceneId,
|
|
961
|
+
x,
|
|
962
|
+
y,
|
|
963
|
+
heading,
|
|
964
|
+
...icon === void 0 ? {} : { icon }
|
|
965
|
+
};
|
|
966
|
+
}
|
|
967
|
+
function parsePanoMap(value, sceneIds, options = {}) {
|
|
968
|
+
if (value === void 0) return void 0;
|
|
969
|
+
try {
|
|
970
|
+
if (!isRecord(value)) throw new Error("map: expected an object");
|
|
971
|
+
if (typeof value.image !== "string" || !options.draft && !value.image.trim())
|
|
972
|
+
throw new Error("map.image: expected a nonempty string");
|
|
973
|
+
const image = value.image;
|
|
974
|
+
if (image.trim()) resolveResource("https://map.invalid/", image);
|
|
975
|
+
const interaction = value.interaction === void 0 ? void 0 : value.interaction === "fixed" || value.interaction === "zoom" ? value.interaction : (() => {
|
|
976
|
+
throw new Error('map.interaction: expected "fixed" or "zoom"');
|
|
977
|
+
})();
|
|
978
|
+
const presentation = value.presentation === void 0 ? void 0 : parsePresentation(value.presentation, "map.presentation");
|
|
979
|
+
const controls = value.controls === void 0 ? void 0 : parseControls(value.controls, "map.controls");
|
|
980
|
+
const iconSize = optionalNumber(value, "iconSize", { exclusiveMin: 0 });
|
|
981
|
+
const maxScale = optionalNumber(value, "maxScale", { min: 1 });
|
|
982
|
+
const dragMotion = value.dragMotion === void 0 ? void 0 : parseDragMotion(value.dragMotion, "map.dragMotion");
|
|
983
|
+
if (!Array.isArray(value.spots)) throw new Error("map.spots: expected an array");
|
|
984
|
+
const spotIds = /* @__PURE__ */ new Set();
|
|
985
|
+
const spots = value.spots.map((spot, index) => {
|
|
986
|
+
const parsed = parseSpot(spot, `map.spots[${index}]`, sceneIds);
|
|
987
|
+
if (spotIds.has(parsed.id))
|
|
988
|
+
throw new Error(`map.spots[${index}].id: duplicate spot ID`);
|
|
989
|
+
spotIds.add(parsed.id);
|
|
990
|
+
return parsed;
|
|
991
|
+
});
|
|
992
|
+
const sector = value.sector === void 0 ? void 0 : parseSector(value.sector, "map.sector");
|
|
993
|
+
return {
|
|
994
|
+
image,
|
|
995
|
+
...interaction === void 0 ? {} : { interaction },
|
|
996
|
+
...presentation === void 0 ? {} : { presentation },
|
|
997
|
+
...controls === void 0 ? {} : { controls },
|
|
998
|
+
...iconSize === void 0 ? {} : { iconSize },
|
|
999
|
+
...maxScale === void 0 ? {} : { maxScale },
|
|
1000
|
+
...dragMotion === void 0 ? {} : { dragMotion },
|
|
1001
|
+
spots,
|
|
1002
|
+
...sector === void 0 ? {} : { sector }
|
|
1003
|
+
};
|
|
1004
|
+
} catch (cause) {
|
|
1005
|
+
if (cause instanceof PanoDataError) throw cause;
|
|
1006
|
+
const message = cause instanceof Error ? cause.message : "Invalid map";
|
|
1007
|
+
throw new PanoDataError("INVALID_PROJECT", message, {
|
|
1008
|
+
field: message.split(":")[0],
|
|
1009
|
+
cause
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
function resolvePanoMapAssets(map, baseUrl, resolveAssetUrl) {
|
|
1014
|
+
const resolve = (path, spotId) => mapResource(
|
|
1015
|
+
path,
|
|
1016
|
+
{
|
|
1017
|
+
defaultUrl: resolveResource(baseUrl, path),
|
|
1018
|
+
kind: "map",
|
|
1019
|
+
sceneId: spotId ?? "",
|
|
1020
|
+
hotspotId: spotId
|
|
1021
|
+
},
|
|
1022
|
+
resolveAssetUrl
|
|
1023
|
+
);
|
|
1024
|
+
return {
|
|
1025
|
+
...map,
|
|
1026
|
+
image: resolve(map.image),
|
|
1027
|
+
spots: map.spots.map(
|
|
1028
|
+
(spot) => spot.icon ? { ...spot, icon: resolve(spot.icon, spot.id) } : { ...spot }
|
|
1029
|
+
)
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// src/data/project.ts
|
|
1034
|
+
function optionalString2(record, key) {
|
|
1035
|
+
return record[key] === void 0 ? void 0 : requiredString(record[key], key);
|
|
1036
|
+
}
|
|
816
1037
|
function parsePanoProject(value, options = {}) {
|
|
817
1038
|
if (!isRecord(value))
|
|
818
1039
|
throw new PanoDataError("INVALID_PROJECT", "Expected a project object");
|
|
@@ -843,7 +1064,7 @@ function parsePanoProject(value, options = {}) {
|
|
|
843
1064
|
throw new Error(
|
|
844
1065
|
"source: provide exactly one of viewerUrl or viewerConfig"
|
|
845
1066
|
);
|
|
846
|
-
const baseUrl =
|
|
1067
|
+
const baseUrl = optionalString2(src, "baseUrl");
|
|
847
1068
|
const source = src.viewerUrl !== void 0 ? {
|
|
848
1069
|
viewerUrl: requiredString(src.viewerUrl, "source.viewerUrl"),
|
|
849
1070
|
baseUrl
|
|
@@ -883,7 +1104,7 @@ function parsePanoProject(value, options = {}) {
|
|
|
883
1104
|
);
|
|
884
1105
|
return {
|
|
885
1106
|
id,
|
|
886
|
-
title:
|
|
1107
|
+
title: optionalString2(entry, "title"),
|
|
887
1108
|
source,
|
|
888
1109
|
initialView,
|
|
889
1110
|
...viewLimits === void 0 ? {} : { viewLimits },
|
|
@@ -904,7 +1125,7 @@ function parsePanoProject(value, options = {}) {
|
|
|
904
1125
|
}
|
|
905
1126
|
}
|
|
906
1127
|
);
|
|
907
|
-
const initialSceneId =
|
|
1128
|
+
const initialSceneId = optionalString2(value, "initialSceneId") ?? scenes[0]?.id;
|
|
908
1129
|
if (initialSceneId !== void 0 && !ids.has(initialSceneId))
|
|
909
1130
|
throw new PanoDataError(
|
|
910
1131
|
"INVALID_REFERENCE",
|
|
@@ -932,12 +1153,14 @@ function parsePanoProject(value, options = {}) {
|
|
|
932
1153
|
}
|
|
933
1154
|
}
|
|
934
1155
|
}
|
|
1156
|
+
const map = parsePanoMap(value.map, ids, options);
|
|
935
1157
|
return {
|
|
936
1158
|
schemaVersion: 1,
|
|
937
|
-
title:
|
|
1159
|
+
title: optionalString2(value, "title"),
|
|
938
1160
|
initialSceneId,
|
|
939
1161
|
scenes,
|
|
940
|
-
...popups === void 0 ? {} : { popups }
|
|
1162
|
+
...popups === void 0 ? {} : { popups },
|
|
1163
|
+
...map === void 0 ? {} : { map }
|
|
941
1164
|
};
|
|
942
1165
|
} catch (cause) {
|
|
943
1166
|
if (cause instanceof PanoDataError) throw cause;
|
|
@@ -1036,7 +1259,14 @@ async function loadPanoProject(source, options = {}) {
|
|
|
1036
1259
|
title: definition.title,
|
|
1037
1260
|
initialSceneId: definition.initialSceneId,
|
|
1038
1261
|
scenes,
|
|
1039
|
-
...definition.popups === void 0 ? {} : { popups: definition.popups.map((popup) => resolveProjectPopupAssets(popup, root, options.resolvePopupAssetUrl)) }
|
|
1262
|
+
...definition.popups === void 0 ? {} : { popups: definition.popups.map((popup) => resolveProjectPopupAssets(popup, root, options.resolvePopupAssetUrl)) },
|
|
1263
|
+
...definition.map === void 0 ? {} : {
|
|
1264
|
+
map: resolvePanoMapAssets(
|
|
1265
|
+
definition.map,
|
|
1266
|
+
root,
|
|
1267
|
+
options.resolveAssetUrl
|
|
1268
|
+
)
|
|
1269
|
+
}
|
|
1040
1270
|
};
|
|
1041
1271
|
} finally {
|
|
1042
1272
|
signal?.removeEventListener("abort", cancel);
|
|
@@ -1047,8 +1277,10 @@ export {
|
|
|
1047
1277
|
hotspotAction,
|
|
1048
1278
|
loadPanoProject,
|
|
1049
1279
|
loadSceneFromJson,
|
|
1280
|
+
parsePanoMap,
|
|
1050
1281
|
parsePanoProject,
|
|
1051
1282
|
projectHotspotTriggers,
|
|
1283
|
+
resolvePanoMapAssets,
|
|
1052
1284
|
resolveHotspotAssets as resolveProjectHotspotAssets,
|
|
1053
1285
|
resolveProjectPopupAssets,
|
|
1054
1286
|
sceneFromViewerConfig,
|