@geogirafe/lib-geoportal 1.2.0-dev.2816732111 → 1.2.0-dev.2829479244

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.
@@ -52,6 +52,7 @@
52
52
  "color_option_intensity": "Intensität",
53
53
  "color_option_natural": "Natürlich",
54
54
  "color_option_uniform": "Einheitliche Farbe",
55
+ "Complete the theme": "Das Thema vervollständigen",
55
56
  "Connecting to the print server, please wait.": "Verbindung zum Druckserver wird hergestellt, bitte warten.",
56
57
  "contact-panel": "Kontaktformular",
57
58
  "Control opacity": "Deckkraft steuern",
@@ -52,6 +52,7 @@
52
52
  "color_option_intensity": "Intensity",
53
53
  "color_option_natural": "Natural",
54
54
  "color_option_uniform": "Unique color",
55
+ "Complete the theme": "Complete the theme",
55
56
  "Connecting to the print server, please wait.": "Connecting to the print server, please wait.",
56
57
  "contact-panel": "Contact us",
57
58
  "contains": "contains",
@@ -52,6 +52,7 @@
52
52
  "color_option_intensity": "Intensité",
53
53
  "color_option_natural": "Naturelle",
54
54
  "color_option_uniform": "Couleur unique",
55
+ "Complete the theme": "Compléter le thème",
55
56
  "Connecting to the print server, please wait.": "Connexion au serveur d'impression, veuillez patienter.",
56
57
  "contact-panel": "Nous contacter",
57
58
  "Control opacity": "Contrôler l'opacité",
@@ -52,6 +52,7 @@
52
52
  "color_option_intensity": "Intensità",
53
53
  "color_option_natural": "Naturale",
54
54
  "color_option_uniform": "Colore unico",
55
+ "Complete the theme": "Completare il tema",
55
56
  "Connecting to the print server, please wait.": "Connessione al server di stampa, attendere prego.",
56
57
  "contact-panel": "Contattaci",
