@geogirafe/lib-geoportal 1.1.0-dev.2563874711 → 1.1.0-dev.2572458576

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE CHANGED
@@ -240,6 +240,7 @@ From:
240
240
  - https://www.svgrepo.com/svg/310006/star
241
241
  - https://www.svgrepo.com/svg/310008/star-add
242
242
  - https://www.svgrepo.com/svg/309521/directions
243
+ - https://www.svgrepo.com/svg/309871/photo-filter
243
244
 
244
245
 
245
246
  The MIT License (MIT)
@@ -64,6 +64,8 @@
64
64
  "Copy value": "Wert kopieren",
65
65
  "copy-cell-content": "Zelleninhalt kopieren",
66
66
  "Create custom theme": "Thema erstellen",
67
+ "create-bookmark": "Lesezeichen erstellen",
68
+ "create-bookmark-by-shortcut": "Drücken Sie ${shortcut} oder nutzen Sie die Option <i>Zu Favoriten hinzufügen</i> in Ihrem Browser.<br/><br/>Schliessen Sie diese Nachricht erst, nachdem Sie den Lesezeichen hinzugefügt haben.",
67
69
  "cross-section-settings": "Querschnitt",
68
70
  "CSV export": "CSV-Export",
69
71
  "Current Feature": "Aktuelles Objekt",
@@ -64,6 +64,8 @@
64
64
  "Copy value": "Copier value",
65
65
  "copy-cell-content": "Copy cell content",
66
66
  "Create custom theme": "Create custom theme",
67
+ "create-bookmark": "Create bookmark",
68
+ "create-bookmark-by-shortcut": "Press ${shortcut} or use your browser's <i>Add to Favorites</i> option.<br/><br/>Do not close this message until you have added the favorite.",
67
69
  "cross-section-settings": "Cross-section",
68
70
  "CSV export": "CSV export",
69
71
  "Current Feature": "Current Feature",
@@ -64,6 +64,8 @@
64
64
  "Copy value": "Copier la valeur",
65
65
  "copy-cell-content": "Copier le contenu de la cellule",
66
66
  "Create custom theme": "Créer un thème personnalisé",
67
+ "create-bookmark": "Créer un favori",
68
+ "create-bookmark-by-shortcut": "Appuyer sur ${shortcut} ou utilisez l'option <i>Ajouter aux favoris</i> de votre navigateur. <br/><br/>Ne fermez ce message qu'après avoir ajouté le favori.",
67
69
  "cross-section-settings": "Coupe transversale",
68
70
  "CSV export": "Export CSV",
69
71
  "Current Feature": "Entité actuelle",
@@ -64,6 +64,8 @@
64
64
  "Copy value": "Copia il valore",
65
65
  "copy-cell-content": "Copia contenuto cella",
66
66
  "Create custom theme": "Crea un tema personalizzato",
67
+ "create-bookmark": "Crea segnalibro",
68
+ "create-bookmark-by-shortcut": "Premere ${shortcut} o usare l'opzione <i>Aggiungi ai preferiti</i> del browser.<br/><br/>Non chiudere questo messaggio finché il preferito non è stato aggiunto.",
67
69
  "cross-section-settings": "Sezione trasversale",
68
70
  "CSV export": "Esporta CSV",
69
71
  "Current Feature": "Elemento corrente",
