@panoramax/web-viewer 4.0.3 → 4.1.0-develop-55fdf56c

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 (106) hide show
  1. package/CHANGELOG.md +46 -1
  2. package/build/index.css +9 -9
  3. package/build/index.css.map +1 -1
  4. package/build/index.js +640 -456
  5. package/build/index.js.map +1 -1
  6. package/build/map.html +1 -1
  7. package/build/photo.html +1 -1
  8. package/build/viewer.html +3 -3
  9. package/build/widgets.html +1 -1
  10. package/config/jest/mocks.js +9 -1
  11. package/docs/03_URL_settings.md +21 -0
  12. package/docs/09_Develop.md +6 -0
  13. package/docs/images/comparative_3drender.jpg +0 -0
  14. package/docs/index.md +13 -0
  15. package/docs/reference/components/core/Editor.md +18 -0
  16. package/docs/reference/components/core/PhotoViewer.md +1 -0
  17. package/docs/reference/components/core/Viewer.md +1 -0
  18. package/docs/reference/components/menus/MapLegend.md +17 -0
  19. package/docs/reference/components/menus/MiniPictureLegend.md +15 -0
  20. package/docs/reference/components/menus/PictureLegend.md +17 -0
  21. package/docs/reference/components/ui/AnnotationsSwitch.md +15 -0
  22. package/docs/reference/components/ui/Button.md +1 -1
  23. package/docs/reference/components/ui/CopyButton.md +1 -1
  24. package/docs/reference/components/ui/LinkButton.md +1 -1
  25. package/docs/reference/components/ui/Map.md +18 -2
  26. package/docs/reference/components/ui/MapMore.md +6 -2
  27. package/docs/reference/components/ui/SemanticsEditor.md +87 -0
  28. package/docs/reference/components/ui/widgets/Legend.md +5 -4
  29. package/docs/reference/utils/URLHandler.md +7 -0
  30. package/docs/reference.md +3 -1
  31. package/docs/tutorials/aerial_imagery.md +13 -11
  32. package/mkdocs.yml +3 -1
  33. package/package.json +7 -7
  34. package/public/map.html +3 -3
  35. package/public/photo.html +1 -1
  36. package/public/viewer.html +3 -3
  37. package/public/widgets.html +32 -0
  38. package/src/components/core/Basic.css +2 -0
  39. package/src/components/core/Basic.js +3 -1
  40. package/src/components/core/CoverageMap.css +1 -0
  41. package/src/components/core/CoverageMap.js +6 -0
  42. package/src/components/core/Editor.css +2 -0
  43. package/src/components/core/Editor.js +56 -7
  44. package/src/components/core/PhotoViewer.css +10 -10
  45. package/src/components/core/PhotoViewer.js +56 -23
  46. package/src/components/core/Viewer.css +16 -4
  47. package/src/components/core/Viewer.js +62 -33
  48. package/src/components/layout/BottomDrawer.js +2 -1
  49. package/src/components/layout/Tabs.js +4 -0
  50. package/src/components/menus/AnnotationsList.js +13 -9
  51. package/src/components/menus/MapBackground.js +8 -3
  52. package/src/components/menus/MapFilters.js +11 -2
  53. package/src/components/menus/MapLayers.js +3 -2
  54. package/src/components/menus/MapLegend.js +35 -4
  55. package/src/components/menus/MiniPictureLegend.js +74 -0
  56. package/src/components/menus/PictureLegend.js +89 -33
  57. package/src/components/menus/PictureMetadata.js +49 -17
  58. package/src/components/menus/PlayerOptions.js +3 -3
  59. package/src/components/menus/Share.js +3 -3
  60. package/src/components/menus/index.js +5 -4
  61. package/src/components/styles.js +11 -0
  62. package/src/components/ui/AnnotationsSwitch.js +171 -0
  63. package/src/components/ui/Button.js +1 -1
  64. package/src/components/ui/CopyButton.js +1 -1
  65. package/src/components/ui/LinkButton.js +1 -1
  66. package/src/components/ui/Map.css +4 -0
  67. package/src/components/ui/Map.js +17 -5
  68. package/src/components/ui/MapMore.js +61 -25
  69. package/src/components/ui/Photo.css +11 -2
  70. package/src/components/ui/Photo.js +6 -3
  71. package/src/components/ui/SemanticsEditor.js +157 -0
  72. package/src/components/ui/index.js +2 -1
  73. package/src/components/ui/widgets/GeoSearch.js +3 -2
  74. package/src/components/ui/widgets/Legend.js +76 -15
  75. package/src/components/ui/widgets/MapFiltersButton.js +3 -3
  76. package/src/components/ui/widgets/MapLayersButton.js +3 -3
  77. package/src/components/ui/widgets/OSMEditors.js +2 -2
  78. package/src/components/ui/widgets/PictureLegendActions.js +24 -42
  79. package/src/components/ui/widgets/Player.js +3 -3
  80. package/src/components/ui/widgets/Zoom.js +4 -2
  81. package/src/translations/ar.json +1 -0
  82. package/src/translations/da.json +3 -2
  83. package/src/translations/de.json +64 -13
  84. package/src/translations/en.json +5 -1
  85. package/src/translations/eo.json +32 -2
  86. package/src/translations/fr.json +7 -1
  87. package/src/translations/it.json +33 -2
  88. package/src/translations/nl.json +53 -11
  89. package/src/translations/zh_Hant.json +29 -2
  90. package/src/utils/API.js +17 -1
  91. package/src/utils/InitParameters.js +46 -4
  92. package/src/utils/URLHandler.js +9 -1
  93. package/src/utils/map.js +24 -1
  94. package/src/utils/semantics.js +53 -1
  95. package/src/utils/services.js +16 -0
  96. package/src/utils/widgets.js +38 -0
  97. package/tests/components/core/Editor.test.js +1 -1
  98. package/tests/components/core/__snapshots__/PhotoViewer.test.js.snap +18 -6
  99. package/tests/components/core/__snapshots__/Viewer.test.js.snap +15 -3
  100. package/tests/components/ui/Photo.test.js +1 -0
  101. package/tests/components/ui/__snapshots__/Map.test.js.snap +164 -0
  102. package/tests/utils/InitParameters.test.js +27 -0
  103. package/tests/utils/map.test.js +12 -0
  104. package/tests/utils/semantics.test.js +34 -5
  105. package/docs/reference/components/ui/HashTags.md +0 -15
  106. package/src/components/ui/HashTags.js +0 -98