57
58
  "Control opacity": "Controlla opacità",
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free v7.0.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path d="M553.2 88.012a23.9 23.9 0 0 0-11.8 2.488l-124.9 61.6L244 93.4c-13.7-4.7-28.7-3.8-41.9 2.3L77.8 154.3C69.4 158.2 64 166.7 64 176v352c0 8.2 4.2 15.9 11.1 20.3s15.6 4.9 23.1 1.4l87.314-41.085a26391 26391 0 0 0-36.004-35.976L112 490.3v-55.36c-8.583-8.635-17.184-17.181-25.738-25.904l-.67-.683-.639-.715C46.7 364.883 63.898 298.923 112 276.36v-85.06l96-45.201v145.748c6.27 5.447 11.381 10.818 15.57 15.345l32.43 32.43V148.301l128 43.5v34.976c17.223-17.184 34.406-34.387 51.719-51.527l.75-.742.787-.705c15.335-13.721 33.667-20.314 51.914-20.908 9.15-.299 18.257.98 26.97 3.56L528 150.6v10.34c23.753 11.01 42.627 32.744 47.318 61.912.273 1.384.48 2.758.682 4.13V112c0-8.3-4.3-16-11.3-20.4-3.5-2.2-7.476-3.4-11.5-3.588M576 250.746c-2.544 17.889-10.825 34.037-23.193 46.914l-.31.324-.321.319c-8.063 8.041-16.117 16.13-24.176 24.183v126.315L432 494v-75.496c-15.956 15.97-32.01 31.895-48 47.853V492.1l-19.346-6.454c-12.67 12.624-25.285 25.275-37.992 37.875L396.2 546.7c13.7 4.6 28.502 3.7 41.602-2.5l124.398-58.5C570.6 481.7 576 473.3 576 464z" style="display:inline;fill:gray"/><path d="M522.628 268.675c-77.356 77.15-154.308 154.724-231.92 231.606-18.749 16.296-48.36 10.581-62.826-8.485-37.197-37.402-74.815-74.403-111.745-112.059-20.803-23.25-5.73-64.519 25.111-69.02 21.933-5.55 38.705 10.957 52.233 25.562l70.124 70.125c67.235-67.086 134.06-134.602 201.552-201.417 23.25-20.803 64.519-5.73 69.021 25.111 2.986 13.83-2.138 28.778-11.55 38.577" style="color:#000;display:inline;fill:#000;stroke-width:83.6876;-inkscape-stroke:none;paint-order:stroke fill markers"/></svg>
@@ -61,10 +61,10 @@ declare class PrintComponent extends GirafeHTMLElement implements IGirafePanel {
61
61
  */
62
62
  onScaleChanged(event: KeyboardEvent): void;
63
63
  /**
64
- * On pressing enter take the value from the event target (input)
65
- * and set the selected scale in the state and update the mask.
64
+ * Take the value from the input and set the selected scale
65
+ * in the state and update the mask.
66
66
  */
67
- onCustomScaleTyped(evt: KeyboardEvent): void;
67
+ onCustomScaleTyped(evt: Event): void;
68
68
  /**
69
69
  * @Returns the current print scale.
70
70
  */
@@ -29,7 +29,7 @@ class PrintComponent extends GirafeHTMLElement {
29
29
  #panel{min-width:20rem;height:100%;overflow:auto}#content{background:var(--bkg-color);color:var(--text-color);margin:0;padding:1.5rem}#content .message{text-align:center;width:100%}#content .message *{margin:.25rem}.option{margin-top:.5rem;display:flex}.option label{width:40%}.option label:after{content:" :"}.option input,.option select,.option textarea{flex-grow:1}.option input.gg-checkbox{flex-grow:0}.rotation{flex-grow:1;gap:.5rem;display:flex}#rotationSlider{width:0}#rotationNumber{flex-grow:0;width:4rem}#content #export-pdf{float:right;margin-top:1rem}#content button{background-color:var(--bkg-color);color:var(--text-color);border:solid 1px var(--text-color);cursor:pointer;border-radius:3px;outline:0}#content button:hover{background-color:var(--bkg-color-grad2)}#content #print-list{margin-top:5rem}#print-list .print-item{border:solid 1px var(--text-color);color:var(--text-color);margin-top:.5rem;padding:.5rem;position:relative}#print-list .print-item .container{text-align:unset;border:none;width:100%;display:inline}#print-list .print-item .cancel{cursor:pointer;color:var(--text-color-grad1);background-color:#0000;border:none;position:absolute;top:.5rem;right:.5rem}.print-item .cancel i:before{content:"uf00d"}.print-item .cancel:hover{border-color:var(--text-color);color:var(--text-color)}.print-item i.status{float:left;text-align:center;width:3rem;margin-right:1rem}.print-item:has(.success):hover{background-color:var(--bkg-color-grad2);cursor:pointer}.print-item i.status:before{line-height:3rem;display:block}.print-item p{line-height:1.3rem}.print-item p.time{margin-right:.5rem;position:absolute;bottom:0;right:0}#custom-scale{color:var(--text-color);flex-flow:wrap;justify-content:flex-start;align-items:center;display:flex}#custom-scale .svg-info{margin-left:.5rem}#custom-scale>div{align-items:center;width:100%;display:flex}.gg-custom-scale{justify-content:flex-end}.warning{border-top:1px solid #333;border-bottom:1px solid #333;margin-top:1rem;margin-bottom:1rem;padding:.5rem}.warning h3{margin:0 0 1rem;font-size:1rem}.warning span{display:block}
30
30
  </style>
31
31
  <style>${this.customStyle}</style>
32
- <div id="panel"><div id="content"><div class="${this.getRenderState() === 'loading' ? '' : 'hidden'}"><div class="message"><div i18n="Connecting to the print server, please wait."></div><img alt="loading-icon" src="icons/loading.svg" class="gg-spin"></div></div><div class="${this.getRenderState() === 'error' ? '' : 'hidden'}"><div class="message" i18n="The print server is unavailable. Please, try later."></div></div><div class="${this.getRenderState() === 'setup' ? '' : 'hidden'}"><div class="option"><label for="layout" i18n="Layout" class="gg-label">Layout</label> <select id="layout" class="gg-select" onchange="${(evt) => this.onLayoutChanged(evt)}">${this.layouts.map((layout) => uHtml `<option i18n="${layout.name}" ?selected="${this.selectedLayout?.name === layout.name}" value="${layout.name}">${layout.name}</option>`)}</select></div><div class="option gg-custom-scale"><label for="scale" i18n="Scale" class="gg-label">Scale</label> <select id="scale" class="gg-select" onchange="${(evt) => this.onScaleChanged(evt)}">${this.scales.map((scale) => uHtml `<option ?selected="${this.getSelectedScale() === scale}" value="${scale}"><span>1&nbsp;:&nbsp;</span><span i18n="${scale}" i18nfn="formatNumber">${scale}</span></option>`)}<option i18n="Manual entry" class="${this.context.configManager.Config.print?.customScale === true ? '' : 'hidden'}" value="custom">Manual entry</select></div><div class="${this.showCustomScale === true ? 'option' : 'hidden'}"><div id="custom-scale"><label for="scale" i18n="Custom scale" class="gg-label">Custom scale</label><div><span>1&nbsp;:&nbsp;</span><input class="gg-input" type="number" min="10" onkeypress="${(evt) => this.onCustomScaleTyped(evt)}"> <span class="svg-info" tip="Press 'Enter' to apply"><img alt="Info" src="icons/info.svg"></span></div></div></div><div class="option"><label for="format" i18n="Format" class="gg-label">Format</label> <select id="format" class="gg-select" onchange="${(evt) => this.onFormatChanged(evt)}">${this.printFormats.map((format) => uHtml `<option ?selected="${this.getSelectedFormat() === format}" value="${format}">${format.toUpperCase()}</option>`)}</select></div>${uHtml `<div class="option"><label for="dpi" class="gg-label">DPI</label> <select id="dpi" class="gg-select" onchange="${(evt) => this.onDpiChanged(evt)}">${this.dpis.map((dpi) => uHtml `<option ?selected="${this.getSelectedDpi() === dpi}" value="${dpi}">${dpi}</option>`)}</select></div>`}<div class="option"><label for="rotationSlider" i18n="Rotation" class="gg-label">Rotation</label><div class="rotation"><input id="rotationSlider" class="gg-range" type="range" min="-180" max="180" step="1" value="${this.getRotation()}" oninput="${(evt) => this.onInputRotationChanged(evt)}"> <input id="rotationNumber" class="gg-input" type="number" min="-180" max="180" step="1" value="${this.getRotation()}" oninput="${(evt) => this.onInputRotationChanged(evt)}"></div></div>${this.attributeNames.map((attrName) => { if (this.getAttributeInputType(attrName) === 'number') {
32
+ <div id="panel"><div id="content"><div class="${this.getRenderState() === 'loading' ? '' : 'hidden'}"><div class="message"><div i18n="Connecting to the print server, please wait."></div><img alt="loading-icon" src="icons/loading.svg" class="gg-spin"></div></div><div class="${this.getRenderState() === 'error' ? '' : 'hidden'}"><div class="message" i18n="The print server is unavailable. Please, try later."></div></div><div class="${this.getRenderState() === 'setup' ? '' : 'hidden'}"><div class="option"><label for="layout" i18n="Layout" class="gg-label">Layout</label> <select id="layout" class="gg-select" onchange="${(evt) => this.onLayoutChanged(evt)}">${this.layouts.map((layout) => uHtml `<option i18n="${layout.name}" ?selected="${this.selectedLayout?.name === layout.name}" value="${layout.name}">${layout.name}</option>`)}</select></div><div class="option gg-custom-scale"><label for="scale" i18n="Scale" class="gg-label">Scale</label> <select id="scale" class="gg-select" onchange="${(evt) => this.onScaleChanged(evt)}">${this.scales.map((scale) => uHtml `<option ?selected="${this.getSelectedScale() === scale}" value="${scale}"><span>1&nbsp;:&nbsp;</span><span i18n="${scale}" i18nfn="formatNumber">${scale}</span></option>`)}<option i18n="Manual entry" class="${this.context.configManager.Config.print?.customScale === true ? '' : 'hidden'}" value="custom">Manual entry</select></div><div class="${this.showCustomScale === true ? 'option' : 'hidden'}"><div id="custom-scale"><label for="scale" i18n="Custom scale" class="gg-label">Custom scale</label><div><span>1&nbsp;:&nbsp;</span><input class="gg-input" type="number" min="10" oninput="${(evt) => this.onCustomScaleTyped(evt)}"></div></div></div><div class="option"><label for="format" i18n="Format" class="gg-label">Format</label> <select id="format" class="gg-select" onchange="${(evt) => this.onFormatChanged(evt)}">${this.printFormats.map((format) => uHtml `<option ?selected="${this.getSelectedFormat() === format}" value="${format}">${format.toUpperCase()}</option>`)}</select></div>${uHtml `<div class="option"><label for="dpi" class="gg-label">DPI</label> <select id="dpi" class="gg-select" onchange="${(evt) => this.onDpiChanged(evt)}">${this.dpis.map((dpi) => uHtml `<option ?selected="${this.getSelectedDpi() === dpi}" value="${dpi}">${dpi}</option>`)}</select></div>`}<div class="option"><label for="rotationSlider" i18n="Rotation" class="gg-label">Rotation</label><div class="rotation"><input id="rotationSlider" class="gg-range" type="range" min="-180" max="180" step="1" value="${this.getRotation()}" oninput="${(evt) => this.onInputRotationChanged(evt)}"> <input id="rotationNumber" class="gg-input" type="number" min="-180" max="180" step="1" value="${this.getRotation()}" oninput="${(evt) => this.onInputRotationChanged(evt)}"></div></div>${this.attributeNames.map((attrName) => { if (this.getAttributeInputType(attrName) === 'number') {
33
33
  return uHtml `<div class="option"><label for="${attrName}" i18n="${this.attrNameForI18n(attrName)}" class="gg-label">${attrName}</label> <input id="${attrName}" type="number" class="gg-input" value="${this.getCapabilitiesAttribute(attrName)?.default}"></div>`;
34
34
  } if (this.getAttributeInputType(attrName) === 'checkbox') {
35
35
  return uHtml `<div class="option"><label for="${attrName}" i18n="${this.attrNameForI18n(attrName)}" class="gg-label">${attrName}</label> <input id="${attrName}" type="checkbox" class="gg-checkbox" ?checked="${this.getCapabilitiesAttribute(attrName)?.default}"></div>`;
@@ -129,12 +129,12 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
129
129
  this.printMaskManager?.zoomToScale(this.state.print.scale);
130
130
  }
131
131
  /**
132
- * On pressing enter take the value from the event target (input)
133
- * and set the selected scale in the state and update the mask.
132
+ * Take the value from the input and set the selected scale
133
+ * in the state and update the mask.
134
134
  */
135
135
  onCustomScaleTyped(evt) {
136
136
  const value = parseInt(evt.target?.value);
137
- if (evt.key !== 'Enter' || !value) {
137
+ if (!value) {
138
138
  return;
139
139
  }
140
140
  this.state.print.scale = value;
@@ -44,10 +44,12 @@ declare class SearchComponent extends GirafeHTMLElement {
44
44
  * @param e
45
45
  */
46
46
  doSearchDebounced(e: Event): Promise<void>;
47
+ addFullThemeIfNotPresent(feature: Feature): void;
47
48
  getGroupIcon(searchGroup: string): any;
48
49
  getProviderClass(provider: string): "hidden" | "provider entry";
49
50
  getGroupClass(group: string): "hidden" | "group entry";
50
51
  getLabel(result: Feature): unknown;
52
+ isFeatureActionThemeRelated(feature: Feature): boolean;
51
53
  onMouseOver(result: Feature): void;
52
54
  onMouseLeave(): void;
53
55
  private focusResultFromIndex;
@@ -56,6 +58,9 @@ declare class SearchComponent extends GirafeHTMLElement {
56
58
  private addFeatureToPreview;
57
59
  clearPreview(): void;
58
60
  onSelect(feature: Feature): void;
61
+ private closeDropdown;
62
+ private updateSearchboxWithResult;
63
+ private getFeatureFirstAction;
59
64
  private addResultToTreeView;
60
65
  private zoomTo;
61
66
  onMouseMove(): void;
@@ -28,10 +28,10 @@ class SearchComponent extends GirafeHTMLElement {
28
28
  return uHtml `<style>
29
29
  .hidden{display:none!important}.gg-rotate90{transform:rotate(90deg)}.gg-rotate180{transform:rotate(180deg)}.gg-rotate270{transform:rotate(270deg)}img{filter:var(--svg-filter)}img.legend-image{filter:var(--svg-map-filter);background:var(--svg-legend-bkg)}div{scrollbar-width:thin}a,a:visited{color:var(--link-color)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spin-wait{0%{transform:rotate(0)}7%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.gg-spin{animation-name:spin;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}.gg-spin-wait{animation-name:spin-wait;animation-duration:10s;animation-timing-function:linear;animation-iteration-count:infinite}::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:#999}button,input,select,textarea{font:inherit}.gg-button,.gg-select,.gg-input,.gg-textarea{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);box-sizing:border-box;cursor:pointer;border-radius:3px;outline:0;margin:0;padding:0 0 0 .5rem;display:inline-block}.gg-label{background-color:var(--bkg-color);color:var(--text-color);border:none;align-items:center;margin:0;padding:0;display:flex}.gg-button,.gg-select,.gg-input,.gg-label{min-height:calc(var(--app-standard-height) / 1.5)}.gg-textarea{max-height:initial;resize:vertical;height:6rem;padding:.5rem;line-height:1.3rem}.gg-input{cursor:text}.gg-checkbox{accent-color:var(--text-color);width:1.2rem}.gg-range{accent-color:var(--text-color)}.gg-button{padding:0 .5rem}.gg-button.active{border:solid 1px var(--text-color-grad1);background-color:var(--bkg-color-grad2)}.gg-button:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed;border:none}.gg-button:disabled img{filter:opacity(.6)}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed}.gg-button>img{vertical-align:middle}.gg-icon-button{color:var(--text-color);cursor:pointer;background-color:#0000;border:none;flex-direction:column;justify-content:center;align-items:center;padding:0;display:flex}.gg-icon{justify-content:center;align-items:center;display:flex}.gg-big,.gg-big-withtext{min-width:var(--app-standard-height);min-height:var(--app-standard-height);max-height:var(--app-standard-height)}.gg-big img,.gg-big-withtext img{width:calc(var(--app-standard-height) - 1.5rem);margin:0}.gg-big-withtext span{font-variant:small-caps;padding:0 1rem;font-size:.9rem}.gg-medium,.gg-medium-withtext{min-width:calc(var(--app-standard-height) / 1.2);min-height:calc(var(--app-standard-height) / 1.2);max-height:calc(var(--app-standard-height) / 1.2);flex-direction:row}.gg-medium img{width:calc(var(--app-standard-height) / 2.4);margin:0}.gg-medium-withtext img{width:calc(var(--app-standard-height) / 2.4);margin-left:.5rem}.gg-medium-withtext span{padding:0 1rem 0 .5rem;font-size:.9rem}.gg-small,.gg-small-withtext{min-width:calc(var(--app-standard-height) / 2);min-height:calc(var(--app-standard-height) / 2);max-height:calc(var(--app-standard-height) / 2);flex-direction:row}.gg-small img{width:calc(var(--app-standard-height) / 3);margin:0}.gg-small-withtext img{width:calc(var(--app-standard-height) / 3);margin-left:.5rem}.gg-small-withtext span{padding:0 .5rem 0 .3rem;font-size:.9rem}.gg-button:hover:not(:disabled),.gg-select:hover:not(:disabled),.gg-input:hover:not(:disabled),.gg-textarea:hover:not(:disabled),.gg-icon-button:hover:not(:disabled){background-color:var(--bkg-color-grad1)}.gg-opacity{opacity:.5}.gg-opacity:hover{opacity:1;background-color:#0000}.gg-tabs{cursor:pointer;grid-auto-flow:column;padding-bottom:1rem;font-size:1rem;display:grid}.gg-tab{border:none;border-bottom:var(--app-standard-border);cursor:pointer;color:var(--text-color);background:0 0;padding:.5rem}.gg-tab.active{border-bottom:solid 1px var(--text-color)}.girafe-button-big,.girafe-button-large,.girafe-button-small,.girafe-button-tiny{color:var(--text-color);background-color:#0000;border:none;flex-direction:column;display:flex}.girafe-button-big:hover,.girafe-button-large:hover,.girafe-button-small:hover,.girafe-button-tiny:hover{background-color:var(--bkg-color-grad1);cursor:pointer}.girafe-button-big.dark,.girafe-button-large.dark,.girafe-button-small.dark,.girafe-button-tiny.dark{background-color:var(--bkg-color);filter:invert()}.girafe-button-big{width:var(--app-standard-height);height:var(--app-standard-height);align-items:center;padding:1rem}.girafe-button-big img{overflow:hidden}.girafe-button-large{flex-direction:row}.girafe-button-large img{height:2rem;margin:.3rem}.girafe-button-large span{height:2rem;margin:.3rem;line-height:2rem}.girafe-button-small{min-width:calc(var(--app-standard-height) / 2);height:calc(var(--app-standard-height) / 2);align-items:center;padding:.5rem}.girafe-button-small img{overflow:hidden}.girafe-button-small span{text-align:left;text-overflow:ellipsis;width:100%;overflow:hidden}.girafe-button-tiny{align-items:center;width:1rem;height:1rem;padding:0}.girafe-button-tiny img{overflow:hidden}.girafe-onboarding-theme{background-color:var(--bkg-color)!important;color:var(--text-color)!important}.girafe-onboarding-theme button{background-color:var(--bkg-color)!important;color:var(--text-color)!important;text-shadow:none!important}.girafe-onboarding-theme button.driver-popover-close-btn{z-index:10000}.tippy-box{background-color:var(--bkg-color)!important;border:var(--app-standard-border)!important}.tippy-content{color:var(--text-color)!important}.tippy-arrow{color:var(--bkg-color)!important}.tippy-box[data-theme~=error]{background-color:var(--error-color);white-space:pre-line}div.tippy-box[data-theme=image-popup]{border:var(--app-standard-border)!important;& div.tippy-content{background-color:var(--bkg-color)!important;& img{max-width:20rem;max-height:20rem}}& div.tippy-arrow{color:var(--bkg-color)!important}}
30
30
  </style><style>
31
- .hidden{display:none}#searchbox{background-color:var(--bkg-color);border:1px solid #cfd6dd;border-radius:4px;flex-direction:row;width:100%;height:2.25rem;display:flex;& slot[name=search-tooltip]{display:none}}#search{-webkit-appearance:none;width:auto;height:2.25rem;color:var(--text-color);background:0 0;border:0;outline:none;-webkit-flex:auto;-ms-flex:auto;flex:auto;margin:0 0 0 .5rem;padding-left:.8rem;font-size:1rem}.search-icon,.close-icon,.select-color-icon{width:14px;color:var(--text-color);padding:0 1rem}.close-button,.select-color-button{cursor:pointer;background-color:#0000;border:none;padding:0}#results{z-index:1000;background-color:var(--bkg-color);scrollbar-width:thin;width:36rem;max-height:31rem;margin-top:.1rem;margin-bottom:.5rem;position:absolute;top:3.5rem;overflow-x:hidden}.entry{width:34rem;color:var(--text-color);padding:.3rem 1rem;line-height:1.3rem;display:inline-block}.provider,.group{font-size:1.2rem;font-weight:600;line-height:3rem}.provider{background-color:color-mix(in srgb, var(--bkg-color-grad1) 75%, transparent);margin-top:1rem;font-size:1.5rem}#results>.provider:first-child{margin-top:0}.result{cursor:pointer;text-align:left;background:0 0;border:none;border-top:1px solid #ccc;width:100%}.entry img{width:.8em;margin-right:1rem}.entry span{text-transform:uppercase}.result.selected{color:var(--text-color);background-color:var(--bkg-color-grad2)!important}#no-result{background:var(--warning-color);opacity:.75;visibility:hidden;pointer-events:none;border-radius:6px;width:fit-content;margin:auto;padding:.5rem 0;font-size:1rem;position:absolute;top:3.5rem;left:0;right:0}#no-result>span{padding:1.3rem}@keyframes fadeOut{0%{opacity:1}80%{opacity:1}to{opacity:0}}.shown-then-fadeout{animation:2s ease-in-out forwards fadeOut;visibility:visible!important}
31
+ .hidden{display:none}#searchbox{background-color:var(--bkg-color);border:1px solid #cfd6dd;border-radius:4px;flex-direction:row;width:100%;height:2.25rem;display:flex;& slot[name=search-tooltip]{display:none}}#search{-webkit-appearance:none;width:auto;height:2.25rem;color:var(--text-color);background:0 0;border:0;outline:none;-webkit-flex:auto;-ms-flex:auto;flex:auto;margin:0 0 0 .5rem;padding-left:.8rem;font-size:1rem}.search-icon,.close-icon,.theme-icon,.select-color-icon{width:14px;color:var(--text-color);padding:0 1rem}.close-button,.select-color-button{cursor:pointer;background-color:#0000;border:none;padding:0}#results{z-index:1000;background-color:var(--bkg-color);scrollbar-width:thin;width:36rem;max-height:31rem;margin-top:.1rem;margin-bottom:.5rem;position:absolute;top:3.5rem;overflow-x:hidden}.entry{width:34rem;color:var(--text-color);padding:.3rem 1rem;line-height:1.3rem;display:inline-block}.entry img{width:.8em;margin-right:1rem}.entry span{text-transform:uppercase}.provider,.group{font-size:1.2rem;font-weight:600;line-height:3rem}.provider{background-color:color-mix(in srgb, var(--bkg-color-grad1) 75%, transparent);margin-top:1rem;font-size:1.5rem}#results>.provider:first-child{margin-top:0}.result{background:0 0;border-top:1px solid #ccc;align-items:stretch;width:100%;padding:0;display:flex}.result button{cursor:pointer;text-align:left;width:100%;color:var(--text-color);background:0 0;border:none;padding:.3rem 1rem}.result button:hover{color:var(--text-color);background-color:var(--bkg-color-grad2)!important}.result .tools{width:1.3rem;margin:0}.result .tools button{height:-webkit-fill-available;margin:0;padding:0 .3rem}.result .tools button img{margin:0;padding:0}#no-result{background:var(--warning-color);opacity:.75;visibility:hidden;pointer-events:none;border-radius:6px;width:fit-content;margin:auto;padding:.5rem 0;font-size:1rem;position:absolute;top:3.5rem;left:0;right:0}#no-result>span{padding:1.3rem}@keyframes fadeOut{0%{opacity:1}80%{opacity:1}to{opacity:0}}.shown-then-fadeout{animation:2s ease-in-out forwards fadeOut;visibility:visible!important}
32
32
  </style>
33
33
  <style>${this.customStyle}</style>
34
- <link rel="stylesheet" href="lib/vanilla-picker/vanilla-picker.csp.css"><link rel="stylesheet" href="styles/girafecolorpicker.css"><div id="searchbox"><slot name="search-tooltip"></slot><input id="search" class="gg-input" length="20" maxlength="1000" autocomplete="off" autocorrect="off" i18n="Search..." placeholder="Search..." aria-label="Search" oninput="${(e) => this.doSearchDebounced(e)}" onfocusin="${() => this.onFocusIn()}" onfocusout="${() => this.onFocusOut()}" onkeydown="${(e) => this.onKeyDown(e)}"> <img class="${this.allResults.length > 0 ? 'hidden' : 'search-icon'}" alt="search-icon" src="${this.searchIcon}"> <button class="${this.allResults.length > 0 ? 'close-button' : 'hidden'}" onclick="${() => this.clearSearch(true)}"><img class="close-icon" alt="close-icon" src="icons/close.svg"></button> <button tip="change_search_result_color" id="colorPickerBtn" class="${this.paintSearchResults ? 'select-color-button' : 'hidden'}"><img class="select-color-icon" alt="select-color-icon" src="${this.paintbrushIcon}"></button><div id="results" class="${!this.allResults.length || this.forceHide ? 'hidden' : ''}" onmousemove="${() => this.onMouseMove()}">${(Object.keys(this.groupedResults) ?? []).map(provider => uHtml `<div class="${this.getProviderClass(provider)}"><img alt="provider icon" src="${this.searchIcon}"> <span i18n="${provider}">${provider}</span></div>${(Object.keys(this.groupedResults[provider]) ?? []).map(group => uHtml `<div class="${this.getGroupClass(group)}"><img alt="result icon" src="${this.getGroupIcon(group)}"> <span i18n="${group}">${group}</span></div>${(this.groupedResults[provider][group] ?? []).map(result => uHtml ` <button class="${result.get('selected') ? 'result entry selected' : 'result entry'}" onmousedown="${() => this.onMouseDown()}" onclick="${() => this.onSelect(result)}" onmouseover="${() => this.onMouseOver(result)}" onmouseleave="${() => this.onMouseLeave()}">${this.getLabel(result)}</button> `)} `)} `)}</div><div id="no-result" class="${this.showNoResultWarning ? 'shown-then-fadeout' : ''}"><span i18n="${'No match found'}"></span></div></div>
34
+ <link rel="stylesheet" href="lib/vanilla-picker/vanilla-picker.csp.css"><link rel="stylesheet" href="styles/girafecolorpicker.css"><div id="searchbox"><slot name="search-tooltip"></slot><input id="search" class="gg-input" length="20" maxlength="1000" autocomplete="off" autocorrect="off" i18n="Search..." placeholder="Search..." aria-label="Search" oninput="${(e) => this.doSearchDebounced(e)}" onfocusin="${() => this.onFocusIn()}" onfocusout="${() => this.onFocusOut()}" onkeydown="${(e) => this.onKeyDown(e)}"> <img class="${this.allResults.length > 0 ? 'hidden' : 'search-icon'}" alt="search-icon" src="${this.searchIcon}"> <button class="${this.allResults.length > 0 ? 'close-button' : 'hidden'}" onclick="${() => this.clearSearch(true)}"><img class="close-icon" alt="close-icon" src="icons/close.svg"></button> <button tip="change_search_result_color" id="colorPickerBtn" class="${this.paintSearchResults ? 'select-color-button' : 'hidden'}"><img class="select-color-icon" alt="select-color-icon" src="${this.paintbrushIcon}"></button><div id="results" class="${!this.allResults.length || this.forceHide ? 'hidden' : ''}" onmousemove="${() => this.onMouseMove()}">${(Object.keys(this.groupedResults) ?? []).map(provider => uHtml `<div class="${this.getProviderClass(provider)}"><img alt="provider icon" src="${this.searchIcon}"> <span i18n="${provider}">${provider}</span></div>${(Object.keys(this.groupedResults[provider]) ?? []).map(group => uHtml `<div class="${this.getGroupClass(group)}"><img alt="result icon" src="${this.getGroupIcon(group)}"> <span i18n="${group}">${group}</span></div>${(this.groupedResults[provider][group] ?? []).map(result => uHtml `<div class="${result.get('selected') ? 'result entry selected' : 'result entry'}"><button onmouseover="${() => this.onMouseOver(result)}" onmouseleave="${() => this.onMouseLeave()}" onmousedown="${() => this.onMouseDown()}" onclick="${() => this.onSelect(result)}">${this.getLabel(result)}</button><div class="${this.isFeatureActionThemeRelated(result) ? 'tools' : 'hidden'}"><button class="addFullTree" onclick="${() => this.addFullThemeIfNotPresent(result)}"><img class="theme-icon" alt="theme-icon" src="icons/theme-checked.svg" tip="Complete the theme"></button></div></div>`)} `)} `)}</div><div id="no-result" class="${this.showNoResultWarning ? 'shown-then-fadeout' : ''}"><span i18n="${'No match found'}"></span></div></div>
35
35
  ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
36
36
  };
37
37
  searchIcon = SearchIcon;
@@ -205,6 +205,33 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
205
205
  this.doSearch(syntheticEvent);
206
206
  }, 300);
207
207
  }
208
+ addFullThemeIfNotPresent(feature) {
209
+ this.clearPreview();
210
+ const action = this.getFeatureFirstAction(feature);
211
+ if (!action) {
212
+ return;
213
+ }
214
+ const layer = this.context.themesHelper.findLayerByName(action.data);
215
+ if (!layer) {
216
+ return;
217
+ }
218
+ const theme = this.context.themesHelper.getOriginalTheme(layer);
219
+ if (!theme) {
220
+ return;
221
+ }
222
+ const added = this.context.themesHelper.addThemeIfNotPresent(theme);
223
+ if (!added) {
224
+ // If it's not added because it's already in the tree, complete the theme.
225
+ this.context.themesHelper.completeTheme(theme);
226
+ }
227
+ const insertedLayer = this.context.themesHelper.findBaseLayerRecursiveById(this.state.layers.layersList, layer.id);
228
+ if (insertedLayer) {
229
+ insertedLayer.activeState = 'on';
230
+ }
231
+ this.closeDropdown(feature);
232
+ this.onFocusOut();
233
+ this.updateSearchboxWithResult(feature);
234
+ }
208
235
  getGroupIcon(searchGroup) {
209
236
  switch (searchGroup) {
210
237
  case 'Group':
@@ -230,6 +257,16 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
230
257
  getLabel(result) {
231
258
  return result.get('label_html') ? this.htmlUnsafe(result.get('label_html')) : result.get('label');
232
259
  }
260
+ isFeatureActionThemeRelated(feature) {
261
+ const action = this.getFeatureFirstAction(feature);
262
+ const kind = action?.action;
263
+ const hasThemeRelatedAction = kind?.startsWith('add_layer') || kind?.startsWith('add_group') || kind?.startsWith('add_theme') || false;
264
+ if (!hasThemeRelatedAction || !action) {
265
+ return false;
266
+ }
267
+ const layer = this.context.themesHelper.findLayerByName(action.data);
268
+ return !!layer;
269
+ }
233
270
  onMouseOver(result) {
234
271
  this.focusResult(result);
235
272
  }
@@ -249,7 +286,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
249
286
  if (this.focusedResult) {
250
287
  this.focusedResult.set('selected', false);
251
288
  }
252
- // Set new selected object, and activate preview
289
+ // Set the newly selected object and activate a preview.
253
290
  this.focusedResultIndex = this.allResults.indexOf(result);
254
291
  this.focusedResult = this.allResults[this.focusedResultIndex];
255
292
  this.focusedResult.set('selected', true);
@@ -265,7 +302,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
265
302
  this.addFeatureToPreview(result);
266
303
  this.updatePreviewLayerStyle();
267
304
  }
268
- const firstAction = result.get('actions')?.[0];
305
+ const firstAction = this.getFeatureFirstAction(result);
269
306
  if (firstAction?.action.startsWith('add_layer') && this.context.configManager.Config.search?.layerPreview) {
270
307
  const layer = this.context.themesHelper.findLayerByName(firstAction.data);
271
308
  if (layer) {
@@ -294,11 +331,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
294
331
  }
295
332
  }
296
333
  onSelect(feature) {
297
- this.selectedResult = feature;
298
- this.ignoreBlur = false;
299
- this.forceHide = true;
300
- this.previewLayers = undefined;
301
- super.render();
334
+ this.closeDropdown(feature);
302
335
  // If the result has a specific zoom-to extent defined or contains a geometry, zoom to it
303
336
  const zoomBbox = this.context.searchManager.getZoomToBbox(feature);
304
337
  if (zoomBbox) {
@@ -308,33 +341,46 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
308
341
  this.addResultToTreeView(feature);
309
342
  }
310
343
  this.onFocusOut();
311
- // Update searchbox with result
344
+ this.updateSearchboxWithResult(feature);
345
+ }
346
+ closeDropdown(feature) {
347
+ this.selectedResult = feature;
348
+ this.ignoreBlur = false;
349
+ this.forceHide = true;
350
+ this.previewLayers = undefined;
351
+ super.render();
352
+ }
353
+ updateSearchboxWithResult(feature) {
312
354
  if (this.searchInput && feature.get('label')) {
313
355
  this.searchInput.value = feature.get('label');
314
356
  }
315
357
  }
316
- addResultToTreeView(feature) {
317
- let clonedTheme;
358
+ getFeatureFirstAction(feature) {
318
359
  const actions = feature.get('actions');
319
- if (!actions || actions.length === 0) {
360
+ return actions?.length ? actions[0] : undefined;
361
+ }
362
+ addResultToTreeView(feature) {
363
+ const action = this.getFeatureFirstAction(feature);
364
+ if (!action) {
320
365
  // Nothing to add
321
366
  return;
322
367
  }
368
+ let clonedTheme;
323
369
  let activate = false;
324
- if (actions[0].action.startsWith('add_theme')) {
325
- const theme = this.context.themesHelper.findThemeByName(actions[0].data);
370
+ if (action.action.startsWith('add_theme')) {
371
+ const theme = this.context.themesHelper.findThemeByName(action.data);
326
372
  if (theme) {
327
373
  clonedTheme = theme.clone();
328
374
  }
329
375
  }
330
- else if (actions[0].action.startsWith('add_group')) {
331
- const group = this.context.themesHelper.findGroupByName(actions[0].data);
376
+ else if (action.action.startsWith('add_group')) {
377
+ const group = this.context.themesHelper.findGroupByName(action.data);
332
378
  if (group) {
333
379
  clonedTheme = this.context.themesHelper.getMinimalClonedThemeForLayer(group);
334
380
  }
335
381
  }
336
- else if (actions[0].action.startsWith('add_layer')) {
337
- const layer = this.context.themesHelper.findLayerByName(actions[0].data);
382
+ else if (action.action.startsWith('add_layer')) {
383
+ const layer = this.context.themesHelper.findLayerByName(action.data);
338
384
  if (layer) {
339
385
  clonedTheme = this.context.themesHelper.getMinimalClonedThemeForLayer(layer);
340
386
  activate = true;
@@ -516,6 +562,9 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
516
562
  return `data:image/svg+xml;utf8,${encodeURIComponent(pin)}`;
517
563
  }
518
564
  }
565
+ __decorate([
566
+ UsedInTemplateOnly()
567
+ ], SearchComponent.prototype, "addFullThemeIfNotPresent", null);
519
568
  __decorate([
520
569
  UsedInTemplateOnly()
521
570
  ], SearchComponent.prototype, "getGroupIcon", null);
@@ -528,4 +577,7 @@ __decorate([
528
577
  __decorate([
529
578
  UsedInTemplateOnly()
530
579
  ], SearchComponent.prototype, "getLabel", null);
580
+ __decorate([
581
+ UsedInTemplateOnly()
582
+ ], SearchComponent.prototype, "isFeatureActionThemeRelated", null);
531
583
  export default SearchComponent;
@@ -35,6 +35,7 @@
35
35
 
36
36
  .search-icon,
37
37
  .close-icon,
38
+ .theme-icon,
38
39
  .select-color-icon {
39
40
  width: 14px;
40
41
  color: var(--text-color);
@@ -70,6 +71,15 @@
70
71
  color: var(--text-color);
71
72
  }
72
73
 
74
+ .entry img {
75
+ margin-right: 1rem;
76
+ width: 0.8em;
77
+ }
78
+
79
+ .entry span {
80
+ text-transform: uppercase;
81
+ }
82
+
73
83
  .provider,
74
84
  .group {
75
85
  font-weight: 600;
@@ -88,26 +98,43 @@
88
98
  }
89
99
 
90
100
  .result {
101
+ border-top: solid 1px #ccc;
102
+ background: transparent;
103
+ width: 100%;
104
+ display: flex;
105
+ align-items: stretch;
106
+ padding: 0;
107
+ }
108
+
109
+ .result button {
110
+ padding: 0.3rem 1rem;
91
111
  cursor: pointer;
92
112
  border: none;
93
- border-top: solid 1px #ccc;
94
113
  background: transparent;
95
114
  text-align: left;
96
115
  width: 100%;
116
+ color: var(--text-color);
97
117
  }
98
118
 
99
- .entry img {
100
- margin-right: 1rem;
101
- width: 0.8em;
119
+ .result button:hover {
120
+ background-color: var(--bkg-color-grad2) !important;
121
+ color: var(--text-color);
102
122
  }
103
123
 
104
- .entry span {
105
- text-transform: uppercase;
124
+ .result .tools {
125
+ width: 1.3rem;
126
+ margin: 0;
106
127
  }
107
128
 
108
- .result.selected {
109
- background-color: var(--bkg-color-grad2) !important;
110
- color: var(--text-color);
129
+ .result .tools button {
130
+ margin: 0;
131
+ padding: 0 0.3rem;
132
+ height: -webkit-fill-available;
133
+ }
134
+
135
+ .result .tools button img {
136
+ margin: 0;
137
+ padding: 0;
111
138
  }
112
139
 
113
140
  #no-result {
@@ -10,6 +10,10 @@ header {
10
10
  display: none !important;
11
11
  }
12
12
 
13
+ .tool.visible {
14
+ display: flex !important;
15
+ }
16
+
13
17
  header:hover .tool {
14
18
  display: flex !important;
15
19
  }
@@ -8,7 +8,7 @@ class TreeViewGroupComponent extends TreeViewGroupElement {
8
8
  styleUrls = null;
9
9
  template = () => {
10
10
  return uHtml `<style>
11
- header{border-top:1px solid #ddd;align-items:center;height:2rem;display:flex}.tool{display:none!important}header:hover .tool{display:flex!important}.tool.active{order:100;opacity:1!important;display:flex!important}.label-container{flex-grow:1;align-items:center;display:flex;overflow:auto}.label-out-resolution span{font-style:italic}.resolution-tool{margin-left:.5rem}.legend{display:flex}.legend img{background:var(--svg-legend-bkg);height:fit-content;filter:var(--svg-map-filter)}.legend-icon{background:var(--svg-legend-bkg);max-height:1.5rem;filter:var(--svg-map-filter);margin-left:.5rem}.children{flex-basis:100%;order:10;padding-left:1.3rem}.dragAfter{border-bottom:1rem dashed #ccc}.dragBefore{border-top:1rem dashed #ccc}.gg-checkbox{width:1.2rem}.gg-caret,.gg-spacer{text-align:center;width:1.5rem;padding:0}.gg-caret{transform:scale(1.3)}.gg-caret.gg-rotate90{transform:scale(1.3)rotate(90deg)}.gg-small.gg-lock{min-width:1rem}.gg-opacity.gg-lock:hover{opacity:.5!important}.gg-tree-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;padding-left:0;overflow:hidden}.gg-grab:hover{cursor:grab}.gg-selected{opacity:1}.legend-file{color:var(--text-color);font-style:italic;display:flex}@keyframes headerFade{0%{background-color:var(--highlight-color)}to{background-color:#0000}}.highlight{animation:3s ease-in-out forwards headerFade}
11
+ header{border-top:1px solid #ddd;align-items:center;height:2rem;display:flex}.tool{display:none!important}.tool.visible,header:hover .tool{display:flex!important}.tool.active{order:100;opacity:1!important;display:flex!important}.label-container{flex-grow:1;align-items:center;display:flex;overflow:auto}.label-out-resolution span{font-style:italic}.resolution-tool{margin-left:.5rem}.legend{display:flex}.legend img{background:var(--svg-legend-bkg);height:fit-content;filter:var(--svg-map-filter)}.legend-icon{background:var(--svg-legend-bkg);max-height:1.5rem;filter:var(--svg-map-filter);margin-left:.5rem}.children{flex-basis:100%;order:10;padding-left:1.3rem}.dragAfter{border-bottom:1rem dashed #ccc}.dragBefore{border-top:1rem dashed #ccc}.gg-checkbox{width:1.2rem}.gg-caret,.gg-spacer{text-align:center;width:1.5rem;padding:0}.gg-caret{transform:scale(1.3)}.gg-caret.gg-rotate90{transform:scale(1.3)rotate(90deg)}.gg-small.gg-lock{min-width:1rem}.gg-opacity.gg-lock:hover{opacity:.5!important}.gg-tree-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;padding-left:0;overflow:hidden}.gg-grab:hover{cursor:grab}.gg-selected{opacity:1}.legend-file{color:var(--text-color);font-style:italic;display:flex}@keyframes headerFade{0%{background-color:var(--highlight-color)}to{background-color:#0000}}.highlight{animation:3s ease-in-out forwards headerFade}
12
12
  </style><style>
13
13
  .hidden{display:none!important}.gg-rotate90{transform:rotate(90deg)}.gg-rotate180{transform:rotate(180deg)}.gg-rotate270{transform:rotate(270deg)}img{filter:var(--svg-filter)}img.legend-image{filter:var(--svg-map-filter);background:var(--svg-legend-bkg)}div{scrollbar-width:thin}a,a:visited{color:var(--link-color)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spin-wait{0%{transform:rotate(0)}7%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.gg-spin{animation-name:spin;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}.gg-spin-wait{animation-name:spin-wait;animation-duration:10s;animation-timing-function:linear;animation-iteration-count:infinite}::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:#999}button,input,select,textarea{font:inherit}.gg-button,.gg-select,.gg-input,.gg-textarea{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);box-sizing:border-box;cursor:pointer;border-radius:3px;outline:0;margin:0;padding:0 0 0 .5rem;display:inline-block}.gg-label{background-color:var(--bkg-color);color:var(--text-color);border:none;align-items:center;margin:0;padding:0;display:flex}.gg-button,.gg-select,.gg-input,.gg-label{min-height:calc(var(--app-standard-height) / 1.5)}.gg-textarea{max-height:initial;resize:vertical;height:6rem;padding:.5rem;line-height:1.3rem}.gg-input{cursor:text}.gg-checkbox{accent-color:var(--text-color);width:1.2rem}.gg-range{accent-color:var(--text-color)}.gg-button{padding:0 .5rem}.gg-button.active{border:solid 1px var(--text-color-grad1);background-color:var(--bkg-color-grad2)}.gg-button:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed;border:none}.gg-button:disabled img{filter:opacity(.6)}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed}.gg-button>img{vertical-align:middle}.gg-icon-button{color:var(--text-color);cursor:pointer;background-color:#0000;border:none;flex-direction:column;justify-content:center;align-items:center;padding:0;display:flex}.gg-icon{justify-content:center;align-items:center;display:flex}.gg-big,.gg-big-withtext{min-width:var(--app-standard-height);min-height:var(--app-standard-height);max-height:var(--app-standard-height)}.gg-big img,.gg-big-withtext img{width:calc(var(--app-standard-height) - 1.5rem);margin:0}.gg-big-withtext span{font-variant:small-caps;padding:0 1rem;font-size:.9rem}.gg-medium,.gg-medium-withtext{min-width:calc(var(--app-standard-height) / 1.2);min-height:calc(var(--app-standard-height) / 1.2);max-height:calc(var(--app-standard-height) / 1.2);flex-direction:row}.gg-medium img{width:calc(var(--app-standard-height) / 2.4);margin:0}.gg-medium-withtext img{width:calc(var(--app-standard-height) / 2.4);margin-left:.5rem}.gg-medium-withtext span{padding:0 1rem 0 .5rem;font-size:.9rem}.gg-small,.gg-small-withtext{min-width:calc(var(--app-standard-height) / 2);min-height:calc(var(--app-standard-height) / 2);max-height:calc(var(--app-standard-height) / 2);flex-direction:row}.gg-small img{width:calc(var(--app-standard-height) / 3);margin:0}.gg-small-withtext img{width:calc(var(--app-standard-height) / 3);margin-left:.5rem}.gg-small-withtext span{padding:0 .5rem 0 .3rem;font-size:.9rem}.gg-button:hover:not(:disabled),.gg-select:hover:not(:disabled),.gg-input:hover:not(:disabled),.gg-textarea:hover:not(:disabled),.gg-icon-button:hover:not(:disabled){background-color:var(--bkg-color-grad1)}.gg-opacity{opacity:.5}.gg-opacity:hover{opacity:1;background-color:#0000}.gg-tabs{cursor:pointer;grid-auto-flow:column;padding-bottom:1rem;font-size:1rem;display:grid}.gg-tab{border:none;border-bottom:var(--app-standard-border);cursor:pointer;color:var(--text-color);background:0 0;padding:.5rem}.gg-tab.active{border-bottom:solid 1px var(--text-color)}.girafe-button-big,.girafe-button-large,.girafe-button-small,.girafe-button-tiny{color:var(--text-color);background-color:#0000;border:none;flex-direction:column;display:flex}.girafe-button-big:hover,.girafe-button-large:hover,.girafe-button-small:hover,.girafe-button-tiny:hover{background-color:var(--bkg-color-grad1);cursor:pointer}.girafe-button-big.dark,.girafe-button-large.dark,.girafe-button-small.dark,.girafe-button-tiny.dark{background-color:var(--bkg-color);filter:invert()}.girafe-button-big{width:var(--app-standard-height);height:var(--app-standard-height);align-items:center;padding:1rem}.girafe-button-big img{overflow:hidden}.girafe-button-large{flex-direction:row}.girafe-button-large img{height:2rem;margin:.3rem}.girafe-button-large span{height:2rem;margin:.3rem;line-height:2rem}.girafe-button-small{min-width:calc(var(--app-standard-height) / 2);height:calc(var(--app-standard-height) / 2);align-items:center;padding:.5rem}.girafe-button-small img{overflow:hidden}.girafe-button-small span{text-align:left;text-overflow:ellipsis;width:100%;overflow:hidden}.girafe-button-tiny{align-items:center;width:1rem;height:1rem;padding:0}.girafe-button-tiny img{overflow:hidden}.girafe-onboarding-theme{background-color:var(--bkg-color)!important;color:var(--text-color)!important}.girafe-onboarding-theme button{background-color:var(--bkg-color)!important;color:var(--text-color)!important;text-shadow:none!important}.girafe-onboarding-theme button.driver-popover-close-btn{z-index:10000}.tippy-box{background-color:var(--bkg-color)!important;border:var(--app-standard-border)!important}.tippy-content{color:var(--text-color)!important}.tippy-arrow{color:var(--bkg-color)!important}.tippy-box[data-theme~=error]{background-color:var(--error-color);white-space:pre-line}div.tippy-box[data-theme=image-popup]{border:var(--app-standard-border)!important;& div.tippy-content{background-color:var(--bkg-color)!important;& img{max-width:20rem;max-height:20rem}}& div.tippy-arrow{color:var(--bkg-color)!important}}
14
14
  </style>
@@ -14,7 +14,7 @@ class TreeViewItemComponent extends TreeViewElement {
14
14
  styleUrls = null;
15
15
  template = () => {
16
16
  return uHtml `<style>
17
- header{border-top:1px solid #ddd;align-items:center;height:2rem;display:flex}.tool{display:none!important}header:hover .tool{display:flex!important}.tool.active{order:100;opacity:1!important;display:flex!important}.label-container{flex-grow:1;align-items:center;display:flex;overflow:auto}.label-out-resolution span{font-style:italic}.resolution-tool{margin-left:.5rem}.legend{display:flex}.legend img{background:var(--svg-legend-bkg);height:fit-content;filter:var(--svg-map-filter)}.legend-icon{background:var(--svg-legend-bkg);max-height:1.5rem;filter:var(--svg-map-filter);margin-left:.5rem}.children{flex-basis:100%;order:10;padding-left:1.3rem}.dragAfter{border-bottom:1rem dashed #ccc}.dragBefore{border-top:1rem dashed #ccc}.gg-checkbox{width:1.2rem}.gg-caret,.gg-spacer{text-align:center;width:1.5rem;padding:0}.gg-caret{transform:scale(1.3)}.gg-caret.gg-rotate90{transform:scale(1.3)rotate(90deg)}.gg-small.gg-lock{min-width:1rem}.gg-opacity.gg-lock:hover{opacity:.5!important}.gg-tree-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;padding-left:0;overflow:hidden}.gg-grab:hover{cursor:grab}.gg-selected{opacity:1}.legend-file{color:var(--text-color);font-style:italic;display:flex}@keyframes headerFade{0%{background-color:var(--highlight-color)}to{background-color:#0000}}.highlight{animation:3s ease-in-out forwards headerFade}
17
+ header{border-top:1px solid #ddd;align-items:center;height:2rem;display:flex}.tool{display:none!important}.tool.visible,header:hover .tool{display:flex!important}.tool.active{order:100;opacity:1!important;display:flex!important}.label-container{flex-grow:1;align-items:center;display:flex;overflow:auto}.label-out-resolution span{font-style:italic}.resolution-tool{margin-left:.5rem}.legend{display:flex}.legend img{background:var(--svg-legend-bkg);height:fit-content;filter:var(--svg-map-filter)}.legend-icon{background:var(--svg-legend-bkg);max-height:1.5rem;filter:var(--svg-map-filter);margin-left:.5rem}.children{flex-basis:100%;order:10;padding-left:1.3rem}.dragAfter{border-bottom:1rem dashed #ccc}.dragBefore{border-top:1rem dashed #ccc}.gg-checkbox{width:1.2rem}.gg-caret,.gg-spacer{text-align:center;width:1.5rem;padding:0}.gg-caret{transform:scale(1.3)}.gg-caret.gg-rotate90{transform:scale(1.3)rotate(90deg)}.gg-small.gg-lock{min-width:1rem}.gg-opacity.gg-lock:hover{opacity:.5!important}.gg-tree-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;padding-left:0;overflow:hidden}.gg-grab:hover{cursor:grab}.gg-selected{opacity:1}.legend-file{color:var(--text-color);font-style:italic;display:flex}@keyframes headerFade{0%{background-color:var(--highlight-color)}to{background-color:#0000}}.highlight{animation:3s ease-in-out forwards headerFade}
18
18
  </style><style>
19
19
  .hidden{display:none!important}.gg-rotate90{transform:rotate(90deg)}.gg-rotate180{transform:rotate(180deg)}.gg-rotate270{transform:rotate(270deg)}img{filter:var(--svg-filter)}img.legend-image{filter:var(--svg-map-filter);background:var(--svg-legend-bkg)}div{scrollbar-width:thin}a,a:visited{color:var(--link-color)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spin-wait{0%{transform:rotate(0)}7%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.gg-spin{animation-name:spin;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}.gg-spin-wait{animation-name:spin-wait;animation-duration:10s;animation-timing-function:linear;animation-iteration-count:infinite}::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:#999}button,input,select,textarea{font:inherit}.gg-button,.gg-select,.gg-input,.gg-textarea{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);box-sizing:border-box;cursor:pointer;border-radius:3px;outline:0;margin:0;padding:0 0 0 .5rem;display:inline-block}.gg-label{background-color:var(--bkg-color);color:var(--text-color);border:none;align-items:center;margin:0;padding:0;display:flex}.gg-button,.gg-select,.gg-input,.gg-label{min-height:calc(var(--app-standard-height) / 1.5)}.gg-textarea{max-height:initial;resize:vertical;height:6rem;padding:.5rem;line-height:1.3rem}.gg-input{cursor:text}.gg-checkbox{accent-color:var(--text-color);width:1.2rem}.gg-range{accent-color:var(--text-color)}.gg-button{padding:0 .5rem}.gg-button.active{border:solid 1px var(--text-color-grad1);background-color:var(--bkg-color-grad2)}.gg-button:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed;border:none}.gg-button:disabled img{filter:opacity(.6)}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed}.gg-button>img{vertical-align:middle}.gg-icon-button{color:var(--text-color);cursor:pointer;background-color:#0000;border:none;flex-direction:column;justify-content:center;align-items:center;padding:0;display:flex}.gg-icon{justify-content:center;align-items:center;display:flex}.gg-big,.gg-big-withtext{min-width:var(--app-standard-height);min-height:var(--app-standard-height);max-height:var(--app-standard-height)}.gg-big img,.gg-big-withtext img{width:calc(var(--app-standard-height) - 1.5rem);margin:0}.gg-big-withtext span{font-variant:small-caps;padding:0 1rem;font-size:.9rem}.gg-medium,.gg-medium-withtext{min-width:calc(var(--app-standard-height) / 1.2);min-height:calc(var(--app-standard-height) / 1.2);max-height:calc(var(--app-standard-height) / 1.2);flex-direction:row}.gg-medium img{width:calc(var(--app-standard-height) / 2.4);margin:0}.gg-medium-withtext img{width:calc(var(--app-standard-height) / 2.4);margin-left:.5rem}.gg-medium-withtext span{padding:0 1rem 0 .5rem;font-size:.9rem}.gg-small,.gg-small-withtext{min-width:calc(var(--app-standard-height) / 2);min-height:calc(var(--app-standard-height) / 2);max-height:calc(var(--app-standard-height) / 2);flex-direction:row}.gg-small img{width:calc(var(--app-standard-height) / 3);margin:0}.gg-small-withtext img{width:calc(var(--app-standard-height) / 3);margin-left:.5rem}.gg-small-withtext span{padding:0 .5rem 0 .3rem;font-size:.9rem}.gg-button:hover:not(:disabled),.gg-select:hover:not(:disabled),.gg-input:hover:not(:disabled),.gg-textarea:hover:not(:disabled),.gg-icon-button:hover:not(:disabled){background-color:var(--bkg-color-grad1)}.gg-opacity{opacity:.5}.gg-opacity:hover{opacity:1;background-color:#0000}.gg-tabs{cursor:pointer;grid-auto-flow:column;padding-bottom:1rem;font-size:1rem;display:grid}.gg-tab{border:none;border-bottom:var(--app-standard-border);cursor:pointer;color:var(--text-color);background:0 0;padding:.5rem}.gg-tab.active{border-bottom:solid 1px var(--text-color)}.girafe-button-big,.girafe-button-large,.girafe-button-small,.girafe-button-tiny{color:var(--text-color);background-color:#0000;border:none;flex-direction:column;display:flex}.girafe-button-big:hover,.girafe-button-large:hover,.girafe-button-small:hover,.girafe-button-tiny:hover{background-color:var(--bkg-color-grad1);cursor:pointer}.girafe-button-big.dark,.girafe-button-large.dark,.girafe-button-small.dark,.girafe-button-tiny.dark{background-color:var(--bkg-color);filter:invert()}.girafe-button-big{width:var(--app-standard-height);height:var(--app-standard-height);align-items:center;padding:1rem}.girafe-button-big img{overflow:hidden}.girafe-button-large{flex-direction:row}.girafe-button-large img{height:2rem;margin:.3rem}.girafe-button-large span{height:2rem;margin:.3rem;line-height:2rem}.girafe-button-small{min-width:calc(var(--app-standard-height) / 2);height:calc(var(--app-standard-height) / 2);align-items:center;padding:.5rem}.girafe-button-small img{overflow:hidden}.girafe-button-small span{text-align:left;text-overflow:ellipsis;width:100%;overflow:hidden}.girafe-button-tiny{align-items:center;width:1rem;height:1rem;padding:0}.girafe-button-tiny img{overflow:hidden}.girafe-onboarding-theme{background-color:var(--bkg-color)!important;color:var(--text-color)!important}.girafe-onboarding-theme button{background-color:var(--bkg-color)!important;color:var(--text-color)!important;text-shadow:none!important}.girafe-onboarding-theme button.driver-popover-close-btn{z-index:10000}.tippy-box{background-color:var(--bkg-color)!important;border:var(--app-standard-border)!important}.tippy-content{color:var(--text-color)!important}.tippy-arrow{color:var(--bkg-color)!important}.tippy-box[data-theme~=error]{background-color:var(--error-color);white-space:pre-line}div.tippy-box[data-theme=image-popup]{border:var(--app-standard-border)!important;& div.tippy-content{background-color:var(--bkg-color)!important;& img{max-width:20rem;max-height:20rem}}& div.tippy-arrow{color:var(--bkg-color)!important}}
20
20
  </style>
@@ -10,5 +10,7 @@ declare class TreeViewThemeComponent extends TreeViewGroupElement {
10
10
  registerEvents(): void;
11
11
  protected connectedCallback(): void;
12
12
  deleteTheme(): void;
13
+ isThemeComplete(): boolean;
14
+ completeTheme(): void;
13
15
  }
14
16
  export default TreeViewThemeComponent;
@@ -12,12 +12,12 @@ class TreeViewThemeComponent extends TreeViewGroupElement {
12
12
  return uHtml `<style>
13
13
  .hidden{display:none!important}.gg-rotate90{transform:rotate(90deg)}.gg-rotate180{transform:rotate(180deg)}.gg-rotate270{transform:rotate(270deg)}img{filter:var(--svg-filter)}img.legend-image{filter:var(--svg-map-filter);background:var(--svg-legend-bkg)}div{scrollbar-width:thin}a,a:visited{color:var(--link-color)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spin-wait{0%{transform:rotate(0)}7%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.gg-spin{animation-name:spin;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}.gg-spin-wait{animation-name:spin-wait;animation-duration:10s;animation-timing-function:linear;animation-iteration-count:infinite}::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:#999}button,input,select,textarea{font:inherit}.gg-button,.gg-select,.gg-input,.gg-textarea{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);box-sizing:border-box;cursor:pointer;border-radius:3px;outline:0;margin:0;padding:0 0 0 .5rem;display:inline-block}.gg-label{background-color:var(--bkg-color);color:var(--text-color);border:none;align-items:center;margin:0;padding:0;display:flex}.gg-button,.gg-select,.gg-input,.gg-label{min-height:calc(var(--app-standard-height) / 1.5)}.gg-textarea{max-height:initial;resize:vertical;height:6rem;padding:.5rem;line-height:1.3rem}.gg-input{cursor:text}.gg-checkbox{accent-color:var(--text-color);width:1.2rem}.gg-range{accent-color:var(--text-color)}.gg-button{padding:0 .5rem}.gg-button.active{border:solid 1px var(--text-color-grad1);background-color:var(--bkg-color-grad2)}.gg-button:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed;border:none}.gg-button:disabled img{filter:opacity(.6)}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed}.gg-button>img{vertical-align:middle}.gg-icon-button{color:var(--text-color);cursor:pointer;background-color:#0000;border:none;flex-direction:column;justify-content:center;align-items:center;padding:0;display:flex}.gg-icon{justify-content:center;align-items:center;display:flex}.gg-big,.gg-big-withtext{min-width:var(--app-standard-height);min-height:var(--app-standard-height);max-height:var(--app-standard-height)}.gg-big img,.gg-big-withtext img{width:calc(var(--app-standard-height) - 1.5rem);margin:0}.gg-big-withtext span{font-variant:small-caps;padding:0 1rem;font-size:.9rem}.gg-medium,.gg-medium-withtext{min-width:calc(var(--app-standard-height) / 1.2);min-height:calc(var(--app-standard-height) / 1.2);max-height:calc(var(--app-standard-height) / 1.2);flex-direction:row}.gg-medium img{width:calc(var(--app-standard-height) / 2.4);margin:0}.gg-medium-withtext img{width:calc(var(--app-standard-height) / 2.4);margin-left:.5rem}.gg-medium-withtext span{padding:0 1rem 0 .5rem;font-size:.9rem}.gg-small,.gg-small-withtext{min-width:calc(var(--app-standard-height) / 2);min-height:calc(var(--app-standard-height) / 2);max-height:calc(var(--app-standard-height) / 2);flex-direction:row}.gg-small img{width:calc(var(--app-standard-height) / 3);margin:0}.gg-small-withtext img{width:calc(var(--app-standard-height) / 3);margin-left:.5rem}.gg-small-withtext span{padding:0 .5rem 0 .3rem;font-size:.9rem}.gg-button:hover:not(:disabled),.gg-select:hover:not(:disabled),.gg-input:hover:not(:disabled),.gg-textarea:hover:not(:disabled),.gg-icon-button:hover:not(:disabled){background-color:var(--bkg-color-grad1)}.gg-opacity{opacity:.5}.gg-opacity:hover{opacity:1;background-color:#0000}.gg-tabs{cursor:pointer;grid-auto-flow:column;padding-bottom:1rem;font-size:1rem;display:grid}.gg-tab{border:none;border-bottom:var(--app-standard-border);cursor:pointer;color:var(--text-color);background:0 0;padding:.5rem}.gg-tab.active{border-bottom:solid 1px var(--text-color)}.girafe-button-big,.girafe-button-large,.girafe-button-small,.girafe-button-tiny{color:var(--text-color);background-color:#0000;border:none;flex-direction:column;display:flex}.girafe-button-big:hover,.girafe-button-large:hover,.girafe-button-small:hover,.girafe-button-tiny:hover{background-color:var(--bkg-color-grad1);cursor:pointer}.girafe-button-big.dark,.girafe-button-large.dark,.girafe-button-small.dark,.girafe-button-tiny.dark{background-color:var(--bkg-color);filter:invert()}.girafe-button-big{width:var(--app-standard-height);height:var(--app-standard-height);align-items:center;padding:1rem}.girafe-button-big img{overflow:hidden}.girafe-button-large{flex-direction:row}.girafe-button-large img{height:2rem;margin:.3rem}.girafe-button-large span{height:2rem;margin:.3rem;line-height:2rem}.girafe-button-small{min-width:calc(var(--app-standard-height) / 2);height:calc(var(--app-standard-height) / 2);align-items:center;padding:.5rem}.girafe-button-small img{overflow:hidden}.girafe-button-small span{text-align:left;text-overflow:ellipsis;width:100%;overflow:hidden}.girafe-button-tiny{align-items:center;width:1rem;height:1rem;padding:0}.girafe-button-tiny img{overflow:hidden}.girafe-onboarding-theme{background-color:var(--bkg-color)!important;color:var(--text-color)!important}.girafe-onboarding-theme button{background-color:var(--bkg-color)!important;color:var(--text-color)!important;text-shadow:none!important}.girafe-onboarding-theme button.driver-popover-close-btn{z-index:10000}.tippy-box{background-color:var(--bkg-color)!important;border:var(--app-standard-border)!important}.tippy-content{color:var(--text-color)!important}.tippy-arrow{color:var(--bkg-color)!important}.tippy-box[data-theme~=error]{background-color:var(--error-color);white-space:pre-line}div.tippy-box[data-theme=image-popup]{border:var(--app-standard-border)!important;& div.tippy-content{background-color:var(--bkg-color)!important;& img{max-width:20rem;max-height:20rem}}& div.tippy-arrow{color:var(--bkg-color)!important}}
14
14
  </style><style>
15
- header{border-top:1px solid #ddd;align-items:center;height:2rem;display:flex}.tool{display:none!important}header:hover .tool{display:flex!important}.tool.active{order:100;opacity:1!important;display:flex!important}.label-container{flex-grow:1;align-items:center;display:flex;overflow:auto}.label-out-resolution span{font-style:italic}.resolution-tool{margin-left:.5rem}.legend{display:flex}.legend img{background:var(--svg-legend-bkg);height:fit-content;filter:var(--svg-map-filter)}.legend-icon{background:var(--svg-legend-bkg);max-height:1.5rem;filter:var(--svg-map-filter);margin-left:.5rem}.children{flex-basis:100%;order:10;padding-left:1.3rem}.dragAfter{border-bottom:1rem dashed #ccc}.dragBefore{border-top:1rem dashed #ccc}.gg-checkbox{width:1.2rem}.gg-caret,.gg-spacer{text-align:center;width:1.5rem;padding:0}.gg-caret{transform:scale(1.3)}.gg-caret.gg-rotate90{transform:scale(1.3)rotate(90deg)}.gg-small.gg-lock{min-width:1rem}.gg-opacity.gg-lock:hover{opacity:.5!important}.gg-tree-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;padding-left:0;overflow:hidden}.gg-grab:hover{cursor:grab}.gg-selected{opacity:1}.legend-file{color:var(--text-color);font-style:italic;display:flex}@keyframes headerFade{0%{background-color:var(--highlight-color)}to{background-color:#0000}}.highlight{animation:3s ease-in-out forwards headerFade}
15
+ header{border-top:1px solid #ddd;align-items:center;height:2rem;display:flex}.tool{display:none!important}.tool.visible,header:hover .tool{display:flex!important}.tool.active{order:100;opacity:1!important;display:flex!important}.label-container{flex-grow:1;align-items:center;display:flex;overflow:auto}.label-out-resolution span{font-style:italic}.resolution-tool{margin-left:.5rem}.legend{display:flex}.legend img{background:var(--svg-legend-bkg);height:fit-content;filter:var(--svg-map-filter)}.legend-icon{background:var(--svg-legend-bkg);max-height:1.5rem;filter:var(--svg-map-filter);margin-left:.5rem}.children{flex-basis:100%;order:10;padding-left:1.3rem}.dragAfter{border-bottom:1rem dashed #ccc}.dragBefore{border-top:1rem dashed #ccc}.gg-checkbox{width:1.2rem}.gg-caret,.gg-spacer{text-align:center;width:1.5rem;padding:0}.gg-caret{transform:scale(1.3)}.gg-caret.gg-rotate90{transform:scale(1.3)rotate(90deg)}.gg-small.gg-lock{min-width:1rem}.gg-opacity.gg-lock:hover{opacity:.5!important}.gg-tree-label{text-align:left;white-space:nowrap;text-overflow:ellipsis;padding-left:0;overflow:hidden}.gg-grab:hover{cursor:grab}.gg-selected{opacity:1}.legend-file{color:var(--text-color);font-style:italic;display:flex}@keyframes headerFade{0%{background-color:var(--highlight-color)}to{background-color:#0000}}.highlight{animation:3s ease-in-out forwards headerFade}
16
16
  </style><style>
17
17
  header{border:none;flex-direction:row-reverse;margin-top:.5rem;margin-bottom:.5rem;position:relative}.theme-button{font-size:1.2rem;font-weight:600}.theme-button span{margin-left:.5rem}.theme-button:hover{cursor:default;background-color:#0000;border:none}.children{padding-left:.5rem}.children:first-child{border:none}.label-container{position:absolute;inset:0}.label-container button{text-align:center;flex-grow:1}.tool{z-index:1}
18
18
  </style>
19
19
  <style>${this.customStyle}</style>
20
- <link rel="stylesheet" href="lib/tippy.js/tippy.css"><link rel="stylesheet" href="lib/tippy.js/backdrop.css"><link rel="stylesheet" href="lib/tippy.js/border.css"><link rel="stylesheet" href="lib/tippy.js/svg-arrow.css"><div id="container"><header><div class="label-container"><button class="gg-icon-button gg-small theme-button" onclick="${() => this.layer.isExpanded = !this.layer.isExpanded}"><img alt="theme-icon" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNjQwIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIHY3LjAuMSBieSBAZm9udGF3ZXNvbWUgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbSBMaWNlbnNlIC0gaHR0cHM6Ly9mb250YXdlc29tZS5jb20vbGljZW5zZS9mcmVlIENvcHlyaWdodCAyMDI1IEZvbnRpY29ucywgSW5jLi0tPjxwYXRoIGQ9Ik01NzYgMTEyQzU3NiAxMDMuNyA1NzEuNyA5NiA1NjQuNyA5MS42QzU1Ny43IDg3LjIgNTQ4LjggODYuOCA1NDEuNCA5MC41TDQxNi41IDE1Mi4xTDI0NCA5My40QzIzMC4zIDg4LjcgMjE1LjMgODkuNiAyMDIuMSA5NS43TDc3LjggMTU0LjNDNjkuNCAxNTguMiA2NCAxNjYuNyA2NCAxNzZMNjQgNTI4QzY0IDUzNi4yIDY4LjIgNTQzLjkgNzUuMSA1NDguM0M4MiA1NTIuNyA5MC43IDU1My4yIDk4LjIgNTQ5LjdMMjI1LjUgNDg5LjhMMzk2LjIgNTQ2LjdDNDA5LjkgNTUxLjMgNDI0LjcgNTUwLjQgNDM3LjggNTQ0LjJMNTYyLjIgNDg1LjdDNTcwLjYgNDgxLjcgNTc2IDQ3My4zIDU3NiA0NjRMNTc2IDExMnpNMjA4IDE0Ni4xTDIwOCA0NDUuMUwxMTIgNDkwLjNMMTEyIDE5MS4zTDIwOCAxNDYuMXpNMjU2IDQ0OS40TDI1NiAxNDguM0wzODQgMTkxLjhMMzg0IDQ5Mi4xTDI1NiA0NDkuNHpNNDMyIDE5OEw1MjggMTUwLjZMNTI4IDQ0OC44TDQzMiA0OTRMNDMyIDE5OHoiLz48L3N2Zz4="> <span i18n="${this.layer.name}" class="gg-tree-label">${this.layer.name}</span></button></div><button class="${this.getButtonClass('removable')}" tip="Remove theme" onclick="${() => this.deleteTheme()}"><img alt="Remove theme" src="icons/trash.svg"></button> <button id="drag-button" class="${this.getButtonClass('draggable')}" tip="Move"><img alt="Move layer" src="icons/drag.svg"></button></header><div class="${((this.layer.isExpanded && this.layer.isVisible) ? 'children' : 'hidden')}">${this.sortedChildren().filter(layer => layer.isVisible).map(layer => (layer instanceof GroupLayer) ? uHtmlFor(layer, layer.treeItemId) `<girafe-tree-view-group groupid="${layer.treeItemId}"></girafe-tree-view-group>` : uHtmlFor(layer, layer.treeItemId) `<girafe-tree-view-item layerid="${layer.treeItemId}"></girafe-tree-view-item>`)}</div></div>
20
+ <link rel="stylesheet" href="lib/tippy.js/tippy.css"><link rel="stylesheet" href="lib/tippy.js/backdrop.css"><link rel="stylesheet" href="lib/tippy.js/border.css"><link rel="stylesheet" href="lib/tippy.js/svg-arrow.css"><div id="container"><header><div class="label-container"><button class="gg-icon-button gg-small theme-button" onclick="${() => this.layer.isExpanded = !this.layer.isExpanded}"><img alt="theme-icon" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA2NDAgNjQwIj48IS0tIUZvbnQgQXdlc29tZSBGcmVlIHY3LjAuMSBieSBAZm9udGF3ZXNvbWUgLSBodHRwczovL2ZvbnRhd2Vzb21lLmNvbSBMaWNlbnNlIC0gaHR0cHM6Ly9mb250YXdlc29tZS5jb20vbGljZW5zZS9mcmVlIENvcHlyaWdodCAyMDI1IEZvbnRpY29ucywgSW5jLi0tPjxwYXRoIGQ9Ik01NzYgMTEyQzU3NiAxMDMuNyA1NzEuNyA5NiA1NjQuNyA5MS42QzU1Ny43IDg3LjIgNTQ4LjggODYuOCA1NDEuNCA5MC41TDQxNi41IDE1Mi4xTDI0NCA5My40QzIzMC4zIDg4LjcgMjE1LjMgODkuNiAyMDIuMSA5NS43TDc3LjggMTU0LjNDNjkuNCAxNTguMiA2NCAxNjYuNyA2NCAxNzZMNjQgNTI4QzY0IDUzNi4yIDY4LjIgNTQzLjkgNzUuMSA1NDguM0M4MiA1NTIuNyA5MC43IDU1My4yIDk4LjIgNTQ5LjdMMjI1LjUgNDg5LjhMMzk2LjIgNTQ2LjdDNDA5LjkgNTUxLjMgNDI0LjcgNTUwLjQgNDM3LjggNTQ0LjJMNTYyLjIgNDg1LjdDNTcwLjYgNDgxLjcgNTc2IDQ3My4zIDU3NiA0NjRMNTc2IDExMnpNMjA4IDE0Ni4xTDIwOCA0NDUuMUwxMTIgNDkwLjNMMTEyIDE5MS4zTDIwOCAxNDYuMXpNMjU2IDQ0OS40TDI1NiAxNDguM0wzODQgMTkxLjhMMzg0IDQ5Mi4xTDI1NiA0NDkuNHpNNDMyIDE5OEw1MjggMTUwLjZMNTI4IDQ0OC44TDQzMiA0OTRMNDMyIDE5OHoiLz48L3N2Zz4="> <span i18n="${this.layer.name}" class="gg-tree-label">${this.layer.name}</span></button></div>${this.isThemeComplete() ? '' : uHtml ` <button class="gg-icon-button gg-small tool visible theme-button" tip="Complete the theme" onclick="${() => this.completeTheme()}"><img alt="Complete the theme" src="icons/theme-checked.svg"></button> `} <button class="${this.getButtonClass('removable')}" tip="Remove theme" onclick="${() => this.deleteTheme()}"><img alt="Remove theme" src="icons/trash.svg"></button> <button id="drag-button" class="${this.getButtonClass('draggable')}" tip="Move"><img alt="Move layer" src="icons/drag.svg"></button></header><div class="${((this.layer.isExpanded && this.layer.isVisible) ? 'children' : 'hidden')}">${this.sortedChildren().filter(layer => layer.isVisible).map(layer => (layer instanceof GroupLayer) ? uHtmlFor(layer, layer.treeItemId) `<girafe-tree-view-group groupid="${layer.treeItemId}"></girafe-tree-view-group>` : uHtmlFor(layer, layer.treeItemId) `<girafe-tree-view-item layerid="${layer.treeItemId}"></girafe-tree-view-item>`)}</div></div>
21
21
  ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
22
22
  };
23
23
  layer;
@@ -37,7 +37,10 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
37
37
  registerEvents() {
38
38
  this.subscribe(/layers\.layersList\..*\.isExpanded/, (_oldValue, _newValue, theme) => this.refreshRender(theme));
39
39
  this.subscribe(/layers\.layersList\..*\.activeState/, (_oldValue, _newValue, theme) => this.refreshRender(theme));
40
- this.subscribe(/layers\.layersList\..*\.children/, (_oldValue, _newValue, group) => this.refreshRender(group));
40
+ this.subscribe(/layers\.layersList\..*\.children/, (_oldValue, _newValue, group) => {
41
+ this.refreshRender(group);
42
+ this.refreshRender(group.parent);
43
+ });
41
44
  this.subscribe(/layers\.layersList\..*\.filter/, (_oldValue, _newValue, layer) => this.refreshRender(layer));
42
45
  this.subscribe(/layers\.layersList\..*\.order/, (_oldValue, _newValue, layer) => {
43
46
  this.refreshRender(layer);
@@ -58,5 +61,17 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
58
61
  this.removeFromParent();
59
62
  this.state.themes.lastSelectedTheme = null;
60
63
  }
64
+ isThemeComplete() {
65
+ return this.context.themesHelper.isThemeComplete(this.layer);
66
+ }
67
+ completeTheme() {
68
+ const originalTheme = this.context.themesHelper.getOriginalTheme(this.layer);
69
+ if (originalTheme) {
70
+ this.context.themesHelper.completeTheme(originalTheme);
71
+ }
72
+ else {
73
+ console.error('Cannot complete theme, original theme not found.');
74
+ }
75
+ }
61
76
  }
62
77
  export default TreeViewThemeComponent;
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "GeoGirafe PSC",
6
6
  "url": "https://doc.geogirafe.org"
7
7
  },
8
- "version": "1.2.0-dev.2816732111",
8
+ "version": "1.2.0-dev.2829479244",
9
9
  "type": "module",
10
10
  "engines": {
11
11
  "node": ">=20.19.0"
@@ -1 +1 @@
1
- {"version":"1.2.0-dev.2816732111", "build":"2816732111", "date":"03/09/2026"}
1
+ {"version":"1.2.0-dev.2829479244", "build":"2829479244", "date":"08/09/2026"}
@@ -11,13 +11,48 @@ export default class ThemesHelper extends GirafeSingleton {
11
11
  initializeSingleton(): void;
12
12
  private get state();
13
13
  findBaseLayerById(layerId: number): BaseLayer;
14
- private findBaseLayerRecursiveById;
14
+ findBaseLayerRecursiveById(layers: BaseLayer[], layerId: number): BaseLayer | null;
15
15
  findThemeByName(themename: string): ThemeLayer | null;
16
16
  findGroupByName(groupname: string): GroupLayer | null;
17
17
  findLayerByName(layername: string): Layer | null;
18
18
  private findBaseLayerByName;
19
19
  private findLayerRecursive;
20
+ /**
21
+ * @param layer any layer or layer group (or theme) to find the related theme.
22
+ * @returns The original theme configuration.
23
+ */
24
+ getOriginalTheme(layer: BaseLayer): ThemeLayer | null;
25
+ /**
26
+ * Count the layer in both the given theme and the matching original theme.
27
+ * @param theme the theme to check.
28
+ * @returns true if the count is the same. False otherwise.
29
+ */
30
+ isThemeComplete(theme: ThemeLayer): boolean;
31
+ /**
32
+ * @return the number of all sublayers (recursively) of the given layer.
33
+ * @private
34
+ */
35
+ private countLayer;
36
+ /**
37
+ * Add the related theme if it's not already (even partially) in the layertree. If it's
38
+ * already in it, don't add it, warn the user, and highlight it.
39
+ * @returns true if the related theme is added to the layertree, false otherwise.
40
+ */
41
+ addThemeIfNotPresent(theme: ThemeLayer): boolean;
42
+ /**
43
+ * Add the missing layers of an in-use theme, from an original theme description.
44
+ * The added layers are deactivated.
45
+ * @param originalTheme a Theme object, or model, as initially defined.
46
+ */
47
+ completeTheme(originalTheme: ThemeLayer): void;
48
+ /**
49
+ * Deactivate all layers and sublayer recursively.
50
+ * Meaning state="off" and isDefaultChecked=false.
51
+ * @private
52
+ */
53
+ private deactivateAllLayers;
20
54
  private onSelectedThemeChanged;
55
+ private getThemeInLayersList;
21
56
  private onThemeChanged;
22
57
  getMinimalClonedThemeForLayer(layer: BaseLayer): ThemeLayer;
23
58
  private removeOthers;
@@ -50,6 +85,14 @@ export default class ThemesHelper extends GirafeSingleton {
50
85
  private addLayerToLayerTree;
51
86
  removeLayersFromLayerTree(layersToRemove: BaseLayer[]): void;
52
87
  private removeLayersFromExistingLayerTree;
88
+ /**
89
+ * Find the initial position of the layer (relative to its sibling layers) by looking
90
+ * at the original theme object and insert the layer following this original order.
91
+ * (The "layer.order" value is incremented with the user operations, that's why we can
92
+ * rely on it for new layers and must adapt it to the existing sibling layers.)
93
+ * @private
94
+ */
95
+ private insertLayerToLayerTreeAtInitialPosition;
53
96
  /**
54
97
  * Removes all layers from the layer tree except themes that are pinned.
55
98
  */
@@ -93,6 +93,97 @@ export default class ThemesHelper extends GirafeSingleton {
93
93
  }
94
94
  return null;
95
95
  }
96
+ /**
97
+ * @param layer any layer or layer group (or theme) to find the related theme.
98
+ * @returns The original theme configuration.
99
+ */
100
+ getOriginalTheme(layer) {
101
+ const hierarchy = this.getHierarchyFromLayer(layer);
102
+ const theme = hierarchy?.length ? hierarchy[0] : undefined;
103
+ return theme ? this.findThemeByName(theme.name) : null;
104
+ }
105
+ /**
106
+ * Count the layer in both the given theme and the matching original theme.
107
+ * @param theme the theme to check.
108
+ * @returns true if the count is the same. False otherwise.
109
+ */
110
+ isThemeComplete(theme) {
111
+ const originalTheme = this.getOriginalTheme(theme);
112
+ if (!originalTheme) {
113
+ return false;
114
+ }
115
+ const themeCount = this.countLayer(theme);
116
+ const originalThemeCount = this.countLayer(originalTheme);
117
+ return themeCount === originalThemeCount;
118
+ }
119
+ /**
120
+ * @return the number of all sublayers (recursively) of the given layer.
121
+ * @private
122
+ */
123
+ countLayer(layer) {
124
+ let count = 1;
125
+ const layerAsUnknownGroup = layer;
126
+ if (layerAsUnknownGroup.children) {
127
+ layerAsUnknownGroup.children.forEach((child) => {
128
+ count += this.countLayer(child);
129
+ });
130
+ }
131
+ return count;
132
+ }
133
+ /**
134
+ * Add the related theme if it's not already (even partially) in the layertree. If it's
135
+ * already in it, don't add it, warn the user, and highlight it.
136
+ * @returns true if the related theme is added to the layertree, false otherwise.
137
+ */
138
+ addThemeIfNotPresent(theme) {
139
+ const themeInLayersList = this.getThemeInLayersList(theme);
140
+ // The theme is not yet added, add it!
141
+ if (themeInLayersList === undefined) {
142
+ if (this.state.themes.lastSelectedTheme?.id === theme.id) {
143
+ this.state.themes.lastSelectedTheme = null;
144
+ }
145
+ this.state.themes.lastSelectedTheme = theme;
146
+ return true;
147
+ }
148
+ // The theme is in the layer tree, then warn the user.
149
+ themeInLayersList.isHighlighted = true;
150
+ const layerName = this.context.i18nManager.getTranslation(theme.name);
151
+ const msg = this.context.i18nManager
152
+ .getTranslation('The theme {name} is already present in the treeview.')
153
+ .replace('{name}', layerName);
154
+ this.state.infobox.elements.push({
155
+ id: theme.treeItemId,
156
+ text: msg,
157
+ type: 'info',
158
+ duration: 5000
159
+ });
160
+ return false;
161
+ }
162
+ /**
163
+ * Add the missing layers of an in-use theme, from an original theme description.
164
+ * The added layers are deactivated.
165
+ * @param originalTheme a Theme object, or model, as initially defined.
166
+ */
167
+ completeTheme(originalTheme) {
168
+ const clonedTheme = originalTheme.clone();
169
+ this.deactivateAllLayers(clonedTheme);
170
+ this.mergeLayerWithExistingLayerTree(clonedTheme, this.state.layers.layersList, false);
171
+ }
172
+ /**
173
+ * Deactivate all layers and sublayer recursively.
174
+ * Meaning state="off" and isDefaultChecked=false.
175
+ * @private
176
+ */
177
+ deactivateAllLayers(layer) {
178
+ layer.activeState = 'off';
179
+ layer.isDefaultChecked = false;
180
+ const layerAsUnknownGroup = layer;
181
+ if (layerAsUnknownGroup.children) {
182
+ layerAsUnknownGroup.children.forEach((child) => {
183
+ this.deactivateAllLayers(child);
184
+ });
185
+ }
186
+ }
96
187
  onSelectedThemeChanged(theme) {
97
188
  if (!theme) {
98
189
  // Theme is null, nothing to do here
@@ -108,7 +199,11 @@ export default class ThemesHelper extends GirafeSingleton {
108
199
  this.onThemeChanged(theme);
109
200
  }
110
201
  }
202
+ getThemeInLayersList(theme) {
203
+ return this.state.layers.layersList.find((l) => l.id == theme.id);
204
+ }
111
205
  onThemeChanged(theme) {
206
+ const themeInLayersList = this.getThemeInLayersList(theme);
112
207
  // Create a clone of the theme object to use it in the treeview.
113
208
  // This is essential, otherwise all changes are done in the layers
114
209
  // (For example, when expanding legend, expanding a group, or activating the layer).
@@ -116,20 +211,10 @@ export default class ThemesHelper extends GirafeSingleton {
116
211
  // And when a theme is selected aging from the themes-selector
117
212
  // The default configuration will have been overwritten.
118
213
  const clonedTheme = theme.clone();
119
- const themeAlreadyInLayersList = this.state.layers.layersList.find((l) => l.id == clonedTheme.id);
120
- if (themeAlreadyInLayersList) {
121
- themeAlreadyInLayersList.isHighlighted = true;
122
- const layername = this.context.i18nManager.getTranslation(clonedTheme.name);
123
- const msg = this.context.i18nManager
124
- .getTranslation('The theme {name} is already present in the treeview.')
125
- .replace('{name}', layername);
126
- this.state.infobox.elements.push({
127
- id: theme.treeItemId,
128
- text: msg,
129
- type: 'info',
130
- duration: 5000
131
- });
132
- return;
214
+ // Delete (replace) this theme if it already exists.
215
+ if (themeInLayersList !== undefined) {
216
+ this.context.stateManager.batchChanges(() => this.removeLayersFromLayerTree([themeInLayersList]));
217
+ clonedTheme.order = themeInLayersList.order;
133
218
  }
134
219
  if (this.context.configManager.Config.themes.selectionMode === 'replace') {
135
220
  // Mode is <replace>
@@ -138,7 +223,6 @@ export default class ThemesHelper extends GirafeSingleton {
138
223
  }
139
224
  else {
140
225
  // Mode is <add>
141
- clonedTheme.order = this.getInitialOrderForNewTheme();
142
226
  this.state.layers.layersList.push(clonedTheme);
143
227
  }
144
228
  const themeFunctionalities = this.state.themes._allFunctionalities[theme.id];
@@ -384,7 +468,7 @@ export default class ThemesHelper extends GirafeSingleton {
384
468
  if (parent) {
385
469
  newLayer.parent = parent;
386
470
  }
387
- existingList.push(newLayer);
471
+ this.insertLayerToLayerTreeAtInitialPosition(newLayer, existingList);
388
472
  if (activate) {
389
473
  this.context.layerManager.toggle(newLayer, 'on');
390
474
  }
@@ -411,6 +495,55 @@ export default class ThemesHelper extends GirafeSingleton {
411
495
  }
412
496
  }
413
497
  }
498
+ /**
499
+ * Find the initial position of the layer (relative to its sibling layers) by looking
500
+ * at the original theme object and insert the layer following this original order.
501
+ * (The "layer.order" value is incremented with the user operations, that's why we can
502
+ * rely on it for new layers and must adapt it to the existing sibling layers.)
503
+ * @private
504
+ */
505
+ insertLayerToLayerTreeAtInitialPosition(newLayer, existingList) {
506
+ const originalLayer = this.findBaseLayerById(newLayer.id);
507
+ const originalParent = originalLayer.parent;
508
+ if (!originalParent || existingList.length === 0) {
509
+ existingList.push(newLayer);
510
+ return;
511
+ }
512
+ // Following the original order, find the nearest previous or next sibling layer in the existingList.
513
+ const originalSiblings = originalParent.children;
514
+ const existingListIds = new Set(existingList.map((layer) => layer.id));
515
+ let previousOriginalSibling;
516
+ let nextOriginalSibling;
517
+ let originalLayerEncountered = false;
518
+ originalSiblings.some((oLayer) => {
519
+ if (originalLayerEncountered && (previousOriginalSibling || nextOriginalSibling)) {
520
+ return true;
521
+ }
522
+ if (oLayer.id === newLayer.id) {
523
+ originalLayerEncountered = true;
524
+ }
525
+ else if (existingListIds.has(oLayer.id)) {
526
+ if (originalLayerEncountered) {
527
+ nextOriginalSibling = oLayer;
528
+ }
529
+ else {
530
+ previousOriginalSibling = oLayer;
531
+ }
532
+ }
533
+ });
534
+ // Find back the matching sibling layer in the ExistingList and update the "newLayer.order"
535
+ // to be just before or just after the sibling layer.
536
+ if (previousOriginalSibling) {
537
+ const previousSibling = existingList.find((layer) => layer.id === previousOriginalSibling?.id);
538
+ newLayer.order = previousSibling?.order ? previousSibling.order : newLayer.order;
539
+ }
540
+ else if (nextOriginalSibling) {
541
+ const nextSibling = existingList.find((layer) => layer.id === nextOriginalSibling?.id);
542
+ newLayer.order = nextSibling?.order ? nextSibling.order - 1 : newLayer.order;
543
+ }
544
+ // Push the layer in the list. The orderManager will move the layer regarding the order values.
545
+ existingList.push(newLayer);
546
+ }
414
547
  /**
415
548
  * Removes all layers from the layer tree except themes that are pinned.
416
549
  */