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

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 (153) hide show
  1. package/build/cjs/index.js +1 -1
  2. package/build/cjs/index_photoviewer.js +1 -1
  3. package/build/esm/components/core/Basic.js +1 -1
  4. package/build/esm/translations/el.json +92 -1
  5. package/package.json +1 -1
  6. package/build/bundle.cjs +0 -3399
  7. package/build/bundle.cjs.map +0 -1
  8. package/build/bundle_photoviewer.cjs +0 -2510
  9. package/build/bundle_photoviewer.cjs.map +0 -1
  10. package/build/components/core/Basic.css +0 -56
  11. package/build/components/core/Basic.js +0 -378
  12. package/build/components/core/CoverageMap.css +0 -10
  13. package/build/components/core/CoverageMap.js +0 -169
  14. package/build/components/core/Editor.css +0 -33
  15. package/build/components/core/Editor.js +0 -398
  16. package/build/components/core/PhotoViewer.css +0 -70
  17. package/build/components/core/PhotoViewer.js +0 -650
  18. package/build/components/core/Viewer.css +0 -130
  19. package/build/components/core/Viewer.js +0 -711
  20. package/build/components/core/index.js +0 -10
  21. package/build/components/index.js +0 -11
  22. package/build/components/index_photoviewer.js +0 -6
  23. package/build/components/layout/BottomDrawer.js +0 -258
  24. package/build/components/layout/CorneredGrid.js +0 -143
  25. package/build/components/layout/Mini.js +0 -121
  26. package/build/components/layout/Tabs.js +0 -140
  27. package/build/components/layout/index.js +0 -9
  28. package/build/components/menus/LocationPrecisionDoc.js +0 -42
  29. package/build/components/menus/MapBackground.js +0 -110
  30. package/build/components/menus/MapFilters.js +0 -567
  31. package/build/components/menus/MapLayers.js +0 -238
  32. package/build/components/menus/MapLegend.js +0 -68
  33. package/build/components/menus/MiniPictureLegend.js +0 -73
  34. package/build/components/menus/PictureLegend.js +0 -379
  35. package/build/components/menus/PictureMetadata.js +0 -380
  36. package/build/components/menus/PlayerOptions.js +0 -93
  37. package/build/components/menus/QualityScoreDoc.js +0 -42
  38. package/build/components/menus/ReportForm.js +0 -132
  39. package/build/components/menus/SemanticsDoc.js +0 -38
  40. package/build/components/menus/SemanticsDownload.js +0 -33
  41. package/build/components/menus/SemanticsFilters.js +0 -153
  42. package/build/components/menus/SemanticsList.js +0 -413
  43. package/build/components/menus/SemanticsMetadata.js +0 -368
  44. package/build/components/menus/Share.js +0 -105
  45. package/build/components/menus/index.js +0 -22
  46. package/build/components/menus/index_photoviewer.js +0 -11
  47. package/build/components/styles.js +0 -557
  48. package/build/components/ui/AnnotationsSwitch.js +0 -159
  49. package/build/components/ui/Button.js +0 -77
  50. package/build/components/ui/ButtonGroup.css +0 -59
  51. package/build/components/ui/ButtonGroup.js +0 -69
  52. package/build/components/ui/CopyButton.js +0 -110
  53. package/build/components/ui/Grade.js +0 -54
  54. package/build/components/ui/GradeFilter.js +0 -122
  55. package/build/components/ui/IconSwitch.js +0 -193
  56. package/build/components/ui/LinkButton.js +0 -67
  57. package/build/components/ui/ListGroup.js +0 -66
  58. package/build/components/ui/ListItem.js +0 -90
  59. package/build/components/ui/Loader.js +0 -203
  60. package/build/components/ui/Map.css +0 -63
  61. package/build/components/ui/Map.js +0 -853
  62. package/build/components/ui/MapMore.js +0 -175
  63. package/build/components/ui/Photo.css +0 -50
  64. package/build/components/ui/Photo.js +0 -1502
  65. package/build/components/ui/Popup.js +0 -145
  66. package/build/components/ui/ProgressBar.js +0 -104
  67. package/build/components/ui/QualityScore.js +0 -147
  68. package/build/components/ui/SearchBar.js +0 -374
  69. package/build/components/ui/SemanticsEditor.js +0 -191
  70. package/build/components/ui/SemanticsTable.js +0 -88
  71. package/build/components/ui/Switch.js +0 -139
  72. package/build/components/ui/TogglableGroup.js +0 -157
  73. package/build/components/ui/index.js +0 -29
  74. package/build/components/ui/index_photoviewer.js +0 -21
  75. package/build/components/ui/widgets/CopyCoordinates.js +0 -75
  76. package/build/components/ui/widgets/GeoSearch.css +0 -21
  77. package/build/components/ui/widgets/GeoSearch.js +0 -150
  78. package/build/components/ui/widgets/Legend.js +0 -190
  79. package/build/components/ui/widgets/LevelSelect.css +0 -51
  80. package/build/components/ui/widgets/LevelSelect.js +0 -143
  81. package/build/components/ui/widgets/MapFiltersButton.js +0 -114
  82. package/build/components/ui/widgets/MapLayersButton.js +0 -79
  83. package/build/components/ui/widgets/OSMEditors.js +0 -155
  84. package/build/components/ui/widgets/PictureLegendActions.js +0 -99
  85. package/build/components/ui/widgets/Player.css +0 -7
  86. package/build/components/ui/widgets/Player.js +0 -154
  87. package/build/components/ui/widgets/SemanticsFiltersButton.js +0 -65
  88. package/build/components/ui/widgets/Zoom.js +0 -84
  89. package/build/components/ui/widgets/index.js +0 -16
  90. package/build/components/ui/widgets/index_photoviewer.js +0 -7
  91. package/build/img/arrow_360.svg +0 -14
  92. package/build/img/arrow_flat.svg +0 -11
  93. package/build/img/arrow_triangle.svg +0 -9
  94. package/build/img/arrow_turn.svg +0 -8
  95. package/build/img/bg_aerial.jpg +0 -0
  96. package/build/img/bg_streets.jpg +0 -0
  97. package/build/img/loader_base.jpg +0 -0
  98. package/build/img/logo_dead.svg +0 -91
  99. package/build/img/marker.svg +0 -17
  100. package/build/img/marker_blue.svg +0 -20
  101. package/build/img/osm.svg +0 -49
  102. package/build/img/panoramax.svg +0 -13
  103. package/build/img/switch_big.svg +0 -54
  104. package/build/img/switch_mini.svg +0 -48
  105. package/build/img/wd.svg +0 -1
  106. package/build/index_photoviewer.js +0 -4
  107. package/build/package.json +0 -148
  108. package/build/servers.js +0 -14
  109. package/build/translations/ar.json +0 -1
  110. package/build/translations/be.json +0 -257
  111. package/build/translations/br.json +0 -81
  112. package/build/translations/cy.json +0 -117
  113. package/build/translations/da.json +0 -300
  114. package/build/translations/de.json +0 -309
  115. package/build/translations/en.json +0 -294
  116. package/build/translations/eo.json +0 -235
  117. package/build/translations/es.json +0 -292
  118. package/build/translations/fi.json +0 -1
  119. package/build/translations/fr.json +0 -294
  120. package/build/translations/hr.json +0 -294
  121. package/build/translations/hu.json +0 -294
  122. package/build/translations/it.json +0 -306
  123. package/build/translations/ja.json +0 -182
  124. package/build/translations/ko.json +0 -1
  125. package/build/translations/nl.json +0 -305
  126. package/build/translations/nn.json +0 -1
  127. package/build/translations/pl.json +0 -169
  128. package/build/translations/pt.json +0 -296
  129. package/build/translations/pt_BR.json +0 -304
  130. package/build/translations/sv.json +0 -182
  131. package/build/translations/ti.json +0 -9
  132. package/build/translations/tr.json +0 -297
  133. package/build/translations/uk.json +0 -268
  134. package/build/translations/zh_Hant.json +0 -309
  135. package/build/utils/API.js +0 -928
  136. package/build/utils/InitParameters.js +0 -521
  137. package/build/utils/MapStyleComposer.js +0 -889
  138. package/build/utils/PanoraMapProtocol.js +0 -49
  139. package/build/utils/PhotoAdapter.js +0 -49
  140. package/build/utils/PresetsManager.js +0 -148
  141. package/build/utils/SemanticsMapProtocol.js +0 -144
  142. package/build/utils/URLHandler.js +0 -426
  143. package/build/utils/geocoder.js +0 -203
  144. package/build/utils/i18n.js +0 -128
  145. package/build/utils/index.js +0 -17
  146. package/build/utils/index_photoviewer.js +0 -14
  147. package/build/utils/indoor.js +0 -200
  148. package/build/utils/map.js +0 -788
  149. package/build/utils/picture.js +0 -507
  150. package/build/utils/semantics.js +0 -321
  151. package/build/utils/services.js +0 -148
  152. package/build/utils/utils.js +0 -433
  153. package/build/utils/widgets.js +0 -110