@@ -1,6 +1,7 @@
1
- import { LitElement, html, nothing, css } from "lit";
1
+ import { LitElement, nothing, css } from "lit";
2
+ import { html, unsafeStatic } from "lit/static-html.js";
2
3
  import { classMap } from "lit/directives/class-map.js";
3
- import { fa } from "../../utils/widgets";
4
+ import { fa, onceParentAvailable } from "../../utils/widgets";
4
5
  import { faArrowLeft } from "@fortawesome/free-solid-svg-icons/faArrowLeft";
5
6
  import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
6
7
  import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
@@ -10,6 +11,7 @@ import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons/faTrian
10
11
  import { faShareNodes } from "@fortawesome/free-solid-svg-icons/faShareNodes";
11
12
  import { placeholder, panel, hidden } from "../styles";
12
13
  import { reverseGeocodingNominatim } from "../../utils/geocoder";
14
+ import { PanoramaxMetaCatalogURL } from "../../utils/services";
13
15
 
14
16
  /**
15
17
  * Picture legend shows info about picture author, capture date, address, and access to metadata popup.
@@ -26,16 +28,17 @@ export default class PictureLegend extends LitElement {
26
28
  /** @private */
27
29
  static styles = [placeholder, panel, hidden, css`
28
30
  :host {
29
- overflow-y: auto;
30
- overflow-x: hidden;
31
- display: block;
31
+ display: flex;
32
+ flex-direction: column;
32
33
  margin: 0;
33
34
  font-family: var(--font-family);
35
+ flex-wrap: nowrap;
34
36
  }
35
37
 
36
38
  @media screen and (min-width: 576px) {
37
- :host { max-height: 70vh; }
38
- pnx-picture-metadata { width: 30vw; }
39
+ :host {
40
+ max-height: 70vh;
41
+ }
39
42
  }
40
43
 
41
44
  /* Top bar */
@@ -45,6 +48,12 @@ export default class PictureLegend extends LitElement {
45
48
  align-items: center;
46
49
  margin: 10px 10px 5px 10px;
47
50
  justify-content: space-between;
51
+ flex: 1;
52
+ }
53
+
54
+ .headline-buttons {
55
+ display: flex;
56
+ gap: 5px;
48
57
  }
49
58
 
50
59
  /* Address line */
@@ -54,6 +63,9 @@ export default class PictureLegend extends LitElement {
54
63
  margin-bottom: 2px;
55
64
  flex-grow: 5;
56
65
  font-weight: 800;
66
+ text-overflow: ellipsis;
67
+ overflow: hidden;
68
+ white-space: nowrap;
57
69
  }
58
70
 
59
71
  #pic-legend-addr span {
@@ -84,6 +96,7 @@ export default class PictureLegend extends LitElement {
84
96
  display: block;
85
97
  margin-top: 5px;
86
98
  max-width: 100%;
99
+ flex: 1;
87
100
  }
88
101
  #pic-legend-expand::part(btn) {
89
102
  border-radius: 10px;
@@ -93,10 +106,11 @@ export default class PictureLegend extends LitElement {
93
106
 
94
107
  /* Details block */
95
108
  pnx-picture-metadata {
96
- margin: 5px 10px 10px;
109
+ margin: 5px 10px;
97
110
  display: block;
98
- max-width: 450px;
99
111
  box-sizing: border-box;
112
+ flex: 1;
113
+ overflow-y: auto;
100
114
  }
101
115
 
102
116
  /* Details actions */
@@ -107,6 +121,7 @@ export default class PictureLegend extends LitElement {
107
121
  border-bottom-right-radius: 10px;
108
122
  gap: 5px;
109
123
  flex-wrap: wrap;
124
+ flex: 1;
110
125
  }
111
126
 
112
127
  /* More options menu */
@@ -114,10 +129,22 @@ export default class PictureLegend extends LitElement {
114
129
 
115
130
  /* Editors */
116
131
  #pic-legend-editors { margin: 0 10px; }
132
+
133
+ /* Light version */
134
+ .pnx-picture-legend-light {
135
+ width: max-content;
136
+ font-size: 10px;
137
+ }
117
138
  `];
