@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.cjs
CHANGED
|
@@ -24,8 +24,10 @@ __export(data_exports, {
|
|
|
24
24
|
hotspotAction: () => hotspotAction,
|
|
25
25
|
loadPanoProject: () => loadPanoProject,
|
|
26
26
|
loadSceneFromJson: () => loadSceneFromJson,
|
|
27
|
+
parsePanoMap: () => parsePanoMap,
|
|
27
28
|
parsePanoProject: () => parsePanoProject,
|
|
28
29
|
projectHotspotTriggers: () => projectHotspotTriggers,
|
|
30
|
+
resolvePanoMapAssets: () => resolvePanoMapAssets,
|
|
29
31
|
resolveProjectHotspotAssets: () => resolveHotspotAssets,
|
|
30
32
|
resolveProjectPopupAssets: () => resolveProjectPopupAssets,
|
|
31
33
|
sceneFromViewerConfig: () => sceneFromViewerConfig,
|
|
@@ -844,10 +846,231 @@ function resolveHotspotAssets(hotspot, baseUrl, sceneId, resolver) {
|
|
|
844
846
|
return result;
|
|
845
847
|
}
|
|
846
848
|
|
|
847
|
-
// src/data/
|
|
849
|
+
// src/data/map.ts
|
|
850
|
+
var CORNERS = /* @__PURE__ */ new Set([
|
|
851
|
+
"top-left",
|
|
852
|
+
"top-right",
|
|
853
|
+
"bottom-left",
|
|
854
|
+
"bottom-right"
|
|
855
|
+
]);
|
|
856
|
+
function optionalNumber(record, key, options = {}) {
|
|
857
|
+
const value = record[key];
|
|
858
|
+
if (value === void 0) return void 0;
|
|
859
|
+
if (typeof value !== "number" || !Number.isFinite(value))
|
|
860
|
+
throw new Error(`${key}: expected a finite number`);
|
|
861
|
+
if (options.min !== void 0 && value < options.min)
|
|
862
|
+
throw new Error(`${key}: expected a number >= ${options.min}`);
|
|
863
|
+
if (options.max !== void 0 && value > options.max)
|
|
864
|
+
throw new Error(`${key}: expected a number <= ${options.max}`);
|
|
865
|
+
if (options.exclusiveMin !== void 0 && value <= options.exclusiveMin)
|
|
866
|
+
throw new Error(`${key}: expected a number > ${options.exclusiveMin}`);
|
|
867
|
+
if (options.exclusiveMax !== void 0 && value >= options.exclusiveMax)
|
|
868
|
+
throw new Error(`${key}: expected a number < ${options.exclusiveMax}`);
|
|
869
|
+
return value;
|
|
870
|
+
}
|
|
871
|
+
function requiredNumber(record, key, options = {}) {
|
|
872
|
+
const value = optionalNumber(record, key, options);
|
|
873
|
+
if (value === void 0) throw new Error(`${key}: expected a finite number`);
|
|
874
|
+
return value;
|
|
875
|
+
}
|
|
848
876
|
function optionalString(record, key) {
|
|
849
877
|
return record[key] === void 0 ? void 0 : requiredString(record[key], key);
|
|
850
878
|
}
|
|
879
|
+
function parseGradientStop(value, field) {
|
|
880
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object`);
|
|
881
|
+
const offset = requiredNumber(value, "offset", { min: 0, max: 1 });
|
|
882
|
+
const color = requiredString(value.color, `${field}.color`);
|
|
883
|
+
const opacity = optionalNumber(value, "opacity", { min: 0, max: 1 });
|
|
884
|
+
return {
|
|
885
|
+
offset,
|
|
886
|
+
color,
|
|
887
|
+
...opacity === void 0 ? {} : { opacity }
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
function parseSector(value, field) {
|
|
891
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object`);
|
|
892
|
+
const angle = requiredNumber(value, "angle", { exclusiveMin: 0, exclusiveMax: 360 });
|
|
893
|
+
const radius = requiredNumber(value, "radius", { exclusiveMin: 0 });
|
|
894
|
+
const followFov = value.followFov === void 0 ? void 0 : value.followFov === true ? true : value.followFov === false ? false : (() => {
|
|
895
|
+
throw new Error(`${field}.followFov: expected a boolean`);
|
|
896
|
+
})();
|
|
897
|
+
const color = optionalString(value, "color");
|
|
898
|
+
const opacity = optionalNumber(value, "opacity", { min: 0, max: 1 });
|
|
899
|
+
const strokeColor = optionalString(value, "strokeColor");
|
|
900
|
+
const strokeWidth = optionalNumber(value, "strokeWidth", { min: 0 });
|
|
901
|
+
const strokeOpacity = optionalNumber(value, "strokeOpacity", { min: 0, max: 1 });
|
|
902
|
+
let gradient;
|
|
903
|
+
if (value.gradient !== void 0) {
|
|
904
|
+
if (!isRecord(value.gradient))
|
|
905
|
+
throw new Error(`${field}.gradient: expected an object`);
|
|
906
|
+
const type = value.gradient.type;
|
|
907
|
+
if (type !== "radial" && type !== "linear")
|
|
908
|
+
throw new Error(`${field}.gradient.type: expected "radial" or "linear"`);
|
|
909
|
+
if (!Array.isArray(value.gradient.stops) || value.gradient.stops.length < 2)
|
|
910
|
+
throw new Error(`${field}.gradient.stops: expected at least two stops`);
|
|
911
|
+
gradient = {
|
|
912
|
+
type,
|
|
913
|
+
stops: value.gradient.stops.map(
|
|
914
|
+
(stop, index) => parseGradientStop(stop, `${field}.gradient.stops[${index}]`)
|
|
915
|
+
)
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
return {
|
|
919
|
+
angle,
|
|
920
|
+
radius,
|
|
921
|
+
...followFov === void 0 ? {} : { followFov },
|
|
922
|
+
...color === void 0 ? {} : { color },
|
|
923
|
+
...opacity === void 0 ? {} : { opacity },
|
|
924
|
+
...strokeColor === void 0 ? {} : { strokeColor },
|
|
925
|
+
...strokeWidth === void 0 ? {} : { strokeWidth },
|
|
926
|
+
...strokeOpacity === void 0 ? {} : { strokeOpacity },
|
|
927
|
+
...gradient === void 0 ? {} : { gradient }
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
function parsePresentation(value, field) {
|
|
931
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object`);
|
|
932
|
+
const mode = value.mode;
|
|
933
|
+
if (mode !== "docked" && mode !== "dialog")
|
|
934
|
+
throw new Error(`${field}.mode: expected "docked" or "dialog"`);
|
|
935
|
+
const cornerValue = value.corner;
|
|
936
|
+
const corner = cornerValue === void 0 ? void 0 : typeof cornerValue === "string" && CORNERS.has(cornerValue) ? cornerValue : (() => {
|
|
937
|
+
throw new Error(
|
|
938
|
+
`${field}.corner: expected top-left, top-right, bottom-left, or bottom-right`
|
|
939
|
+
);
|
|
940
|
+
})();
|
|
941
|
+
const width = optionalNumber(value, "width", { exclusiveMin: 0 });
|
|
942
|
+
const height = optionalNumber(value, "height", { exclusiveMin: 0 });
|
|
943
|
+
const offsetX = optionalNumber(value, "offsetX", { min: 0 });
|
|
944
|
+
const offsetY = optionalNumber(value, "offsetY", { min: 0 });
|
|
945
|
+
return {
|
|
946
|
+
mode,
|
|
947
|
+
...corner === void 0 ? {} : { corner },
|
|
948
|
+
...width === void 0 ? {} : { width },
|
|
949
|
+
...height === void 0 ? {} : { height },
|
|
950
|
+
...offsetX === void 0 ? {} : { offsetX },
|
|
951
|
+
...offsetY === void 0 ? {} : { offsetY }
|
|
952
|
+
};
|
|
953
|
+
}
|
|
954
|
+
function parseOptionalBoolean(value, field) {
|
|
955
|
+
if (value === void 0) return void 0;
|
|
956
|
+
if (value === true) return true;
|
|
957
|
+
if (value === false) return false;
|
|
958
|
+
throw new Error(`${field}: expected a boolean`);
|
|
959
|
+
}
|
|
960
|
+
function parseDragMotion(value, field) {
|
|
961
|
+
if (value === false) return false;
|
|
962
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object or false`);
|
|
963
|
+
const inertia = parseOptionalBoolean(value.inertia, `${field}.inertia`);
|
|
964
|
+
const bounce = parseOptionalBoolean(value.bounce, `${field}.bounce`);
|
|
965
|
+
const motion = {
|
|
966
|
+
...inertia === void 0 ? {} : { inertia },
|
|
967
|
+
...bounce === void 0 ? {} : { bounce }
|
|
968
|
+
};
|
|
969
|
+
return motion;
|
|
970
|
+
}
|
|
971
|
+
function parseControls(value, field) {
|
|
972
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object`);
|
|
973
|
+
const collapse = value.collapse === void 0 ? void 0 : value.collapse === true ? true : value.collapse === false ? false : (() => {
|
|
974
|
+
throw new Error(`${field}.collapse: expected a boolean`);
|
|
975
|
+
})();
|
|
976
|
+
const zoom = value.zoom === void 0 ? void 0 : value.zoom === true ? true : value.zoom === false ? false : (() => {
|
|
977
|
+
throw new Error(`${field}.zoom: expected a boolean`);
|
|
978
|
+
})();
|
|
979
|
+
return {
|
|
980
|
+
...collapse === void 0 ? {} : { collapse },
|
|
981
|
+
...zoom === void 0 ? {} : { zoom }
|
|
982
|
+
};
|
|
983
|
+
}
|
|
984
|
+
function parseSpot(value, field, sceneIds) {
|
|
985
|
+
if (!isRecord(value)) throw new Error(`${field}: expected an object`);
|
|
986
|
+
const id = requiredString(value.id, `${field}.id`);
|
|
987
|
+
const sceneId = requiredString(value.sceneId, `${field}.sceneId`);
|
|
988
|
+
if (!sceneIds.has(sceneId))
|
|
989
|
+
throw new Error(`${field}.sceneId: scene does not exist`);
|
|
990
|
+
const x = requiredNumber(value, "x", { min: 0, max: 1 });
|
|
991
|
+
const y = requiredNumber(value, "y", { min: 0, max: 1 });
|
|
992
|
+
const heading = requiredNumber(value, "heading");
|
|
993
|
+
const icon = optionalString(value, "icon");
|
|
994
|
+
if (icon) resolveResource("https://map.invalid/", icon);
|
|
995
|
+
return {
|
|
996
|
+
id,
|
|
997
|
+
sceneId,
|
|
998
|
+
x,
|
|
999
|
+
y,
|
|
1000
|
+
heading,
|
|
1001
|
+
...icon === void 0 ? {} : { icon }
|
|
1002
|
+
};
|
|
1003
|
+
}
|
|
1004
|
+
function parsePanoMap(value, sceneIds, options = {}) {
|
|
1005
|
+
if (value === void 0) return void 0;
|
|
1006
|
+
try {
|
|
1007
|
+
if (!isRecord(value)) throw new Error("map: expected an object");
|
|
1008
|
+
if (typeof value.image !== "string" || !options.draft && !value.image.trim())
|
|
1009
|
+
throw new Error("map.image: expected a nonempty string");
|
|
1010
|
+
const image = value.image;
|
|
1011
|
+
if (image.trim()) resolveResource("https://map.invalid/", image);
|
|
1012
|
+
const interaction = value.interaction === void 0 ? void 0 : value.interaction === "fixed" || value.interaction === "zoom" ? value.interaction : (() => {
|
|
1013
|
+
throw new Error('map.interaction: expected "fixed" or "zoom"');
|
|
1014
|
+
})();
|
|
1015
|
+
const presentation = value.presentation === void 0 ? void 0 : parsePresentation(value.presentation, "map.presentation");
|
|
1016
|
+
const controls = value.controls === void 0 ? void 0 : parseControls(value.controls, "map.controls");
|
|
1017
|
+
const iconSize = optionalNumber(value, "iconSize", { exclusiveMin: 0 });
|
|
1018
|
+
const maxScale = optionalNumber(value, "maxScale", { min: 1 });
|
|
1019
|
+
const dragMotion = value.dragMotion === void 0 ? void 0 : parseDragMotion(value.dragMotion, "map.dragMotion");
|
|
1020
|
+
if (!Array.isArray(value.spots)) throw new Error("map.spots: expected an array");
|
|
1021
|
+
const spotIds = /* @__PURE__ */ new Set();
|
|
1022
|
+
const spots = value.spots.map((spot, index) => {
|
|
1023
|
+
const parsed = parseSpot(spot, `map.spots[${index}]`, sceneIds);
|
|
1024
|
+
if (spotIds.has(parsed.id))
|
|
1025
|
+
throw new Error(`map.spots[${index}].id: duplicate spot ID`);
|
|
1026
|
+
spotIds.add(parsed.id);
|
|
1027
|
+
return parsed;
|
|
1028
|
+
});
|
|
1029
|
+
const sector = value.sector === void 0 ? void 0 : parseSector(value.sector, "map.sector");
|
|
1030
|
+
return {
|
|
1031
|
+
image,
|
|
1032
|
+
...interaction === void 0 ? {} : { interaction },
|
|
1033
|
+
...presentation === void 0 ? {} : { presentation },
|
|
1034
|
+
...controls === void 0 ? {} : { controls },
|
|
1035
|
+
...iconSize === void 0 ? {} : { iconSize },
|
|
1036
|
+
...maxScale === void 0 ? {} : { maxScale },
|
|
1037
|
+
...dragMotion === void 0 ? {} : { dragMotion },
|
|
1038
|
+
spots,
|
|
1039
|
+
...sector === void 0 ? {} : { sector }
|
|
1040
|
+
};
|
|
1041
|
+
} catch (cause) {
|
|
1042
|
+
if (cause instanceof PanoDataError) throw cause;
|
|
1043
|
+
const message = cause instanceof Error ? cause.message : "Invalid map";
|
|
1044
|
+
throw new PanoDataError("INVALID_PROJECT", message, {
|
|
1045
|
+
field: message.split(":")[0],
|
|
1046
|
+
cause
|
|
1047
|
+
});
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
function resolvePanoMapAssets(map, baseUrl, resolveAssetUrl) {
|
|
1051
|
+
const resolve = (path, spotId) => mapResource(
|
|
1052
|
+
path,
|
|
1053
|
+
{
|
|
1054
|
+
defaultUrl: resolveResource(baseUrl, path),
|
|
1055
|
+
kind: "map",
|
|
1056
|
+
sceneId: spotId ?? "",
|
|
1057
|
+
hotspotId: spotId
|
|
1058
|
+
},
|
|
1059
|
+
resolveAssetUrl
|
|
1060
|
+
);
|
|
1061
|
+
return {
|
|
1062
|
+
...map,
|
|
1063
|
+
image: resolve(map.image),
|
|
1064
|
+
spots: map.spots.map(
|
|
1065
|
+
(spot) => spot.icon ? { ...spot, icon: resolve(spot.icon, spot.id) } : { ...spot }
|
|
1066
|
+
)
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
// src/data/project.ts
|
|
1071
|
+
function optionalString2(record, key) {
|
|
1072
|
+
return record[key] === void 0 ? void 0 : requiredString(record[key], key);
|
|
1073
|
+
}
|
|
851
1074
|
function parsePanoProject(value, options = {}) {
|
|
852
1075
|
if (!isRecord(value))
|
|
853
1076
|
throw new PanoDataError("INVALID_PROJECT", "Expected a project object");
|
|
@@ -878,7 +1101,7 @@ function parsePanoProject(value, options = {}) {
|
|
|
878
1101
|
throw new Error(
|
|
879
1102
|
"source: provide exactly one of viewerUrl or viewerConfig"
|
|
880
1103
|
);
|
|
881
|
-
const baseUrl =
|
|
1104
|
+
const baseUrl = optionalString2(src, "baseUrl");
|
|
882
1105
|
const source = src.viewerUrl !== void 0 ? {
|
|
883
1106
|
viewerUrl: requiredString(src.viewerUrl, "source.viewerUrl"),
|
|
884
1107
|
baseUrl
|
|
@@ -918,7 +1141,7 @@ function parsePanoProject(value, options = {}) {
|
|
|
918
1141
|
);
|
|
919
1142
|
return {
|
|
920
1143
|
id,
|
|
921
|
-
title:
|
|
1144
|
+
title: optionalString2(entry, "title"),
|
|
922
1145
|
source,
|
|
923
1146
|
initialView,
|
|
924
1147
|
...viewLimits === void 0 ? {} : { viewLimits },
|
|
@@ -939,7 +1162,7 @@ function parsePanoProject(value, options = {}) {
|
|
|
939
1162
|
}
|
|
940
1163
|
}
|
|
941
1164
|
);
|
|
942
|
-
const initialSceneId =
|
|
1165
|
+
const initialSceneId = optionalString2(value, "initialSceneId") ?? scenes[0]?.id;
|
|
943
1166
|
if (initialSceneId !== void 0 && !ids.has(initialSceneId))
|
|
944
1167
|
throw new PanoDataError(
|
|
945
1168
|
"INVALID_REFERENCE",
|
|
@@ -967,12 +1190,14 @@ function parsePanoProject(value, options = {}) {
|
|
|
967
1190
|
}
|
|
968
1191
|
}
|
|
969
1192
|
}
|
|
1193
|
+
const map = parsePanoMap(value.map, ids, options);
|
|
970
1194
|
return {
|
|
971
1195
|
schemaVersion: 1,
|
|
972
|
-
title:
|
|
1196
|
+
title: optionalString2(value, "title"),
|
|
973
1197
|
initialSceneId,
|
|
974
1198
|
scenes,
|
|
975
|
-
...popups === void 0 ? {} : { popups }
|
|
1199
|
+
...popups === void 0 ? {} : { popups },
|
|
1200
|
+
...map === void 0 ? {} : { map }
|
|
976
1201
|
};
|
|
977
1202
|
} catch (cause) {
|
|
978
1203
|
if (cause instanceof PanoDataError) throw cause;
|
|
@@ -1071,7 +1296,14 @@ async function loadPanoProject(source, options = {}) {
|
|
|
1071
1296
|
title: definition.title,
|
|
1072
1297
|
initialSceneId: definition.initialSceneId,
|
|
1073
1298
|
scenes,
|
|
1074
|
-
...definition.popups === void 0 ? {} : { popups: definition.popups.map((popup) => resolveProjectPopupAssets(popup, root, options.resolvePopupAssetUrl)) }
|
|
1299
|
+
...definition.popups === void 0 ? {} : { popups: definition.popups.map((popup) => resolveProjectPopupAssets(popup, root, options.resolvePopupAssetUrl)) },
|
|
1300
|
+
...definition.map === void 0 ? {} : {
|
|
1301
|
+
map: resolvePanoMapAssets(
|
|
1302
|
+
definition.map,
|
|
1303
|
+
root,
|
|
1304
|
+
options.resolveAssetUrl
|
|
1305
|
+
)
|
|
1306
|
+
}
|
|
1075
1307
|
};
|
|
1076
1308
|
} finally {
|
|
1077
1309
|
signal?.removeEventListener("abort", cancel);
|