@geogirafe/lib-geoportal 1.1.0-dev.2318696406 → 1.1.0-dev.2321641404
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 +2 -0
- package/api/apicontext.d.ts +2 -0
- package/api/apicontext.js +4 -0
- package/assets/i18n/de.json +5 -1
- package/assets/i18n/en.json +4 -0
- package/assets/i18n/fr.json +4 -0
- package/assets/i18n/it.json +4 -0
- package/assets/icons/favorite-add.svg +1 -0
- package/assets/icons/favorite-no.svg +1 -0
- package/assets/icons/favorite-remove.svg +1 -0
- package/assets/icons/favorite-yes.svg +1 -0
- package/components/themes/component.d.ts +16 -4
- package/components/themes/component.js +65 -11
- package/package.json +1 -1
- package/templates/public/about.json +1 -1
- package/tools/context/context.d.ts +2 -0
- package/tools/context/context.js +4 -0
- package/tools/context/icontext.d.ts +2 -0
- package/tools/main.d.ts +1 -0
- package/tools/main.js +1 -0
- package/tools/share/serializers/activebasemapsserializer.js +1 -2
- package/tools/tests/mockcontext.d.ts +2 -0
- package/tools/tests/mockcontext.js +4 -0
- package/tools/themes/themefavoritesmanager.d.ts +17 -0
- package/tools/themes/themefavoritesmanager.js +53 -0
- package/tools/themes/themefavoritesmanager.spec.d.ts +1 -0
- package/tools/themes/themefavoritesmanager.spec.js +103 -0
package/LICENSE
CHANGED
|
@@ -237,6 +237,8 @@ From:
|
|
|
237
237
|
- https://www.svgrepo.com/svg/309943/select-all-off
|
|
238
238
|
- https://www.svgrepo.com/svg/310303/arrow-forward
|
|
239
239
|
- https://www.svgrepo.com/svg/310306/arrow-import
|
|
240
|
+
- https://www.svgrepo.com/svg/310006/star
|
|
241
|
+
- https://www.svgrepo.com/svg/310008/star-add
|
|
240
242
|
|
|
241
243
|
|
|
242
244
|
The MIT License (MIT)
|
package/api/apicontext.d.ts
CHANGED
|
@@ -30,6 +30,7 @@ import WmsManager from '../tools/wms/wmsmanager.js';
|
|
|
30
30
|
import IGirafeContext from '../tools/context/icontext.js';
|
|
31
31
|
import ApiSessionManager from './apisessionmanager.js';
|
|
32
32
|
import OnBoardingManager from '../tools/onboarding/onboardingmanager.js';
|
|
33
|
+
import ThemeFavoritesManager from '../tools/themes/themefavoritesmanager.js';
|
|
33
34
|
export default class GirafeApiContext implements IGirafeContext {
|
|
34
35
|
readonly userDataManager: UserDataManager;
|
|
35
36
|
readonly configManager: ConfigManager;
|
|
@@ -62,6 +63,7 @@ export default class GirafeApiContext implements IGirafeContext {
|
|
|
62
63
|
readonly ogcApiFeaturesManager: OgcApiFeaturesManager;
|
|
63
64
|
readonly localFileManager: LocalFileManager;
|
|
64
65
|
readonly onBoardingManager: OnBoardingManager;
|
|
66
|
+
readonly themeFavoritesManager: ThemeFavoritesManager;
|
|
65
67
|
constructor();
|
|
66
68
|
initialize(): Promise<void>;
|
|
67
69
|
}
|
package/api/apicontext.js
CHANGED
|
@@ -29,6 +29,7 @@ import WfsManager from '../tools/wfs/wfsmanager.js';
|
|
|
29
29
|
import WmsManager from '../tools/wms/wmsmanager.js';
|
|
30
30
|
import ApiSessionManager from './apisessionmanager.js';
|
|
31
31
|
import OnBoardingManager from '../tools/onboarding/onboardingmanager.js';
|
|
32
|
+
import ThemeFavoritesManager from '../tools/themes/themefavoritesmanager.js';
|
|
32
33
|
export default class GirafeApiContext {
|
|
33
34
|
userDataManager;
|
|
34
35
|
configManager;
|
|
@@ -61,6 +62,7 @@ export default class GirafeApiContext {
|
|
|
61
62
|
ogcApiFeaturesManager;
|
|
62
63
|
localFileManager;
|
|
63
64
|
onBoardingManager;
|
|
65
|
+
themeFavoritesManager;
|
|
64
66
|
constructor() {
|
|
65
67
|
this.componentManager = new ComponentManager(this);
|
|
66
68
|
this.userDataManager = new UserDataManager(this);
|
|
@@ -93,6 +95,7 @@ export default class GirafeApiContext {
|
|
|
93
95
|
this.localFileManager = new LocalFileManager(this);
|
|
94
96
|
this.ogcApiFeaturesManager = new OgcApiFeaturesManager(this);
|
|
95
97
|
this.onBoardingManager = new OnBoardingManager(this);
|
|
98
|
+
this.themeFavoritesManager = new ThemeFavoritesManager(this);
|
|
96
99
|
}
|
|
97
100
|
async initialize() {
|
|
98
101
|
// NOTE : This initialization order is important, because some singleton will need other ones !
|
|
@@ -129,5 +132,6 @@ export default class GirafeApiContext {
|
|
|
129
132
|
this.localFileManager.initializeSingleton();
|
|
130
133
|
this.ogcApiFeaturesManager.initializeSingleton();
|
|
131
134
|
this.onBoardingManager.initializeSingleton();
|
|
135
|
+
this.themeFavoritesManager.initializeSingleton();
|
|
132
136
|
}
|
|
133
137
|
}
|
package/assets/i18n/de.json
CHANGED
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"Control opacity": "Deckkraft steuern",
|
|
55
55
|
"Copy ShortUrl": "Kurz-URL kopieren",
|
|
56
56
|
"Copy value": "Wert kopieren",
|
|
57
|
-
"Create custom theme": "
|
|
57
|
+
"Create custom theme": "Thema erstellen",
|
|
58
58
|
"cross-section-settings": "Querschnitt",
|
|
59
59
|
"CSV export": "CSV-Export",
|
|
60
60
|
"Current Feature": "Aktuelles Objekt",
|
|
@@ -415,6 +415,10 @@
|
|
|
415
415
|
"Theme selection": "Themenauswahl",
|
|
416
416
|
"Themes": "Themen",
|
|
417
417
|
"themes": "themen",
|
|
418
|
+
"themes-add-remove-favorites": "Zu Favoriten hinzufügen/entfernen",
|
|
419
|
+
"themes-type-all": "Alle Themen",
|
|
420
|
+
"themes-type-custom": "Benutzerdefinierte",
|
|
421
|
+
"themes-type-favorites": "Favoriten",
|
|
418
422
|
"This instance of GeoGirafe cannot be used at the moment.": "Diese Instanz von GeoGirafe kann momentan nicht verwendet werden.",
|
|
419
423
|
"Toggle all legends": "Alle Legenden umschalten",
|
|
420
424
|
"Toggle legend": "Legende umschalten",
|
package/assets/i18n/en.json
CHANGED
|
@@ -417,6 +417,10 @@
|
|
|
417
417
|
"Theme selection": "Theme selection",
|
|
418
418
|
"Themes": "Themes",
|
|
419
419
|
"themes": "themes",
|
|
420
|
+
"themes-add-remove-favorites": "Add/Remove to Favorites",
|
|
421
|
+
"themes-type-all": "All Themes",
|
|
422
|
+
"themes-type-custom": "Custom Themes",
|
|
423
|
+
"themes-type-favorites": "Favorite Themes",
|
|
420
424
|
"This instance of GeoGirafe cannot be used at the moment.": "This instance of GeoGirafe cannot be used at the moment.",
|
|
421
425
|
"Toggle all legends": "Toggle all legends",
|
|
422
426
|
"Toggle legend": "Toggle legend",
|
package/assets/i18n/fr.json
CHANGED
|
@@ -415,6 +415,10 @@
|
|
|
415
415
|
"Theme selection": "Sélection du thème",
|
|
416
416
|
"Themes": "Thèmes",
|
|
417
417
|
"themes": "thèmes",
|
|
418
|
+
"themes-add-remove-favorites": "Ajouter aux favoris / retirer des favoris",
|
|
419
|
+
"themes-type-all": "Tous les thèmes",
|
|
420
|
+
"themes-type-custom": "Personnalisés",
|
|
421
|
+
"themes-type-favorites": "Favoris",
|
|
418
422
|
"This instance of GeoGirafe cannot be used at the moment.": "Cette instance de GeoGirafe ne peut pas être utilisée pour le moment.",
|
|
419
423
|
"Toggle all legends": "Afficher / Masquer toutes les légendes",
|
|
420
424
|
"Toggle legend": "Afficher / Masquer la légende",
|
package/assets/i18n/it.json
CHANGED
|
@@ -415,6 +415,10 @@
|
|
|
415
415
|
"Theme selection": "Selezione del tema",
|
|
416
416
|
"Themes": "Temi",
|
|
417
417
|
"themes": "temi",
|
|
418
|
+
"themes-add-remove-favorites": "Aggiungi ai preferiti / rimuovi dai preferiti",
|
|
419
|
+
"themes-type-all": "Tutti i temi",
|
|
420
|
+
"themes-type-custom": "Personalizzati",
|
|
421
|
+
"themes-type-favorites": "Preferiti",
|
|
418
422
|
"This instance of GeoGirafe cannot be used at the moment.": "Questa istanza di GeoGirafe non può essere utilizzata al momento.",
|
|
419
423
|
"Toggle all legends": "Attiva / Disattiva tutte le legende",
|
|
420
424
|
"Toggle legend": "Attiva / Disattiva legenda",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" viewBox="0 0 24 24"><title>ic_fluent_star_add_24_regular</title><path fill="#212121" fill-rule="nonzero" d="M17.5 12a5.5 5.5 0 1 1 0 11 5.5 5.5 0 0 1 0-11m-4.827-9.24 2.683 5.448 6.011.869a.75.75 0 0 1 .416 1.279l-1.368 1.333a6.5 6.5 0 0 0-1.583-.552l.815-.793-4.896-.708a.75.75 0 0 1-.566-.41L12 4.787 9.815 9.225a.75.75 0 0 1-.566.411l-4.895.707 3.545 3.45a.75.75 0 0 1 .216.665l-.84 4.873L11 17.37a6.6 6.6 0 0 0 .167 1.607l-4.541 2.392a.75.75 0 0 1-1.09-.79l1.032-5.986-4.352-4.236a.75.75 0 0 1 .416-1.28l6.01-.868 2.684-5.448a.75.75 0 0 1 1.346 0M17.5 14l-.09.007a.5.5 0 0 0-.402.402L17 14.5V17L14.498 17l-.09.008a.5.5 0 0 0-.402.402l-.008.09.008.09a.5.5 0 0 0 .402.402l.09.008H17v2.503l.008.09a.5.5 0 0 0 .402.402l.09.008.09-.008a.5.5 0 0 0 .402-.402l.008-.09V18l2.504.001.09-.008a.5.5 0 0 0 .402-.402l.008-.09-.008-.09a.5.5 0 0 0-.403-.402l-.09-.008H18v-2.5l-.008-.09a.5.5 0 0 0-.402-.403z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" viewBox="0 0 28 28"><title>ic_fluent_star_28_regular</title><path fill="#212121" fill-rule="nonzero" d="M14.437 3.103a1 1 0 0 1 .455.455l2.923 5.924 6.538.95a1 1 0 0 1 .555 1.706l-4.731 4.611 1.116 6.512a1 1 0 0 1-1.45 1.054l-5.848-3.074-5.848 3.074a1 1 0 0 1-1.45-1.054l1.116-6.512-4.731-4.611a1 1 0 0 1 .554-1.706l6.538-.95 2.924-5.924a1 1 0 0 1 1.34-.455m-3.267 7.75-6.316.918 4.57 4.455-1.078 6.29 5.649-2.97 5.649 2.97-1.08-6.29 4.571-4.455-6.316-.918-2.824-5.723z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" fill="#eab308" viewBox="0 0 24 24"><title>ic_fluent_star_add_24_regular</title><g fill="#212121" fill-rule="nonzero"><path d="M14.437 3.103a1 1 0 0 1 .455.455l2.923 5.924 6.538.95a1 1 0 0 1 .555 1.706l-4.731 4.611c-6.224 4.484-.045.03-6.182 4.492l-5.848 3.074a1 1 0 0 1-1.45-1.054l1.116-6.512-4.731-4.611a1 1 0 0 1 .554-1.706l6.538-.95 2.924-5.924a1 1 0 0 1 1.34-.455" class="UnoptimicedTransforms" style="display:inline;fill:#eab308;fill-opacity:1;stroke:none;stroke-opacity:1" transform="matrix(.82693 0 0 .81171 .481 .565)"/><path d="M17.5 12a5.5 5.5 0 1 1 0 11 5.5 5.5 0 0 1 0-11m-4.827-9.24 2.683 5.448 6.011.869a.75.75 0 0 1 .416 1.279l-1.368 1.333a6.5 6.5 0 0 0-1.583-.552l.815-.793-4.896-.708a.75.75 0 0 1-.566-.41L12 4.787 9.815 9.225a.75.75 0 0 1-.566.411l-4.895.707 3.545 3.45a.75.75 0 0 1 .216.665l-.84 4.873L11 17.37a6.6 6.6 0 0 0 .167 1.607l-4.541 2.392a.75.75 0 0 1-1.09-.79l1.032-5.986-4.352-4.236a.75.75 0 0 1 .416-1.28l6.01-.868 2.684-5.448a.75.75 0 0 1 1.346 0M14.5 17a.5.5 0 0 0 0 1h6a.5.5 0 0 0 0-1Z" style="display:inline"/></g></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="800" viewBox="0 0 28 28"><title>ic_fluent_star_28_regular</title><g fill="#212121" fill-rule="nonzero"><path d="M14.437 3.103a1 1 0 0 1 .455.455l2.923 5.924 6.538.95a1 1 0 0 1 .555 1.706l-4.731 4.611 1.116 6.512a1 1 0 0 1-1.45 1.054l-5.848-3.074-5.848 3.074a1 1 0 0 1-1.45-1.054l1.116-6.512-4.731-4.611a1 1 0 0 1 .554-1.706l6.538-.95 2.924-5.924a1 1 0 0 1 1.34-.455" class="UnoptimicedTransforms" style="display:inline;fill:#eab308;fill-opacity:1;stroke:none;stroke-opacity:1"/><path d="M14.437 3.103a1 1 0 0 1 .455.455l2.923 5.924 6.538.95a1 1 0 0 1 .555 1.706l-4.731 4.611 1.116 6.512a1 1 0 0 1-1.45 1.054l-5.848-3.074-5.848 3.074a1 1 0 0 1-1.45-1.054l1.116-6.512-4.731-4.611a1 1 0 0 1 .554-1.706l6.538-.95 2.924-5.924a1 1 0 0 1 1.34-.455m-3.267 7.75-6.316.918 4.57 4.455-1.078 6.29 5.649-2.97 5.649 2.97-1.08-6.29 4.571-4.455-6.316-.918-2.824-5.723z" class="UnoptimicedTransforms" style="display:inline"/><path d="M14.437 3.103a1 1 0 0 1 .455.455l2.923 5.924 6.538.95a1 1 0 0 1 .555 1.706l-4.731 4.611 1.116 6.512a1 1 0 0 1-1.45 1.054l-5.848-3.074-5.848 3.074a1 1 0 0 1-1.45-1.054l1.116-6.512-4.731-4.611a1 1 0 0 1 .554-1.706l6.538-.95 2.924-5.924a1 1 0 0 1 1.34-.455m-3.267 7.75-6.316.918 4.57 4.455-1.078 6.29 5.649-2.97 5.649 2.97-1.08-6.29 4.571-4.455-6.316-.918-2.824-5.723z"/></g></svg>
|
|
@@ -1,22 +1,34 @@
|
|
|
1
1
|
import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
|
|
2
2
|
import ThemeLayer from '../../models/layers/themelayer.js';
|
|
3
|
-
import Theme from '../../models/theme.js';
|
|
4
3
|
import CustomTheme from '../../models/customtheme.js';
|
|
4
|
+
declare const ThemeTypes: readonly ["all", "favorites", "custom"];
|
|
5
|
+
type ThemeType = (typeof ThemeTypes)[number];
|
|
5
6
|
declare class ThemeComponent extends GirafeHTMLElement {
|
|
6
7
|
template: () => import("uhtml").Hole;
|
|
7
8
|
newIcon: string;
|
|
8
9
|
menuOpen: boolean;
|
|
9
10
|
openedOnce: boolean;
|
|
11
|
+
activeThemeType: ThemeType;
|
|
12
|
+
clickOutsideContainer: HTMLElement | null;
|
|
10
13
|
get customThemes(): CustomTheme[];
|
|
14
|
+
get allThemes(): Record<number, ThemeLayer>;
|
|
15
|
+
get favoriteThemes(): (ThemeLayer | CustomTheme)[];
|
|
16
|
+
get activeThemes(): Record<number, ThemeLayer> | (ThemeLayer | CustomTheme)[];
|
|
11
17
|
constructor();
|
|
12
18
|
registerEvents(): void;
|
|
19
|
+
protected render(): void;
|
|
13
20
|
onBlur(): void;
|
|
14
21
|
toggleThemesList(): void;
|
|
15
|
-
|
|
16
|
-
|
|
22
|
+
activateThemeType(themeType: ThemeType): void;
|
|
23
|
+
onThemeChanged(theme: ThemeLayer | CustomTheme): void;
|
|
24
|
+
isThemeActive(theme: ThemeLayer | CustomTheme): boolean;
|
|
25
|
+
isThemeFavorite(theme: ThemeLayer | CustomTheme): boolean;
|
|
26
|
+
onThemeFavoriteChanged(theme: ThemeLayer | CustomTheme, e: Event): void;
|
|
27
|
+
isThemeTypeActive(themeType: ThemeType): boolean;
|
|
28
|
+
isCustomTheme(theme: ThemeLayer | CustomTheme): theme is CustomTheme;
|
|
17
29
|
onCustomThemeChanged(customTheme: CustomTheme): void;
|
|
18
30
|
onAddCustomTheme(): Promise<void>;
|
|
19
|
-
onDeleteCustomTheme(
|
|
31
|
+
onDeleteCustomTheme(customTheme: CustomTheme, e: Event): Promise<void>;
|
|
20
32
|
protected connectedCallback(): void;
|
|
21
33
|
}
|
|
22
34
|
export default ThemeComponent;
|
|
@@ -1,48 +1,86 @@
|
|
|
1
1
|
import { htmlFor as uHtmlFor } from 'uhtml/keyed';
|
|
2
2
|
import { html as uHtml } from 'uhtml';
|
|
3
3
|
import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
|
|
4
|
+
import ThemeLayer from '../../models/layers/themelayer.js';
|
|
4
5
|
import NewIcon from './images/new.svg';
|
|
6
|
+
import CustomTheme from '../../models/customtheme.js';
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
8
|
+
const ThemeTypes = ['all', 'favorites', 'custom'];
|
|
5
9
|
class ThemeComponent extends GirafeHTMLElement {
|
|
6
10
|
template = () => {
|
|
7
11
|
return uHtml `<style>
|
|
8
12
|
*{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
13
|
</style><style>
|
|
10
|
-
.themes{background-color:var(--bkg-color);border:1px solid #444;border-radius:3px;flex-
|
|
14
|
+
.themes{background-color:var(--bkg-color);border:1px solid #444;border-radius:3px;flex-flow:column;width:58rem;max-height:70vh;margin-top:0;padding:1rem;display:flex;position:absolute;overflow-y:auto}#close-themes-menu{z-index:-1;background:0 0;width:100vw;height:100vh;display:none;position:absolute;top:0;left:0}#active-themes{flex-flow:wrap;flex:1;justify-content:flex-start;display:flex}.theme-card{border:var(--app-standard-border);background-color:#0000;border-radius:6px;flex-direction:column;align-items:center;width:7rem;height:9rem;margin:.25rem;padding:.25rem;display:flex;&:hover{cursor:pointer;background-color:var(--bkg-color-grad2)}& img.theme-favorite{content:url(icons/favorite-no.svg);align-self:flex-start;width:1.5rem;height:1.5rem;&:hover{content:url(icons/favorite-add.svg)}&.yes{content:url(icons/favorite-yes.svg);&:hover{content:url(icons/favorite-remove.svg)}}&.placeholder{content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=)}}& img.delete-icon{content:url(icons/close.svg);align-self:flex-end;width:1.5rem;height:1.5rem;margin-top:-1.5rem;&.placeholder{content:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=)}}& img.theme-icon{max-width:5rem;max-height:2.5rem;margin-top:1.25rem;margin-bottom:1.25rem}&.selected{background-color:var(--bkg-color-grad1);border:solid 1px var(--text-color)}& span.theme-name{text-align:center;word-break:break-word;max-width:6rem;color:var(--text-color);max-height:2.2rem;line-height:1.1rem;overflow:hidden}}.theme,.custom-theme,.new-theme{cursor:pointer;background-color:#0000;border:none;border-radius:.5rem;align-content:flex-end;width:8rem;margin:.5rem;padding:0;position:relative}.themes>button>span,.custom-theme>button>span,.new-theme>button>span{text-align:center;width:95%;color:var(--text-color);display:inline-block}.themes>button>span,.custom-theme>button>span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.theme>img,.custom-theme>button>img,.new-theme>img{max-width:100%}button.select{cursor:pointer;background-color:#0000;border:none;width:100%;padding:0}button.select>img{height:3rem;margin-bottom:1rem}button.delete{position:absolute;top:0;right:0}.theme.selected,.custom-theme.selected{background-color:var(--bkg-color-grad2)}.theme-icon{--svg-filter:none}#themes-type-chooser{flex:1;justify-content:space-between;display:flex}.gg-tabs{flex-grow:1;margin:0}
|
|
11
15
|
</style>
|
|
12
|
-
<button class="gg-icon-button gg-big-withtext" tip="Theme selection" onclick="${() => this.toggleThemesList()}"
|
|
16
|
+
<button class="gg-icon-button gg-big-withtext" tip="Theme selection" onclick="${() => this.toggleThemesList()}"><img alt="menu-icon" src="icons/themes.svg" class="${(!this.state.loading ? (this.openedOnce ? '' : 'gg-spin-wait') : 'hidden')}"> <img alt="loading-icon" src="icons/loading.svg" class="${(this.state.loading ? 'gg-spin' : 'hidden')}"> <span i18n="Themes">Themes</span></button><div class="${(this.menuOpen ? 'themes' : 'hidden')}"><div id="themes-type-chooser"><div class="gg-tabs">${Object.values(ThemeTypes).map((themeType) => uHtml ` <button id="${'choose-themes-type-' + themeType}" i18n="${'themes-type-' + themeType}" class="${this.isThemeTypeActive(themeType) ? 'gg-tab active' : 'gg-tab'}" .active="${this.isThemeTypeActive(themeType)}" onclick="${() => this.activateThemeType(themeType)}"></button> `)}</div></div><div id="active-themes">${Object.values(this.activeThemes).map(theme => uHtmlFor(theme, theme.id) `<div role="button" tabindex="0" class="${this.isThemeActive(theme) ? 'theme-card selected' : 'theme-card'}" onkeyup="${() => this.onThemeChanged(theme)}" onclick="${() => this.onThemeChanged(theme)}"><img class="${this.isThemeFavorite(theme) ? 'theme-favorite yes' : 'theme-favorite'}" tip="themes-add-remove-favorites" alt="themes-add-remove-favorites" src="" onclick="${(e) => this.onThemeFavoriteChanged(theme, e)}" onkeyup="${(e) => this.onThemeFavoriteChanged(theme, e)}"> <img class="${this.isCustomTheme(theme) ? 'delete-icon' : 'delete-icon placeholder'}" onclick="${(e) => this.onDeleteCustomTheme(theme, e)}" onkeyup="${(e) => this.onDeleteCustomTheme(theme, e)}" alt="delete-icon" src=""> <img class="theme-icon" alt="${'Icon for ' + theme.name}" src="${theme.icon}"> <span i18n="${theme.name}" class="theme-name">${theme.name}</span></div>`)}<div role="button" tabindex="0" class="${this.isThemeTypeActive('custom') ? 'theme-card' : 'hidden'}" onclick="${() => this.onAddCustomTheme()}" onkeyup="${() => this.onAddCustomTheme()}"><img class="theme-favorite placeholder" src="" alt="" disabled="disabled"> <img class="theme-icon" alt="Create custom theme" src="${this.newIcon}" tip="Save the current layer configuration as new theme"> <span i18n="Create custom theme" class="theme-name">Create custom theme</span></div></div></div><div id="close-themes-menu" onclick="${() => this.onBlur()}"></div>`;
|
|
13
17
|
};
|
|
14
18
|
newIcon = NewIcon;
|
|
15
19
|
menuOpen = false;
|
|
16
20
|
openedOnce = false;
|
|
21
|
+
activeThemeType = 'all';
|
|
22
|
+
clickOutsideContainer = null;
|
|
17
23
|
get customThemes() {
|
|
18
24
|
return this.context.customThemesManager.customThemes;
|
|
19
25
|
}
|
|
26
|
+
get allThemes() {
|
|
27
|
+
return this.state.themes._allThemes ?? {};
|
|
28
|
+
}
|
|
29
|
+
get favoriteThemes() {
|
|
30
|
+
return [
|
|
31
|
+
...Object.values(this.allThemes).filter((theme) => this.context.themeFavoritesManager.isThemeInFavorites(theme)),
|
|
32
|
+
...Object.values(this.customThemes).filter((theme) => this.context.themeFavoritesManager.isThemeInFavorites(theme))
|
|
33
|
+
];
|
|
34
|
+
}
|
|
35
|
+
get activeThemes() {
|
|
36
|
+
switch (this.activeThemeType) {
|
|
37
|
+
case 'all':
|
|
38
|
+
return this.allThemes;
|
|
39
|
+
case 'favorites':
|
|
40
|
+
return this.favoriteThemes;
|
|
41
|
+
case 'custom':
|
|
42
|
+
return this.customThemes;
|
|
43
|
+
default:
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
20
47
|
constructor() {
|
|
21
48
|
super('themes');
|
|
22
49
|
}
|
|
23
50
|
registerEvents() {
|
|
24
|
-
this.subscribe('loading', () =>
|
|
51
|
+
this.subscribe('loading', () => this.render());
|
|
25
52
|
this.subscribe('themes.isLoaded', () => {
|
|
26
53
|
if (this.state.themes.isLoaded) {
|
|
27
|
-
|
|
54
|
+
this.render();
|
|
28
55
|
super.girafeTranslate();
|
|
29
56
|
}
|
|
30
57
|
});
|
|
31
58
|
}
|
|
59
|
+
render() {
|
|
60
|
+
super.render();
|
|
61
|
+
if (this.clickOutsideContainer)
|
|
62
|
+
this.clickOutsideContainer.style.display = this.menuOpen ? 'block' : 'none';
|
|
63
|
+
}
|
|
32
64
|
onBlur() {
|
|
33
65
|
this.menuOpen = false;
|
|
34
|
-
|
|
66
|
+
this.render();
|
|
35
67
|
}
|
|
36
68
|
toggleThemesList() {
|
|
37
69
|
this.openedOnce = true;
|
|
38
70
|
this.menuOpen = !this.menuOpen;
|
|
39
|
-
|
|
71
|
+
this.render();
|
|
72
|
+
}
|
|
73
|
+
activateThemeType(themeType) {
|
|
74
|
+
this.activeThemeType = themeType;
|
|
75
|
+
this.render();
|
|
40
76
|
}
|
|
41
77
|
onThemeChanged(theme) {
|
|
42
78
|
if (this.context.configManager.Config.themes.selectionMode === 'add') {
|
|
43
79
|
this.state.themes.lastSelectedTheme = null;
|
|
44
80
|
}
|
|
45
81
|
this.state.themes.lastSelectedTheme = theme;
|
|
82
|
+
if (theme instanceof CustomTheme)
|
|
83
|
+
return;
|
|
46
84
|
if (theme.disclaimer) {
|
|
47
85
|
this.state.infobox.elements.push({
|
|
48
86
|
id: theme.treeItemId,
|
|
@@ -64,7 +102,22 @@ class ThemeComponent extends GirafeHTMLElement {
|
|
|
64
102
|
if (this.context.configManager.Config.themes.selectionMode === 'replace') {
|
|
65
103
|
return theme.id === this.state.themes.lastSelectedTheme?.id;
|
|
66
104
|
}
|
|
67
|
-
return
|
|
105
|
+
return this.state.layers.layersList.some((layer) => layer instanceof ThemeLayer && layer.id === theme.id);
|
|
106
|
+
}
|
|
107
|
+
isThemeFavorite(theme) {
|
|
108
|
+
return this.context.themeFavoritesManager.isThemeInFavorites(theme);
|
|
109
|
+
}
|
|
110
|
+
onThemeFavoriteChanged(theme, e) {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
e.stopPropagation();
|
|
113
|
+
this.context.themeFavoritesManager.addOrRemoveThemeFromFavorites(theme);
|
|
114
|
+
this.render();
|
|
115
|
+
}
|
|
116
|
+
isThemeTypeActive(themeType) {
|
|
117
|
+
return (this.activeThemeType ?? 'all') === themeType;
|
|
118
|
+
}
|
|
119
|
+
isCustomTheme(theme) {
|
|
120
|
+
return theme instanceof CustomTheme;
|
|
68
121
|
}
|
|
69
122
|
onCustomThemeChanged(customTheme) {
|
|
70
123
|
this.state.themes.lastSelectedTheme = customTheme;
|
|
@@ -74,15 +127,15 @@ class ThemeComponent extends GirafeHTMLElement {
|
|
|
74
127
|
const themeName = await window.gPrompt('Enter a name for your custom theme', 'Create custom theme', 'Enter a name...');
|
|
75
128
|
if (themeName !== false && themeName.trim().length > 0) {
|
|
76
129
|
this.context.customThemesManager.addTheme(themeName, this.state.layers.layersList);
|
|
77
|
-
|
|
130
|
+
this.render();
|
|
78
131
|
}
|
|
79
132
|
}
|
|
80
|
-
async onDeleteCustomTheme(
|
|
133
|
+
async onDeleteCustomTheme(customTheme, e) {
|
|
81
134
|
e.stopPropagation();
|
|
82
135
|
const confirm = await window.gConfirm('Do you want to delete this theme?', 'Delete Theme');
|
|
83
136
|
if (confirm) {
|
|
84
|
-
this.context.customThemesManager.deleteTheme(
|
|
85
|
-
|
|
137
|
+
this.context.customThemesManager.deleteTheme(customTheme);
|
|
138
|
+
this.render();
|
|
86
139
|
}
|
|
87
140
|
}
|
|
88
141
|
connectedCallback() {
|
|
@@ -90,6 +143,7 @@ class ThemeComponent extends GirafeHTMLElement {
|
|
|
90
143
|
super.render();
|
|
91
144
|
this.registerEvents();
|
|
92
145
|
this.girafeTranslate();
|
|
146
|
+
this.clickOutsideContainer = this.shadow.getElementById('close-themes-menu');
|
|
93
147
|
}
|
|
94
148
|
}
|
|
95
149
|
export default ThemeComponent;
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.1.0-dev.
|
|
1
|
+
{"version":"1.1.0-dev.2321641404", "build":"2321641404", "date":"12/02/2026"}
|
|
@@ -30,6 +30,7 @@ import UserDataManager from '../userdata/userdatamanager.js';
|
|
|
30
30
|
import WfsManager from '../wfs/wfsmanager.js';
|
|
31
31
|
import WmsManager from '../wms/wmsmanager.js';
|
|
32
32
|
import IGirafeContext from './icontext.js';
|
|
33
|
+
import ThemeFavoritesManager from '../themes/themefavoritesmanager.js';
|
|
33
34
|
export default class GirafeContext implements IGirafeContext {
|
|
34
35
|
readonly userDataManager: UserDataManager;
|
|
35
36
|
readonly configManager: ConfigManager;
|
|
@@ -62,6 +63,7 @@ export default class GirafeContext implements IGirafeContext {
|
|
|
62
63
|
readonly ogcApiFeaturesManager: OgcApiFeaturesManager;
|
|
63
64
|
readonly localFileManager: LocalFileManager;
|
|
64
65
|
readonly onBoardingManager: OnBoardingManager;
|
|
66
|
+
readonly themeFavoritesManager: ThemeFavoritesManager;
|
|
65
67
|
constructor();
|
|
66
68
|
initialize(): Promise<void>;
|
|
67
69
|
}
|
package/tools/context/context.js
CHANGED
|
@@ -29,6 +29,7 @@ import UrlManager from '../url/urlmanager.js';
|
|
|
29
29
|
import UserDataManager from '../userdata/userdatamanager.js';
|
|
30
30
|
import WfsManager from '../wfs/wfsmanager.js';
|
|
31
31
|
import WmsManager from '../wms/wmsmanager.js';
|
|
32
|
+
import ThemeFavoritesManager from '../themes/themefavoritesmanager.js';
|
|
32
33
|
export default class GirafeContext {
|
|
33
34
|
userDataManager;
|
|
34
35
|
configManager;
|
|
@@ -61,6 +62,7 @@ export default class GirafeContext {
|
|
|
61
62
|
ogcApiFeaturesManager;
|
|
62
63
|
localFileManager;
|
|
63
64
|
onBoardingManager;
|
|
65
|
+
themeFavoritesManager;
|
|
64
66
|
constructor() {
|
|
65
67
|
this.componentManager = new ComponentManager(this);
|
|
66
68
|
this.userDataManager = new UserDataManager(this);
|
|
@@ -93,6 +95,7 @@ export default class GirafeContext {
|
|
|
93
95
|
this.localFileManager = new LocalFileManager(this);
|
|
94
96
|
this.ogcApiFeaturesManager = new OgcApiFeaturesManager(this);
|
|
95
97
|
this.onBoardingManager = new OnBoardingManager(this);
|
|
98
|
+
this.themeFavoritesManager = new ThemeFavoritesManager(this);
|
|
96
99
|
}
|
|
97
100
|
async initialize() {
|
|
98
101
|
// NOTE : This initialization order is important, because some singleton will need other ones !
|
|
@@ -129,5 +132,6 @@ export default class GirafeContext {
|
|
|
129
132
|
this.localFileManager.initializeSingleton();
|
|
130
133
|
this.ogcApiFeaturesManager.initializeSingleton();
|
|
131
134
|
this.onBoardingManager.initializeSingleton();
|
|
135
|
+
this.themeFavoritesManager.initializeSingleton();
|
|
132
136
|
}
|
|
133
137
|
}
|
|
@@ -29,6 +29,7 @@ import UrlManager from '../url/urlmanager.js';
|
|
|
29
29
|
import UserDataManager from '../userdata/userdatamanager.js';
|
|
30
30
|
import WfsManager from '../wfs/wfsmanager.js';
|
|
31
31
|
import WmsManager from '../wms/wmsmanager.js';
|
|
32
|
+
import ThemeFavoritesManager from '../themes/themefavoritesmanager.js';
|
|
32
33
|
export default interface IGirafeContext {
|
|
33
34
|
readonly userDataManager: UserDataManager;
|
|
34
35
|
readonly configManager: ConfigManager;
|
|
@@ -61,5 +62,6 @@ export default interface IGirafeContext {
|
|
|
61
62
|
readonly ogcApiFeaturesManager: OgcApiFeaturesManager;
|
|
62
63
|
readonly localFileManager: LocalFileManager;
|
|
63
64
|
readonly onBoardingManager: OnBoardingManager;
|
|
65
|
+
readonly themeFavoritesManager: ThemeFavoritesManager;
|
|
64
66
|
initialize(): Promise<void>;
|
|
65
67
|
}
|
package/tools/main.d.ts
CHANGED
|
@@ -90,6 +90,7 @@ export { default as UserInteractionManager } from './state/userInteractionManage
|
|
|
90
90
|
export type { GgUserInteractionEvent } from './state/userinteractionevent.js';
|
|
91
91
|
export { gGEventDependencies, isPrimaryPointerAction, isAlternateMouseClick, isMouseWheelClick } from './state/userinteractionevent.js';
|
|
92
92
|
export { default as CustomThemesManager } from './themes/customthemesmanager.js';
|
|
93
|
+
export { default as ThemeFavoritesManager } from './themes/themefavoritesmanager.js';
|
|
93
94
|
export { DEFAULT_OPACITY, OPACITY_FOR_DEFAULT_BASEMAP } from './themes/themes-config.js';
|
|
94
95
|
export type { LayerTreeChanges } from './themes/themeshelper.js';
|
|
95
96
|
export { default as ThemesHelper } from './themes/themeshelper.js';
|
package/tools/main.js
CHANGED
|
@@ -68,6 +68,7 @@ export { default as StateManager } from './state/statemanager.js';
|
|
|
68
68
|
export { default as UserInteractionManager } from './state/userInteractionManager.js';
|
|
69
69
|
export { gGEventDependencies, isPrimaryPointerAction, isAlternateMouseClick, isMouseWheelClick } from './state/userinteractionevent.js';
|
|
70
70
|
export { default as CustomThemesManager } from './themes/customthemesmanager.js';
|
|
71
|
+
export { default as ThemeFavoritesManager } from './themes/themefavoritesmanager.js';
|
|
71
72
|
export { DEFAULT_OPACITY, OPACITY_FOR_DEFAULT_BASEMAP } from './themes/themes-config.js';
|
|
72
73
|
export { default as ThemesHelper } from './themes/themeshelper.js';
|
|
73
74
|
export { default as ThemesManager } from './themes/themesmanager.js';
|
|
@@ -13,8 +13,7 @@ export default class ActiveBasemapsSerializer {
|
|
|
13
13
|
return this.serialize(activeBasemaps);
|
|
14
14
|
}
|
|
15
15
|
brainDeserialize(serializedString) {
|
|
16
|
-
|
|
17
|
-
this.state.activeBasemaps = activeBasemaps;
|
|
16
|
+
this.state.activeBasemaps = this.deserialize(serializedString);
|
|
18
17
|
}
|
|
19
18
|
serialize(basemaps) {
|
|
20
19
|
const sharedBasemaps = [];
|
|
@@ -30,6 +30,7 @@ import UserDataManager from '../userdata/userdatamanager';
|
|
|
30
30
|
import WfsManager from '../wfs/wfsmanager';
|
|
31
31
|
import WmsManager from '../wms/wmsmanager';
|
|
32
32
|
import OnBoardingManager from '../onboarding/onboardingmanager';
|
|
33
|
+
import ThemeFavoritesManager from '../themes/themefavoritesmanager';
|
|
33
34
|
export default class MockGirafeContext implements IGirafeContext {
|
|
34
35
|
readonly userDataManager: UserDataManager;
|
|
35
36
|
readonly configManager: ConfigManager;
|
|
@@ -62,6 +63,7 @@ export default class MockGirafeContext implements IGirafeContext {
|
|
|
62
63
|
readonly ogcApiFeaturesManager: OgcApiFeaturesManager;
|
|
63
64
|
readonly localFileManager: LocalFileManager;
|
|
64
65
|
readonly onBoardingManager: OnBoardingManager;
|
|
66
|
+
readonly themeFavoritesManager: ThemeFavoritesManager;
|
|
65
67
|
constructor();
|
|
66
68
|
initialize(): Promise<void>;
|
|
67
69
|
}
|
|
@@ -31,6 +31,7 @@ import WfsManager from '../wfs/wfsmanager';
|
|
|
31
31
|
import WmsManager from '../wms/wmsmanager';
|
|
32
32
|
import { MockConfig } from './mockconfig';
|
|
33
33
|
import OnBoardingManager from '../onboarding/onboardingmanager';
|
|
34
|
+
import ThemeFavoritesManager from '../themes/themefavoritesmanager';
|
|
34
35
|
export default class MockGirafeContext {
|
|
35
36
|
userDataManager;
|
|
36
37
|
configManager;
|
|
@@ -63,6 +64,7 @@ export default class MockGirafeContext {
|
|
|
63
64
|
ogcApiFeaturesManager;
|
|
64
65
|
localFileManager;
|
|
65
66
|
onBoardingManager;
|
|
67
|
+
themeFavoritesManager;
|
|
66
68
|
constructor() {
|
|
67
69
|
this.componentManager = new ComponentManager(this);
|
|
68
70
|
this.userDataManager = new UserDataManager(this);
|
|
@@ -95,6 +97,7 @@ export default class MockGirafeContext {
|
|
|
95
97
|
this.localFileManager = new LocalFileManager(this);
|
|
96
98
|
this.ogcApiFeaturesManager = new OgcApiFeaturesManager(this);
|
|
97
99
|
this.onBoardingManager = new OnBoardingManager(this);
|
|
100
|
+
this.themeFavoritesManager = new ThemeFavoritesManager(this);
|
|
98
101
|
this.initialize();
|
|
99
102
|
}
|
|
100
103
|
async initialize() {
|
|
@@ -137,5 +140,6 @@ export default class MockGirafeContext {
|
|
|
137
140
|
this.localFileManager.initializeSingleton();
|
|
138
141
|
this.ogcApiFeaturesManager.initializeSingleton();
|
|
139
142
|
this.onBoardingManager.initializeSingleton();
|
|
143
|
+
this.themeFavoritesManager.initializeSingleton();
|
|
140
144
|
}
|
|
141
145
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import GirafeSingleton from '../../base/GirafeSingleton.js';
|
|
2
|
+
import CustomTheme from '../../models/customtheme.js';
|
|
3
|
+
import ThemeLayer from '../../models/layers/themelayer.js';
|
|
4
|
+
declare class ThemeFavoritesManager extends GirafeSingleton {
|
|
5
|
+
private themeFavorites;
|
|
6
|
+
private readonly storagePath;
|
|
7
|
+
initializeSingleton(): void;
|
|
8
|
+
private saveThemeFavorites;
|
|
9
|
+
private loadThemeFavorites;
|
|
10
|
+
addThemeToFavorites(theme: ThemeLayer | CustomTheme): void;
|
|
11
|
+
removeThemeFromFavorites(theme: ThemeLayer | CustomTheme): void;
|
|
12
|
+
addOrRemoveThemeFromFavorites(theme: ThemeLayer | CustomTheme): void;
|
|
13
|
+
clearThemeFavorites(): void;
|
|
14
|
+
isThemeInFavorites(theme: ThemeLayer | CustomTheme): boolean;
|
|
15
|
+
getThemeFavorites(): Array<number | string>;
|
|
16
|
+
}
|
|
17
|
+
export default ThemeFavoritesManager;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import GirafeSingleton from '../../base/GirafeSingleton.js';
|
|
2
|
+
import ThemeLayer from '../../models/layers/themelayer.js';
|
|
3
|
+
class ThemeFavoritesManager extends GirafeSingleton {
|
|
4
|
+
themeFavorites = [];
|
|
5
|
+
storagePath = 'themeFavorites';
|
|
6
|
+
initializeSingleton() {
|
|
7
|
+
super.initializeSingleton();
|
|
8
|
+
this.loadThemeFavorites();
|
|
9
|
+
}
|
|
10
|
+
saveThemeFavorites() {
|
|
11
|
+
const serializedThemeFavorites = JSON.stringify(this.themeFavorites);
|
|
12
|
+
this.context.userDataManager.saveUserData(this.storagePath, serializedThemeFavorites);
|
|
13
|
+
}
|
|
14
|
+
loadThemeFavorites() {
|
|
15
|
+
const serializedThemeFavorites = this.context.userDataManager.getUserData(this.storagePath);
|
|
16
|
+
if (serializedThemeFavorites) {
|
|
17
|
+
this.themeFavorites = JSON.parse(serializedThemeFavorites);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
addThemeToFavorites(theme) {
|
|
21
|
+
this.themeFavorites.push(theme instanceof ThemeLayer ? theme.id : theme.name);
|
|
22
|
+
this.saveThemeFavorites();
|
|
23
|
+
}
|
|
24
|
+
removeThemeFromFavorites(theme) {
|
|
25
|
+
const index = this.themeFavorites.findIndex((themeIdOrName) => theme instanceof ThemeLayer ? themeIdOrName === theme.id : themeIdOrName === theme.name);
|
|
26
|
+
if (index >= 0) {
|
|
27
|
+
this.themeFavorites.splice(index, 1);
|
|
28
|
+
this.saveThemeFavorites();
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
throw new Error('The theme to be removed cannot be found in the list of favorites');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
addOrRemoveThemeFromFavorites(theme) {
|
|
35
|
+
if (this.isThemeInFavorites(theme)) {
|
|
36
|
+
this.removeThemeFromFavorites(theme);
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
this.addThemeToFavorites(theme);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
clearThemeFavorites() {
|
|
43
|
+
this.themeFavorites = [];
|
|
44
|
+
this.saveThemeFavorites();
|
|
45
|
+
}
|
|
46
|
+
isThemeInFavorites(theme) {
|
|
47
|
+
return this.themeFavorites.includes(theme instanceof ThemeLayer ? theme.id : theme.name);
|
|
48
|
+
}
|
|
49
|
+
getThemeFavorites() {
|
|
50
|
+
return this.themeFavorites;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export default ThemeFavoritesManager;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { beforeAll, beforeEach, describe, expect, it } from 'vitest';
|
|
2
|
+
import MockHelper from '../tests/mockhelper';
|
|
3
|
+
import ThemeLayer from '../../models/layers/themelayer';
|
|
4
|
+
import CustomTheme from '../../models/customtheme';
|
|
5
|
+
let themeFavoritesManager;
|
|
6
|
+
let context;
|
|
7
|
+
const storagePath = 'themeFavorites';
|
|
8
|
+
const themeLayer = new ThemeLayer(42, "ThemeLayer", 1);
|
|
9
|
+
const customTheme = new CustomTheme("CustomTheme");
|
|
10
|
+
beforeAll(() => {
|
|
11
|
+
context = MockHelper.startMocking();
|
|
12
|
+
themeFavoritesManager = context.themeFavoritesManager;
|
|
13
|
+
});
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
context.userDataManager.deleteAllUserData();
|
|
16
|
+
themeFavoritesManager.clearThemeFavorites();
|
|
17
|
+
});
|
|
18
|
+
describe('ThemeFavoritesManager.addThemeToFavorites', () => {
|
|
19
|
+
it('adding a ThemeLayer should result in an number array', () => {
|
|
20
|
+
themeFavoritesManager.addThemeToFavorites(themeLayer);
|
|
21
|
+
expect(context.userDataManager.getUserData(storagePath)).toEqual(`[${themeLayer.id}]`);
|
|
22
|
+
});
|
|
23
|
+
it('adding a CustomTheme should result in an string array', () => {
|
|
24
|
+
themeFavoritesManager.addThemeToFavorites(customTheme);
|
|
25
|
+
expect(context.userDataManager.getUserData(storagePath)).toEqual(`["${customTheme.name}"]`);
|
|
26
|
+
});
|
|
27
|
+
it('adding a ThemeLayer and a CustomTheme should result in an mixed array', () => {
|
|
28
|
+
themeFavoritesManager.addThemeToFavorites(themeLayer);
|
|
29
|
+
themeFavoritesManager.addThemeToFavorites(customTheme);
|
|
30
|
+
expect(context.userDataManager.getUserData(storagePath)).toEqual(`[${themeLayer.id},"${customTheme.name}"]`);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
describe('ThemeFavoritesManager.initializeSingleton', () => {
|
|
34
|
+
it('starting without persisted favorites should result in an empty array', () => {
|
|
35
|
+
themeFavoritesManager.initializeSingleton();
|
|
36
|
+
expect(themeFavoritesManager.getThemeFavorites()).toEqual([]);
|
|
37
|
+
});
|
|
38
|
+
it('starting with an empty string as persisted data should result in an empty array', () => {
|
|
39
|
+
context.userDataManager.saveUserData(storagePath, '');
|
|
40
|
+
themeFavoritesManager.initializeSingleton();
|
|
41
|
+
expect(themeFavoritesManager.getThemeFavorites()).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
it('starting with "[42]" as persisted data should result in [42]', () => {
|
|
44
|
+
context.userDataManager.saveUserData(storagePath, '[42]');
|
|
45
|
+
themeFavoritesManager.initializeSingleton();
|
|
46
|
+
expect(themeFavoritesManager.getThemeFavorites()).toEqual([42]);
|
|
47
|
+
});
|
|
48
|
+
it('starting with "["CustomTheme""]" as persisted data should result in ["CustomTheme"]', () => {
|
|
49
|
+
context.userDataManager.saveUserData(storagePath, '["CustomTheme"]');
|
|
50
|
+
themeFavoritesManager.initializeSingleton();
|
|
51
|
+
expect(themeFavoritesManager.getThemeFavorites()).toEqual(['CustomTheme']);
|
|
52
|
+
});
|
|
53
|
+
it('starting with "[42,"CustomTheme""]" as persisted data should result in [42,"CustomTheme"]', () => {
|
|
54
|
+
context.userDataManager.saveUserData(storagePath, '[42,"CustomTheme"]');
|
|
55
|
+
themeFavoritesManager.initializeSingleton();
|
|
56
|
+
expect(themeFavoritesManager.getThemeFavorites()).toEqual([42, 'CustomTheme']);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
describe('ThemeFavoritesManager.isThemeInFavorites', () => {
|
|
60
|
+
it('adding ThemeLayer should result in ThemeLayer being a favorite', () => {
|
|
61
|
+
themeFavoritesManager.addThemeToFavorites(themeLayer);
|
|
62
|
+
expect(themeFavoritesManager.isThemeInFavorites(themeLayer)).toBeTruthy();
|
|
63
|
+
});
|
|
64
|
+
it('adding CustomTheme should result in CustomTheme being a favorite', () => {
|
|
65
|
+
themeFavoritesManager.addThemeToFavorites(customTheme);
|
|
66
|
+
expect(themeFavoritesManager.isThemeInFavorites(customTheme)).toBeTruthy();
|
|
67
|
+
});
|
|
68
|
+
it('adding CustomTheme should result in ThemeLayer being not a favorite', () => {
|
|
69
|
+
themeFavoritesManager.addThemeToFavorites(customTheme);
|
|
70
|
+
expect(themeFavoritesManager.isThemeInFavorites(themeLayer)).toBeFalsy();
|
|
71
|
+
});
|
|
72
|
+
it('adding ThemeLayer should result in CustomTheme being not a favorite', () => {
|
|
73
|
+
themeFavoritesManager.addThemeToFavorites(themeLayer);
|
|
74
|
+
expect(themeFavoritesManager.isThemeInFavorites(customTheme)).toBeFalsy();
|
|
75
|
+
});
|
|
76
|
+
it('adding ThemeLayer and CustomTheme should result in both being a favorite', () => {
|
|
77
|
+
themeFavoritesManager.addThemeToFavorites(themeLayer);
|
|
78
|
+
themeFavoritesManager.addThemeToFavorites(customTheme);
|
|
79
|
+
expect(themeFavoritesManager.isThemeInFavorites(themeLayer)).toBeTruthy();
|
|
80
|
+
expect(themeFavoritesManager.isThemeInFavorites(customTheme)).toBeTruthy();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
describe('ThemeFavoritesManager.clearThemeFavorites', () => {
|
|
84
|
+
it('clearing favorites should result in an empty array', () => {
|
|
85
|
+
context.userDataManager.saveUserData(storagePath, '[42,"CustomTheme"]');
|
|
86
|
+
themeFavoritesManager.initializeSingleton();
|
|
87
|
+
expect(themeFavoritesManager.getThemeFavorites().length).toEqual(2);
|
|
88
|
+
themeFavoritesManager.clearThemeFavorites();
|
|
89
|
+
expect(themeFavoritesManager.getThemeFavorites().length).toEqual(0);
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
describe('ThemeFavoritesManager.addOrRemoveThemeFromFavorites', () => {
|
|
93
|
+
it('not already being a favorite should result in being a favorite afterwards ', () => {
|
|
94
|
+
themeFavoritesManager.addOrRemoveThemeFromFavorites(themeLayer);
|
|
95
|
+
expect(themeFavoritesManager.isThemeInFavorites(themeLayer)).toBeTruthy();
|
|
96
|
+
});
|
|
97
|
+
it('already being a favorite should result in no longer being a favorite afterwards ', () => {
|
|
98
|
+
context.userDataManager.saveUserData(storagePath, '[42]');
|
|
99
|
+
themeFavoritesManager.initializeSingleton();
|
|
100
|
+
themeFavoritesManager.addOrRemoveThemeFromFavorites(themeLayer);
|
|
101
|
+
expect(themeFavoritesManager.isThemeInFavorites(themeLayer)).toBeFalsy();
|
|
102
|
+
});
|
|
103
|
+
});
|