118
139
 
119
- /** @private */
140
+ /**
141
+ * Component properties.
142
+ * @memberof Panoramax.components.menus.PictureLegend#
143
+ * @type {Object}
144
+ * @property {boolean} [light=false] Lighter version (for iframes)
145
+ */
120
146
  static properties = {
147
+ light: {type: Boolean},
121
148
  _caption: { state: true },
122
149
  _addr: { state: true },
123
150
  _expanded: { state: true },
@@ -129,6 +156,7 @@ export default class PictureLegend extends LitElement {
129
156
  super();
130
157
  this._expanded = true;
131
158
  this.collapsable = false;
159
+ this.light = false;
132
160
  }
133
161
 
134
162
  /** @private */
@@ -138,18 +166,20 @@ export default class PictureLegend extends LitElement {
138
166
  this._expanded = !this.collapsable;
139
167
  this._prevSearches = {};
140
168
 
141
- this._parent.onceReady().then(() => {
142
- this._onPicChange(this._parent.psv.getPictureMetadata());
143
- this._parent.psv.addEventListener("picture-loaded", () => {
169
+ onceParentAvailable(this)
170
+ .then(() => this._parent.onceReady())
171
+ .then(() => {
144
172
  this._onPicChange(this._parent.psv.getPictureMetadata());
173
+ this._parent.psv.addEventListener("picture-loaded", () => {
174
+ this._onPicChange(this._parent.psv.getPictureMetadata());
175
+ });
176
+ this._parent.psv.addEventListener("sequence-stopped", () => {
177
+ this._onPicChange(this._parent.psv.getPictureMetadata());
178
+ });
179
+ this._parent.psv.addEventListener("annotation-click", () => {
180
+ this._expanded = true;
181
+ });
145
182
  });
146
- this._parent.psv.addEventListener("sequence-stopped", () => {
147
- this._onPicChange(this._parent.psv.getPictureMetadata());
148
- });
149
- this._parent.psv.addEventListener("annotation-click", () => {
150
- this._expanded = true;
151
- });
152
- });
153
183
  }
154
184
 
155
185
  /** @private */
@@ -196,6 +226,22 @@ export default class PictureLegend extends LitElement {
196
226
  const hiddenExpanded = classMap({"pnx-hidden": this._expanded});
197
227
  const shownExpanded = classMap({"pnx-hidden": !this._expanded});
198
228
 
229
+ if(this.light) {
230
+ return html`<div class="pnx-picture-legend-light">
231
+ ${this._caption.producer?.length > 0 ? html`
232
+ <a
233
+ href=${PanoramaxMetaCatalogURL()+"/?pic="+this._parent.psv.getPictureMetadata().id}
234
+ target="_blank"
235
+ title=${this._parent?._t.pnx.share_page}
236
+ >${this._caption.producer[this._caption.producer.length-1]}</a>
237
+ </div>` : nothing}
238
+
239
+ ${this._caption.producer?.length > 0 && this._caption?.license ? "|" : ""}
240
+
241
+ ${this._caption?.license ? html`${unsafeStatic(this._caption.license)}` : nothing}
242
+ `;
243
+ }
244
+
199
245
  return html`
200
246
  <div class="headline">
201
247
  ${this._parent._setFocus ? html`
@@ -207,15 +253,26 @@ export default class PictureLegend extends LitElement {
207
253
  </pnx-button>
208
254
  ` : nothing}
209
255
 
210
- <div id="pic-legend-addr">
256
+ <div id="pic-legend-addr" title=${this._addr || ""}>
211
257
  ${this._addr?.length > 0 ? this._addr : html`<span class="pnx-placeholder-loading">&nbsp;</span>`}
212
258
  </div>
213
259
 
214
- <pnx-picture-legend-actions
215
- @click=${e => e.stopPropagation()}
216
- ._parent=${this._parent}
217
- ?full=${this._expanded}
218
- ></pnx-picture-legend-actions>
260
+ <div class="headline-buttons">
261
+ <pnx-button
262
+ size="sm"
263
+ class=${hiddenExpanded}
264
+ title=${this._parent?._t.pnx.share}
265
+ @click=${() => this._parent._showShareOptions()}
266
+ >
267
+ ${fa(faShareNodes)}
268
+ </pnx-button>
269
+
270
+ <pnx-picture-legend-actions
271
+ @click=${e => e.stopPropagation()}
272
+ ._parent=${this._parent}
273
+ ?full=${this._expanded}
274
+ ></pnx-picture-legend-actions>
275
+ </div>
219
276
  </div>
220
277
 
221
278
  <div id="pic-legend-info" class=${hiddenExpanded}>
@@ -226,21 +283,20 @@ export default class PictureLegend extends LitElement {
226
283
 
227
284
  ${this._caption.date ? html`<div class="info-block">
228
285
  ${fa(faCalendarAlt)}
229
- ${this._caption.date.toLocaleDateString(this._parent?.lang || window.navigator.language, { year: "numeric", month: "long" })}
230
- </div>` : nothing}
286
+ ${this._caption.date.toLocaleDateString(this._parent?.lang || window.navigator.language, { year: "numeric", month: "long", day: "numeric" })} </div>` : nothing}
231
287
  </div>
232
288
 
233
289
  <div id="pic-legend-cta" class=${shownExpanded}>
290
+ <pnx-button size="sm" @click=${() => this._parent._showShareOptions()}>
291
+ ${fa(faShareNodes)} ${this._parent?._t.pnx.share}
292
+ </pnx-button>
293
+
234
294
  ${this._parent.api._endpoints.report ? html`
235
- <pnx-button size="sm" @click=${() => this._parent._showReportForm()}>
295
+ <pnx-button kind="fullwarn" size="sm" @click=${() => this._parent._showReportForm()}>
236
296
  ${fa(faTriangleExclamation)} ${this._parent?._t.pnx.report}
237
297
  </pnx-button>
238
298
  ` : nothing}
239
299
 
240
- <pnx-button size="sm" @click=${() => this._parent._showShareOptions()}>
241
- ${fa(faShareNodes)} ${this._parent?._t.pnx.share}
242
- </pnx-button>
243
-
244
300
  <slot name="editors">
245
301
  <pnx-widget-osmeditors ._parent=${this._parent} />
246
302
  </slot>
@@ -1,6 +1,6 @@
1
1
  import { LitElement, nothing, css } from "lit";
2
2
  import { html, unsafeStatic } from "lit/static-html.js";
3
- import { fa } from "../../utils/widgets";
3
+ import { fa, onceParentAvailable } from "../../utils/widgets";
4
4
  import { faLocationDot } from "@fortawesome/free-solid-svg-icons/faLocationDot";
5
5
  import { faMedal } from "@fortawesome/free-solid-svg-icons/faMedal";
6
6
  import { faCamera } from "@fortawesome/free-solid-svg-icons/faCamera";
@@ -14,7 +14,8 @@ import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
14
14
  import { faTags } from "@fortawesome/free-solid-svg-icons/faTags";
15
15
  import { faSvg, titles, textarea, hidden } from "../styles";
16
16
  import { createWebComp } from "../../utils/widgets";
17
- import { getGPSPrecision } from "../../utils/picture";
17
+ import { getGPSPrecision, getHashTags } from "../../utils/picture";
18
+ import { PanoramaxMetaCatalogURL } from "../../utils/services";
18
19
  import {
19
20
  getGrade, QUALITYSCORE_GPS_VALUES, QUALITYSCORE_RES_360_VALUES,
20
21
  QUALITYSCORE_RES_FLAT_VALUES, QUALITYSCORE_POND_GPS, QUALITYSCORE_POND_RES
@@ -81,14 +82,16 @@ export default class PictureMetadata extends LitElement {
81
82
  connectedCallback() {
82
83
  super.connectedCallback();
83
84
 
84
- this._meta = this._parent?.psv?.getPictureMetadata();
85
- this._parent?.oncePSVReady?.().then(() => {
86
- this._parent.psv.addEventListener("picture-loaded", () => {
87
- this._meta = this._parent.psv.getPictureMetadata();
88
- });
89
- this._parent.psv.addEventListener("annotation-click", () => {
90
- const tabs = this.shadowRoot.querySelector("pnx-tabs");
91
- if(tabs) { tabs.setAttribute("activeTabIndex", 4); }
85
+ onceParentAvailable(this).then(() => {
86
+ this._meta = this._parent?.psv?.getPictureMetadata();
87
+ this._parent?.oncePSVReady?.().then(() => {
88
+ this._parent.psv.addEventListener("picture-loaded", () => {
89
+ this._meta = this._parent.psv.getPictureMetadata();
90
+ });
91
+ this._parent.psv.addEventListener("annotation-click", () => {
92
+ const tabs = this.shadowRoot.querySelector("pnx-tabs");
93
+ if(tabs) { tabs.setAttribute("activeTabIndex", 4); }
94
+ });
92
95
  });
93
96
  });
94
97
  }
@@ -101,6 +104,14 @@ export default class PictureMetadata extends LitElement {
101
104
  }
102
105
  }
103
106
 
107
+ /** @private */
108
+ _onCaptureDateClick() {
109
+ const qsTab = this.shadowRoot.querySelector("pnx-tabs");
110
+ if(qsTab) {
111
+ qsTab.setAttribute("activeTabIndex", 1);
112
+ }
113
+ }
114
+
104
115
  /** @private */
105
116
  _toTab(title, data) {
106
117
  return html`
@@ -108,7 +119,7 @@ export default class PictureMetadata extends LitElement {
108
119
  <div slot="content" class="data-blocks">
109
120
  ${data.filter(b => b).map(b => html`<div class="data-block" style=${b.style}>
110
121
  <h5>${b.title}</h5>
111
- <div style=${b.content_style}>${b.content}</div>
122
+ <div style=${b.content_style} @click=${b.click}>${b.content}</div>
112
123
  </div>`)}
113
124
  </div>
114
125
  `;
@@ -234,6 +245,16 @@ export default class PictureMetadata extends LitElement {
234
245
  );
235
246
  let semanticsData = [];
236
247
  if(hasSemantics) {
248
+ // Hashtags
249
+ const hashtags = getHashTags(this._meta);
250
+ if(hashtags.length > 0) {
251
+ semanticsData.push({
252
+ title: this._parent?._t.pnx.semantics_hashtags,
253
+ style: "width: 100%",
254
+ content: hashtags.join(" ")
255
+ });
256
+ }
257
+
237
258
  // Full list of picture tags
238
259
  semanticsData.push({
239
260
  title: this._parent?._t.pnx.semantics_tags_picture,
@@ -284,7 +305,7 @@ export default class PictureMetadata extends LitElement {
284
305
  }
285
306
  return {
286
307
  title: key,
287
- content: value.length > 30 ? html`<textarea readonly>${value}</textarea>`: value,
308
+ content: value.length > 30 ? html`<textarea readonly .value=${value}></textarea>`: value,
288
309
  style: value.length > 30 ? "width: 100%" : undefined
289
310
  };
290
311
  });
@@ -313,9 +334,11 @@ export default class PictureMetadata extends LitElement {
313
334
  // Capture date
314
335
  this._meta?.caption?.date && {
315
336
  title: this._parent?._t.pnx.metadata_general_date,
337
+ click: this._onCaptureDateClick,
338
+ content_style: "cursor: pointer",
316
339
  content: html`
317
340
  <strong>${new Intl.DateTimeFormat(lang, {timeZone: this._meta.caption.tz, dateStyle: "long"}).format(this._meta.caption.date)}</strong>
318
- <br />${new Intl.DateTimeFormat(lang, {timeZone: this._meta.caption.tz, hour: "numeric",minute:"numeric"}).format(this._meta.caption.date)}
341
+ <br />${new Intl.DateTimeFormat(lang, {timeZone: this._meta.caption.tz, hour: "numeric",minute:"numeric", second: "numeric"}).format(this._meta.caption.date)}
319
342
  `
320
343
  },
321
344
  // Camera
@@ -334,10 +357,10 @@ export default class PictureMetadata extends LitElement {
334
357
  // Quality score
335
358
  hasQualityScore && {
336
359
  title: this._parent?._t.pnx.metadata_quality,
360
+ click: this._onQualityScoreClick,
337
361
  content: html`<pnx-quality-score
338
362
  grade=${generalGrade}
339
363
  style="font-size: 14px; cursor: pointer"
340
- @click=${this._onQualityScoreClick}
341
364
  />`
342
365
  },
343
366
  // Copy ID
@@ -367,9 +390,18 @@ export default class PictureMetadata extends LitElement {
367
390
  },
368
391
  this._meta?.origInstance && {
369
392
  title: this._parent?._t.pnx.metadata_general_instance,
370
- content: html`<strong><a href=${this._meta.origInstance.href+window.location.search} target="_blank">
371
- ${this._meta.origInstance.instance_name || this._meta.origInstance.href.replace(/^http.?:\/\//, "")}
372
- </a></strong>`
393
+ content: html`<strong>
394
+ <a href=${this._meta.origInstance.href+window.location.search} target="_blank" style="text-decoration: none">
395
+ <img
396
+ src=${PanoramaxMetaCatalogURL()+"/api/instances/"+this._meta.origInstance.instance_name+"/logo"}
397
+ style="height: 30px; max-width: 150px; vertical-align: middle"
398
+ alt=""
399
+ />
400
+ <span style="text-decoration: underline">
401
+ ${this._meta.origInstance.instance_name || this._meta.origInstance.href.replace(/^http.?:\/\//, "")}
402
+ </span>
403
+ </a>
404
+ </strong>`
373
405
  }
374
406
  ];
375
407
 
@@ -4,7 +4,7 @@ import { faRocket } from "@fortawesome/free-solid-svg-icons/faRocket";
4
4
  import { faLightbulb } from "@fortawesome/free-solid-svg-icons/faLightbulb";
5
5
  import { faPersonBiking } from "@fortawesome/free-solid-svg-icons/faPersonBiking";
6
6
  import { PIC_MAX_STAY_DURATION } from "../ui/Photo";
7
- import { fa } from "../../utils/widgets";
7
+ import { fa, onceParentAvailable } from "../../utils/widgets";
8
8
 
9
9
  /**
10
10
  * Player Options menu displays player speed and contrast settings.
@@ -40,9 +40,9 @@ export default class PlayerOptions extends LitElement {
40
40
  /** @private */
41
41
  connectedCallback() {
42
42
  super.connectedCallback();
43
- this._parent?.psv?.addEventListener("transition-duration-changed", e => {
43
+ onceParentAvailable(this).then(() => this._parent?.psv?.addEventListener("transition-duration-changed", e => {
44
44
  this.renderRoot.querySelector("#pnx-player-speed").value = PIC_MAX_STAY_DURATION - e.detail.value;
45
- });
45
+ }));
46
46
  }
47
47
 
48
48
  /** @private */
@@ -1,5 +1,5 @@
1
1
  import { LitElement, css, html } from "lit";
2
- import { fa } from "../../utils/widgets";
2
+ import { fa, onceParentAvailable } from "../../utils/widgets";
3
3
  import { faSvg, textarea, titles, input } from "../styles";
4
4
  import { faMap } from "@fortawesome/free-solid-svg-icons/faMap";
5
5
  import { faCircleInfo } from "@fortawesome/free-solid-svg-icons/faCircleInfo";
@@ -47,7 +47,7 @@ export default class ShareMenu extends LitElement {
47
47
  /** @private */
48
48
  connectedCallback() {
49
49
  super.connectedCallback();
50
- this._parent?.onceReady().then(() => {
50
+ onceParentAvailable(this).then(() => this._parent.onceReady()).then(() => {
51
51
  this._onUrlChange();
52
52
  this._parent.urlHandler.addEventListener("url-changed", this._onUrlChange.bind(this));
53
53
  });
@@ -85,7 +85,7 @@ export default class ShareMenu extends LitElement {
85
85
  >
86
86
  ${fa(faCircleInfo)} ${this._parent?._t.pnx.share_embed_docs}
87
87
  </pnx-link-button>
88
- <textarea readonly>${iframe}</textarea>
88
+ <textarea readonly .value=${iframe}></textarea>
89
89
  <pnx-copy-button
90
90
  ._t=${this._parent?._t}
91
91
  text=${iframe}
@@ -4,13 +4,14 @@
4
4
  */
5
5
 
6
6
  export {default as AnnotationsList} from "./AnnotationsList";
7
+ export {default as MapBackground} from "./MapBackground";
7
8
  export {default as MapFilters} from "./MapFilters";
8
9
  export {default as MapLayers} from "./MapLayers";
9
- export {default as MapBackground} from "./MapBackground";
10
+ export {default as MapLegend} from "./MapLegend";
11
+ export {default as MiniPictureLegend} from "./MiniPictureLegend";
12
+ export {default as PictureLegend} from "./PictureLegend";
13
+ export {default as PictureMetadata} from "./PictureMetadata";
10
14
  export {default as PlayerOptions} from "./PlayerOptions";
11
15
  export {default as QualityScoreDoc} from "./QualityScoreDoc";
12
16
  export {default as ReportForm} from "./ReportForm";
13
17
  export {default as Share} from "./Share";
14
- export {default as PictureLegend} from "./PictureLegend";
15
- export {default as MapLegend} from "./MapLegend";
16
- export {default as PictureMetadata} from "./PictureMetadata";
@@ -212,6 +212,17 @@ export const btn = css`
212
212
  background-color: var(--widget-bg-primary-hover);
213
213
  }
214
214
 
215
+ /* Fully-filled warning style */
216
+ .pnx-btn-fullwarn {
217
+ background-color: var(--widget-bg-warn);
218
+ color: var(--widget-font-warn);
219
+ border: none;
220
+ }
221
+
222
+ .pnx-btn-fullwarn:not(:disabled):hover {
223
+ background-color: var(--widget-bg-warn);
224
+ }
225
+
215
226
  /* Outline style */
216
227
  .pnx-btn-outline {
217
228
  border: 1px solid var(--widget-border-btn);
@@ -0,0 +1,171 @@
1
+ import { LitElement, html, css, nothing } from "lit";
2
+ import { fa, onceParentAvailable } from "../../utils/widgets";
3
+ import { faTags } from "@fortawesome/free-solid-svg-icons/faTags";
4
+ import { faTriangleExclamation } from "@fortawesome/free-solid-svg-icons/faTriangleExclamation";
5
+ import { hasAnnotations } from "../../utils/picture";
6
+ import { classMap } from "lit/directives/class-map.js";
7
+ import { panel } from "../styles";
8
+
9
+ const DISABLE_ANNOTATIONS_PARAM = "pnx-disable-annotations";
10
+
11
+ /**
12
+ * AnnotationsSwitch component allows to switch on/off pictures annotations.
13
+ * @class Panoramax.components.ui.AnnotationsSwitch
14
+ * @element pnx-annotations-switch
15
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
16
+ * @example
17
+ * ```html
18
+ * <pnx-annotations-switch ._parent=${viewer} />
19
+ * ```
20
+ */
21
+ export default class AnnotationsSwitch extends LitElement {
22
+ /** @private */
23
+ static styles = [ panel, css`
24
+ .pnx-panel {
25
+ padding: 5px;
26
+ margin-top: 5px;
27
+ width: max-content;
28
+ min-width: unset;
29
+ }
30
+
31
+ /* Custom button look */
32
+ pnx-button::part(btn) {
33
+ border-radius: 8px;
34
+ height: 26px;
35
+ width: 26px;
36
+ }
37
+
38
+ pnx-button[size="xl"]::part(btn) {
39
+ border-radius: 8px;
40
+ height: 38px;
41
+ width: 38px;
42
+ }
43
+
44
+ pnx-button[active]::part(btn),
45
+ pnx-button[active]:hover::part(btn) {
46
+ background-color: var(--widget-bg-active) !important;
47
+ border-color: var(--widget-bg-active) !important;
48
+ color: var(--widget-font-active) !important;
49
+ }
50
+
51
+ /* No-annotations badge */
52
+ .pnx-annotations-switch-empty {
53
+ position: absolute;
54
+ top: 1px;
55
+ right: 1px;
56
+ color: var(--orange);
57
+ height: 8px;
58
+ }
59
+
60
+ pnx-button[size="xl"] .pnx-annotations-switch-empty {
61
+ top: 2px;
62
+ right: 2px;
63
+ height: 12px;
64
+ }
65
+
66
+ pnx-button[active] .pnx-annotations-switch-empty {
67
+ color: var(--yellow);
68
+ }
69
+ `];
70
+
71
+ /**
72
+ * Component properties.
73
+ * @memberof Panoramax.components.ui.AnnotationsSwitch#
74
+ * @type {Object}
75
+ * @property {string} [size=xl] Button size (md, xl)
76
+ */
77
+ /** @private */
78
+ static properties = {
79
+ size: {type: String},
80
+ _annotationsToggled: {state: true},
81
+ _warnNoAnnot: {state: true},
82
+ _warnNoAnnotTooltip: {state: true},
83
+ };
84
+
85
+ constructor() {
86
+ super();
87
+ this._annotationsToggled = false;
88
+ this._warnNoAnnot = false;
89
+ this._warnNoAnnotTooltip = false;
90
+ this.size = "xl";
91
+ }
92
+
93
+ /** @private */
94
+ connectedCallback() {
95
+ super.connectedCallback();
96
+
97
+ // Check if annotations have been explicitly disabled
98
+ const annotsDisabled = localStorage.getItem(DISABLE_ANNOTATIONS_PARAM) === "true";
99
+
100
+ onceParentAvailable(this).then(() => {
101
+ this._parent.onceReady().then(() => {
102
+ this._parent.psv.addEventListener("annotations-toggled", e => {
103
+ this._annotationsToggled = e.detail.visible;
104
+ this._persistAnnotationsLocalStorage(this._annotationsToggled);
105
+ });
106
+
107
+ this._warnNoAnnot = !hasAnnotations(this._parent.psv.getPictureMetadata());
108
+ this._parent.psv.addEventListener("picture-loaded", () => {
109
+ this._warnNoAnnot = !hasAnnotations(this._parent.psv.getPictureMetadata());
110
+ });
111
+ });
112
+
113
+ this._parent.onceFirstPicLoaded().then(() => {
114
+ this._annotationsToggled = this._parent.psv.areAnnotationsVisible() || false;
115
+
116
+ if(!this._annotationsToggled && !annotsDisabled) {
117
+ this._parent.psv.toggleAllAnnotations(true);
118
+ this._annotationsToggled = true;
119
+ }
120
+ this._persistAnnotationsLocalStorage(this._annotationsToggled);
121
+ });
122
+ });
123
+ }
124
+
125
+ /** @private */
126
+ _persistAnnotationsLocalStorage(isAnnotToggled) {
127
+ localStorage.setItem(DISABLE_ANNOTATIONS_PARAM, isAnnotToggled ? "false": "true");
128
+ }
129
+
130
+ /** @private */
131
+ _onClick() {
132
+ if(!this._annotationsToggled && this._warnNoAnnot) {
133
+ this._warnNoAnnotTooltip = true;
134
+ setTimeout(() => this._warnNoAnnotTooltip = false, 2000);
135
+ }
136
+ this._persistAnnotationsLocalStorage(!this._annotationsToggled);
137
+ this._parent.psv.toggleAllAnnotations(!this._annotationsToggled);
138
+ }
139
+
140
+ /** @private */
141
+ render() {
142
+ /* eslint-disable indent */
143
+ const panelClasses = {
144
+ "pnx-panel": true,
145
+ "pnx-hidden": !this._warnNoAnnotTooltip,
146
+ };
147
+
148
+ return html`
149
+ <pnx-button
150
+ kind="superflat"
151
+ size=${this.size}
152
+ style="vertical-align: middle"
153
+ class="pnx-print-hidden"
154
+ title=${this._annotationsToggled ? this._parent._t?.pnx.semantics_hide_annotations : this._parent._t?.pnx.semantics_show_annotations}
155
+ active=${this._annotationsToggled ? "" : nothing}
156
+ @click=${this._onClick}
157
+ >
158
+ ${fa(faTags, { styles: { "height": "20px" }})}
159
+ ${this._warnNoAnnot ? fa(faTriangleExclamation, { classes: "pnx-annotations-switch-empty" }) : nothing}
160
+ </pnx-button>
161
+ <div
162
+ class=${classMap(panelClasses)}
163
+ @click=${() => this._warnNoAnnotTooltip = false}
164
+ >
165
+ ${this._parent._t?.pnx.semantics_zero_annotations}
166
+ </div>
167
+ `;
168
+ }
169
+ }
170
+
171
+ customElements.define("pnx-annotations-switch", AnnotationsSwitch);
@@ -23,7 +23,7 @@ export default class Button extends LitElement {
23
23
  * @memberof Panoramax.components.ui.Button#
24
24
  * @type {Object}
25
25
  * @property {boolean} [active=false] Whether the button is in an active state.
26
- * @property {string} [kind=full] The style variation of the button (full, outline, flat, superflat, inline, superinline)
26
+ * @property {string} [kind=full] The style variation of the button (full, fullwarn, outline, flat, superflat, inline, superinline)
27
27
  * @property {string} [size=md] The size of the button (sm, md, l, xl, xxl)
28
28
  * @property {boolean} [disabled=false] Whether the button is disabled.
29
29
  * @property {string} [type] The button type (e.g., 'submit').
@@ -38,7 +38,7 @@ export default class CopyButton extends LitElement {
38
38
  * @type {Object}
39
39
  * @property {string} [text] Text to copy in clipboard on click (use either this parameter or input, not both)
40
40
  * @property {input} [input] ID of a HTML input field to copy content from in clipboard (use either this parameter or text, not both)
41
- * @property {string} [kind=full] The style variation of the button (full, outline, flat, superflat, inline, superinline)
41
+ * @property {string} [kind=full] The style variation of the button (full, fullwarn, outline, flat, superflat, inline, superinline)
42
42
  * @property {string} [size=md] The size of the button (sm, md, l, xl, xxl)
43
43
  * @property {boolean} [unstyled=false] Disable all styling (for list group integration)
44
44
  * @property {string} [title] Tooltip text displayed when hovering over the button
@@ -31,7 +31,7 @@ export default class LinkButton extends LitElement {
31
31
  * @property {string} [title] Tooltip text displayed when hovering over the button
32
32
  * @property {string} [download] Indicates if the linked resource should be downloaded
33
33
  * @property {string} [class] Custom CSS class for additional styling
34
- * @property {string} [kind=full] The style variation of the button (full, outline, flat, superflat)
34
+ * @property {string} [kind=full] The style variation of the button (full, fullwarn, outline, flat, superflat)
35
35
  * @property {string} [size=md] The size of the button (sm, md, l, xl, xxl)
36
36
  */
37
37
  static properties = {
@@ -1,3 +1,7 @@
1
+ .maplibregl-map.pnx-map {
2
+ background: white;
3
+ }
4
+
1
5
  /* Picture thumbnail on map */
2
6
  .maplibregl-popup-content {
3
7
  padding: 5px !important;