@@ -1 +1 @@
1
- <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" fill="none" viewBox="0 0 24 24"><mask id="a" width="24" height="24" x="0" y="0" maskUnits="userSpaceOnUse" style="mask-type:alpha"><path fill="#d9d9d9" d="M0 0h24v24H0z"/></mask><g mask="url(#a)"><path fill="#1c1b1f" d="M12 15q1.25 0 2.125-.875A2.9 2.9 0 0 0 15 12q0-1.25-.875-2.125A2.9 2.9 0 0 0 12 9q-1.25 0-2.125.875A2.9 2.9 0 0 0 9 12q0 1.25.875 2.125A2.9 2.9 0 0 0 12 15m0 2q-2.075 0-3.537-1.463Q7 14.075 7 12q0-2.075 1.463-3.537Q9.926 7 12 7q2.075 0 3.537 1.463Q17 9.926 17 12q0 2.075-1.463 3.537Q14.075 17 12 17M2 13a.97.97 0 0 1-.712-.287A.97.97 0 0 1 1 12q0-.424.288-.713A.97.97 0 0 1 2 11h2q.424 0 .713.287Q5 11.576 5 12q0 .424-.287.713A.97.97 0 0 1 4 13zm18 0a.97.97 0 0 1-.712-.287A.97.97 0 0 1 19 12q0-.424.288-.713A.97.97 0 0 1 20 11h2q.424 0 .712.287.288.288.288.713 0 .424-.288.713A.97.97 0 0 1 22 13zm-8-8a.97.97 0 0 1-.713-.287A.97.97 0 0 1 11 4V2q0-.424.287-.712A.97.97 0 0 1 12 1q.424 0 .713.288Q13 1.575 13 2v2q0 .424-.287.713A.97.97 0 0 1 12 5m0 18a.97.97 0 0 1-.713-.288A.97.97 0 0 1 11 22v-2q0-.424.287-.712A.97.97 0 0 1 12 19q.424 0 .713.288.287.287.287.712v2q0 .424-.287.712A.97.97 0 0 1 12 23M5.65 7.05 4.575 6a.87.87 0 0 1-.288-.7q.014-.425.288-.725.3-.3.725-.3t.7.3L7.05 5.65q.275.3.275.7t-.275.7a.86.86 0 0 1-.687.288 1.07 1.07 0 0 1-.713-.288M18 19.425l-1.05-1.075a1.02 1.02 0 0 1-.275-.713q0-.412.275-.687a.86.86 0 0 1 .688-.287q.412.012.712.287L19.425 18a.87.87 0 0 1 .287.7 1.09 1.09 0 0 1-.287.725q-.3.3-.725.3a.9.9 0 0 1-.7-.3M16.95 7.05a.86.86 0 0 1-.287-.687 1.07 1.07 0 0 1 .287-.713L18 4.575a.87.87 0 0 1 .7-.288q.425.014.725.288.3.3.3.725t-.3.7L18.35 7.05q-.3.275-.7.275t-.7-.275M4.575 19.425q-.3-.3-.3-.725t.3-.7l1.075-1.05q.3-.275.712-.275.414 0 .688.275.3.275.288.688a1.07 1.07 0 0 1-.288.712L6 19.425a.87.87 0 0 1-.7.287 1.09 1.09 0 0 1-.725-.287"/></g></svg>
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="800" height="800" viewBox="0 0 24 24"><defs><pattern xlink:href="#a" id="b" x="0" y="0" patternTransform="rotate(-45)" preserveAspectRatio="xMidYMid"/><pattern id="a" width="2" height="10" patternTransform="scale(2)" patternUnits="userSpaceOnUse" preserveAspectRatio="xMidYMid" style="fill:#000"><path d="M0-.5h1v11H0z" style="stroke:none"/></pattern></defs><title>ic_fluent_photo_filter_24_regular</title><g transform="translate(-.012 -.008)"><path d="M9.472 2.738a6.76 6.76 0 0 0-6.76 6.76 6.76 6.76 0 0 0 5.242 6.586 6.8 6.8 0 0 1-.178-1.542 6.76 6.76 0 0 1 6.76-6.76 6.8 6.8 0 0 1 1.506.171 6.76 6.76 0 0 0-6.57-5.215zm6.65 5.62a7 7 0 0 1 .055.314 7 7 0 0 0-.054-.313zm.106 1.082.003.061v-.004z" style="display:inline;opacity:1;fill:#000;fill-opacity:1;stroke:#000;stroke-width:1.44;stroke-dasharray:none;stroke-opacity:1"/><circle cx="14.536" cy="14.539" r="6.759" style="fill:#fff;stroke:#000;stroke-width:1.44;stroke-dasharray:none;stroke-opacity:1;fill-opacity:1"/><path d="M14.542 7.778a6.76 6.76 0 0 0-6.76 6.76 6.8 6.8 0 0 0 .18 1.545 6.8 6.8 0 0 0 1.516.173 6.76 6.76 0 0 0 6.76-6.76 6.8 6.8 0 0 0-.18-1.546 6.8 6.8 0 0 0-1.516-.172Z" style="display:inline;fill:url(#b);stroke:#000;stroke-width:1.44;stroke-dasharray:none"/></g></svg>
@@ -0,0 +1,21 @@
1
+ import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
+ import { IUrlShortener } from '../../tools/share/iurlshortener.js';
3
+ declare class AddBookmarkComponent extends GirafeHTMLElement {
4
+ protected templateUrl: string | null;
5
+ protected styleUrls: string[] | null;
6
+ template: () => import("uhtml").Hole;
7
+ private readonly isMac;
8
+ protected awaitingBookmarkLink: boolean;
9
+ private showingModal;
10
+ private bookmarkLink;
11
+ get urlShortener(): IUrlShortener | undefined;
12
+ constructor();
13
+ render(): void;
14
+ protected connectedCallback(): void;
15
+ protected disconnectedCallback(): void;
16
+ addBookmark(): Promise<void>;
17
+ private generateBookmarkLink;
18
+ private closeOnKeyUp;
19
+ private handleBookmarkShortcut;
20
+ }
21
+ export default AddBookmarkComponent;
@@ -0,0 +1,94 @@
1
+ import { html as uHtml } from 'uhtml';
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
4
+ import { noop } from '../../tools/utils/async.js';
5
+ class AddBookmarkComponent extends GirafeHTMLElement {
6
+ templateUrl = null;
7
+ styleUrls = null;
8
+ template = () => {
9
+ return uHtml `<style>
10
+ *{font-family:Arial,sans-serif}.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}.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-grad2);background-color:var(--text-color-grad2);color:var(--bkg-color)}.gg-button:disabled{color:gray;cursor:not-allowed;background-color:#d3d3d3;border:none}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{color:gray;cursor:not-allowed;background-color:#d3d3d3}.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,.gg-select:hover,.gg-input:hover,.gg-textarea:hover,.gg-icon-button:hover{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}
11
+ </style>
12
+ <style>${this.customStyle}</style>
13
+ <div class="add-bookmark"><button tip="add_bookmark" class="gg-icon-button gg-big" onclick="${() => this.addBookmark()}"><img alt="add-icon" src="icons/bookmark-add.svg" ?hidden="${this.awaitingBookmarkLink}"> <img ?hidden="${!this.awaitingBookmarkLink}" loading="eager" src="icons/progress.svg" class="gg-spin gg-small" alt="${this.context.i18nManager.getTranslation('Loading')}"></button></div>`;
14
+ };
15
+ isMac = navigator.userAgent.includes('Mac');
16
+ awaitingBookmarkLink = false;
17
+ showingModal = false;
18
+ bookmarkLink;
19
+ get urlShortener() {
20
+ return this.context.shareManager.UrlShortener;
21
+ }
22
+ constructor() {
23
+ super('addbookmark');
24
+ this.closeOnKeyUp = this.closeOnKeyUp.bind(this);
25
+ this.handleBookmarkShortcut = this.handleBookmarkShortcut.bind(this);
26
+ }
27
+ render() {
28
+ super.render();
29
+ }
30
+ connectedCallback() {
31
+ super.connectedCallback();
32
+ this.render();
33
+ super.girafeTranslate();
34
+ globalThis.addEventListener('keydown', this.handleBookmarkShortcut);
35
+ }
36
+ disconnectedCallback() {
37
+ super.disconnectedCallback();
38
+ globalThis.removeEventListener('keydown', this.handleBookmarkShortcut);
39
+ }
40
+ async addBookmark() {
41
+ this.showingModal = true;
42
+ const currentUrl = globalThis.location.href;
43
+ await this.generateBookmarkLink();
44
+ if (!this.bookmarkLink) {
45
+ return;
46
+ }
47
+ const messages = ['create-bookmark-by-shortcut'];
48
+ // Change URL so that this is used to create a bookmark when the User presses the Shortcut
49
+ globalThis.history.pushState({}, '', this.bookmarkLink);
50
+ const shortcut = this.isMac ? '⌘ + D' : 'Ctrl + D';
51
+ globalThis.addEventListener('keydown', this.closeOnKeyUp);
52
+ window
53
+ .gInfo(messages, 'create-bookmark', (translatedMessage) => {
54
+ return Array.isArray(translatedMessage)
55
+ ? translatedMessage.map((message) => message.replace('${shortcut}', shortcut)).join('<br /><br />')
56
+ : translatedMessage;
57
+ })
58
+ .then(() => {
59
+ this.showingModal = false;
60
+ globalThis.removeEventListener('keydown', this.closeOnKeyUp);
61
+ // Restore the URL afterwards
62
+ globalThis.history.pushState({}, '', currentUrl);
63
+ });
64
+ }
65
+ async generateBookmarkLink() {
66
+ if (!this.urlShortener) {
67
+ return;
68
+ }
69
+ this.awaitingBookmarkLink = true;
70
+ this.refreshRender();
71
+ const baseUrl = this.context.urlManager.getBaseUrlPath();
72
+ const hash = this.context.shareManager.getStateToShare();
73
+ const bookmarkUrl = `${baseUrl}#${hash}`;
74
+ const response = await this.urlShortener.shortenUrl(bookmarkUrl);
75
+ if (response.success) {
76
+ this.bookmarkLink = response.shorturl;
77
+ }
78
+ this.awaitingBookmarkLink = false;
79
+ this.refreshRender();
80
+ }
81
+ closeOnKeyUp(event) {
82
+ if (event.key === 'Escape') {
83
+ window.gCloseModal();
84
+ }
85
+ }
86
+ handleBookmarkShortcut(event) {
87
+ const addBookmarkShortcutPressed = event.key === 'd' && ((!this.isMac && event.ctrlKey) || (this.isMac && event.metaKey));
88
+ if (addBookmarkShortcutPressed && !this.showingModal) {
89
+ event.preventDefault();
90
+ this.addBookmark().then(noop);
91
+ }
92
+ }
93
+ }
94
+ export default AddBookmarkComponent;
@@ -1,4 +1,5 @@
1
1
  export { default as AboutComponent } from './about/component.js';
