@panoramax/web-viewer 3.2.3 → 4.0.0-develop-39167b4d

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.
Files changed (255) hide show
  1. package/.gitlab-ci.yml +13 -6
  2. package/CHANGELOG.md +53 -1
  3. package/CODE_OF_CONDUCT.md +1 -1
  4. package/README.md +1 -1
  5. package/build/editor.html +10 -1
  6. package/build/index.css +12 -12
  7. package/build/index.css.map +1 -1
  8. package/build/index.html +1 -1
  9. package/build/index.js +2126 -14
  10. package/build/index.js.map +1 -1
  11. package/build/map.html +1 -1
  12. package/build/photo.html +1 -0
  13. package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff +0 -0
  14. package/build/static/media/atkinson-hyperlegible-next-latin-400-normal..woff2 +0 -0
  15. package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff +0 -0
  16. package/build/static/media/atkinson-hyperlegible-next-latin-ext-400-normal..woff2 +0 -0
  17. package/build/viewer.html +12 -1
  18. package/build/widgets.html +1 -0
  19. package/config/jest/mocks.js +201 -0
  20. package/config/paths.js +2 -0
  21. package/config/webpack.config.js +52 -0
  22. package/docs/03_URL_settings.md +14 -16
  23. package/docs/05_Compatibility.md +59 -76
  24. package/docs/09_Develop.md +46 -11
  25. package/docs/90_Releases.md +2 -2
  26. package/docs/images/class_diagram.drawio +60 -45
  27. package/docs/images/class_diagram.jpg +0 -0
  28. package/docs/images/screenshot.jpg +0 -0
  29. package/docs/index.md +135 -0
  30. package/docs/reference/components/core/Basic.md +196 -0
  31. package/docs/reference/components/core/CoverageMap.md +210 -0
  32. package/docs/reference/components/core/Editor.md +224 -0
  33. package/docs/reference/components/core/PhotoViewer.md +307 -0
  34. package/docs/reference/components/core/Viewer.md +350 -0
  35. package/docs/reference/components/layout/BottomDrawer.md +35 -0
  36. package/docs/reference/components/layout/CorneredGrid.md +29 -0
  37. package/docs/reference/components/layout/Mini.md +45 -0
  38. package/docs/reference/components/layout/Tabs.md +45 -0
  39. package/docs/reference/components/menus/MapBackground.md +32 -0
  40. package/docs/reference/components/menus/MapFilters.md +15 -0
  41. package/docs/reference/components/menus/MapLayers.md +15 -0
  42. package/docs/reference/components/menus/MapLegend.md +15 -0
  43. package/docs/reference/components/menus/PictureLegend.md +16 -0
  44. package/docs/reference/components/menus/PictureMetadata.md +15 -0
  45. package/docs/reference/components/menus/PlayerOptions.md +15 -0
  46. package/docs/reference/components/menus/QualityScoreDoc.md +15 -0
  47. package/docs/reference/components/menus/ReportForm.md +15 -0
  48. package/docs/reference/components/menus/ShareMenu.md +15 -0
  49. package/docs/reference/components/ui/Button.md +40 -0
  50. package/docs/reference/components/ui/ButtonGroup.md +36 -0
  51. package/docs/reference/components/ui/CopyButton.md +38 -0
  52. package/docs/reference/components/ui/Grade.md +32 -0
  53. package/docs/reference/components/ui/LinkButton.md +45 -0
  54. package/docs/reference/components/ui/ListGroup.md +22 -0
  55. package/docs/reference/components/ui/Loader.md +56 -0
  56. package/docs/reference/components/ui/Map.md +239 -0
  57. package/docs/reference/components/ui/MapMore.md +256 -0
  58. package/docs/reference/components/ui/Photo.md +385 -0
  59. package/docs/reference/components/ui/Popup.md +56 -0
  60. package/docs/reference/components/ui/ProgressBar.md +32 -0
  61. package/docs/reference/components/ui/QualityScore.md +45 -0
  62. package/docs/reference/components/ui/SearchBar.md +63 -0
  63. package/docs/reference/components/ui/TogglableGroup.md +39 -0
  64. package/docs/reference/components/ui/widgets/GeoSearch.md +32 -0
  65. package/docs/reference/components/ui/widgets/Legend.md +49 -0
  66. package/docs/reference/components/ui/widgets/MapFiltersButton.md +33 -0
  67. package/docs/reference/components/ui/widgets/MapLayersButton.md +15 -0
  68. package/docs/reference/components/ui/widgets/OSMEditors.md +15 -0
  69. package/docs/reference/components/ui/widgets/PictureLegendActions.md +32 -0
  70. package/docs/reference/components/ui/widgets/Player.md +33 -0
  71. package/docs/reference/components/ui/widgets/Zoom.md +15 -0
  72. package/docs/reference/utils/API.md +334 -0
  73. package/docs/reference/utils/InitParameters.md +68 -0
  74. package/docs/reference/utils/URLHandler.md +107 -0
  75. package/docs/reference.md +79 -0
  76. package/docs/shortcuts.md +11 -0
  77. package/docs/tutorials/aerial_imagery.md +19 -0
  78. package/docs/tutorials/authentication.md +10 -0
  79. package/docs/tutorials/custom_widgets.md +59 -0
  80. package/docs/tutorials/map_style.md +39 -0
  81. package/docs/tutorials/migrate_v4.md +153 -0
  82. package/docs/tutorials/synced_coverage.md +43 -0
  83. package/mkdocs.yml +66 -5
  84. package/package.json +22 -17
  85. package/public/editor.html +21 -29
  86. package/public/index.html +17 -12
  87. package/public/map.html +19 -18
  88. package/public/photo.html +55 -0
  89. package/public/viewer.html +22 -26
  90. package/public/widgets.html +306 -0
  91. package/scripts/doc.js +79 -0
  92. package/src/components/core/Basic.css +48 -0
  93. package/src/components/core/Basic.js +349 -0
  94. package/src/components/core/CoverageMap.css +9 -0
  95. package/src/components/core/CoverageMap.js +139 -0
  96. package/src/components/core/Editor.css +23 -0
  97. package/src/components/core/Editor.js +390 -0
  98. package/src/components/core/PhotoViewer.css +48 -0
  99. package/src/components/core/PhotoViewer.js +499 -0
  100. package/src/components/core/Viewer.css +98 -0
  101. package/src/components/core/Viewer.js +564 -0
  102. package/src/components/core/index.js +12 -0
  103. package/src/components/index.js +13 -0
  104. package/src/components/layout/BottomDrawer.js +257 -0
  105. package/src/components/layout/CorneredGrid.js +112 -0
  106. package/src/components/layout/Mini.js +117 -0
  107. package/src/components/layout/Tabs.js +133 -0
  108. package/src/components/layout/index.js +9 -0
  109. package/src/components/menus/MapBackground.js +106 -0
  110. package/src/components/menus/MapFilters.js +400 -0
  111. package/src/components/menus/MapLayers.js +143 -0
  112. package/src/components/menus/MapLegend.js +34 -0
  113. package/src/components/menus/PictureLegend.js +257 -0
  114. package/src/components/menus/PictureMetadata.js +317 -0
  115. package/src/components/menus/PlayerOptions.js +95 -0
  116. package/src/components/menus/QualityScoreDoc.js +36 -0
  117. package/src/components/menus/ReportForm.js +133 -0
  118. package/src/components/menus/Share.js +100 -0
  119. package/src/components/menus/index.js +15 -0
  120. package/src/components/styles.js +383 -0
  121. package/src/components/ui/Button.js +77 -0
  122. package/src/components/ui/ButtonGroup.css +57 -0
  123. package/src/components/ui/ButtonGroup.js +68 -0
  124. package/src/components/ui/CopyButton.js +106 -0
  125. package/src/components/ui/Grade.js +54 -0
  126. package/src/components/ui/LinkButton.js +67 -0
  127. package/src/components/ui/ListGroup.js +66 -0
  128. package/src/components/ui/Loader.js +203 -0
  129. package/src/components/{Map.css → ui/Map.css} +5 -17
  130. package/src/components/{Map.js → ui/Map.js} +148 -156
  131. package/src/components/ui/MapMore.js +324 -0
  132. package/src/components/{Photo.css → ui/Photo.css} +6 -6
  133. package/src/components/{Photo.js → ui/Photo.js} +313 -101
  134. package/src/components/ui/Popup.js +145 -0
  135. package/src/components/ui/ProgressBar.js +104 -0
  136. package/src/components/ui/QualityScore.js +147 -0
  137. package/src/components/ui/SearchBar.js +367 -0
  138. package/src/components/ui/TogglableGroup.js +157 -0
  139. package/src/components/ui/index.js +22 -0
  140. package/src/components/ui/widgets/GeoSearch.css +21 -0
  141. package/src/components/ui/widgets/GeoSearch.js +139 -0
  142. package/src/components/ui/widgets/Legend.js +113 -0
  143. package/src/components/ui/widgets/MapFiltersButton.js +104 -0
  144. package/src/components/ui/widgets/MapLayersButton.js +79 -0
  145. package/src/components/ui/widgets/OSMEditors.js +155 -0
  146. package/src/components/ui/widgets/PictureLegendActions.js +117 -0
  147. package/src/components/ui/widgets/Player.css +7 -0
  148. package/src/components/ui/widgets/Player.js +151 -0
  149. package/src/components/ui/widgets/Zoom.js +82 -0
  150. package/src/components/ui/widgets/index.js +13 -0
  151. package/src/img/loader_base.jpg +0 -0
  152. package/src/img/panoramax.svg +13 -0
  153. package/src/img/switch_big.svg +20 -10
  154. package/src/index.js +7 -9
  155. package/src/translations/br.json +1 -0
  156. package/src/translations/da.json +38 -15
  157. package/src/translations/de.json +5 -3
  158. package/src/translations/en.json +35 -15
  159. package/src/translations/eo.json +38 -15
  160. package/src/translations/es.json +1 -1
  161. package/src/translations/fr.json +36 -16
  162. package/src/translations/hu.json +1 -1
  163. package/src/translations/it.json +39 -16
  164. package/src/translations/ja.json +182 -1
  165. package/src/translations/nl.json +106 -6
  166. package/src/translations/pl.json +1 -1
  167. package/src/translations/sv.json +182 -0
  168. package/src/translations/zh_Hant.json +35 -14
  169. package/src/utils/API.js +109 -49
  170. package/src/utils/InitParameters.js +388 -0
  171. package/src/utils/PhotoAdapter.js +1 -0
  172. package/src/utils/URLHandler.js +362 -0
  173. package/src/utils/geocoder.js +152 -0
  174. package/src/utils/{I18n.js → i18n.js} +7 -3
  175. package/src/utils/index.js +11 -0
  176. package/src/utils/{Map.js → map.js} +256 -77
  177. package/src/utils/picture.js +442 -0
  178. package/src/utils/utils.js +324 -0
  179. package/src/utils/widgets.js +55 -0
  180. package/tests/components/core/Basic.test.js +121 -0
  181. package/tests/components/core/BasicMock.js +25 -0
  182. package/tests/components/core/CoverageMap.test.js +20 -0
  183. package/tests/components/core/Editor.test.js +20 -0
  184. package/tests/components/core/PhotoViewer.test.js +57 -0
  185. package/tests/components/core/Viewer.test.js +84 -0
  186. package/tests/components/core/__snapshots__/PhotoViewer.test.js.snap +73 -0
  187. package/tests/components/core/__snapshots__/Viewer.test.js.snap +145 -0
  188. package/tests/components/ui/CopyButton.test.js +52 -0
  189. package/tests/components/ui/Loader.test.js +55 -0
  190. package/tests/components/{Map.test.js → ui/Map.test.js} +73 -61
  191. package/tests/components/{Photo.test.js → ui/Photo.test.js} +97 -63
  192. package/tests/components/ui/Popup.test.js +26 -0
  193. package/tests/components/ui/QualityScore.test.js +18 -0
  194. package/tests/components/ui/SearchBar.test.js +110 -0
  195. package/tests/components/ui/__snapshots__/CopyButton.test.js.snap +33 -0
  196. package/tests/components/ui/__snapshots__/Loader.test.js.snap +56 -0
  197. package/tests/components/{__snapshots__ → ui/__snapshots__}/Map.test.js.snap +11 -38
  198. package/tests/components/{__snapshots__ → ui/__snapshots__}/Photo.test.js.snap +70 -6
  199. package/tests/components/ui/__snapshots__/Popup.test.js.snap +29 -0
  200. package/tests/components/ui/__snapshots__/QualityScore.test.js.snap +11 -0
  201. package/tests/components/ui/__snapshots__/SearchBar.test.js.snap +65 -0
  202. package/tests/utils/API.test.js +83 -83
  203. package/tests/utils/InitParameters.test.js +499 -0
  204. package/tests/utils/URLHandler.test.js +401 -0
  205. package/tests/utils/__snapshots__/API.test.js.snap +10 -0
  206. package/tests/utils/__snapshots__/URLHandler.test.js.snap +21 -0
  207. package/tests/utils/__snapshots__/{Map.test.js.snap → geocoder.test.js.snap} +1 -1
  208. package/tests/utils/__snapshots__/map.test.js.snap +11 -0
  209. package/tests/utils/__snapshots__/picture.test.js.snap +327 -0
  210. package/tests/utils/__snapshots__/widgets.test.js.snap +19 -0
  211. package/tests/utils/geocoder.test.js +37 -0
  212. package/tests/utils/{I18n.test.js → i18n.test.js} +8 -8
  213. package/tests/utils/map.test.js +126 -0
  214. package/tests/utils/picture.test.js +745 -0
  215. package/tests/utils/utils.test.js +288 -0
  216. package/tests/utils/widgets.test.js +31 -0
  217. package/docs/01_Start.md +0 -149
  218. package/docs/02_Usage.md +0 -831
  219. package/docs/04_Advanced_examples.md +0 -216
  220. package/src/Editor.css +0 -37
  221. package/src/Editor.js +0 -361
  222. package/src/StandaloneMap.js +0 -114
  223. package/src/Viewer.css +0 -203
  224. package/src/Viewer.js +0 -1246
  225. package/src/components/CoreView.css +0 -70
  226. package/src/components/CoreView.js +0 -175
  227. package/src/components/Loader.css +0 -74
  228. package/src/components/Loader.js +0 -120
  229. package/src/img/loader_hd.jpg +0 -0
  230. package/src/utils/Exif.js +0 -193
  231. package/src/utils/Utils.js +0 -631
  232. package/src/utils/Widgets.js +0 -562
  233. package/src/viewer/URLHash.js +0 -469
  234. package/src/viewer/Widgets.css +0 -880
  235. package/src/viewer/Widgets.js +0 -1470
  236. package/tests/Editor.test.js +0 -126
  237. package/tests/StandaloneMap.test.js +0 -45
  238. package/tests/Viewer.test.js +0 -366
  239. package/tests/__snapshots__/Editor.test.js.snap +0 -298
  240. package/tests/__snapshots__/StandaloneMap.test.js.snap +0 -30
  241. package/tests/__snapshots__/Viewer.test.js.snap +0 -195
  242. package/tests/components/CoreView.test.js +0 -92
  243. package/tests/components/Loader.test.js +0 -38
  244. package/tests/components/__snapshots__/Loader.test.js.snap +0 -15
  245. package/tests/utils/Exif.test.js +0 -124
  246. package/tests/utils/Map.test.js +0 -113
  247. package/tests/utils/Utils.test.js +0 -300
  248. package/tests/utils/Widgets.test.js +0 -107
  249. package/tests/utils/__snapshots__/Exif.test.js.snap +0 -43
  250. package/tests/utils/__snapshots__/Utils.test.js.snap +0 -41
  251. package/tests/utils/__snapshots__/Widgets.test.js.snap +0 -44
  252. package/tests/viewer/URLHash.test.js +0 -559
  253. package/tests/viewer/Widgets.test.js +0 -127
  254. package/tests/viewer/__snapshots__/URLHash.test.js.snap +0 -108
  255. package/tests/viewer/__snapshots__/Widgets.test.js.snap +0 -403