@@ -1,33 +0,0 @@
1
- import { LitElement, html } from "lit";
2
- import { faSvg, titles } from "../styles.js";
3
- import { fa } from "../../utils/widgets.js";
4
- import { faDownload } from "@fortawesome/free-solid-svg-icons";
5
-
6
- /**
7
- * Semantics Download docs displays a synthetic summary of you can download semantics metadata.
8
- * @class Panoramax.components.menus.SemanticsDownload
9
- * @element pnx-semantics-download
10
- * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
11
- * @example
12
- * ```html
13
- * <pnx-semantics-download ._t=${viewer._t} />
14
- * ```
15
- */
16
- export default class SemanticsDownload extends LitElement {
17
- static styles = [ titles, faSvg ];
18
-
19
- render() {
20
- return html`
21
- <h4>${fa(faDownload)} ${this._t?.pnx.semantics_download_title}</h4>
22
- <p>${this._t?.pnx.semantics_download_info}</p>
23
- <pnx-link-button
24
- href="https://docs.panoramax.fr/federated-catalog/data_export/"
25
- target="_blank"
26
- kind="outline"
27
- style="width: 100%"
28
- >${this._t?.pnx.semantics_download_cta}</pnx-link-button>
29
- `;
30
- }
31
- }
32
-
33
- customElements.define("pnx-semantics-download", SemanticsDownload);
@@ -1,153 +0,0 @@
1
- import { LitElement, css, html } from "lit";
2
- import { fa, onceParentAvailable } from "../../utils/widgets.js";
3
- import { faDownload, faInfoCircle } from "@fortawesome/free-solid-svg-icons";
4
- import { SEMANTICS_OVERLAY_MINZOOM } from "../../utils/SemanticsMapProtocol.js";
5
-
6
- /**
7
- * Semantics Filters menu allows user to show/hide specific semantic tags over the map.
8
- * @class Panoramax.components.menus.SemanticsFilters
9
- * @element pnx-map-semantics-filters-menu
10
- * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
11
- * @fires Panoramax.components.menus.SemanticsFilters#change
12
- * @example
13
- * ```html
14
- * <pnx-map-semantics-filters-menu _parent=${viewer} />
15
- * ```
16
- */
17
- export default class SemanticsFilters extends LitElement {
18
- /** @private */
19
- static styles = css`
20
- .pnx-tags-zoomin {
21
- z-index: 131;
22
- background-color: rgba(255,255,255,0.8);
23
- text-align: center;
24
- font-weight: 800;
25
- position: absolute;
26
- top: 0;
27
- right: 0;
28
- left: 0;
29
- bottom: 0;
30
- display: flex;
31
- justify-content: center;
32
- align-items: center;
33
- border-radius: 25px;
34
- }
35
- .pnx-tags-zoomin.hidden {
36
- display: none;
37
- }
38
- .pnx-tags-cta {
39
- display: flex;
40
- align-items: center;
41
- justify-content: end;
42
- }
43
- pnx-switch {
44
- display: block;
45
- margin: 5px 0;
46
- }
47
- `;
48
-
49
- /** @private */
50
- static properties = {
51
- _showZoomIn: {state: true},
52
- _layers: {state: true},
53
- };
54
-
55
- constructor() {
56
- super();
57
- this.showZoomIn = true;
58
- this._layers = [];
59
-
60
- // Map zoom
61
- onceParentAvailable(this).then(() => this._parent.onceMapReady?.().then(() => {
62
- const postUpdate = () => {
63
- this._onLayersChange();
64
- this.requestUpdate();
65
- };
66
- this._parent.map.on("zoomend", this._onMapZoom.bind(this));
67
- this._parent.map.on("styledata", postUpdate);
68
- this._parent.mapStyleComposer.addEventListener("dataoverlay-added", postUpdate);
69
- this._onMapZoom();
70
- postUpdate();
71
- }));
72
- }
73
-
74
- /** @private */
75
- _layersToSet(layers) {
76
- return layers
77
- .map(l => l.source+"/"+this._parent.mapStyleComposer.dataOverlays.has(l.source))
78
- .sort()
79
- .join("|");
80
- }
81
-
82
- /** @private */
83
- _onLayersChange() {
84
- clearTimeout(this._debouncedChange);
85
- this._debouncedChange = setTimeout(
86
- () => {
87
- const newlayers = this._parent?.map?.getSemanticOverlays();
88
- // eslint-disable-next-line eqeqeq
89
- if(this._layersToSet(newlayers) != this._layersToSet(this._layers)) {
90
- this._layers = newlayers;
91
-
92
- /**
93
- * Event for semantics overlay visibility changes
94
- * @event Panoramax.components.menus.SemanticsFilters#change
95
- * @type {CustomEvent}
96
- * @property {object[]} detail.layers List of semantics layers, from MapLibre
97
- */
98
- this.dispatchEvent(new CustomEvent("change", {
99
- bubbles: true,
100
- composed: true,
101
- detail: { layers: newlayers }
102
- }));
103
- }
104
- },
105
- 10
106
- );
107
- }
108
-
109
- /**
110
- * Map zoom event handler: show/hide "zoom in" labels
111
- * @private
112
- */
113
- _onMapZoom() {
114
- this._showZoomIn = this._parent.map.getZoom() < SEMANTICS_OVERLAY_MINZOOM;
115
- }
116
-
117
- /** @private */
118
- render() {
119
- if(!this._layers || this._layers.length === 0) { return; }
120
-
121
- return html`
122
- <div class="pnx-tags-zoomin ${this._showZoomIn ? "" : "hidden"}">
123
- ${this._parent?._t.pnx.semantics_zoom_in}
124
- </div>
125
-
126
- <div class="pnx-tags-cta">
127
- <pnx-button
128
- kind="full"
129
- size="sm"
130
- @click=${() => this._parent?._showSemanticsDownload()}
131
- >${fa(faDownload)} ${this._parent?._t.pnx.semantics_download}</pnx-button>
132
-
133
- <pnx-button
134
- id="pnx-semfilt-doc"
135
- kind="superinline"
136
- size="md"
137
- title=${this._parent?._t.pnx.semantics_doc}
138
- @click=${() => this._parent?._showSemanticsDoc()}
139
- >${fa(faInfoCircle)}</pnx-button>
140
- </div>
141
-
142
- ${this._layers.filter(l => l.metadata).map(l => html`
143
- <pnx-switch
144
- name=${"pnx-semfilt-"+l.id}
145
- .checked=${this._parent.mapStyleComposer.dataOverlays.has(l.source)}
146
- @change=${e => this._parent.mapStyleComposer.switchDataOverlayVisibility(l.source, e.target.checked)}
147
- >${this._parent?._t.pnx.semantics_overlays[l.id] || l.metadata.name || l.id}</pnx-switch>
148
- `)}
149
- `;
150
- }
151
- }
152
-
153
- customElements.define("pnx-map-semantics-filters-menu", SemanticsFilters);
@@ -1,413 +0,0 @@
1
- import { LitElement, css, html, nothing } from "lit";
2
- import { badgeCount, faSvg, iconify } from "../styles.js";
3
- import { fa, moreIcons, onceParentAvailable } from "../../utils/widgets.js";
4
- import { groupByPrefix, semanticsToString } from "../../utils/semantics.js";
5
- import {
6
- faArrowLeft, faCheck, faChevronRight, faCopy,
7
- faImages, faPen, faTrash, faVectorSquare
8
- } from "@fortawesome/free-solid-svg-icons";
9
- import "iconify-icon";
10
-
11
- /**
12
- * Semantics list shows listing of both picture tags and annotations.
13
- * It uses the parent component currently selected picture.
14
- *
15
- * @class Panoramax.components.menus.SemanticsList
16
- * @element pnx-semantics-list
17
- * @extends [lit.LitElement](https://lit.dev/docs/api/LitElement/)
18
- * @fires Panoramax.components.menus.SemanticsList#select
19
- * @fires Panoramax.components.menus.SemanticsList#edit-click
20
- * @fires Panoramax.components.menus.SemanticsList#delete-click
21
- * @example
22
- * ```html
23
- * <pnx-semantics-list _parent=${viewer} />
24
- * ```
25
- */
26
- export default class SemanticsList extends LitElement {
27
- /** @private */
28
- static styles = [ faSvg, iconify, badgeCount, css`
29
- .annotation { background: var(--white); }
30
- pnx-list-item {
31
- display: block;
32
- margin: 5px 0;
33
- }
34
- ` ];
35
-
36
- /**
37
- * Component properties.
38
- * @memberof Panoramax.components.menus.SemanticsList#
39
- * @type {Object}
40
- * @property {boolean} [editable=false] Show an "Edit" button ?
41
- */
42
- static properties = {
43
- editable: {type: Boolean},
44
- _meta: {state: true},
45
- _seqMeta: {state: true},
46
- _entries: {state: true},
47
- _selectedEntry: {state: true},
48
- _annotPresets: {state: true},
49
- _picPresets: {state: true},
50
- _seqPresets: {state: true},
51
- };
52
-
53
- constructor() {
54
- super();
55
- this._meta = {};
56
- this._seqMeta = {};
57
- this._selectedEntry = null;
58
- this._annotPresets = {};
59
- this._picPresets = [];
60
- this._seqPresets = [];
61
- this._entries = null;
62
- this.editable = false;
63
- moreIcons();
64
- }
65
-
66
- /** @private */
67
- connectedCallback() {
68
- super.connectedCallback();
69
-
70
- this._onPicChange();
71
-
72
- onceParentAvailable(this).then(() => {
73
- this._parent.psv?.addEventListener("picture-loaded", this._onPicChange.bind(this));
74
- this._parent.psv?.addEventListener("annotation-focused", e => this.showAnnotation(e.detail.annotationId));
75
- this._parent.psv?.addEventListener("annotations-unfocused", () => this._onListItemClick(null));
76
- this._parent.psv?.addEventListener("semantics-unfocused", () => this._onListItemClick(null));
77
-
78
- const selectedAnnots = this._parent.psv.getSelectedAnnotations();
79
- if(selectedAnnots.length > 0) {
80
- this.showAnnotation(selectedAnnots[0]);
81
- }
82
- });
83
- }
84
-
85
- /**
86
- * Display a specific annotation details.
87
- * @param {string} id The annotation UUID
88
- * @memberof Panoramax.components.menus.SemanticsList#
89
- */
90
- showAnnotation(id) {
91
- if(!this._entries) { setTimeout(() => this.showAnnotation(id), 200); }
92
- else { this._onListItemClick(this._entries.find(e => e.type === "annotation" && e.annotation.id == id)); }
93
- }
94
-
95
- /**
96
- * Display a specific tag group details.
97
- * @param {object} tags The tags to find
98
- * @memberof Panoramax.components.menus.SemanticsList#
99
- */
100
- showTagsGroup(tags) {
101
- if(!this._entries) { setTimeout(() => this.showTagsGroup(tags), 200); }
102
- else {
103
- this._onListItemClick(this._entries.find(e => {
104
- if(e?.type === "annotation") { return false; }
105
- for(let i=0; i < tags.length; i++) {
106
- if(!e.tagGroup.semantics.find(s => tags[i].key === s.key && tags[i].value === s.value)) {
107
- return false;
108
- }
109
- }
110
- return true;
111
- }));
112
- }
113
- }
114
-
115
- /** @private */
116
- _onPicChange() {
117
- this._meta = this._parent?.psv?.getPictureMetadata();
118
- this._getSeqMeta();
119
- delete this._prevPsvView;
120
- this._selectedEntry = null;
121
- this._entries = null;
122
- this.dispatchEvent(new CustomEvent("select", { detail: { item: null } }));
123
-
124
- // Load presets
125
- if(this._meta && this._parent?.presetsManager) {
126
- this._annotPresets = {};
127
- this._picPresets = [];
128
-
129
- // Picture tags
130
- this._parent.presetsManager.getPresets(this._meta.properties).then(p => {
131
- this._picPresets = p;
132
- this._computeEntries();
133
- });
134
-
135
- // Annotations
136
- if(this._meta.properties?.annotations?.length > 0) {
137
- Promise.all(this._meta.properties.annotations.map(a => this._parent.presetsManager.getPreset(a).then(p => {
138
- this._annotPresets[a.id] = p;
139
- }))).then(() => this._computeEntries());
140
- }
141
- }
142
- }
143
-
144
- /** @private */
145
- async _getSeqMeta() {
146
- const seqId = this._meta?.sequence?.id;
147
-
148
- // If no cache, call API
149
- if(seqId && !this._parent._sequencesMetadata[seqId]) {
150
- this._parent._sequencesMetadata[seqId] = await this._parent.getAPI().getSequenceMetadata(seqId);
151
- }
152
-
153
- this._seqMeta = this._parent._sequencesMetadata[seqId];
154
- this._seqPresets = [];
155
-
156
- if(this._seqMeta?.semantics?.length > 0) {
157
- const p = await this._parent.presetsManager.getPresets(this._seqMeta);
158
- this._seqPresets = p;
159
- this._computeEntries();
160
- }
161
- }
162
-
163
- /** @private */
164
- _getGroupedTags(semantics, type, unknownTagId, presets) {
165
- const entries = [];
166
- const groupToList = (t, pkey) => {
167
- const g = [ {key: pkey, value: t.value} ];
168
- if(t.qualifiers) { t.qualifiers.forEach(q => g.push({key: q.key, value: q.value})); }
169
- return g;
170
- };
171
-
172
- const tagsGroups = groupByPrefix(semantics);
173
- const nonMatchingTags = [];
174
- tagsGroups.forEach(g => {
175
- const usedTags = {};
176
- g.tags.forEach(t => {
177
- const pkey = g.prefix != "" ? `${g.prefix}|${t.key}` : t.key;
178
-
179
- // Skip if tag found in a previous preset
180
- if(usedTags[`${pkey}=${t.value}`]) {
181
- groupToList(t, pkey).forEach(v => usedTags[`${pkey}=${t.value}`].push(v));
182
- return;
183
- }
184
-
185
- // Check if a picture preset matches this tag
186
- const preset = presets.find(p => ["*", t.value].includes(p.tags[pkey]));
187
- if(preset) {
188
- const groupTags = groupToList(t, pkey);
189
-
190
- // Add all used tags for this preset into used ones
191
- let usedTagsCount = 0;
192
- Object.entries(preset.tags).forEach(([prk, prv]) => {
193
- let utv = prv;
194
- if(prv === "*") {
195
- utv = this._meta.properties.semantics.find(s => s.key === pkey)?.value;
196
- }
197
- usedTags[prk+"="+utv] = groupTags;
198
- usedTagsCount++;
199
- });
200
-
201
- entries.push({
202
- title: preset.name,
203
- icon: preset.iconify || "fa6-solid:cube",
204
- associatedTags: (t.qualifiers?.length || 0) + usedTagsCount,
205
- tagGroup: { semantics: groupTags },
206
- type: type,
207
- });
208
- }
209
- else {
210
- nonMatchingTags.push({key: pkey, value: t.value, qualifiers: t.qualifiers});
211
- }
212
- });
213
- });
214
-
215
- if(nonMatchingTags.length > 0) {
216
- entries.push({
217
- title: this._parent?._t.pnx.semantics_features_default_title.replace("{nb}", unknownTagId++),
218
- icon: "fa6-solid:tag",
219
- associatedTags: nonMatchingTags.length,
220
- tagGroup: { semantics: nonMatchingTags },
221
- type: type,
222
- });
223
- }
224
-
225
- return [ entries, unknownTagId ];
226
- }
227
-
228
- /** @private */
229
- _computeEntries() {
230
- let unknownTagId = 1;
231
- let entries = [];
232
- let entries2 = [];
233
-
234
- // Sequence tags
235
- if(this._seqMeta?.semantics?.length > 0) {
236
- [ entries, unknownTagId ] = this._getGroupedTags(this._seqMeta.semantics, "seqtags", unknownTagId, this._seqPresets);
237
- }
238
-
239
- // Picture tags
240
- [ entries2, unknownTagId ] = this._getGroupedTags(this._meta.properties?.semantics, "pictags", unknownTagId, this._picPresets);
241
- entries = entries.concat(entries2);
242
-
243
- // Annotations
244
- entries = entries.concat(this._meta.properties?.annotations?.map(a => ({
245
- title: this._annotPresets[a.id]?.name || this._parent?._t.pnx.semantics_features_default_title.replace("{nb}", unknownTagId++),
246
- icon: this._annotPresets[a.id]?.iconify || "fa6-solid:cube",
247
- associatedTags: a.semantics.filter(s => s.action !== "delete").length,
248
- type: "annotation",
249
- annotation: a,
250
- })) || []);
251
-
252
- this._entries = entries;
253
- }
254
-
255
- /** @private */
256
- _onListItemHover(item) {
257
- if(item?.type === "annotation") {
258
- // Save position before hover to allow reset after
259
- if(!this._prevPsvView) { this._prevPsvView = [this._parent.psv.getZoomLevel(), this._parent.psv.getPosition()]; }
260
-
261
- this._parent.psv.focusOnAnnotation(item.annotation.id, undefined, true);
262
- }
263
- else {
264
- this._parent.psv.unfocusAnnotation();
265
-
266
- // Restore previous PSV position
267
- if(this._prevPsvView) {
268
- this._parent.psv.zoom(this._prevPsvView[0]);
269
- this._parent.psv.rotate(this._prevPsvView[1]);
270
- delete this._prevPsvView;
271
- }
272
- }
273
- }
274
-
275
- /** @private */
276
- _onListItemClick(item) {
277
- this._selectedEntry = item;
278
- this._onListItemHover(item);
279
-
280
- /**
281
- * Event for item selection
282
- *
283
- * @event Panoramax.components.menus.SemanticsList#select
284
- * @type {CustomEvent}
285
- * @property {object} detail.item The selected annotation/tag group (or null if none)
286
- */
287
- this.dispatchEvent(new CustomEvent("select", {
288
- detail: { item }
289
- }));
290
- }
291
-
292
- /** @private */
293
- _onItemEditClick(e) {
294
- e.stopPropagation();
295
-
296
- /**
297
- * Event for edit button click
298
- *
299
- * @event Panoramax.components.menus.SemanticsList#edit-click
300
- * @type {CustomEvent}
301
- * @property {object} detail.item The annotation/tag group to edit
302
- */
303
- this.dispatchEvent(new CustomEvent("edit-click", {
304
- detail: { item: this._selectedEntry }
305
- }));
306
- }
307
-
308
- /** @private */
309
- _onItemDeleteClick(e) {
310
- e.stopPropagation();
311
-
312
- /**
313
- * Event for delete button click
314
- *
315
- * @event Panoramax.components.menus.SemanticsList#delete-click
316
- * @type {CustomEvent}
317
- * @property {object} detail.item The annotation/tag group to delete
318
- */
319
- this.dispatchEvent(new CustomEvent("delete-click", {
320
- detail: { item: this._selectedEntry }
321
- }));
322
- }
323
-
324
- /** @private */
325
- render() {
326
- if(!this._meta) { return nothing; }
327
-
328
- // Selected entry
329
- if(this._selectedEntry) {
330
- return html`<div class="annotation">
331
- <pnx-list-item
332
- title=${this._selectedEntry.title}
333
- @click=${() => this._onListItemClick(null)}
334
- >
335
- ${fa(faArrowLeft, {transform: {size: 24}, attributes: {slot: "icon"}})}
336
- <iconify-icon
337
- slot="icon"
338
- icon=${this._selectedEntry.icon}
339
- style="font-size: 1.5em"
340
- ></iconify-icon>
341
-
342
- <pnx-copy-button
343
- kind="superinline"
344
- text=${semanticsToString((this._selectedEntry?.annotation || this._selectedEntry?.tagGroup)?.semantics)}
345
- slot="action"
346
- ._t=${this._parent?._t}
347
- title=${this._parent?._t.pnx.semantics_copy}
348
- >
349
- ${fa(faCopy, {transform: {size: 18}})}
350
- ${fa(faCheck, {attributes: {slot: "oncopy"}, transform: {size: 18}})}
351
- </pnx-copy-button>
352
-
353
- ${this.editable && this._selectedEntry.type !== "seqtags" ? html`
354
- <pnx-button
355
- kind="superinline"
356
- title=${this._parent?._t.pnx.semantics_edit}
357
- slot="action"
358
- @click=${this._onItemEditClick}
359
- >
360
- ${fa(faPen, {transform: {size: 18}})}
361
- </pnx-button>
362
- <pnx-button
363
- kind="superinline"
364
- title=${this._parent?._t.pnx.semantics_delete_annotation}
365
- slot="action"
366
- @click=${this._onItemDeleteClick}
367
- >
368
- ${fa(faTrash, {transform: {size: 18}})}
369
- </pnx-button>`
370
- : nothing}
371
- </pnx-list-item>
372
- <pnx-semantics-table
373
- ._t=${this._parent?._t}
374
- .source=${this._selectedEntry?.type === "annotation" ? this._selectedEntry.annotation : this._selectedEntry.tagGroup}
375
- />
376
- </div>`;
377
- }
378
- // General listing
379
- else {
380
- const titles = {
381
- "seqtags": this._parent?._t.pnx.semantics_sequence_tags,
382
- "pictags": this._parent?._t.pnx.semantics_picture_tags,
383
- "annotation": this._parent?._t.pnx.semantics_annotation_tags
384
- };
385
-
386
- return html`<div class="list">
387
- ${(this._entries || []).map(e => html`
388
- <pnx-list-item
389
- title=${e.title}
390
- tooltip=${titles[e.type]}
391
- @click=${() => this._onListItemClick(e)}
392
- @mouseover=${() => this._onListItemHover(e)}
393
- @mouseout=${() => this._onListItemHover(null)}
394
- >
395
- <iconify-icon
396
- slot="icon"
397
- icon=${e.icon}
398
- style="font-size: 1.5em"
399
- ></iconify-icon>
400
- ${e.associatedTags > 1 ? html`
401
- <span slot="icon" class="pnx-badge-count">${e.associatedTags}</span>
402
- ` : nothing}
403
- ${e?.type === "annotation" ? fa(faVectorSquare, {transform: {size: 20}, styles: { "margin-right": "20px" }, attributes: {slot: "action"}}) : nothing}
404
- ${e?.type === "seqtags" ? fa(faImages, {transform: {size: 20}, styles: { "margin-right": "20px" }, attributes: {slot: "action"}}) : nothing}
405
- ${fa(faChevronRight, {transform: {size: 20}, attributes: {slot: "action"}})}
406
- </pnx-list-item>
407
- `)}
408
- </div>`;
409
- }
410
- }
411
- }
412
-
413
- customElements.define("pnx-semantics-list", SemanticsList);