2
+ export { default as AddBookmarkComponent } from './addbookmark/component.js';
2
3
  export { default as AdvancedFilterComponent } from './advancedfilter/component.js';
3
4
  export { default as AlignNorthButtonMobile } from './alignnorthbutton-mobile/component.js';
4
5
  export { default as OauthComponent } from './auth/component.js';
@@ -1,4 +1,5 @@
1
1
  export { default as AboutComponent } from './about/component.js';
2
+ export { default as AddBookmarkComponent } from './addbookmark/component.js';
2
3
  export { default as AdvancedFilterComponent } from './advancedfilter/component.js';
3
4
  export { default as AlignNorthButtonMobile } from './alignnorthbutton-mobile/component.js';
4
5
  export { default as OauthComponent } from './auth/component.js';
@@ -1,5 +1,5 @@
1
1
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
- type ModalType = 'alert' | 'confirm' | 'prompt';
2
+ type ModalType = 'alert' | 'confirm' | 'prompt' | 'info';
3
3
  declare class ModalsComponent extends GirafeHTMLElement {
4
4
  protected templateUrl: string | null;
5
5
  protected styleUrls: string[] | null;
@@ -13,8 +13,10 @@ declare class ModalsComponent extends GirafeHTMLElement {
13
13
  resolvePrompt: (value: string | false) => void;
14
14
  constructor();
15
15
  private initBox;
16
+ private initBoxWithMessageArray;
16
17
  private resetBox;
17
18
  private promptBox;
19
+ private alertInfoBox;
18
20
  private alertConfirmBox;
19
21
  ok(): void;
20
22
  nok(): void;
@@ -8,10 +8,10 @@ class ModalsComponent extends GirafeHTMLElement {
8
8
  return uHtml `<style>
9
9
  *{font-family:Arial,sans-serif}.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}.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-grad2);background-color:var(--text-color-grad2);color:var(--bkg-color)}.gg-button:disabled{color:gray;cursor:not-allowed;background-color:#d3d3d3;border:none}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{color:gray;cursor:not-allowed;background-color:#d3d3d3}.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,.gg-select:hover,.gg-input:hover,.gg-textarea:hover,.gg-icon-button:hover{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}
10
10
  </style><style>
11
- #content{z-index:10000;background-color:#000000a6;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.modal{background:var(--bkg-color);border:var(--app-standard-border);border-radius:10px;width:300px;min-height:400px;box-shadow:5px 5px 10px #0003}header{background:var(--app-color-grey);border-top-left-radius:10px;border-top-right-radius:10px;justify-content:center;height:100px;padding:2rem;display:flex}section{text-align:center;flex-direction:column;padding:30px;display:flex}span{margin-bottom:1rem;font-size:1.2rem;display:inline-block}.confirm-buttons{flex-direction:row;justify-content:center;gap:1rem;margin-top:2rem;display:flex}.confirm-buttons button{width:7rem}h2,span{color:var(--text-color)}
11
+ #content{z-index:10000;background-color:#000000a6;justify-content:center;align-items:center;display:flex;position:fixed;inset:0}.modal{background:var(--bkg-color);border:var(--app-standard-border);border-radius:10px;width:300px;min-height:400px;box-shadow:5px 5px 10px #0003}.wide{width:auto;max-width:480px}header{background:var(--app-color-grey);border-top-left-radius:10px;border-top-right-radius:10px;justify-content:center;height:100px;padding:2rem;display:flex}section{text-align:center;flex-direction:column;padding:30px;display:flex}span{margin-bottom:1rem;font-size:1.2rem;display:inline-block}.confirm-buttons{flex-direction:row;justify-content:center;gap:1rem;margin-top:2rem;display:flex}.confirm-buttons button{width:7rem}h2,span{color:var(--text-color)}
12
12
  </style>
13
13
  <style>${this.customStyle}</style>
14
- <div id="content" class="${this.visible ? '' : 'hidden'}"><div class="modal"><header><img alt="question-icon" src="icons/help.svg" class="${this.boxType === 'alert' ? 'hidden' : ''}"> <img alt="error-icon" src="icons/error.svg" class="${this.boxType === 'alert' ? '' : 'hidden'}"></header><section><h2>${this.boxTitle}</h2><span>${this.boxMessage}</span> <input id="input-text" class="${this.boxType === 'prompt' ? 'gg-input' : 'hidden'}" placeholder="${this.boxPlaceholder}"><div class="${this.boxType === 'alert' ? 'confirm-buttons' : 'hidden'}"><button class="gg-button" onclick="${() => this.ok()}" i18n="Ok">Ok</button></div><div class="${this.boxType === 'confirm' ? 'confirm-buttons' : 'hidden'}"><button class="gg-button" onclick="${() => this.ok()}" i18n="Yes">Yes</button> <button class="gg-button" onclick="${() => this.nok()}" i18n="No">No</button></div><div class="${this.boxType === 'prompt' ? 'confirm-buttons' : 'hidden'}"><button class="gg-button" onclick="${() => this.promptOk()}" i18n="Ok">Ok</button> <button class="gg-button" onclick="${() => this.promptNok()}" i18n="Cancel">Cancel</button></div></section></div></div>`;
14
+ <div id="content" class="${this.visible ? '' : 'hidden'}"><div class="${this.boxType == 'info' ? 'modal wide' : 'modal'}"><header><img alt="question-icon" src="icons/help.svg" class="${(this.boxType === 'alert' || this.boxType === 'info') ? 'hidden' : ''}"> <img alt="error-icon" src="icons/error.svg" class="${this.boxType === 'alert' ? '' : 'hidden'}"> <img alt="info-icon" src="icons/info.svg" class="${this.boxType === 'info' ? '' : 'hidden'}"></header><section><h2>${this.boxTitle}</h2><span>${this.htmlUnsafe(this.boxMessage)}</span> <input id="input-text" class="${this.boxType === 'prompt' ? 'gg-input' : 'hidden'}" placeholder="${this.boxPlaceholder}"><div class="${(this.boxType === 'alert' || this.boxType === 'info') ? 'confirm-buttons' : 'hidden'}"><button class="gg-button" onclick="${() => this.ok()}" i18n="Ok">Ok</button></div><div class="${this.boxType === 'confirm' ? 'confirm-buttons' : 'hidden'}"><button class="gg-button" onclick="${() => this.ok()}" i18n="Yes">Yes</button> <button class="gg-button" onclick="${() => this.nok()}" i18n="No">No</button></div><div class="${this.boxType === 'prompt' ? 'confirm-buttons' : 'hidden'}"><button class="gg-button" onclick="${() => this.promptOk()}" i18n="Ok">Ok</button> <button class="gg-button" onclick="${() => this.promptNok()}" i18n="Cancel">Cancel</button></div></section></div></div>`;
15
15
  };
16
16
  visible = false;
17
17
  boxType;
@@ -35,6 +35,23 @@ class ModalsComponent extends GirafeHTMLElement {
35
35
  this.visible = true;
36
36
  this.refreshRender();
37
37
  }
38
+ initBoxWithMessageArray(type, message, messageHandler, title, placeholder) {
39
+ this.boxType = type;
40
+ if (Array.isArray(message)) {
41
+ this.boxMessage = messageHandler(message.map((msg) => this.context.i18nManager.getTranslation(msg)));
42
+ }
43
+ else {
44
+ this.boxMessage = messageHandler(this.context.i18nManager.getTranslation(message));
45
+ }
46
+ if (title) {
47
+ this.boxTitle = this.context.i18nManager.getTranslation(title);
48
+ }
49
+ if (placeholder) {
50
+ this.boxPlaceholder = this.context.i18nManager.getTranslation(placeholder);
51
+ }
52
+ this.visible = true;
53
+ this.refreshRender();
54
+ }
38
55
  resetBox() {
39
56
  this.visible = false;
40
57
  this.boxType = undefined;
@@ -44,11 +61,17 @@ class ModalsComponent extends GirafeHTMLElement {
44
61
  this.refreshRender();
45
62
  }
46
63
  async promptBox(message, title, placeholder) {
47
- this.initBox('prompt', message, (message) => message, title, placeholder);
64
+ this.initBox('prompt', message, (message) => (Array.isArray(message) ? message[0] : message), title, placeholder);
48
65
  return new Promise((resolve) => {
49
66
  this.resolvePrompt = resolve;
50
67
  });
51
68
  }
69
+ async alertInfoBox(message, title, messageHandler) {
70
+ this.initBoxWithMessageArray('info', message, messageHandler, title);
71
+ return new Promise((resolve) => {
72
+ this.resolveAlertConfirm = resolve;
73
+ });
74
+ }
52
75
  async alertConfirmBox(type, message, title, messageHandler) {
53
76
  this.initBox(type, message, messageHandler, title);
54
77
  return new Promise((resolve) => {
@@ -80,9 +103,14 @@ class ModalsComponent extends GirafeHTMLElement {
80
103
  connectedCallback() {
81
104
  super.connectedCallback();
82
105
  const defaultMessageHandler = (translatedMessage) => translatedMessage;
106
+ const defaultMessageArrayHandler = (translatedMessage) => Array.isArray(translatedMessage) ? translatedMessage[0] : translatedMessage;
83
107
  window.gConfirm = (message, title, messageHandler) => this.alertConfirmBox('confirm', message, title, messageHandler ?? defaultMessageHandler);
84
108
  window.gAlert = (message, title) => this.alertConfirmBox('alert', message, title ?? '', defaultMessageHandler);
85
109
  window.gPrompt = (message, title, placeholder) => this.promptBox(message, title, placeholder);
110
+ window.gInfo = (message, title, messageHandler) => this.alertInfoBox(message, title, messageHandler ?? defaultMessageArrayHandler);
111
+ window.gCloseModal = () => {
112
+ this.ok();
113
+ };
86
114
  this.render();
87
115
  super.girafeTranslate();
88
116
  }
@@ -1,3 +1,4 @@
1
+ import { IUrlShortener } from '../../tools/share/iurlshortener.js';
1
2
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
3
  import { ShareState } from './sharestate.js';
3
4
  import IGirafePanel from '../../tools/state/igirafepanel.js';
@@ -19,15 +20,14 @@ declare class ShareComponent extends GirafeHTMLElement implements IGirafePanel {
19
20
  iframeCode?: string;
20
21
  awaitingShareLink: boolean;
21
22
  awaitingIframeUrl: boolean;
22
- private urlShortener?;
23
23
  private simpleMaskManager?;
24
24
  private readonly eventsCallbacks;
25
25
  private iframeSize;
26
26
  get shareState(): ShareState;
27
+ get urlShortener(): IUrlShortener | undefined;
27
28
  get iframeWidth(): 400 | 600 | 800;
28
29
  get iframeHeight(): 300 | 600 | 450;
29
30
  constructor();
30
- private initializeShortenerService;
31
31
  render(): void;
32
32
  /**
33
33
  * Renders the component by calling the necessary methods.
@@ -4,10 +4,8 @@ import TwitterLogo from './images/twitter.svg';
4
4
  import FacebookLogo from './images/facebook.svg';
5
5
  import LinkedInLogo from './images/linkedin.svg';
6
6
  import MailLogo from './images/mail.svg';
7
- import GmfShareManager from './tools/gmfsharemanager.js';
8
7
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
9
8
  import SimpleMaskManager from '../../tools/layers/simplemaskmanager.js';
10
- import GeoGirafeShareManager from './tools/geogirafesharemanager.js';
11
9
  class ShareComponent extends GirafeHTMLElement {
12
10
  templateUrl = null;
13
11
  styleUrls = null;
@@ -34,13 +32,15 @@ class ShareComponent extends GirafeHTMLElement {
34
32
  iframeCode;
35
33
  awaitingShareLink = false;
36
34
  awaitingIframeUrl = false;
37
- urlShortener;
38
35
  simpleMaskManager;
39
36
  eventsCallbacks = [];
40
37
  iframeSize = '';
41
38
  get shareState() {
42
39
  return this.state.extendedState.share;
43
40
  }
41
+ get urlShortener() {
42
+ return this.context.shareManager.UrlShortener;
43
+ }
44
44
  get iframeWidth() {
45
45
  switch (this.iframeSize) {
46
46
  case 'small':
@@ -68,19 +68,6 @@ class ShareComponent extends GirafeHTMLElement {
68
68
  constructor() {
69
69
  super('share');
70
70
  }
71
- initializeShortenerService() {
72
- const share = this.context.configManager.Config.share;
73
- if (share) {
74
- switch (share.service) {
75
- case 'gmf':
76
- this.urlShortener = new GmfShareManager(share.createUrl);
77
- break;
78
- case 'geogirafe':
79
- this.urlShortener = new GeoGirafeShareManager(share.createUrl, this.context.urlManager);
80
- break;
81
- }
82
- }
83
- }
84
71
  render() {
85
72
  if (this.isPanelVisible) {
86
73
  this.renderComponent();
@@ -238,7 +225,6 @@ class ShareComponent extends GirafeHTMLElement {
238
225
  connectedCallback() {
239
226
  super.connectedCallback();
240
227
  this.render();
241
- this.initializeShortenerService();
242
228
  }
243
229
  }
244
230
  export default ShareComponent;
@@ -1,5 +1,5 @@
1
1
  import UrlManager from '../../../tools/url/urlmanager.js';
2
- import { IUrlShortener, UrlShortenerResponse } from './iurlshortener.js';
2
+ import { IUrlShortener, UrlShortenerResponse } from '../../../tools/share/iurlshortener.js';
3
3
  declare class GeoGirafeShareManager implements IUrlShortener {
4
4
  private readonly serviceUrl;
5
5
  private readonly urlManager;
@@ -1,4 +1,4 @@
1
- import { IUrlShortener, UrlShortenerResponse } from './iurlshortener.js';
1
+ import { IUrlShortener, UrlShortenerResponse } from '../../../tools/share/iurlshortener.js';
2
2
  export type GmfSuccessResponse = {
3
3
  short_url: string;
4
4
  };
package/main.tools.d.ts CHANGED
@@ -13,8 +13,10 @@ declare global {
13
13
  cordova: unknown;
14
14
  gConfirm(message: string, title: string, messageHandler?: (translatedMessage: string) => string): Promise<boolean>;
15
15
  gAlert(message: string, title?: string): Promise<boolean>;
16
+ gInfo(message: string | string[], title: string, messageHandler?: (translatedMessage: string | string[]) => string): Promise<boolean>;
16
17
  gPrompt(message: string, title?: string, placeholder?: string): Promise<string | false>;
17
18
  gOpenWindow(title: string, url: string, width?: string | number, height?: string | number, top?: string | number, left?: string | number): void;
19
+ gCloseModal(): void;
18
20
  }
19
21
  interface Navigator {
20
22
  connection: Connection;
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "GeoGirafe PSC",
6
6
  "url": "https://doc.geomapfish.dev"
7
7
  },
8
- "version": "1.1.0-dev.2563874711",
8
+ "version": "1.1.0-dev.2572458576",
9
9
  "type": "module",
10
10
  "engines": {
11
11
  "node": ">=20.19.0"
@@ -98,6 +98,7 @@
98
98
  "prettier-plugin-sort-json": "4.2.0",
99
99
  "proj4": "2.20.8",
100
100
  "svgo": "4.0.1",
101
+ "tsx": "4.22.3",
101
102
  "typescript": "6.0.2",
102
103
  "vite": "8.0.7",
103
104
  "vite-bundle-analyzer": "1.3.7",
@@ -171,6 +172,7 @@
171
172
  "generate-dev-certs-win": "buildtools\\generate-dev-certs.cmd",
172
173
  "generate-dev-certs-nix": "bash -C './buildtools/generate-dev-certs.sh'",
173
174
  "trust-default-dev-certs-win": "buildtools\\trust-dev-certs.cmd",
174
- "size-analysis": "node buildtools/app-clean.js && cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode analyze"
175
+ "size-analysis": "node buildtools/app-clean.js && cross-env NODE_OPTIONS=--max-old-space-size=8192 vite build --mode analyze",
176
+ "create-component": "tsx buildtools/create-component.ts"
175
177
  }
176
178
  }
package/styles/index.css CHANGED
@@ -186,6 +186,9 @@ girafe-selection-window {
186
186
  girafe-nav-history {
187
187
  z-index: 10;
188
188
  }
189
+ girafe-add-bookmark {
190
+ z-index: 10;
191
+ }
189
192
 
190
193
  girafe-info-window {
191
194
  position: absolute;
@@ -124,7 +124,7 @@
124
124
  </girafe-search>
125
125
  <girafe-news-button plugin="config:news"></girafe-news-button>
126
126
  <girafe-oauth></girafe-oauth>
127
- <girafe-nav-history></girafe-nav-history>
127
+ <girafe-add-bookmark></girafe-add-bookmark>
128
128
  <girafe-video-record></girafe-video-record>
129
129
  </header>
130
130
 
@@ -1 +1 @@
1
- {"version":"1.1.0-dev.2563874711", "build":"2563874711", "date":"30/05/2026"}
1
+ {"version":"1.1.0-dev.2572458576", "build":"2572458576", "date":"03/06/2026"}
@@ -50,6 +50,7 @@ import SelectionToolComponent from '../../components/selectiontool/component.js'
50
50
  import AdvancedFilterComponent from '../../components/advancedfilter/component.js';
51
51
  import FixedDimensionComponent from '../../components/drawing/fixed-dimension/component.js';
52
52
  import GetDirectionsArtifact from '../../components/getdirections/component.js';
53
+ import AddBookmarkComponent from '../../components/addbookmark/component.js';
53
54
  export default class GeoGirafeApp {
54
55
  readyPromise;
55
56
  resolveReady;
@@ -144,6 +145,7 @@ export default class GeoGirafeApp {
144
145
  customElements.define('girafe-video-record', VideoRecordComponent);
145
146
  customElements.define('girafe-fixed-dimension', FixedDimensionComponent);
146
147
  customElements.define('girafe-get-directions', GetDirectionsArtifact);
148
+ customElements.define('girafe-add-bookmark', AddBookmarkComponent);
147
149
  }
148
150
  async initializeServiceWorker() {
149
151
  try {
package/tools/main.d.ts CHANGED
@@ -55,6 +55,7 @@ export type { GeoTransform } from './raster/rasterutils.js';
55
55
  export { extractGeoTransform, mapToPixel, pixelToMap, getImage, getPixelValue } from './raster/rasterutils.js';
56
56
  export { default as ResizeWindow } from './resizewindow.js';
57
57
  export type { default as ISessionManager } from './share/isessionmanager.js';
58
+ export type { UrlShortenerResponse, IUrlShortener } from './share/iurlshortener.js';
58
59
  export { default as ActiveBasemapsSerializer } from './share/serializers/activebasemapsserializer.js';
59
60
  export { default as CustomLayersSerializer } from './share/serializers/customlayersserializer.js';
60
61
  export { default as GlobeSerializer } from './share/serializers/globeserializer.js';
@@ -1,5 +1,9 @@
1
1
  import GirafeSingleton from '../../base/GirafeSingleton.js';
2
+ import { IUrlShortener } from './iurlshortener.js';
2
3
  declare class ShareManager extends GirafeSingleton {
4
+ private urlShortener?;
5
+ get UrlShortener(): IUrlShortener | undefined;
6
+ initializeSingleton(): void;
3
7
  getStateToShare(): string;
4
8
  hasSharedState(): boolean;
5
9
  private getStateFromUrl;
@@ -1,6 +1,25 @@
1
1
  // SPDX-License-Identifier: Apache-2.0
2
2
  import GirafeSingleton from '../../base/GirafeSingleton.js';
3
+ import GmfShareManager from '../../components/share/tools/gmfsharemanager.js';
4
+ import GeoGirafeShareManager from '../../components/share/tools/geogirafesharemanager.js';
3
5
  class ShareManager extends GirafeSingleton {
6
+ urlShortener;
7
+ get UrlShortener() {
8
+ return this.urlShortener;
9
+ }
10
+ initializeSingleton() {
11
+ const share = this.context.configManager.Config.share;
12
+ if (share) {
13
+ switch (share.service) {
14
+ case 'gmf':
15
+ this.urlShortener = new GmfShareManager(share.createUrl);
16
+ break;
17
+ case 'geogirafe':
18
+ this.urlShortener = new GeoGirafeShareManager(share.createUrl, this.context.urlManager);
19
+ break;
20
+ }
21
+ }
22
+ }
4
23
  getStateToShare() {
5
24
  const encodedState = this.context.stateSerializer.getSerializedState();
6
25
  return encodedState;