@@ -0,0 +1,564 @@
1
+ /* eslint-disable no-unused-vars */
2
+
3
+ import "./Viewer.css";
4
+ import { linkMapAndPhoto, saveMapParamsToLocalStorage, getMapParamsFromLocalStorage } from "../../utils/map";
5
+ import PhotoViewer from "./PhotoViewer";
6
+ import Basic from "./Basic";
7
+ import MapMore from "../ui/MapMore";
8
+ import { initMapKeyboardHandler } from "../../utils/map";
9
+ import { isNullId } from "../../utils/utils";
10
+ import { createWebComp } from "../../utils/widgets";
11
+ import { fa } from "../../utils/widgets";
12
+ import { faPanorama } from "@fortawesome/free-solid-svg-icons/faPanorama";
13
+ import { faMap } from "@fortawesome/free-solid-svg-icons/faMap";
14
+ import { querySelectorDeep } from "query-selector-shadow-dom";
15
+ import { default as InitParameters, alterMapState, alterViewerState } from "../../utils/InitParameters";
16
+
17
+
18
+ export const PSV_ZOOM_DELTA = 20;
19
+ const PSV_MOVE_DELTA = Math.PI / 6;
20
+ const MAP_MOVE_DELTA = 100;
21
+
22
+
23
+ /**
24
+ * Viewer is the main component of Panoramax JS library, showing pictures and map.
25
+ *
26
+ * This component has a [CorneredGrid](#Panoramax.components.layout.CorneredGrid) layout, you can use directly any slot element to pass custom widgets.
27
+ *
28
+ * If you need a viewer without map, checkout [Photo Viewer component](#Panoramax.components.core.PhotoViewer).
29
+ *
30
+ * Make sure to set width/height through CSS for proper display.
31
+ * @class Panoramax.components.core.Viewer
32
+ * @element pnx-viewer
33
+ * @extends Panoramax.components.core.PhotoViewer
34
+ * @property {Panoramax.components.ui.Loader} loader The loader screen
35
+ * @property {Panoramax.utils.API} api The API manager
36
+ * @property {Panoramax.components.ui.MapMore} map The MapLibre GL map itself
37
+ * @property {Panoramax.components.ui.Photo} psv The Photo Sphere Viewer component itself
38
+ * @property {Panoramax.components.layout.CorneredGrid} grid The grid layout manager
39
+ * @property {Panoramax.components.layout.Mini} mini The reduced/collapsed map/photo component
40
+ * @property {Panoramax.components.ui.Popup} popup The popup container
41
+ * @property {Panoramax.utils.URLHandler} urlHandler The URL query parameters manager
42
+ * @fires Panoramax.components.core.Basic#select
43
+ * @fires Panoramax.components.core.Basic#ready
44
+ * @fires Panoramax.components.core.Basic#broken
45
+ * @fires Panoramax.components.core.Viewer#focus-changed
46
+ * @slot `top-left` The top-left corner
47
+ * @slot `top` The top middle corner
48
+ * @slot `top-right` The top-right corner
49
+ * @slot `bottom-left` The bottom-left corner
50
+ * @slot `bottom` The bottom middle corner
51
+ * @slot `bottom-right` The bottom-right corner
52
+ * @slot `editors` External links to map editors, or any tool that may be helpful. Defaults to OSM tools (iD & JOSM).
53
+ * @example
54
+ * ```html
55
+ * <!-- Basic example -->
56
+ * <pnx-viewer
57
+ * endpoint="https://panoramax.openstreetmap.fr/"
58
+ * style="width: 300px; height: 250px"
59
+ * />
60
+ *
61
+ * <!-- With slotted widgets -->
62
+ * <pnx-viewer
63
+ * endpoint="https://panoramax.openstreetmap.fr/"
64
+ * style="width: 300px; height: 250px"
65
+ * >
66
+ * <p slot="top-right">My custom text</p>
67
+ * <p slot="editors"><a href="https://my.own.tool/">Edit in my own tool</a></p>
68
+ * </pnx-viewer>
69
+ *
70
+ * <!-- With only your custom widgets -->
71
+ * <pnx-viewer
72
+ * endpoint="https://panoramax.openstreetmap.fr/"
73
+ * style="width: 300px; height: 250px"
74
+ * widgets="false"
75
+ * >
76
+ * <p slot="top-right">My custom text</p>
77
+ * </pnx-viewer>
78
+ *
79
+ * <!-- With map options -->
80
+ * <pnx-viewer
81
+ * endpoint="https://panoramax.openstreetmap.fr/"
82
+ * style="width: 300px; height: 250px"
83
+ * map="{'maxZoom': 15, 'background': 'aerial', 'raster': '...'}"
84
+ * />
85
+ * ```
86
+ */
87
+ export default class Viewer extends PhotoViewer {
88
+ /**
89
+ * Component properties. All of [Basic properties](#Panoramax.components.core.Basic+properties) are available as well.
90
+ * @memberof Panoramax.components.core.Viewer#
91
+ * @mixes Panoramax.components.core.PhotoViewer#properties
92
+ * @type {Object}
93
+ * @property {string} endpoint URL to API to use (must be a [STAC API](https://github.com/radiantearth/stac-api-spec/blob/main/overview.md))
94
+ * @property {object} [map] An object with [any map option available in Map or MapMore class](#Panoramax.components.ui.MapMore).<br />Example: `map="{'background': 'aerial', 'theme': 'age'}"`
95
+ * @property {object} [psv] [Any option to pass to Photo component](#Panoramax.components.ui.Photo) as an object.<br />Example: `psv="{'transitionDuration': 500, 'picturesNavigation': 'pic'}"`
96
+ * @property {string} [url-parameters=true] Should the component add and update URL query parameters to save viewer state ?
97
+ * @property {string} [focus=pic] The component showing up as main component (pic, map)
98
+ * @property {string} [geocoder=nominatim] The geocoder engine to use (nominatim, ban)
99
+ * @property {string} [widgets=true] Use default set of widgets ? Set to false to avoid any widget to show up, and use slots to populate as you like.
100
+ * @property {string} [picture] The picture ID to display
101
+ * @property {string} [sequence] The sequence ID of the picture displayed
102
+ * @property {object} [fetchOptions] Set custom options for fetch calls made against API ([same syntax as fetch options parameter](https://developer.mozilla.org/en-US/docs/Web/API/fetch#parameters))
103
+ * @property {string[]} [users=[geovisio]] List of users IDs to use for map display (defaults to general map, identified as "geovisio")
104
+ * @property {string|object} [mapstyle] The map's MapLibre style. This can be an a JSON object conforming to the schema described in the [MapLibre Style Specification](https://maplibre.org/maplibre-style-spec/), or a URL string pointing to one. Defaults to OSM vector tiles.
105
+ * @property {string} [lang] To override language used for labels. Defaults to using user's preferred languages.
106
+ */
107
+ static properties = {
108
+ map: {converter: PhotoViewer.GetJSONConverter()},
109
+ focus: {type: String, reflect: true},
110
+ geocoder: {type: String},
111
+ ...PhotoViewer.properties
112
+ };
113
+
114
+ constructor() {
115
+ super();
116
+
117
+ // Defaults
118
+ this.map = true;
119
+ this.geocoder = this.getAttribute("geocoder") || "nominatim";
120
+
121
+ // Init DOM containers
122
+ this.mini = createWebComp("pnx-mini", {
123
+ slot: "bottom-left",
124
+ _parent: this,
125
+ onexpand: this._onMiniExpand.bind(this),
126
+ collapsed: isNullId(this.picture) ? true : undefined
127
+ });
128
+ this.mini.addEventListener("expand", this._toggleFocus.bind(this));
129
+ this.grid.appendChild(this.mini);
130
+ this.mapContainer = document.createElement("div");
131
+ }
132
+
133
+ /** @private */
134
+ _createInitParamsHandler() {
135
+ this._initParams = new InitParameters(
136
+ InitParameters.GetComponentProperties(Viewer, this),
137
+ Object.assign({}, this.urlHandler?.currentURLParams(), this.urlHandler?.currentURLParams(true)),
138
+ { map: getMapParamsFromLocalStorage() },
139
+ );
140
+ }
141
+
142
+ /** @private */
143
+ _initWidgets() {
144
+ if(this._initParams.getParentPostInit().widgets !== "false") {
145
+ this.grid.appendChild(createWebComp("pnx-widget-zoom", {
146
+ slot: this.isWidthSmall() ? "top-left" : "bottom-right",
147
+ class: this.isWidthSmall() ? "pnx-only-map pnx-print-hidden" : "pnx-print-hidden",
148
+ _parent: this
149
+ }));
150
+
151
+ if(!this.isWidthSmall()) {
152
+ this.legend = createWebComp("pnx-widget-legend", {
153
+ slot: this.isWidthSmall() ? "top" : "top-left",
154
+ _parent: this,
155
+ focus: this._initParams.getParentPostInit().focus,
156
+ picture: this._initParams.getParentPostInit().picture,
157
+ });
158
+ this.grid.appendChild(this.legend);
159
+ }
160
+ else {
161
+ this.legend = createWebComp("pnx-picture-legend", { _parent: this });
162
+ this.bottomDrawer = createWebComp("pnx-bottom-drawer", {
163
+ slot: "bottom",
164
+ _parent: this,
165
+ class: this._initParams.getParentPostInit().picture ? undefined: "pnx-hidden",
166
+ });
167
+ this.bottomDrawer.appendChild(this.legend);
168
+ this.grid.appendChild(this.bottomDrawer);
169
+ this.addEventListener("select", e => {
170
+ if(isNullId(e.detail.picId)) { this.bottomDrawer.classList.add("pnx-hidden"); }
171
+ else { this.bottomDrawer.classList.remove("pnx-hidden"); }
172
+ });
173
+ }
174
+
175
+ this.grid.appendChild(createWebComp("pnx-widget-player", {
176
+ slot: "top",
177
+ _parent: this,
178
+ class: "pnx-only-psv pnx-print-hidden",
179
+ size: this.isHeightSmall() ? "md": "xl",
180
+ }));
181
+
182
+ this.grid.appendChild(createWebComp("pnx-widget-geosearch", {
183
+ slot: this.isWidthSmall() ? "top-right" : "top-left",
184
+ _parent: this,
185
+ class: "pnx-only-map pnx-print-hidden",
186
+ geocoder: this._initParams.getParentPostInit().geocoder,
187
+ }));
188
+ this.grid.appendChild(createWebComp("pnx-widget-mapfilters", {
189
+ slot: this.isWidthSmall() ? "top-right" : "top-left",
190
+ _parent: this,
191
+ "user-search": this.api._endpoints.user_search !== null && this.api._endpoints.user_tiles !== null,
192
+ "quality-score": this.map?._hasQualityScore?.() || false,
193
+ class: "pnx-only-map pnx-print-hidden",
194
+ }));
195
+ this.grid.appendChild(createWebComp("pnx-widget-maplayers", { slot: "top-right", _parent: this, class: "pnx-only-map pnx-print-hidden" }));
196
+ }
197
+ }
198
+
199
+ getClassName() {
200
+ return "Viewer";
201
+ }
202
+
203
+ getSubComponentsNames() {
204
+ return super.getSubComponentsNames().concat(["mini", "map"]);
205
+ }
206
+
207
+ /**
208
+ * Waits for Viewer to be completely ready (map & PSV loaded, first picture also if one is wanted)
209
+ * @returns {Promise} When viewer is ready
210
+ * @memberof Panoramax.components.core.Viewer#
211
+ */
212
+ onceReady() {
213
+ return Promise.all([this.oncePSVReady(), this.onceMapReady()])
214
+ .then(() => {
215
+ if(this._initParams.getParentPostInit().picture && !this.psv.getPictureMetadata()) { return this.onceFirstPicLoaded(); }
216
+ else { return Promise.resolve(); }
217
+ });
218
+ }
219
+
220
+ /** @private */
221
+ attributeChangedCallback(name, old, value) {
222
+ super.attributeChangedCallback(name, old, value);
223
+
224
+ if(name === "picture") {
225
+ this.legend?.setAttribute?.("picture", value);
226
+
227
+ // First pic load : show map in mini component
228
+ if(isNullId(old) && !isNullId(value)) {
229
+ this.mini.removeAttribute("collapsed");
230
+ }
231
+
232
+ // Unselect -> show map wide instead
233
+ if(isNullId(value)) {
234
+ if(this.map && this.isMapWide()) { this.mini.classList.add("pnx-hidden"); }
235
+ else if(this.map && !this.isMapWide()) { this._setFocus("map"); }
236
+ }
237
+ // Select after none selected -> show pic wide
238
+ else {
239
+ this.mini.classList.remove("pnx-hidden");
240
+ if(isNullId(old)) {
241
+ this._setFocus("pic");
242
+ if(this.bottomDrawer?.getAttribute?.("openness") === "closed") {
243
+ this.bottomDrawer.setAttribute("openness", "half-opened");
244
+ }
245
+ }
246
+ }
247
+ }
248
+
249
+ if(name === "focus") {
250
+ this._setFocus(value);
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Waiting for map to be available.
256
+ * @returns {Promise} When map is ready to use
257
+ * @memberof Panoramax.components.core.Viewer#
258
+ */
259
+ onceMapReady() {
260
+ if(!this.map) { return Promise.resolve(); }
261
+
262
+ let waiter;
263
+ return new Promise(resolve => {
264
+ waiter = setInterval(() => {
265
+ if(typeof this.map === "object") {
266
+ if(this.map?.loaded?.()) {
267
+ clearInterval(waiter);
268
+ resolve();
269
+ }
270
+ else if(this.map?.once) {
271
+ this.map.once("render", () => {
272
+ clearInterval(waiter);
273
+ resolve();
274
+ });
275
+ }
276
+ }
277
+ }, 250);
278
+ });
279
+ }
280
+
281
+ /**
282
+ * Inits MapLibre GL component
283
+ *
284
+ * @private
285
+ * @returns {Promise} Resolves when map is ready
286
+ */
287
+ async _initMap() {
288
+ await new Promise(resolve => {
289
+ this.map = new MapMore(this, this.mapContainer, this._initParams.getMapInit());
290
+ saveMapParamsToLocalStorage(this.map);
291
+ this.map.once("users-changed", () => {
292
+ this.loader.setAttribute("value", 75);
293
+ resolve();
294
+ });
295
+ });
296
+
297
+ alterMapState(this.map, this._initParams.getMapPostInit());
298
+ initMapKeyboardHandler(this);
299
+ linkMapAndPhoto(this);
300
+ }
301
+
302
+ /** @private */
303
+ async _postAPIInit() {
304
+ this.loader.setAttribute("value", 30);
305
+ this._createInitParamsHandler();
306
+
307
+ const myPostInitParams = this._initParams.getParentPostInit();
308
+
309
+ this._initPSV();
310
+ await this._initMap();
311
+ this._initWidgets();
312
+
313
+ // Re-launch slot move (for those depending on widgets)
314
+ this._moveChildToGrid();
315
+
316
+ alterViewerState(this, myPostInitParams);
317
+ this._handleKeyboardManagement();
318
+
319
+ if(myPostInitParams.picture) {
320
+ this.psv.addEventListener("picture-loaded", () => {
321
+ alterViewerState(this, myPostInitParams); // Do it again for forcing focus
322
+ this.loader.dismiss();
323
+ }, {once: true});
324
+ }
325
+ else {
326
+ this.loader.dismiss();
327
+ }
328
+ }
329
+
330
+ /** @private */
331
+ _handleKeyboardManagement() {
332
+ // Switchers
333
+ const keytomap = () => {
334
+ this.psv.stopKeyboardControl();
335
+ this.map.keyboard.enable();
336
+ };
337
+ const keytopsv = () => {
338
+ this.psv.startKeyboardControl();
339
+ this.map?.keyboard?.disable();
340
+ };
341
+ const keytonone = () => {
342
+ this.psv.stopKeyboardControl();
343
+ this.map?.keyboard?.disable();
344
+ };
345
+ const keytofocused = () => {
346
+ if(this.map && this.isMapWide()) { keytomap(); }
347
+ else { keytopsv(); }
348
+ };
349
+
350
+ // General focus change
351
+ this.addEventListener("focus-changed", e => {
352
+ if(e.detail.focus === "map") { keytomap(); }
353
+ else { keytopsv(); }
354
+ });
355
+
356
+ // Popup
357
+ this.popup.addEventListener("open", () => keytonone());
358
+ this.popup.addEventListener("close", () => keytofocused());
359
+
360
+ // Widgets
361
+ for(let cn of this.grid.childNodes) {
362
+ if(cn.getAttribute("slot") !== "bg") {
363
+ cn.addEventListener("focusin", () => keytonone());
364
+ cn.addEventListener("focusout", () => keytofocused());
365
+ }
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Move the view of main component to its center.
371
+ * For map, center view on selected picture.
372
+ * For picture, center view on image center.
373
+ * @memberof Panoramax.components.core.Viewer#
374
+ */
375
+ moveCenter() {
376
+ const meta = this.psv.getPictureMetadata();
377
+ if(!meta) { return; }
378
+
379
+ if(this.map && this.isMapWide()) {
380
+ this.map.flyTo({ center: meta.gps, zoom: 20 });
381
+ }
382
+ else {
383
+ super.moveCenter();
384
+ }
385
+ }
386
+
387
+ /**
388
+ * Moves map or picture viewer to given direction.
389
+ * @param {string} dir Direction to move to (up, left, down, right)
390
+ * @private
391
+ */
392
+ _moveToDirection(dir) {
393
+ if(this.map && this.isMapWide()) {
394
+ let pan;
395
+ switch(dir) {
396
+ case "up":
397
+ pan = [0, -MAP_MOVE_DELTA];
398
+ break;
399
+ case "left":
400
+ pan = [-MAP_MOVE_DELTA, 0];
401
+ break;
402
+ case "down":
403
+ pan = [0, MAP_MOVE_DELTA];
404
+ break;
405
+ case "right":
406
+ pan = [MAP_MOVE_DELTA, 0];
407
+ break;
408
+ }
409
+ this.map.panBy(pan);
410
+ }
411
+ else {
412
+ super._moveToDirection(dir);
413
+ }
414
+ }
415
+
416
+ /**
417
+ * Is the map shown as main element instead of viewer (wide map mode) ?
418
+ * @memberof Panoramax.components.core.Viewer#
419
+ * @returns {boolean} True if map is wider than viewer
420
+ */
421
+ isMapWide() {
422
+ return this.mapContainer.parentNode == this.grid;
423
+ }
424
+
425
+ /**
426
+ * Change the viewer focus (either on picture or map)
427
+ * @memberof Panoramax.components.core.Viewer#
428
+ * @param {string} focus The object to focus on (map, pic)
429
+ * @param {boolean} [skipEvent=false] True to not send focus-changed event
430
+ * @param {boolean} [skipDupCheck=false] True to avoid duplicate calls check
431
+ * @private
432
+ */
433
+ _setFocus(focus, skipEvent = false, skipDupCheck = false) {
434
+ if(focus === "map" && !this.map) { throw new Error("Map is not enabled"); }
435
+ if(!["map", "pic"].includes(focus)) { throw new Error("Invalid focus value (should be pic or map)"); }
436
+ this.focus = focus;
437
+
438
+ if(!skipDupCheck && (
439
+ (focus === "map" && this.map && this.isMapWide())
440
+ || (focus === "pic" && (!this.map || !this.isMapWide()))
441
+ )) { return; }
442
+
443
+ if(focus === "map") {
444
+ // Remove PSV from grid
445
+ if(this.psvContainer.parentNode == this.grid) {
446
+ this.grid.removeChild(this.psvContainer);
447
+ this.psvContainer.removeAttribute("slot");
448
+ }
449
+
450
+ // Remove map from mini
451
+ if(this.mapContainer.parentNode == this.mini) {
452
+ this.mini.removeChild(this.mapContainer);
453
+ }
454
+
455
+ // Add map to grid
456
+ this.mapContainer.setAttribute("slot", "bg");
457
+ this.grid.appendChild(this.mapContainer);
458
+
459
+ // Add PSV to mini
460
+ this.mini.appendChild(this.psvContainer);
461
+ this.mini.icon = fa(faPanorama);
462
+
463
+ // Hide mini icon if no picture selected
464
+ if(isNullId(this.picture)) { this.mini.classList.add("pnx-hidden"); }
465
+ else { this.mini.classList.remove("pnx-hidden"); }
466
+
467
+ this.map.getCanvas().focus();
468
+ }
469
+ else {
470
+ // Remove map from grid
471
+ if(this.mapContainer.parentNode == this.grid) {
472
+ this.grid.removeChild(this.mapContainer);
473
+ this.mapContainer.removeAttribute("slot");
474
+ }
475
+
476
+ // Remove PSV from mini
477
+ if(this.psvContainer.parentNode == this.mini) {
478
+ this.mini.removeChild(this.psvContainer);
479
+ }
480
+
481
+ // Add PSV to grid
482
+ this.psvContainer.setAttribute("slot", "bg");
483
+ this.grid.appendChild(this.psvContainer);
484
+
485
+ // Add map to mini
486
+ this.mini.classList.remove("pnx-hidden");
487
+ this.mini.appendChild(this.mapContainer);
488
+ this.mini.icon = fa(faMap);
489
+
490
+ this.psvContainer.focus();
491
+ }
492
+
493
+ this?.map?.resize?.();
494
+ this.psv.autoSize();
495
+ this.psv.forceRefresh();
496
+ this.legend?.setAttribute?.("focus", this.focus);
497
+
498
+ if(!skipEvent) {
499
+ /**
500
+ * Event for focus change (either map or picture is shown wide)
501
+ * @event Panoramax.components.core.Viewer#focus-changed
502
+ * @type {CustomEvent}
503
+ * @property {string} detail.focus Component now focused on (map, pic)
504
+ */
505
+ const event = new CustomEvent("focus-changed", { detail: { focus } });
506
+ this.dispatchEvent(event);
507
+ }
508
+ }
509
+
510
+ /**
511
+ * Toggle the viewer focus (either on picture or map)
512
+ * @memberof Panoramax.components.core.Viewer#
513
+ * @private
514
+ */
515
+ _toggleFocus() {
516
+ this._setFocus(this.isMapWide() ? "pic" : "map");
517
+ }
518
+
519
+ /** @private */
520
+ _onMiniExpand() {
521
+ this.map.resize();
522
+ this.psv.autoSize();
523
+ }
524
+
525
+ /**
526
+ * Send viewer new map filters values.
527
+ * @private
528
+ */
529
+ _onMapFiltersChange() {
530
+ const mapFiltersMenu = querySelectorDeep("#pnx-map-filters-menu");
531
+ const fMinDate = mapFiltersMenu?.shadowRoot.getElementById("pnx-filter-date-from");
532
+ const fMaxDate = mapFiltersMenu?.shadowRoot.getElementById("pnx-filter-date-end");
533
+ const fTypes = mapFiltersMenu?.shadowRoot.querySelectorAll("input[name='pnx-filter-type']");
534
+ const fMapTheme = querySelectorDeep("#pnx-map-theme");
535
+
536
+ let type = "";
537
+ for(let fTypeId = 0 ; fTypeId < fTypes.length; fTypeId++) {
538
+ const fType = fTypes[fTypeId];
539
+ if(fType.checked) {
540
+ type = fType.value;
541
+ break;
542
+ }
543
+ }
544
+
545
+ let qualityscore = [];
546
+ if(this.map?._hasQualityScore()) {
547
+ const fScore = mapFiltersMenu?.shadowRoot.getElementById("pnx-filter-qualityscore");
548
+ qualityscore = (fScore?.grade || "").split(",").map(v => parseInt(v)).filter(v => !isNaN(v));
549
+ if(qualityscore.length == 5) { qualityscore = []; }
550
+ }
551
+
552
+ const values = {
553
+ minDate: fMinDate?.value,
554
+ maxDate: fMaxDate?.value,
555
+ pic_type: type,
556
+ theme: fMapTheme?.value,
557
+ qualityscore,
558
+ };
559
+
560
+ this.map.setFilters(values);
561
+ }
562
+ }
563
+
564
+ customElements.define("pnx-viewer", Viewer);
@@ -0,0 +1,12 @@
1
+ /* eslint-disable import/no-unused-modules */
2
+
3
+ /**
4
+ * Core graphical components
5
+ * @module Panoramax:components:core
6
+ */
7
+
8
+ export {default as Basic} from "./Basic";
9
+ export {default as CoverageMap} from "./CoverageMap";
10
+ export {default as Editor} from "./Editor";
11
+ export {default as Viewer} from "./Viewer";
12
+ export {default as PhotoViewer} from "./PhotoViewer";
@@ -0,0 +1,13 @@
1
+ /* eslint-disable import/no-unused-modules */
2
+
3
+ /**
4
+ * Graphical components
5
+ * @module Panoramax:components
6
+ */
7
+
8
+ import * as core from "./core";
9
+ import * as layout from "./layout";
10
+ import * as menus from "./menus";
11
+ import * as ui from "./ui";
12
+
13
+ export {core, layout, menus, ui};