@geogirafe/lib-geoportal 1.1.0-dev.2418238104 → 1.1.0-dev.2433737633
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/CONTRIBUTING.md +1 -1
- package/assets/i18n/de.json +2 -0
- package/assets/i18n/en.json +2 -0
- package/assets/i18n/fr.json +2 -0
- package/assets/i18n/it.json +2 -0
- package/assets/icons/bell-warn.svg +1 -0
- package/assets/icons/bell.svg +1 -0
- package/components/main.d.ts +2 -0
- package/components/main.js +2 -0
- package/components/news/news-button/component.d.ts +12 -0
- package/components/news/news-button/component.js +47 -0
- package/components/news/news-panel/component.d.ts +18 -0
- package/components/news/news-panel/component.js +66 -0
- package/components/news/news-panel/newsfeedutils.d.ts +10 -0
- package/components/news/news-panel/newsfeedutils.js +99 -0
- package/package.json +1 -1
- package/templates/index.html +2 -0
- package/templates/public/about.json +1 -1
- package/tools/app/geogirafeapp.js +4 -0
- package/tools/configuration/girafeconfig.d.ts +5 -0
- package/tools/configuration/girafeconfig.js +13 -0
- package/tools/main.d.ts +1 -1
- package/tools/state/graphicalInterface.d.ts +1 -0
- package/tools/state/graphicalInterface.js +1 -0
- package/tools/state/state.d.ts +6 -0
- package/tools/state/state.js +5 -0
- package/tools/state/stateToggleManager.spec.js +5 -1
package/CONTRIBUTING.md
CHANGED
package/assets/i18n/de.json
CHANGED
|
@@ -229,6 +229,7 @@
|
|
|
229
229
|
"My Drawing": "Meine Zeichnung",
|
|
230
230
|
"My selection": "Meine Auswahl",
|
|
231
231
|
"Name": "Name",
|
|
232
|
+
"news": "Nachrichten",
|
|
232
233
|
"No": "Nein",
|
|
233
234
|
"No bookmark": "Kein Lesezeichen",
|
|
234
235
|
"No data": "Keine Daten",
|
|
@@ -383,6 +384,7 @@
|
|
|
383
384
|
"Show metadata": "Metadaten anzeigen",
|
|
384
385
|
"Show or hide measure information": "Massinformationen anzeigen oder ausblenden",
|
|
385
386
|
"Show or hide shape name": "Formname anzeigen oder ausblenden",
|
|
387
|
+
"show_content": "Inhalt anzeigen",
|
|
386
388
|
"sitn2001": "LiDAR 2001",
|
|
387
389
|
"sitn2010": "LiDAR 2010",
|
|
388
390
|
"sitn2016": "LiDAR 2016",
|
package/assets/i18n/en.json
CHANGED
|
@@ -230,6 +230,7 @@
|
|
|
230
230
|
"My Drawing": "My Drawing",
|
|
231
231
|
"My selection": "My selection",
|
|
232
232
|
"Name": "Name",
|
|
233
|
+
"news": "News",
|
|
233
234
|
"No": "No",
|
|
234
235
|
"No bookmark": "No bookmark",
|
|
235
236
|
"No data": "No data",
|
|
@@ -384,6 +385,7 @@
|
|
|
384
385
|
"Show metadata": "Show metadata",
|
|
385
386
|
"Show or hide measure information": "Show or hide measure information",
|
|
386
387
|
"Show or hide shape name": "Show or hide shape name",
|
|
388
|
+
"show_content": "Show content",
|
|
387
389
|
"sitn2001": "LiDAR 2001",
|
|
388
390
|
"sitn2010": "LiDAR 2010",
|
|
389
391
|
"sitn2016": "LiDAR 2016",
|
package/assets/i18n/fr.json
CHANGED
|
@@ -229,6 +229,7 @@
|
|
|
229
229
|
"My Drawing": "Mon dessin",
|
|
230
230
|
"My selection": "Ma sélection",
|
|
231
231
|
"Name": "Nom",
|
|
232
|
+
"news": "Actualités",
|
|
232
233
|
"No": "Non",
|
|
233
234
|
"No bookmark": "Aucun favori",
|
|
234
235
|
"No data": "Pas de données",
|
|
@@ -383,6 +384,7 @@
|
|
|
383
384
|
"Show metadata": "Afficher les métadonnées",
|
|
384
385
|
"Show or hide measure information": "Afficher ou masquer les informations de mesure",
|
|
385
386
|
"Show or hide shape name": "Afficher ou masquer le nom de la forme",
|
|
387
|
+
"show_content": "Afficher le contenu",
|
|
386
388
|
"sitn2001": "LiDAR 2001",
|
|
387
389
|
"sitn2010": "LiDAR 2010",
|
|
388
390
|
"sitn2016": "LiDAR 2016",
|
package/assets/i18n/it.json
CHANGED
|
@@ -229,6 +229,7 @@
|
|
|
229
229
|
"My Drawing": "Il mio disegno",
|
|
230
230
|
"My selection": "La mia selezione",
|
|
231
231
|
"Name": "Nome",
|
|
232
|
+
"news": "Notizie",
|
|
232
233
|
"No": "No",
|
|
233
234
|
"No bookmark": "Nessun segnalibro",
|
|
234
235
|
"No data": "Nessun dato",
|
|
@@ -383,6 +384,7 @@
|
|
|
383
384
|
"Show metadata": "Mostra metadati",
|
|
384
385
|
"Show or hide measure information": "Mostra o nascondi le informazioni di misura",
|
|
385
386
|
"Show or hide shape name": "Mostra o nascondi il nome della forma",
|
|
387
|
+
"show_content": "Visualizzare il contenuto",
|
|
386
388
|
"sitn2001": "LiDAR 2001",
|
|
387
389
|
"sitn2010": "LiDAR 2010",
|
|
388
390
|
"sitn2016": "LiDAR 2016",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="24" height="24" fill="none" viewBox="0 0 24 24"><circle cx="12" cy="3.131" r="1.411" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:1.63248;stroke-linecap:round;stroke-linejoin:round"/><path d="M4 17.2h16C16 17.2 20 4 12 4S8 17.2 4 17.2" style="fill:none;fill-opacity:1;stroke:#000;stroke-width:2.11754;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"/><path d="M14.566 19.778A2.57 2.57 0 0 1 13.283 22a2.57 2.57 0 0 1-2.566 0 2.57 2.57 0 0 1-1.283-2.222H12Z" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:4.212;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"/><circle cx="16.706" cy="7.332" r="5.168" style="fill:red;fill-opacity:1;stroke:none;stroke-width:33.6314"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="24" height="24" fill="none" viewBox="0 0 24 24"><circle cx="12" cy="3.131" r="1.411" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:1.63248;stroke-linecap:round;stroke-linejoin:round"/><path d="M4 17.2h16C16 17.2 20 4 12 4S8 17.2 4 17.2" style="fill:none;fill-opacity:1;stroke:#000;stroke-width:2.11754;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"/><path d="M14.566 19.778A2.57 2.57 0 0 1 13.283 22a2.57 2.57 0 0 1-2.566 0 2.57 2.57 0 0 1-1.283-2.222H12Z" style="fill:#000;fill-opacity:1;stroke:none;stroke-width:4.212;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"/></svg>
|
package/components/main.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ export { default as MenuMobile } from './menu-mobile/component.js';
|
|
|
27
27
|
export { default as MenuButtonComponent } from './menubutton/component.js';
|
|
28
28
|
export { default as ModalsComponent } from './modals/component.js';
|
|
29
29
|
export { default as NavigationComponent } from './navigation/component.js';
|
|
30
|
+
export { default as NewsButtonComponent } from './news/news-button/component.js';
|
|
31
|
+
export { default as NewsPanelComponent } from './news/news-panel/component.js';
|
|
30
32
|
export { default as OfflineComponent } from './offline/component.js';
|
|
31
33
|
export { default as PrintComponent } from './print/component.js';
|
|
32
34
|
export { default as PrototypeBannerComponent } from './prototypebanner/component.js';
|
package/components/main.js
CHANGED
|
@@ -27,6 +27,8 @@ export { default as MenuMobile } from './menu-mobile/component.js';
|
|
|
27
27
|
export { default as MenuButtonComponent } from './menubutton/component.js';
|
|
28
28
|
export { default as ModalsComponent } from './modals/component.js';
|
|
29
29
|
export { default as NavigationComponent } from './navigation/component.js';
|
|
30
|
+
export { default as NewsButtonComponent } from './news/news-button/component.js';
|
|
31
|
+
export { default as NewsPanelComponent } from './news/news-panel/component.js';
|
|
30
32
|
export { default as OfflineComponent } from './offline/component.js';
|
|
31
33
|
export { default as PrintComponent } from './print/component.js';
|
|
32
34
|
export { default as PrototypeBannerComponent } from './prototypebanner/component.js';
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import GirafeHTMLElement from '../../../base/GirafeHTMLElement.js';
|
|
2
|
+
declare class NewsButtonComponent extends GirafeHTMLElement {
|
|
3
|
+
template: () => import("uhtml").Hole;
|
|
4
|
+
visible: boolean;
|
|
5
|
+
warn: boolean;
|
|
6
|
+
constructor();
|
|
7
|
+
setWarn(): void;
|
|
8
|
+
loadLastViewDate(): void;
|
|
9
|
+
getIcon(): "icons/bell-warn.svg" | "icons/bell.svg";
|
|
10
|
+
connectedCallback(): void;
|
|
11
|
+
}
|
|
12
|
+
export default NewsButtonComponent;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { html as uHtml } from 'uhtml';
|
|
2
|
+
import GirafeHTMLElement from '../../../base/GirafeHTMLElement.js';
|
|
3
|
+
class NewsButtonComponent extends GirafeHTMLElement {
|
|
4
|
+
template = () => {
|
|
5
|
+
return uHtml `<style>
|
|
6
|
+
button img{width:1.5rem}
|
|
7
|
+
</style><style>
|
|
8
|
+
*{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}
|
|
9
|
+
</style>
|
|
10
|
+
<button tip="news" class="gg-icon-button gg-big" onclick="document.geogirafe.state.interface.newsPanelVisible=!document.geogirafe.state.interface.newsPanelVisible"><img alt="news-icon" src="${this.getIcon()}"></button>`;
|
|
11
|
+
};
|
|
12
|
+
visible = false;
|
|
13
|
+
warn = false;
|
|
14
|
+
constructor() {
|
|
15
|
+
super('news-button');
|
|
16
|
+
}
|
|
17
|
+
setWarn() {
|
|
18
|
+
this.warn = this.state.news.lastPublishDate > this.state.news.lastViewDate;
|
|
19
|
+
if (this.context.configManager.Config.news?.autoDisplay && this.warn) {
|
|
20
|
+
this.state.interface.newsPanelVisible = true;
|
|
21
|
+
}
|
|
22
|
+
super.render();
|
|
23
|
+
}
|
|
24
|
+
loadLastViewDate() {
|
|
25
|
+
const lastViewDate = this.context.userDataManager.getUserData('lastViewDate');
|
|
26
|
+
if (lastViewDate) {
|
|
27
|
+
this.context.stateManager.state.news.lastViewDate = lastViewDate;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
getIcon() {
|
|
31
|
+
const icon = this.warn ? 'icons/bell-warn.svg' : 'icons/bell.svg';
|
|
32
|
+
return icon;
|
|
33
|
+
}
|
|
34
|
+
connectedCallback() {
|
|
35
|
+
super.connectedCallback();
|
|
36
|
+
if (this.context.configManager.Config.news) {
|
|
37
|
+
this.subscribe('news.lastViewDate', (_oldValue, _newValue) => {
|
|
38
|
+
this.setWarn();
|
|
39
|
+
});
|
|
40
|
+
this.subscribe('news.lastPublishDate', (_oldValue, _newValue) => {
|
|
41
|
+
this.setWarn();
|
|
42
|
+
});
|
|
43
|
+
super.girafeTranslate();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export default NewsButtonComponent;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { FeedEntry } from './newsfeedutils.js';
|
|
2
|
+
import GirafeHTMLElement from '../../../base/GirafeHTMLElement.js';
|
|
3
|
+
import IGirafePanel from '../../../tools/state/igirafepanel.js';
|
|
4
|
+
declare class NewsPanelComponent extends GirafeHTMLElement implements IGirafePanel {
|
|
5
|
+
template: () => import("uhtml").Hole;
|
|
6
|
+
isPanelVisible: boolean;
|
|
7
|
+
panelTitle: string;
|
|
8
|
+
panelTogglePath: string;
|
|
9
|
+
entries: FeedEntry[];
|
|
10
|
+
constructor();
|
|
11
|
+
saveLastViewDate(): void;
|
|
12
|
+
loadLastViewDate(): void;
|
|
13
|
+
togglePanel(visible: boolean): void;
|
|
14
|
+
render(): void;
|
|
15
|
+
private initializeNewsState;
|
|
16
|
+
protected connectedCallback(): void;
|
|
17
|
+
}
|
|
18
|
+
export default NewsPanelComponent;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { htmlFor as uHtmlFor } from 'uhtml/keyed';
|
|
2
|
+
import { html as uHtml } from 'uhtml';
|
|
3
|
+
import GirafeHTMLElement from '../../../base/GirafeHTMLElement.js';
|
|
4
|
+
import { loadFeeds } from './newsfeedutils.js';
|
|
5
|
+
class NewsPanelComponent extends GirafeHTMLElement {
|
|
6
|
+
template = () => {
|
|
7
|
+
return uHtml `<style>
|
|
8
|
+
#panel{box-sizing:border-box;flex-direction:column;min-width:20rem;height:100%;min-height:0;display:flex;overflow:auto}#content{background:var(--bkg-color);box-sizing:border-box;width:100%;min-width:0;color:var(--text-color);flex:auto;margin:0;padding:1.5rem}.news-item{box-sizing:border-box;background-color:#c8d1d12b;border:1px solid #d5d5d5;width:100%;min-width:0;margin:16px 0;padding:15px 13px;box-shadow:0 0 5px #0000001f}.news-header{justify-content:space-between;align-items:flex-start;gap:1rem;display:flex}.news-title{flex:auto;min-width:0;font-size:14px}.news-meta{text-align:right;color:#6b7280;white-space:nowrap;flex:none;font-size:.8rem;line-height:1.3}.news-date{font-weight:500}.news-age{opacity:.8}.news-description{margin-top:.75rem}.news-details{width:100%;min-width:0;margin-top:.75rem}.details-content{box-sizing:border-box;contain:inline-size;width:100%;min-width:0;max-width:100%;overflow-x:auto}.news-details-toggle{cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:none;padding:6px}.news-details-toggle:hover{background-color:#fdf6c3ba}
|
|
9
|
+
</style><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
|
+
<div id="panel"><div id="content">${this.entries.map(f => uHtmlFor(f, f.id) `<div class="news-item"><div class="news-header"><div class="news-title"><b>${f.title}</b></div><div class="news-meta"><div class="news-date">${f.date?.toLocaleDateString(undefined, { day: '2-digit', month: 'short', year: 'numeric' }) ?? ''}</div></div></div><div class="news-description">${this.htmlUnsafe(f.description ?? '')}</div>${f.content ? uHtml `<details class="news-details"><summary class="news-details-toggle" i18n="show_content">Show content</summary><div class="details-content">${this.htmlUnsafe(f.content ?? '')}</div></details>` : ''}</div>`)}</div></div>`;
|
|
13
|
+
};
|
|
14
|
+
isPanelVisible = false;
|
|
15
|
+
panelTitle = 'news';
|
|
16
|
+
panelTogglePath = 'interface.newsPanelVisible'; // Subscribes to changes in the state manager's newsPanelVisible property
|
|
17
|
+
entries = [];
|
|
18
|
+
constructor() {
|
|
19
|
+
super('news-panel');
|
|
20
|
+
}
|
|
21
|
+
saveLastViewDate() {
|
|
22
|
+
const currentTimestamp = Date.now();
|
|
23
|
+
this.context.userDataManager.saveUserData('lastViewDate', currentTimestamp);
|
|
24
|
+
this.context.stateManager.state.news.lastViewDate = currentTimestamp;
|
|
25
|
+
}
|
|
26
|
+
loadLastViewDate() {
|
|
27
|
+
const raw = this.context.userDataManager.getUserData('lastViewDate');
|
|
28
|
+
const lastViewDate = Number(raw);
|
|
29
|
+
this.context.stateManager.state.news.lastViewDate = Number.isFinite(lastViewDate) ? lastViewDate : 0;
|
|
30
|
+
}
|
|
31
|
+
togglePanel(visible) {
|
|
32
|
+
this.isPanelVisible = visible;
|
|
33
|
+
if (visible) {
|
|
34
|
+
this.saveLastViewDate();
|
|
35
|
+
}
|
|
36
|
+
this.render();
|
|
37
|
+
}
|
|
38
|
+
render() {
|
|
39
|
+
if (this.isPanelVisible) {
|
|
40
|
+
super.render();
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
this.hide();
|
|
44
|
+
}
|
|
45
|
+
super.girafeTranslate();
|
|
46
|
+
}
|
|
47
|
+
async initializeNewsState() {
|
|
48
|
+
if (!this.context.configManager.Config.news) {
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
this.loadLastViewDate();
|
|
52
|
+
const urls = this.context.configManager.Config.news.urls;
|
|
53
|
+
this.entries = await loadFeeds(urls);
|
|
54
|
+
// Update state manager
|
|
55
|
+
const lastPublishDate = this.entries.length > 0 ? (this.entries[0].date?.getTime() ?? 0) : 0;
|
|
56
|
+
this.context.stateManager.state.news.lastPublishDate = lastPublishDate;
|
|
57
|
+
}
|
|
58
|
+
connectedCallback() {
|
|
59
|
+
super.connectedCallback();
|
|
60
|
+
if (this.context.configManager.Config.news) {
|
|
61
|
+
void this.initializeNewsState();
|
|
62
|
+
this.render();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export default NewsPanelComponent;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import DOMPurify from 'dompurify';
|
|
2
|
+
export async function loadFeeds(urls) {
|
|
3
|
+
const entries = [];
|
|
4
|
+
for (const url of urls) {
|
|
5
|
+
const res = await fetch(url);
|
|
6
|
+
const xmlText = await res.text();
|
|
7
|
+
const parser = new DOMParser();
|
|
8
|
+
const xml = parser.parseFromString(xmlText, 'text/xml');
|
|
9
|
+
const feedType = detectFeedType(xml);
|
|
10
|
+
if (feedType === 'rss') {
|
|
11
|
+
entries.push(...parseRssFeed(xml));
|
|
12
|
+
}
|
|
13
|
+
else if (feedType === 'atom') {
|
|
14
|
+
entries.push(...parseAtomFeed(xml));
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
console.warn(`Invalid feed URL or unknown feed format: ${url}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
entries.sort((a, b) => (b.date?.getTime() ?? 0) - (a.date?.getTime() ?? 0));
|
|
21
|
+
return entries;
|
|
22
|
+
}
|
|
23
|
+
function detectFeedType(xml) {
|
|
24
|
+
const root = xml.documentElement;
|
|
25
|
+
const tagName = root.tagName.toLowerCase();
|
|
26
|
+
if (tagName.includes('rss')) {
|
|
27
|
+
return 'rss';
|
|
28
|
+
}
|
|
29
|
+
if (tagName.includes('feed')) {
|
|
30
|
+
return 'atom';
|
|
31
|
+
}
|
|
32
|
+
if (tagName.includes('rdf')) {
|
|
33
|
+
return 'rss'; // RSS 1.0
|
|
34
|
+
}
|
|
35
|
+
return 'unknown';
|
|
36
|
+
}
|
|
37
|
+
function parseRssFeed(xml) {
|
|
38
|
+
return Array.from(xml.querySelectorAll('item')).map((entry, index) => {
|
|
39
|
+
const title = entry.querySelector('title')?.textContent?.trim() || '';
|
|
40
|
+
const pubDateText = entry.querySelector('pubDate')?.textContent?.trim() || '';
|
|
41
|
+
const date = pubDateText ? new Date(pubDateText) : null;
|
|
42
|
+
const description = getSanitizedHtml(entry.querySelector('description'));
|
|
43
|
+
const contentNode = entry.getElementsByTagNameNS('http://purl.org/rss/1.0/modules/content/', 'encoded')[0];
|
|
44
|
+
const content = getSanitizedHtml(contentNode);
|
|
45
|
+
const link = entry.querySelector('link')?.textContent?.trim() || '';
|
|
46
|
+
const author = entry.querySelector('author')?.textContent?.trim() ||
|
|
47
|
+
entry.getElementsByTagName('dc:creator')[0]?.textContent?.trim() ||
|
|
48
|
+
'';
|
|
49
|
+
const guid = entry.querySelector('guid')?.textContent?.trim() || '';
|
|
50
|
+
const id = guid || link || `rss-${index}`;
|
|
51
|
+
return {
|
|
52
|
+
id,
|
|
53
|
+
title,
|
|
54
|
+
date,
|
|
55
|
+
description,
|
|
56
|
+
content,
|
|
57
|
+
link,
|
|
58
|
+
author
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function parseAtomFeed(xml) {
|
|
63
|
+
return Array.from(xml.querySelectorAll('entry')).map((entry, index) => {
|
|
64
|
+
const title = entry.querySelector('title')?.textContent?.trim() || '';
|
|
65
|
+
const id = entry.querySelector('id')?.textContent?.trim() || `atom-${index}`;
|
|
66
|
+
const updated = entry.querySelector('updated')?.textContent?.trim() || '';
|
|
67
|
+
const published = entry.querySelector('published')?.textContent?.trim() || '';
|
|
68
|
+
const date = updated || published ? new Date(updated || published) : null;
|
|
69
|
+
const summary = getSanitizedHtml(entry.querySelector('summary'));
|
|
70
|
+
const content = getSanitizedHtml(entry.querySelector('content'));
|
|
71
|
+
const description = summary || content;
|
|
72
|
+
const author = entry.querySelector('author > name')?.textContent?.trim() ||
|
|
73
|
+
entry.querySelector('author')?.textContent?.trim() ||
|
|
74
|
+
'';
|
|
75
|
+
const link = entry.querySelector('link[rel="alternate"]')?.getAttribute('href') ||
|
|
76
|
+
entry.querySelector('link')?.getAttribute('href') ||
|
|
77
|
+
'';
|
|
78
|
+
return {
|
|
79
|
+
id,
|
|
80
|
+
title,
|
|
81
|
+
date,
|
|
82
|
+
description,
|
|
83
|
+
content,
|
|
84
|
+
link,
|
|
85
|
+
author
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function sanitizeHtml(value) {
|
|
90
|
+
return DOMPurify.sanitize(value, {
|
|
91
|
+
USE_PROFILES: { html: true },
|
|
92
|
+
FORBID_TAGS: ['style', 'script', 'iframe', 'object', 'embed'],
|
|
93
|
+
FORBID_ATTR: ['style', 'onerror', 'onload', 'onclick']
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function getSanitizedHtml(node) {
|
|
97
|
+
const raw = node?.textContent?.trim() || '';
|
|
98
|
+
return raw ? sanitizeHtml(raw) : '';
|
|
99
|
+
}
|
package/package.json
CHANGED
package/templates/index.html
CHANGED
|
@@ -94,6 +94,7 @@
|
|
|
94
94
|
</p>
|
|
95
95
|
</div> -->
|
|
96
96
|
</girafe-search>
|
|
97
|
+
<girafe-news-button plugin="config:news"></girafe-news-button>
|
|
97
98
|
<girafe-oauth></girafe-oauth>
|
|
98
99
|
<girafe-nav-history></girafe-nav-history>
|
|
99
100
|
<girafe-video-record></girafe-video-record>
|
|
@@ -136,6 +137,7 @@
|
|
|
136
137
|
<girafe-print slot="main"></girafe-print>
|
|
137
138
|
<girafe-ext-layer slot="main"></girafe-ext-layer>
|
|
138
139
|
<girafe-cross-section-settings slot="main"></girafe-cross-section-settings>
|
|
140
|
+
<girafe-news slot="main"></girafe-news>
|
|
139
141
|
<girafe-edit slot="main"></girafe-edit>
|
|
140
142
|
<girafe-layout slot="main"></girafe-layout>
|
|
141
143
|
<girafe-about slot="main"></girafe-about>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.1.0-dev.
|
|
1
|
+
{"version":"1.1.0-dev.2433737633", "build":"2433737633", "date":"07/04/2026"}
|
|
@@ -21,6 +21,8 @@ import MapComponent from '../../components/map/component.js';
|
|
|
21
21
|
import MenuButtonComponent from '../../components/menubutton/component.js';
|
|
22
22
|
import ModalsComponent from '../../components/modals/component.js';
|
|
23
23
|
import NavigationComponent from '../../components/navigation/component.js';
|
|
24
|
+
import NewsButtonComponent from '../../components/news/news-button/component.js';
|
|
25
|
+
import NewsPanelComponent from '../../components/news/news-panel/component.js';
|
|
24
26
|
import OauthComponent from '../../components/auth/component.js';
|
|
25
27
|
import PrintComponent from '../../components/print/component.js';
|
|
26
28
|
import PrototypeBannerComponent from '../../components/prototypebanner/component.js';
|
|
@@ -110,6 +112,8 @@ export default class GeoGirafeApp {
|
|
|
110
112
|
customElements.define('girafe-menu-button', MenuButtonComponent);
|
|
111
113
|
customElements.define('girafe-modals', ModalsComponent);
|
|
112
114
|
customElements.define('girafe-nav-history', NavigationComponent);
|
|
115
|
+
customElements.define('girafe-news-button', NewsButtonComponent);
|
|
116
|
+
customElements.define('girafe-news', NewsPanelComponent);
|
|
113
117
|
customElements.define('girafe-print', PrintComponent);
|
|
114
118
|
customElements.define('girafe-prototype-banner', PrototypeBannerComponent);
|
|
115
119
|
customElements.define('girafe-query-builder', QueryBuilderComponent);
|
|
@@ -117,6 +117,10 @@ declare class GirafeConfig {
|
|
|
117
117
|
lidar?: {
|
|
118
118
|
url: string;
|
|
119
119
|
};
|
|
120
|
+
news?: {
|
|
121
|
+
urls: string[];
|
|
122
|
+
autoDisplay: boolean;
|
|
123
|
+
};
|
|
120
124
|
externalLayers?: {
|
|
121
125
|
predefinedSources: {
|
|
122
126
|
label: string;
|
|
@@ -257,6 +261,7 @@ declare class GirafeConfig {
|
|
|
257
261
|
private initConfigTreeview;
|
|
258
262
|
private initConfigBasemaps;
|
|
259
263
|
private initConfigLidar;
|
|
264
|
+
private initConfigNews;
|
|
260
265
|
private initConfigCsv;
|
|
261
266
|
private initConfigMetadata;
|
|
262
267
|
private initConfigInfoWindow;
|
|
@@ -15,6 +15,7 @@ class GirafeConfig {
|
|
|
15
15
|
map;
|
|
16
16
|
map3d;
|
|
17
17
|
lidar;
|
|
18
|
+
news;
|
|
18
19
|
externalLayers;
|
|
19
20
|
contextmenu;
|
|
20
21
|
crs;
|
|
@@ -52,6 +53,7 @@ class GirafeConfig {
|
|
|
52
53
|
this.drawing = this.initConfigDrawing(config);
|
|
53
54
|
this.projections = this.initConfigProjections(config);
|
|
54
55
|
this.map = this.initConfigMap(config);
|
|
56
|
+
this.news = this.initConfigNews(config);
|
|
55
57
|
this.lidar = this.initConfigLidar(config);
|
|
56
58
|
this.csv = this.initConfigCsv(config);
|
|
57
59
|
this.metadata = this.initConfigMetadata(config);
|
|
@@ -104,6 +106,14 @@ class GirafeConfig {
|
|
|
104
106
|
// We just display a warning in the console
|
|
105
107
|
console.warn(e);
|
|
106
108
|
}
|
|
109
|
+
try {
|
|
110
|
+
this.news = this.initConfigNews(config);
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
// The application can be started even if the news is not correctly configured
|
|
114
|
+
// We just display a warning in the console
|
|
115
|
+
console.warn(e);
|
|
116
|
+
}
|
|
107
117
|
}
|
|
108
118
|
initConfigProjections(config) {
|
|
109
119
|
if (!config.projections) {
|
|
@@ -229,6 +239,9 @@ class GirafeConfig {
|
|
|
229
239
|
initConfigLidar(config) {
|
|
230
240
|
return config.lidar;
|
|
231
241
|
}
|
|
242
|
+
initConfigNews(config) {
|
|
243
|
+
return config.news;
|
|
244
|
+
}
|
|
232
245
|
initConfigCsv(config) {
|
|
233
246
|
const defaultConfig = {
|
|
234
247
|
encoding: 'utf-8',
|
package/tools/main.d.ts
CHANGED
|
@@ -80,7 +80,7 @@ export type { MapMarker } from './state/mapposition.js';
|
|
|
80
80
|
export { default as MapPosition } from './state/mapposition.js';
|
|
81
81
|
export type { InitialSelectionQuery } from './state/objectselection.js';
|
|
82
82
|
export { default as ObjectSelection } from './state/objectselection.js';
|
|
83
|
-
export type { ThemesConfig, InfoBoxContent, Lidar, InfoWindow, ExtendedState } from './state/state.js';
|
|
83
|
+
export type { ThemesConfig, InfoBoxContent, Lidar, News, InfoWindow, ExtendedState } from './state/state.js';
|
|
84
84
|
export { default as State } from './state/state.js';
|
|
85
85
|
export { default as StateToggleManager } from './state/stateToggleManager.js';
|
|
86
86
|
export type { Callback } from './state/statemanager.js';
|
|
@@ -7,6 +7,7 @@ export default class GraphicalInterface {
|
|
|
7
7
|
printPanelVisible: boolean;
|
|
8
8
|
extLayerPanelVisible: boolean;
|
|
9
9
|
crossSectionPanelVisible: boolean;
|
|
10
|
+
newsPanelVisible: boolean;
|
|
10
11
|
editPanelVisible: boolean;
|
|
11
12
|
sharePanelVisible: boolean;
|
|
12
13
|
selectionComponentVisible: boolean;
|
package/tools/state/state.d.ts
CHANGED
|
@@ -38,6 +38,11 @@ export type Lidar = {
|
|
|
38
38
|
line: OlGeomLineString | null;
|
|
39
39
|
drawActive: boolean;
|
|
40
40
|
};
|
|
41
|
+
export type News = {
|
|
42
|
+
urls: string[] | null;
|
|
43
|
+
lastViewDate: number;
|
|
44
|
+
lastPublishDate: number;
|
|
45
|
+
};
|
|
41
46
|
type Functionalities = {
|
|
42
47
|
authorized_plugins?: string[];
|
|
43
48
|
};
|
|
@@ -97,6 +102,7 @@ export default class State {
|
|
|
97
102
|
userInteractionListeners: GgUserInteractionListener[];
|
|
98
103
|
language: string | null;
|
|
99
104
|
lidar: Lidar;
|
|
105
|
+
news: News;
|
|
100
106
|
loading: boolean;
|
|
101
107
|
application: {
|
|
102
108
|
isConfigurationLoaded: boolean;
|
package/tools/state/state.js
CHANGED
|
@@ -50,6 +50,11 @@ export default class State {
|
|
|
50
50
|
line: null,
|
|
51
51
|
drawActive: false
|
|
52
52
|
};
|
|
53
|
+
news = {
|
|
54
|
+
urls: null,
|
|
55
|
+
lastViewDate: 0,
|
|
56
|
+
lastPublishDate: 0
|
|
57
|
+
};
|
|
53
58
|
// Is the application currently loading map data?
|
|
54
59
|
loading = false;
|
|
55
60
|
// Global variables that represent the state of the application.
|
|
@@ -30,7 +30,8 @@ describe('StateToggleManager class', () => {
|
|
|
30
30
|
new TestPanel('drawing', 'interface.drawingPanelVisible'),
|
|
31
31
|
new TestPanel('print', 'interface.printPanelVisible'),
|
|
32
32
|
new TestPanel('userprefs', 'interface.userPreferencesPanelVisible'),
|
|
33
|
-
new TestPanel('share', 'interface.sharePanelVisible')
|
|
33
|
+
new TestPanel('share', 'interface.sharePanelVisible'),
|
|
34
|
+
new TestPanel('news', 'interface.newsPanelVisible')
|
|
34
35
|
];
|
|
35
36
|
beforeEach(() => {
|
|
36
37
|
stateToggleManager = new StateToggleManager(panels, stateManager);
|
|
@@ -43,12 +44,14 @@ describe('StateToggleManager class', () => {
|
|
|
43
44
|
expect(state.interface.printPanelVisible).toBeFalsy();
|
|
44
45
|
expect(state.interface.userPreferencesPanelVisible).toBeFalsy();
|
|
45
46
|
expect(state.interface.sharePanelVisible).toBeFalsy();
|
|
47
|
+
expect(state.interface.newsPanelVisible).toBeFalsy();
|
|
46
48
|
state.interface.userPreferencesPanelVisible = true;
|
|
47
49
|
expect(state.interface.helpVisible).toBeFalsy();
|
|
48
50
|
expect(state.interface.drawingPanelVisible).toBeFalsy();
|
|
49
51
|
expect(state.interface.printPanelVisible).toBeFalsy();
|
|
50
52
|
expect(state.interface.userPreferencesPanelVisible).toBeTruthy();
|
|
51
53
|
expect(state.interface.sharePanelVisible).toBeFalsy();
|
|
54
|
+
expect(state.interface.newsPanelVisible).toBeFalsy();
|
|
52
55
|
});
|
|
53
56
|
it('deactivateAll', () => {
|
|
54
57
|
const state = stateManager.state;
|
|
@@ -58,5 +61,6 @@ describe('StateToggleManager class', () => {
|
|
|
58
61
|
expect(state.interface.printPanelVisible).toBeFalsy();
|
|
59
62
|
expect(state.interface.userPreferencesPanelVisible).toBeFalsy();
|
|
60
63
|
expect(state.interface.sharePanelVisible).toBeFalsy();
|
|
64
|
+
expect(state.interface.newsPanelVisible).toBeFalsy();
|
|
61
65
|
});
|
|
62
66
|
});
|