@panoramax/web-viewer 5.0.0-develop-d7206514 → 5.0.0-develop-d26305dd

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 (152) hide show
  1. package/build/bundle.cjs +3399 -0
  2. package/build/bundle.cjs.map +1 -0
  3. package/build/bundle_photoviewer.cjs +2510 -0
  4. package/build/bundle_photoviewer.cjs.map +1 -0
  5. package/build/cjs/index.js +1 -1
  6. package/build/cjs/index_photoviewer.js +1 -1
  7. package/build/components/core/Basic.css +56 -0
  8. package/build/components/core/Basic.js +378 -0
  9. package/build/components/core/CoverageMap.css +10 -0
  10. package/build/components/core/CoverageMap.js +169 -0
  11. package/build/components/core/Editor.css +33 -0
  12. package/build/components/core/Editor.js +398 -0
  13. package/build/components/core/PhotoViewer.css +70 -0
  14. package/build/components/core/PhotoViewer.js +650 -0
  15. package/build/components/core/Viewer.css +130 -0
  16. package/build/components/core/Viewer.js +711 -0
  17. package/build/components/core/index.js +10 -0
  18. package/build/components/index.js +11 -0
  19. package/build/components/index_photoviewer.js +6 -0
  20. package/build/components/layout/BottomDrawer.js +258 -0
  21. package/build/components/layout/CorneredGrid.js +143 -0
  22. package/build/components/layout/Mini.js +121 -0
  23. package/build/components/layout/Tabs.js +140 -0
  24. package/build/components/layout/index.js +9 -0
  25. package/build/components/menus/LocationPrecisionDoc.js +42 -0
  26. package/build/components/menus/MapBackground.js +110 -0
  27. package/build/components/menus/MapFilters.js +567 -0
  28. package/build/components/menus/MapLayers.js +238 -0
  29. package/build/components/menus/MapLegend.js +68 -0
  30. package/build/components/menus/MiniPictureLegend.js +73 -0
  31. package/build/components/menus/PictureLegend.js +379 -0
  32. package/build/components/menus/PictureMetadata.js +380 -0
  33. package/build/components/menus/PlayerOptions.js +93 -0
  34. package/build/components/menus/QualityScoreDoc.js +42 -0
  35. package/build/components/menus/ReportForm.js +132 -0
  36. package/build/components/menus/SemanticsDoc.js +38 -0
  37. package/build/components/menus/SemanticsDownload.js +33 -0
  38. package/build/components/menus/SemanticsFilters.js +153 -0
  39. package/build/components/menus/SemanticsList.js +413 -0
  40. package/build/components/menus/SemanticsMetadata.js +368 -0
  41. package/build/components/menus/Share.js +105 -0
  42. package/build/components/menus/index.js +22 -0
  43. package/build/components/menus/index_photoviewer.js +11 -0
  44. package/build/components/styles.js +557 -0
  45. package/build/components/ui/AnnotationsSwitch.js +159 -0
  46. package/build/components/ui/Button.js +77 -0
  47. package/build/components/ui/ButtonGroup.css +59 -0
  48. package/build/components/ui/ButtonGroup.js +69 -0
  49. package/build/components/ui/CopyButton.js +110 -0
  50. package/build/components/ui/Grade.js +54 -0
  51. package/build/components/ui/GradeFilter.js +122 -0
  52. package/build/components/ui/IconSwitch.js +193 -0
  53. package/build/components/ui/LinkButton.js +67 -0
  54. package/build/components/ui/ListGroup.js +66 -0
  55. package/build/components/ui/ListItem.js +90 -0
  56. package/build/components/ui/Loader.js +203 -0
  57. package/build/components/ui/Map.css +63 -0
  58. package/build/components/ui/Map.js +853 -0
  59. package/build/components/ui/MapMore.js +175 -0
  60. package/build/components/ui/Photo.css +50 -0
  61. package/build/components/ui/Photo.js +1502 -0
  62. package/build/components/ui/Popup.js +145 -0
  63. package/build/components/ui/ProgressBar.js +104 -0
  64. package/build/components/ui/QualityScore.js +147 -0
  65. package/build/components/ui/SearchBar.js +374 -0
  66. package/build/components/ui/SemanticsEditor.js +191 -0
  67. package/build/components/ui/SemanticsTable.js +88 -0
  68. package/build/components/ui/Switch.js +139 -0
  69. package/build/components/ui/TogglableGroup.js +157 -0
  70. package/build/components/ui/index.js +29 -0
  71. package/build/components/ui/index_photoviewer.js +21 -0
  72. package/build/components/ui/widgets/CopyCoordinates.js +75 -0
  73. package/build/components/ui/widgets/GeoSearch.css +21 -0
  74. package/build/components/ui/widgets/GeoSearch.js +150 -0
  75. package/build/components/ui/widgets/Legend.js +190 -0
  76. package/build/components/ui/widgets/LevelSelect.css +51 -0
  77. package/build/components/ui/widgets/LevelSelect.js +143 -0
  78. package/build/components/ui/widgets/MapFiltersButton.js +114 -0
  79. package/build/components/ui/widgets/MapLayersButton.js +79 -0
  80. package/build/components/ui/widgets/OSMEditors.js +155 -0
  81. package/build/components/ui/widgets/PictureLegendActions.js +99 -0
  82. package/build/components/ui/widgets/Player.css +7 -0
  83. package/build/components/ui/widgets/Player.js +154 -0
  84. package/build/components/ui/widgets/SemanticsFiltersButton.js +65 -0
  85. package/build/components/ui/widgets/Zoom.js +84 -0
  86. package/build/components/ui/widgets/index.js +16 -0
  87. package/build/components/ui/widgets/index_photoviewer.js +7 -0
  88. package/build/esm/components/core/Basic.js +1 -1
  89. package/build/img/arrow_360.svg +14 -0
  90. package/build/img/arrow_flat.svg +11 -0
  91. package/build/img/arrow_triangle.svg +9 -0
  92. package/build/img/arrow_turn.svg +8 -0
  93. package/build/img/bg_aerial.jpg +0 -0
  94. package/build/img/bg_streets.jpg +0 -0
  95. package/build/img/loader_base.jpg +0 -0
  96. package/build/img/logo_dead.svg +91 -0
  97. package/build/img/marker.svg +17 -0
  98. package/build/img/marker_blue.svg +20 -0
  99. package/build/img/osm.svg +49 -0
  100. package/build/img/panoramax.svg +13 -0
  101. package/build/img/switch_big.svg +54 -0
  102. package/build/img/switch_mini.svg +48 -0
  103. package/build/img/wd.svg +1 -0
  104. package/build/index_photoviewer.js +4 -0
  105. package/build/package.json +148 -0
  106. package/build/servers.js +14 -0
  107. package/build/translations/ar.json +1 -0
  108. package/build/translations/be.json +257 -0
  109. package/build/translations/br.json +81 -0
  110. package/build/translations/cy.json +117 -0
  111. package/build/translations/da.json +300 -0
  112. package/build/translations/de.json +309 -0
  113. package/build/translations/en.json +294 -0
  114. package/build/translations/eo.json +235 -0
  115. package/build/translations/es.json +292 -0
  116. package/build/translations/fi.json +1 -0
  117. package/build/translations/fr.json +294 -0
  118. package/build/translations/hr.json +294 -0
  119. package/build/translations/hu.json +294 -0
  120. package/build/translations/it.json +306 -0
  121. package/build/translations/ja.json +182 -0
  122. package/build/translations/ko.json +1 -0
  123. package/build/translations/nl.json +305 -0
  124. package/build/translations/nn.json +1 -0
  125. package/build/translations/pl.json +169 -0
  126. package/build/translations/pt.json +296 -0
  127. package/build/translations/pt_BR.json +304 -0
  128. package/build/translations/sv.json +182 -0
  129. package/build/translations/ti.json +9 -0
  130. package/build/translations/tr.json +297 -0
  131. package/build/translations/uk.json +268 -0
  132. package/build/translations/zh_Hant.json +309 -0
  133. package/build/utils/API.js +928 -0
  134. package/build/utils/InitParameters.js +521 -0
  135. package/build/utils/MapStyleComposer.js +889 -0
  136. package/build/utils/PanoraMapProtocol.js +49 -0
  137. package/build/utils/PhotoAdapter.js +49 -0
  138. package/build/utils/PresetsManager.js +148 -0
  139. package/build/utils/SemanticsMapProtocol.js +144 -0
  140. package/build/utils/URLHandler.js +426 -0
  141. package/build/utils/geocoder.js +203 -0
  142. package/build/utils/i18n.js +128 -0
  143. package/build/utils/index.js +17 -0
  144. package/build/utils/index_photoviewer.js +14 -0
  145. package/build/utils/indoor.js +200 -0
  146. package/build/utils/map.js +788 -0
  147. package/build/utils/picture.js +507 -0
  148. package/build/utils/semantics.js +321 -0
  149. package/build/utils/services.js +148 -0
  150. package/build/utils/utils.js +433 -0
  151. package/build/utils/widgets.js +110 -0
  152. package/package.json +1 -1
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Core graphical components
3
+ * @module Panoramax:components:core
4
+ */
5
+
6
+ export {default as Basic} from "./Basic.js";
7
+ export {default as CoverageMap} from "./CoverageMap.js";
8
+ export {default as Editor} from "./Editor.js";
9
+ export {default as Viewer} from "./Viewer.js";
10
+ export {default as PhotoViewer} from "./PhotoViewer.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Graphical components
3
+ * @module Panoramax:components
4
+ */
5
+
6
+ import * as core from "./core/index.js";
7
+ import * as layout from "./layout/index.js";
8
+ import * as menus from "./menus/index.js";
9
+ import * as ui from "./ui/index.js";
10
+
11
+ export {core, layout, menus, ui};
@@ -0,0 +1,6 @@
1
+ // Lightweight export for PhotoViewer
2
+ import * as layout from "./layout/index.js";
3
+ import * as menus from "./menus/index_photoviewer.js";
4
+ import * as ui from "./ui/index_photoviewer.js";
5
+
6
+ export {layout, menus, ui};
@@ -0,0 +1,258 @@
1
+ import { LitElement, css, html } from "lit";
2
+ import { classMap } from "lit/directives/class-map.js";
3
+ import { onceParentAvailable } from "../../utils/widgets.js";
4
+
5
+ const OPENNESS_Y_PCT = { "opened": 0, "half-opened": 0.7, "closed": 1 };
6
+
7
+ /**
8
+ * BottomDrawer layout offers a mobile-like drawer menu, coming from bottom of the screen.
9
+ * @class Panoramax.components.layout.BottomDrawer
10
+ * @element pnx-bottom-drawer
11
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
12
+ * @slot `default` The drawer content
13
+ * @example
14
+ * ```html
15
+ * <pnx-bottom-drawer openness="opened">
16
+ * <p>My drawer content</p>
17
+ * </pnx-bottom-drawer>
18
+ * ```
19
+ */
20
+ export default class BottomDrawer extends LitElement {
21
+ /** @private */
22
+ static styles = css`
23
+ :host {
24
+ position: fixed;
25
+ bottom: 0;
26
+ left: 0;
27
+ width: 100%;
28
+ z-index: 130;
29
+ pointer-events: none;
30
+ touch-action: none;
31
+ }
32
+
33
+ .drawer {
34
+ background: var(--widget-bg);
35
+ border-top-left-radius: 16px;
36
+ border-top-right-radius: 16px;
37
+ box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.2);
38
+ display: flex;
39
+ flex-direction: column;
40
+ transition: transform 0.3s ease;
41
+ will-change: transform;
42
+ touch-action: auto;
43
+ pointer-events: auto;
44
+ }
45
+
46
+ .drawer.dragging { transition: none; }
47
+ .drawer.closed { transform: translateY(calc(100% - 30px)); }
48
+ .drawer.half-opened { transform: translateY(70%); }
49
+ .drawer.half-opened .content { overflow-y: hidden; }
50
+ .drawer.opened { transform: translateY(0%); }
51
+
52
+ .handle {
53
+ height: 30px;
54
+ display: flex;
55
+ align-items: center;
56
+ justify-content: center;
57
+ cursor: pointer;
58
+ }
59
+
60
+ .handle-bar {
61
+ width: 40px;
62
+ height: 5px;
63
+ background: var(--grey-pale);
64
+ border-radius: 3px;
65
+ }
66
+
67
+ .content {
68
+ overflow: auto;
69
+ flex: 1;
70
+ }
71
+ `;
72
+
73
+ /**
74
+ * Component properties.
75
+ * @memberof Panoramax.components.layout.BottomDrawer#
76
+ * @type {Object}
77
+ * @property {string} [openness=half-opened] How much the drawer should be opened (closed, half-opened, opened)
78
+ */
79
+ static properties = {
80
+ _fingerY: {state: true},
81
+ _deltaFingerY: {state: true},
82
+ _drawerY: {state: true},
83
+ _isDragging: {state: true},
84
+ _drawerHeight: {state: true},
85
+ openness: {type: String, reflect: true},
86
+ };
87
+
88
+ constructor() {
89
+ super();
90
+ this._isDragging = false;
91
+ this.openness = "half-opened";
92
+ }
93
+
94
+ /** @private */
95
+ firstUpdated() {
96
+ super.firstUpdated();
97
+
98
+ this._boundTouchMove = this._onTouchMove.bind(this);
99
+ this._boundTouchEnd = this._onTouchEnd.bind(this);
100
+
101
+ this._drawerHeight = window.innerHeight - 30;
102
+
103
+ const drawer = this._getDrawer();
104
+ if (!drawer) { return; }
105
+ drawer.style.height = `${this._drawerHeight}px`;
106
+ drawer.style.maxHeight = `${this._drawerHeight}px`;
107
+
108
+ onceParentAvailable(this).then(() => this._parent.onceReady()).then(() => {
109
+ this._parent.map?.addEventListener("click", () => this.openness = "closed");
110
+ this._parent.psv?.addEventListener("click", () => this.openness = "closed");
111
+ });
112
+ }
113
+
114
+ /** @private */
115
+ attributeChangedCallback(name, old, value) {
116
+ super.attributeChangedCallback(name, old, value);
117
+
118
+ if(name === "openness") {
119
+ // If not fully opened, force content scroll back to top
120
+ if(value !== "opened") {
121
+ const content = this.shadowRoot.querySelector(".content");
122
+ if(content) { content.scrollTop = 0; }
123
+ }
124
+
125
+ // Remove hand-defined transform
126
+ const drawer = this._getDrawer();
127
+ if (drawer) { drawer.style.transform = null; }
128
+ }
129
+ }
130
+
131
+ /** @private */
132
+ disconnectedCallback() {
133
+ super.disconnectedCallback();
134
+ this._cleanupTouchListeners();
135
+ }
136
+
137
+ /** @private */
138
+ _getDrawer() {
139
+ return this.shadowRoot?.querySelector(".drawer");
140
+ }
141
+
142
+ /** @private */
143
+ _onHandleClick() {
144
+ if(this.openness === "opened") { this.openness = "closed"; }
145
+ else if(this.openness === "half-opened") { this.openness = "opened"; }
146
+ else if(this.openness === "closed") { this.openness = "half-opened"; }
147
+ }
148
+
149
+ /** @private */
150
+ _onTouchStart(e) {
151
+ this._isDragging = true;
152
+ this._startFingerY = e.touches[0].clientY;
153
+ this._deltaFingerY = 0;
154
+ this._drawerY = this._drawerHeight * OPENNESS_Y_PCT[this.openness];
155
+ window.addEventListener("touchmove", this._boundTouchMove, { passive: true });
156
+ window.addEventListener("touchend", this._boundTouchEnd);
157
+ window.addEventListener("touchcancel", this._boundTouchEnd);
158
+ }
159
+
160
+ /** @private */
161
+ _onTouchMove(e) {
162
+ if (!this._isDragging) {return;}
163
+
164
+ // Check if content has scroll bar, then only move if top scrolled
165
+ const content = this.shadowRoot.querySelector(".content");
166
+ if(content.scrollHeight > content.offsetHeight && content.scrollTop > 0) {
167
+ this._updateDrawerTransform(0);
168
+ return;
169
+ }
170
+
171
+ this._deltaFingerY = e.touches[0].clientY - this._startFingerY;
172
+ this._updateDrawerTransform(this._drawerY + this._deltaFingerY);
173
+ }
174
+
175
+ /** @private */
176
+ _onTouchEnd(e) {
177
+ // Touchend is also called when "clicking" on mobile
178
+ if ((!this._isDragging || Math.abs(this._deltaFingerY) < 30) && !e.target.closest(".handle")) { return; }
179
+ e.preventDefault();
180
+
181
+ this._isDragging = false;
182
+
183
+ if(this._deltaFingerY === 0 && this.openness === "closed") {
184
+ this.openness = "half-opened";
185
+ }
186
+ else {
187
+ this._updateDrawerTransform(this._drawerY + this._deltaFingerY, true);
188
+ }
189
+
190
+ this._cleanupTouchListeners();
191
+ this._startFingerY = null;
192
+ this._deltaFingerY = null;
193
+ }
194
+
195
+ /** @private */
196
+ _cleanupTouchListeners() {
197
+ window.removeEventListener("touchmove", this._boundTouchMove);
198
+ window.removeEventListener("touchend", this._boundTouchEnd);
199
+ window.removeEventListener("touchcancel", this._boundTouchCancel);
200
+ }
201
+
202
+ /** @private */
203
+ _updateDrawerTransform(y, snap = false) {
204
+ const drawer = this._getDrawer();
205
+ if (!drawer) { return; }
206
+
207
+ y = Math.max(0, Math.min(y, this._drawerHeight - 30));
208
+
209
+ // Snap to nearest static position
210
+ if(snap) {
211
+ // Gesture goes up
212
+ if(this._deltaFingerY < 0) {
213
+ if(this.openness === "closed") {
214
+ // How much it goes up ?
215
+ if(Math.abs(this._deltaFingerY) > this._drawerHeight * (1-OPENNESS_Y_PCT["half-opened"])) {
216
+ this.openness = "opened";
217
+ }
218
+ else { this.openness = "half-opened"; }
219
+ }
220
+ else { this.openness = "opened"; }
221
+ }
222
+ // Gesture goes down
223
+ else { this.openness = "closed"; }
224
+
225
+ this._drawerY = null;
226
+ y = Math.max(0, Math.min(OPENNESS_Y_PCT[this.openness] * this._drawerHeight, this._drawerHeight - 30));
227
+ }
228
+
229
+ drawer.style.transform = `translateY(${y}px)`;
230
+ }
231
+
232
+ /** @private */
233
+ render() {
234
+ const classes = {
235
+ "drawer": true,
236
+ [this.openness]: true,
237
+ "dragging": this._isDragging,
238
+ };
239
+
240
+ return html`
241
+ <div
242
+ class=${classMap(classes)}
243
+ @touchstart="${this._onTouchStart}"
244
+ @touchmove="${this._onTouchMove}"
245
+ @touchend="${this._onTouchEnd}"
246
+ >
247
+ <div class="handle" @click=${this._onHandleClick}>
248
+ <div class="handle-bar"></div>
249
+ </div>
250
+ <div class="content">
251
+ <slot></slot>
252
+ </div>
253
+ </div>
254
+ `;
255
+ }
256
+ }
257
+
258
+ customElements.define("pnx-bottom-drawer", BottomDrawer);
@@ -0,0 +1,143 @@
1
+ import { LitElement, css, html } from "lit";
2
+ import { classMap } from "lit/directives/class-map.js";
3
+
4
+ /**
5
+ * CorneredGrid layout offers a convenient way to display overlay widgets on top of a background component.
6
+ * This is useful for showing buttons in corners over a map or picture display.
7
+ * Grid layout is organised through many slots (background and corners) : two rows (top, bottom) and three columns (right, middle, left).
8
+ * @class Panoramax.components.layout.CorneredGrid
9
+ * @element pnx-cornered-grid
10
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
11
+ * @slot `bg` The full size background component
12
+ * @slot `top-left` The top-left corner
13
+ * @slot `top` The top middle corner
14
+ * @slot `top-right` The top-right corner
15
+ * @slot `bottom-left` The bottom-left corner
16
+ * @slot `bottom` The bottom middle corner
17
+ * @slot `bottom-right` The bottom-right corner
18
+ * @example
19
+ * ```html
20
+ * <pnx-cornered-grid>
21
+ * <pnx-map slot="bg" />
22
+ * <pnx-button slot="top-left" />
23
+ * <pnx-button slot="top-left" />
24
+ * <pnx-legend slot="bottom" />
25
+ * </pnx-cornered-grid>
26
+ * ```
27
+ */
28
+ export default class CorneredGrid extends LitElement {
29
+ /** @private */
30
+ static styles = css`
31
+ :host {
32
+ position: relative;
33
+ width: 100%;
34
+ height: 100%;
35
+ display: flex;
36
+ flex-direction: column;
37
+ gap: 5px;
38
+ flex-wrap: no-wrap;
39
+ }
40
+
41
+ .bg {
42
+ position: absolute;
43
+ inset: 0;
44
+ z-index: 0;
45
+ }
46
+
47
+ .row {
48
+ display: flex;
49
+ flex-direction: row;
50
+ flex: 1 1 50%;
51
+ pointer-events: none;
52
+ transition: margin 0.5s ease-in-out, opacity 0.5s ease-in-out;
53
+ }
54
+
55
+ .row.bottom { align-items: flex-end; }
56
+
57
+ .toggled { opacity: 0; }
58
+ .row.top.toggled { margin: -100% 0 200% 0; }
59
+ .row.bottom.toggled { margin: 200% 0 -100% 0; }
60
+ @media screen and (min-width: 576px) {
61
+ .row.top.toggled { margin: -50% 0 100% 0; }
62
+ .row.bottom.toggled { margin: 100% 0 -50% 0; }
63
+ }
64
+
65
+ .corner {
66
+ position: relative;
67
+ flex: 1 1 33%;
68
+ display: flex;
69
+ gap: 10px;
70
+ height: min-content;
71
+ }
72
+
73
+ .corner slot {
74
+ pointer-events: auto;
75
+ position: relative;
76
+ z-index: 120;
77
+ }
78
+
79
+ .row.bottom .corner:not(.middle) { flex-direction: column; }
80
+ .row.bottom .corner.left { flex-direction: column-reverse; justify-content: flex-start; align-items: flex-start; }
81
+ .row.bottom .corner.middle { justify-content: center; align-items: flex-end; }
82
+ .row.bottom .corner.right { justify-content: flex-end; align-items: flex-end; }
83
+
84
+ .row.top .corner { flex-direction: row; }
85
+ .row.top .corner.left { justify-content: flex-start; align-items: flex-start; }
86
+ .row.top .corner.middle { justify-content: center; }
87
+ .row.top .corner.right { justify-content: flex-end; align-items: flex-start; }
88
+ `;
89
+
90
+ /** @private */
91
+ static properties = {
92
+ _hidden: {state: true},
93
+ };
94
+
95
+ constructor() {
96
+ super();
97
+ this._hidden = false;
98
+ }
99
+
100
+ /**
101
+ * Make all overlay components go away smoothly (one-click "focus")
102
+ * @param {boolean} [newState] True to make widgets visible, false to hide, undefined to invert
103
+ * @returns {boolean} True if widgets are hidden
104
+ */
105
+ toggleAway(newState) {
106
+ this._hidden = newState === null || newState === undefined ? !this._hidden : !newState;
107
+ return this._hidden;
108
+ }
109
+
110
+ /** @private */
111
+ render() {
112
+ this.renderRoot.host.className = this._hidden ? "toggled" : "";
113
+ return html`
114
+ <div class="bg">
115
+ <slot name="bg"></slot>
116
+ </div>
117
+ <div class=${classMap({row: true, top: true, toggled: this._hidden})}>
118
+ <div class="corner left" part="corner-top-left">
119
+ <slot name="top-left"></slot>
120
+ </div>
121
+ <div class="corner middle" part="corner-top">
122
+ <slot name="top"></slot>
123
+ </div>
124
+ <div class="corner right" part="corner-top-right">
125
+ <slot name="top-right"></slot>
126
+ </div>
127
+ </div>
128
+ <div class=${classMap({row: true, bottom: true, toggled: this._hidden})}>
129
+ <div class="corner left" part="corner-bottom-left">
130
+ <slot name="bottom-left"></slot>
131
+ </div>
132
+ <div class="corner middle" part="corner-bottom">
133
+ <slot name="bottom"></slot>
134
+ </div>
135
+ <div class="corner right" part="corner-bottom-right">
136
+ <slot name="bottom-right"></slot>
137
+ </div>
138
+ </div>
139
+ `;
140
+ }
141
+ }
142
+
143
+ customElements.define("pnx-cornered-grid", CorneredGrid);
@@ -0,0 +1,121 @@
1
+ import { LitElement, css, html } from "lit";
2
+ import { svgToImg } from "../../utils/utils.js";
3
+ const SwitchBigSVG = await fetch(new URL("../../img/switch_big.svg", import.meta.url).href).then(res => res.text());
4
+ const SwitchMiniSVG = await fetch(new URL("../../img/switch_mini.svg", import.meta.url).href).then(res => res.text());
5
+
6
+ const SwitchBig = svgToImg(SwitchBigSVG);
7
+ const SwitchMini = svgToImg(SwitchMiniSVG);
8
+
9
+ /**
10
+ * Mini layout allows to show a reduced or collapsed component in a corner of a main component.
11
+ * For example, show a reduced map on top of a photo display.
12
+ * @class Panoramax.components.layout.Mini
13
+ * @element pnx-mini
14
+ * @slot `default` The component to display.
15
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
16
+ * @fires Panoramax.components.layout.Mini#expand
17
+ * @example
18
+ * ```html
19
+ * <pnx-mini icon=${fa(faMap)} ._parent=${viewer}>
20
+ * <pnx-map />
21
+ * </pnx-mini>
22
+ * ```
23
+ */
24
+ export default class Mini extends LitElement {
25
+ /** @private */
26
+ static styles = css`
27
+ :host {
28
+ display: block;
29
+ position: absolute;
30
+ inset: 0;
31
+ pointer-events: none;
32
+ }
33
+
34
+ slot {
35
+ z-index: 120;
36
+ pointer-events: auto;
37
+ }
38
+
39
+ pnx-button {
40
+ z-index: 121;
41
+ position: absolute;
42
+ pointer-events: auto;
43
+ }
44
+
45
+ pnx-button img {
46
+ height: 20px;
47
+ }
48
+
49
+ pnx-button:not([size="xxl"])::part(btn) {
50
+ border-top-left-radius: 0;
51
+ border-bottom-right-radius: 0;
52
+ }
53
+ `;
54
+
55
+ /**
56
+ * Component properties.
57
+ * @memberof Panoramax.components.layout.Mini#
58
+ * @type {Object}
59
+ * @property {string} [icon] The icon to use in collapsed button.
60
+ * @property {boolean} [collapsed=false] Collapsed displays only a simple button.
61
+ */
62
+ static properties = {
63
+ icon: {type: String},
64
+ collapsed: {type: Boolean, reflect: true},
65
+ };
66
+
67
+ constructor() {
68
+ super();
69
+
70
+ this.collapsed = false;
71
+ this.icon = null;
72
+ }
73
+
74
+ /** @private */
75
+ render() {
76
+ const isSmall = this._parent?.isWidthSmall() || this._parent?.isWidthMedium() || this._parent?.isHeightSmall();
77
+
78
+ /**
79
+ * Event when user ask for component expand
80
+ * @event Panoramax.components.layout.Mini#expand
81
+ * @type {Event}
82
+ */
83
+ const onExpand = () => this.dispatchEvent(new Event("expand", {bubbles: true, composed: true}));
84
+
85
+ if(this.collapsed || isSmall) {
86
+ const click = isSmall ? onExpand : () => this.collapsed = false;
87
+ return html`<pnx-button
88
+ kind="flat"
89
+ size="xxl"
90
+ style="bottom: 0"
91
+ @click=${click}
92
+ >${this.icon}</pnx-button>`;
93
+ }
94
+ else {
95
+ return html`
96
+ <slot></slot>
97
+
98
+ <pnx-button
99
+ kind="superflat"
100
+ title=${this._parent?._t.pnx.expand_info}
101
+ style="top: 0; right: 0"
102
+ @click=${onExpand}
103
+ >
104
+ ${SwitchBig}
105
+ ${this._parent?._t.pnx.expand}
106
+ </pnx-button>
107
+
108
+ <pnx-button
109
+ kind="flat"
110
+ title=${this._parent?._t.pnx.minimize}
111
+ style="bottom: 0; left: 0"
112
+ @click=${() => this.collapsed = true}
113
+ >
114
+ ${SwitchMini}
115
+ </pnx-button>
116
+ `;
117
+ }
118
+ }
119
+ }
120
+
121
+ customElements.define("pnx-mini", Mini);
@@ -0,0 +1,140 @@
1
+ import { LitElement, css, html, nothing } from "lit";
2
+
3
+ /**
4
+ * Tabs offers a nice paged content rendering based on tabs buttons.
5
+ * The list of tab names are passed through `title` slots, and content using `content` slots.
6
+ * Note that tab names and contents should respect a coherent order.
7
+ * @class Panoramax.components.layout.Tabs
8
+ * @element pnx-tabs
9
+ * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
10
+ * @slot `title` A single tab name
11
+ * @slot `content` A single tab content
12
+ * @example
13
+ * ```html
14
+ * <pnx-tabs>
15
+ * <h4 slot="title">Tab 1</h4>
16
+ * <div slot="content">Tab 1 content</div>
17
+ *
18
+ * <h4 slot="title">Tab 2</h4>
19
+ * <div slot="content">Tab 2 content</div>
20
+ *
21
+ * <h4 slot="title">Tab 3</h4>
22
+ * <div slot="content">Tab 3 content</div>
23
+ * </pnx-tabs>
24
+ * ```
25
+ */
26
+ export default class Tabs extends LitElement {
27
+ /** @private */
28
+ static styles = css`
29
+ /* Tabs */
30
+ nav {
31
+ display: flex;
32
+ gap: 5px;
33
+ align-items: stretch;
34
+ overflow-x: auto;
35
+ touch-action: pan-x;
36
+ position: sticky;
37
+ top: 0;
38
+ background: white;
39
+ z-index: 125;
40
+ }
41
+ nav ::slotted(*) {
42
+ color: var(--grey-dark);
43
+ border-bottom: 2px solid rgba(0,0,0,0);
44
+ cursor: pointer;
45
+ transition: all 0.1s;
46
+ flex: 1;
47
+ }
48
+ nav ::slotted(*:hover:not(.active)) { background-color: var(--blue-pale); }
49
+ nav ::slotted(*.active) {
50
+ color: var(--blue-dark);
51
+ border-bottom: 2px solid var(--blue-dark);
52
+ }
53
+
54
+ /* Content */
55
+ .contents ::slotted(div) { display: none !important; }
56
+ .contents ::slotted(.active) { display: block !important; }
57
+ `;
58
+
59
+ /**
60
+ * Component properties.
61
+ * @memberof Panoramax.components.layout.Tabs#
62
+ * @type {Object}
63
+ * @property {string} [activeTabIndex=0] The selected tab index
64
+ * @property {boolean} [hideNav=false] Hide nav
65
+ */
66
+ static properties = {
67
+ activeTabIndex: {type: Number, reflect: true},
68
+ hideNav: {type: Boolean},
69
+ };
70
+
71
+ constructor() {
72
+ super();
73
+ this.activeTabIndex = 0;
74
+ this.hideNav = false;
75
+ }
76
+
77
+ /** @private */
78
+ _getTabs() {
79
+ return this.shadowRoot.querySelector("slot[name='title']")?.assignedNodes() || [];
80
+ }
81
+
82
+ /** @private */
83
+ _getContents() {
84
+ return this.shadowRoot.querySelector("slot[name='content']")?.assignedNodes() || [];
85
+ }
86
+
87
+ /** @private */
88
+ _changeTab(tabTarget, tabIndex) {
89
+ const tabs = this._getTabs();
90
+ const contents = this._getContents();
91
+
92
+ // Check if tab change is possible
93
+ if(tabTarget !== undefined || tabIndex !== undefined) {
94
+ // For tab target, check if a nav tab has really been clicked
95
+ if(tabTarget) { tabIndex = tabs.findIndex(tab => (
96
+ tab === tabTarget || tab === tabTarget.parentNode
97
+ )); }
98
+
99
+ if(!isNaN(tabIndex) && tabIndex >= 0 && tabIndex < tabs.length) {
100
+ tabs.forEach((tab, index) => {
101
+ if (index == tabIndex) {
102
+ this.activeTabIndex = index;
103
+ contents[index].classList.add("active");
104
+ tab.classList.add("active");
105
+ } else {
106
+ contents[index].classList.remove("active");
107
+ tab.classList.remove("active");
108
+ }
109
+ });
110
+ }
111
+ }
112
+
113
+ }
114
+
115
+ /** @private */
116
+ _handleTabClick(event) {
117
+ this._changeTab(event.target);
118
+ }
119
+
120
+ /** @private */
121
+ updated(changedProperties) {
122
+ if(changedProperties.has("activeTabIndex")) {
123
+ this._changeTab(undefined, this.activeTabIndex);
124
+ }
125
+ }
126
+
127
+ /** @private */
128
+ render() {
129
+ return html`
130
+ ${!this.hideNav ? html`<nav @click="${this._handleTabClick}">
131
+ <slot name="title" @slotchange=${() => this.activeTabIndex = 0}></slot>
132
+ </nav>` : nothing}
133
+ <div class="contents">
134
+ <slot name="content"></slot>
135
+ </div>
136
+ `;
137
+ }
138
+ }
139
+
140
+ customElements.define("pnx-tabs", Tabs);
@@ -0,0 +1,9 @@
1
+ /**
2
+ * General layout components
3
+ * @module Panoramax:components:layout
4
+ */
5
+
6
+ export {default as BottomDrawer} from "./BottomDrawer.js";
7
+ export {default as CorneredGrid} from "./CorneredGrid.js";
8
+ export {default as Mini} from "./Mini.js";
9
+ export {default as Tabs} from "./Tabs.js";