@panoramax/web-viewer 3.2.3-develop-97f87faf → 3.2.3-develop-48da552a

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@panoramax/web-viewer",
3
- "version": "3.2.3-develop-97f87faf",
3
+ "version": "3.2.3-develop-48da552a",
4
4
  "description": "Panoramax web viewer for geolocated pictures",
5
5
  "main": "build/index.js",
6
6
  "author": "Panoramax team",
@@ -526,13 +526,17 @@ export default class Viewer extends PhotoViewer {
526
526
  const mapFiltersMenu = querySelectorDeep("#pnx-map-filters-menu");
527
527
  const fMinDate = mapFiltersMenu?.shadowRoot.getElementById("pnx-filter-date-from");
528
528
  const fMaxDate = mapFiltersMenu?.shadowRoot.getElementById("pnx-filter-date-end");
529
- const fTypeFlat = mapFiltersMenu?.shadowRoot.getElementById("pnx-filter-type-flat");
530
- const fType360 = mapFiltersMenu?.shadowRoot.getElementById("pnx-filter-type-360");
529
+ const fTypes = mapFiltersMenu?.shadowRoot.querySelectorAll("input[name='pnx-filter-type']");
531
530
  const fMapTheme = querySelectorDeep("#pnx-map-theme");
532
531
 
533
532
  let type = "";
534
- if(fType360?.checked && !fTypeFlat?.checked) { type = "equirectangular"; }
535
- if(!fType360?.checked && fTypeFlat?.checked) { type = "flat"; }
533
+ for(let fTypeId = 0 ; fTypeId < fTypes.length; fTypeId++) {
534
+ const fType = fTypes[fTypeId];
535
+ if(fType.checked) {
536
+ type = fType.value;
537
+ break;
538
+ }
539
+ }
536
540
 
537
541
  let qualityscore = [];
538
542
  if(this.map?._hasQualityScore()) {
@@ -2,7 +2,6 @@ import { LitElement, html, css } from "lit";
2
2
  import { classMap } from "lit/directives/class-map.js";
3
3
 
4
4
  const OPENNESS_Y_PCT = { "opened": 0, "half-opened": 0.7, "closed": 1 };
5
- const OPENNESS_Y_PCT_RANGE = { "opened": [0, 0.4], "half-opened": [0.4, 0.8], "closed": [0.8, 1] };
6
5
 
7
6
  /**
8
7
  * BottomDrawer layout offers a mobile-like drawer menu, coming from bottom of the screen.
@@ -27,6 +26,7 @@ export default class BottomDrawer extends LitElement {
27
26
  width: 100%;
28
27
  z-index: 130;
29
28
  pointer-events: none;
29
+ touch-action: none;
30
30
  }
31
31
 
32
32
  .drawer {
@@ -38,7 +38,7 @@ export default class BottomDrawer extends LitElement {
38
38
  flex-direction: column;
39
39
  transition: transform 0.3s ease;
40
40
  will-change: transform;
41
- touch-action: none;
41
+ touch-action: auto;
42
42
  pointer-events: auto;
43
43
  }
44
44
 
@@ -53,9 +53,7 @@ export default class BottomDrawer extends LitElement {
53
53
  display: flex;
54
54
  align-items: center;
55
55
  justify-content: center;
56
- cursor: grab;
57
- touch-action: none;
58
- pointer-events: auto;
56
+ cursor: pointer;
59
57
  }
60
58
 
61
59
  .handle-bar {
@@ -99,7 +97,7 @@ export default class BottomDrawer extends LitElement {
99
97
  this._boundTouchEnd = this._onTouchEnd.bind(this);
100
98
 
101
99
  this._drawerHeight = window.innerHeight - 30;
102
- const drawer = this.shadowRoot?.querySelector(".drawer");
100
+ const drawer = this._getDrawer();
103
101
  if (!drawer) { return; }
104
102
  drawer.style.height = `${this._drawerHeight}px`;
105
103
  drawer.style.maxHeight = `${this._drawerHeight}px`;
@@ -109,9 +107,16 @@ export default class BottomDrawer extends LitElement {
109
107
  attributeChangedCallback(name, old, value) {
110
108
  super.attributeChangedCallback(name, old, value);
111
109
 
112
- if(name === "openness" && value !== "opened") {
113
- const content = this.shadowRoot.querySelector(".content");
114
- if(content) { content.scrollTop = 0; }
110
+ if(name === "openness") {
111
+ // If not fully opened, force content scroll back to top
112
+ if(value !== "opened") {
113
+ const content = this.shadowRoot.querySelector(".content");
114
+ if(content) { content.scrollTop = 0; }
115
+ }
116
+
117
+ // Remove hand-defined transform
118
+ const drawer = this._getDrawer();
119
+ if (drawer) { drawer.style.transform = null; }
115
120
  }
116
121
  }
117
122
 
@@ -121,10 +126,16 @@ export default class BottomDrawer extends LitElement {
121
126
  this._cleanupTouchListeners();
122
127
  }
123
128
 
129
+ /** @private */
130
+ _getDrawer() {
131
+ return this.shadowRoot?.querySelector(".drawer");
132
+ }
133
+
124
134
  /** @private */
125
135
  _onHandleClick() {
126
- if(this.openness === "opened" || this.openness === "closed") { this.openness = "half-opened"; }
136
+ if(this.openness === "opened") { this.openness = "closed"; }
127
137
  else if(this.openness === "half-opened") { this.openness = "opened"; }
138
+ else if(this.openness === "closed") { this.openness = "half-opened"; }
128
139
  }
129
140
 
130
141
  /** @private */
@@ -154,10 +165,19 @@ export default class BottomDrawer extends LitElement {
154
165
  }
155
166
 
156
167
  /** @private */
157
- _onTouchEnd() {
158
- if (!this._isDragging) return;
168
+ _onTouchEnd(e) {
169
+ // Touchend is also called when "clicking" on mobile
170
+ if ((!this._isDragging || Math.abs(this._deltaFingerY) < 30) && !e.target.closest(".handle")) { return; }
171
+ e.preventDefault();
172
+
159
173
  this._isDragging = false;
160
- this._updateDrawerTransform(this._drawerY + this._deltaFingerY, true);
174
+
175
+ if(this._deltaFingerY === 0 && this.openness === "closed") {
176
+ this.openness = "half-opened";
177
+ }
178
+ else {
179
+ this._updateDrawerTransform(this._drawerY + this._deltaFingerY, true);
180
+ }
161
181
 
162
182
  this._cleanupTouchListeners();
163
183
  this._startFingerY = null;
@@ -173,25 +193,32 @@ export default class BottomDrawer extends LitElement {
173
193
 
174
194
  /** @private */
175
195
  _updateDrawerTransform(y, snap = false) {
176
- const drawer = this.shadowRoot?.querySelector(".drawer");
196
+ const drawer = this._getDrawer();
177
197
  if (!drawer) { return; }
178
198
 
179
199
  y = Math.max(0, Math.min(y, this._drawerHeight - 30));
180
200
 
181
201
  // Snap to nearest static position
182
202
  if(snap) {
183
-
184
- const pct = y / (this._drawerHeight - 30);
185
- if(pct > OPENNESS_Y_PCT_RANGE.opened[0] && pct <= OPENNESS_Y_PCT_RANGE.opened[1]) { this.openness = "opened"; }
186
- if(pct > OPENNESS_Y_PCT_RANGE["half-opened"][0] && pct <= OPENNESS_Y_PCT_RANGE["half-opened"][1]) { this.openness = "half-opened"; }
187
- if(pct > OPENNESS_Y_PCT_RANGE.closed[0] && pct <= OPENNESS_Y_PCT_RANGE.closed[1]) { this.openness = "closed"; }
203
+ // Gesture goes up
204
+ if(this._deltaFingerY < 0) {
205
+ if(this.openness === "closed") {
206
+ // How much it goes up ?
207
+ if(Math.abs(this._deltaFingerY) > this._drawerHeight * (1-OPENNESS_Y_PCT["half-opened"])) {
208
+ this.openness = "opened";
209
+ }
210
+ else { this.openness = "half-opened"; }
211
+ }
212
+ else { this.openness = "opened"; }
213
+ }
214
+ // Gesture goes down
215
+ else { this.openness = "closed"; }
216
+
188
217
  this._drawerY = null;
189
- drawer.style.transform = null;
190
- }
191
- // Live drag
192
- else {
193
- drawer.style.transform = `translateY(${y}px)`;
218
+ y = Math.max(0, Math.min(OPENNESS_Y_PCT[this.openness] * this._drawerHeight, this._drawerHeight - 30));
194
219
  }
220
+
221
+ drawer.style.transform = `translateY(${y}px)`;
195
222
  }
196
223
 
197
224
  /** @private */
@@ -5,7 +5,6 @@ import { faSvg, titles } from "../styles";
5
5
  import { faImage } from "@fortawesome/free-solid-svg-icons/faImage";
6
6
  import { faCalendar } from "@fortawesome/free-solid-svg-icons/faCalendar";
7
7
  import { faArrowRight } from "@fortawesome/free-solid-svg-icons/faArrowRight";
8
- import { faPanorama } from "@fortawesome/free-solid-svg-icons/faPanorama";
9
8
  import { faMedal } from "@fortawesome/free-solid-svg-icons/faMedal";
10
9
  import { faInfoCircle } from "@fortawesome/free-solid-svg-icons/faInfoCircle";
11
10
  import { faUser } from "@fortawesome/free-solid-svg-icons/faUser";
@@ -90,10 +89,8 @@ export default class MapFilters extends LitElement {
90
89
  }
91
90
  .pnx-input-shortcuts button {
92
91
  border: none;
93
- height: 20px;
94
- line-height: 20px;
95
- font-size: 0.8em;
96
- padding: 0 8px;
92
+ font-size: 0.75em;
93
+ padding: 2px 6px;
97
94
  vertical-align: middle;
98
95
  background-color: var(--grey-pale);
99
96
  color: var(--black);
@@ -132,12 +129,12 @@ export default class MapFilters extends LitElement {
132
129
  border-top-right-radius: 8px;
133
130
  border-bottom-right-radius: 8px;
134
131
  }
135
- .pnx-checkbox-btns input[type="checkbox"] { display: none; }
136
- .pnx-checkbox-btns input[type="checkbox"]:checked + label {
132
+ .pnx-checkbox-btns input[type="radio"] { display: none; }
133
+ .pnx-checkbox-btns input[type="radio"]:checked + label {
137
134
  background-color: var(--widget-bg-active);
138
135
  color: var(--widget-font-active);
139
136
  }
140
- .pnx-checkbox-btns input[type="checkbox"]:checked + label:first-of-type {
137
+ .pnx-checkbox-btns input[type="radio"]:checked + label:first-of-type {
141
138
  border-right-color: white;
142
139
  }
143
140
 
@@ -158,8 +155,7 @@ export default class MapFilters extends LitElement {
158
155
  showZoomIn: {state: true},
159
156
  minDate: {state: true},
160
157
  maxDate: {state: true},
161
- typeFlat: {state: true},
162
- type360: {state: true},
158
+ type: {state: true},
163
159
  score: {state: true},
164
160
  user: {state: true},
165
161
  };
@@ -217,14 +213,9 @@ export default class MapFilters extends LitElement {
217
213
 
218
214
  this.score = e?.qualityscore?.length < 5 ? e.qualityscore.join(",") : "";
219
215
 
216
+ this.type = "";
220
217
  if(e?.pic_type && e.pic_type != "") {
221
- this.type360 = e.pic_type == "equirectangular";
222
- this.typeFlat = e.pic_type == "flat";
223
- }
224
-
225
- if(this.type360 === this.typeFlat) {
226
- this.type360 = false;
227
- this.typeFlat = false;
218
+ this.type = e.pic_type == "flat" ? "flat" : "equirectangular";
228
219
  }
229
220
  }
230
221
 
@@ -265,8 +256,7 @@ export default class MapFilters extends LitElement {
265
256
  this.shadowRoot.querySelector("#pnx-filter-search-user")?.reset();
266
257
  this.minDate = null;
267
258
  this.maxDate = null;
268
- this.typeFlat = null;
269
- this.type360 = null;
259
+ this.type = "";
270
260
  this.score = null;
271
261
  this.user = null;
272
262
  this._onFormChange();
@@ -302,6 +292,9 @@ export default class MapFilters extends LitElement {
302
292
  <button
303
293
  @click=${this._onShortcutClick("pnx-filter-date-from", new Date(new Date().setMonth(new Date().getMonth() - 1)).toISOString().split("T")[0])}
304
294
  >${this._parent?._t.pnx.filter_date_1month}</button>
295
+ <button
296
+ @click=${this._onShortcutClick("pnx-filter-date-from", new Date(new Date().setMonth(new Date().getMonth() - 6)).toISOString().split("T")[0])}
297
+ >${this._parent?._t.pnx.filter_date_6months}</button>
305
298
  <button
306
299
  @click=${this._onShortcutClick("pnx-filter-date-from", new Date(new Date().setFullYear(new Date().getFullYear() - 1)).toISOString().split("T")[0])}
307
300
  >${this._parent?._t.pnx.filter_date_1year}</button>
@@ -327,19 +320,29 @@ export default class MapFilters extends LitElement {
327
320
  <h4>${fa(faImage)} ${this._parent?._t.pnx.filter_picture}</h4>
328
321
  <div class="pnx-input-group pnx-checkbox-btns" style="justify-content: center;">
329
322
  <input
330
- type="checkbox"
323
+ type="radio"
324
+ id="pnx-filter-type-all"
325
+ name="pnx-filter-type"
326
+ value=""
327
+ .checked=${!this.type || this.type === ""}
328
+ />
329
+ <label for="pnx-filter-type-all">${this._parent?._t.pnx.picture_all}</label>
330
+ <input
331
+ type="radio"
331
332
  id="pnx-filter-type-flat"
332
- name="flat"
333
- .checked=${this.typeFlat}
333
+ name="pnx-filter-type"
334
+ value="flat"
335
+ .checked=${this.type === "flat"}
334
336
  />
335
- <label for="pnx-filter-type-flat">${fa(faImage)} ${this._parent?._t.pnx.picture_flat}</label>
337
+ <label for="pnx-filter-type-flat">${this._parent?._t.pnx.picture_flat}</label>
336
338
  <input
337
- type="checkbox"
339
+ type="radio"
338
340
  id="pnx-filter-type-360"
339
- name="360"
340
- .checked=${this.type360}
341
+ name="pnx-filter-type"
342
+ value="equirectangular"
343
+ .checked=${this.type === "equirectangular"}
341
344
  />
342
- <label for="pnx-filter-type-360">${fa(faPanorama)} ${this._parent?._t.pnx.picture_360}</label>
345
+ <label for="pnx-filter-type-360">${this._parent?._t.pnx.picture_360}</label>
343
346
  </div>
344
347
  </div>
345
348
 
@@ -235,9 +235,9 @@ export default class MapMore extends Map {
235
235
  }
236
236
 
237
237
  if(filters.pic_type && filters.pic_type !== "") {
238
- this._mapFilters.pic_type = filters.pic_type;
239
- mapSeqFilters.push(["==", ["get", "type"], filters.pic_type]);
240
- mapPicFilters.push(["==", ["get", "type"], filters.pic_type]);
238
+ this._mapFilters.pic_type = filters.pic_type === "flat" ? "flat" : "equirectangular";
239
+ mapSeqFilters.push(["==", ["get", "type"], this._mapFilters.pic_type]);
240
+ mapPicFilters.push(["==", ["get", "type"], this._mapFilters.pic_type]);
241
241
  }
242
242
  if(this._hasGridStats()) {
243
243
  reloadMapStyle = true;
@@ -105,11 +105,13 @@
105
105
  "error_api_compatibility": "The pictures server is not compatible with this viewer version",
106
106
  "filter_date": "Date",
107
107
  "filter_date_1month": "1 month",
108
+ "filter_date_6months": "6 months",
108
109
  "filter_date_1year": "1 year",
109
110
  "filter_user": "User",
110
111
  "filter_user_mypics": "My pictures",
111
112
  "filter_picture": "Picture type",
112
- "filter_zoom_in": "Zoom-in to see this filter",
113
+ "filter_zoom_in": "Zoom-in the map to see this filter",
114
+ "picture_all": "All",
113
115
  "picture_flat": "Classic",
114
116
  "picture_360": "360°",
115
117
  "picture_flat_long": "Classic picture",
@@ -105,10 +105,12 @@
105
105
  "error_api_compatibility": "Le serveur de photos n'est pas compatible avec cette visionneuse",
106
106
  "filter_date": "Date",
107
107
  "filter_date_1month": "1 mois",
108
+ "filter_date_6months": "6 mois",
108
109
  "filter_date_1year": "1 an",
109
110
  "filter_user": "Utilisateur",
110
111
  "filter_user_mypics": "Mes photos",
111
112
  "filter_picture": "Type d'image",
113
+ "picture_all": "Tout",
112
114
  "picture_flat": "Classique",
113
115
  "picture_360": "360°",
114
116
  "picture_flat_long": "Photo classique",
@@ -120,7 +122,7 @@
120
122
  "qualityscore_doc_2": "Le score est affiché sous la forme d'une lettre A/B/C/D/E (A étant le meilleur, E le moins bon), et visualisable grâce à cette échelle :",
121
123
  "qualityscore_doc_3": "Il est calculé en fonction de la précision du GPS ainsi que la résolution de la photo. Un matériel professionnel aura une note de A, une caméra sportive 360° une note de B, et un smartphone une note de C à E.",
122
124
  "qualityscore_doc_link": "En savoir plus sur le Score de Qualité",
123
- "filter_zoom_in": "Zoomez plus pour voir ce filtre",
125
+ "filter_zoom_in": "Zoomez la carte pour voir ce filtre",
124
126
  "map_background": "Fond de carte",
125
127
  "map_background_aerial": "Satellite",
126
128
  "map_background_streets": "Plan",