@geogirafe/lib-geoportal 1.2.0-dev.2843240863 → 1.2.0-dev.2845014898

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/api/apigeogirafeapp.js +4 -2
  2. package/api/style.css +4 -10
  3. package/assets/i18n/de.json +4 -0
  4. package/assets/i18n/en.json +4 -0
  5. package/assets/i18n/fr.json +4 -0
  6. package/assets/i18n/it.json +4 -0
  7. package/components/basemap/component.js +1 -1
  8. package/components/layout/component.d.ts +34 -3
  9. package/components/layout/component.js +66 -28
  10. package/components/layout/style.css +13 -0
  11. package/components/main.d.ts +1 -0
  12. package/components/main.js +1 -0
  13. package/components/map-2d/component.d.ts +0 -6
  14. package/components/map-2d/component.js +3 -22
  15. package/components/map-3d/component.d.ts +4 -2
  16. package/components/map-3d/component.js +7 -18
  17. package/components/map-grid/component.d.ts +70 -0
  18. package/components/map-grid/component.js +206 -0
  19. package/components/map-grid/style.css +99 -0
  20. package/components/menu-buttons-mobile/MenuMobile3dButton.js +3 -1
  21. package/components/themes-mobile/layer-selected/component.js +1 -1
  22. package/components/treeview/treeviewitem/component.js +1 -1
  23. package/package.json +1 -1
  24. package/styles/iframe.css +4 -10
  25. package/styles/index.css +4 -10
  26. package/styles/mobile.css +4 -10
  27. package/templates/iframe.html +2 -2
  28. package/templates/index.html +2 -2
  29. package/templates/mobile.html +2 -2
  30. package/templates/public/about.json +1 -1
  31. package/tools/app/geogirafeapp-mobile.js +2 -0
  32. package/tools/app/geogirafeapp.js +2 -0
  33. package/tools/configuration/girafeconfig.d.ts +4 -0
  34. package/tools/configuration/girafeconfig.js +20 -2
  35. package/tools/main.d.ts +3 -0
  36. package/tools/main.js +2 -0
  37. package/tools/share/serializers/globeserializer.d.ts +2 -0
  38. package/tools/share/serializers/globeserializer.js +26 -4
  39. package/tools/share/serializers/layoutserializer.d.ts +17 -0
  40. package/tools/share/serializers/layoutserializer.js +44 -0
  41. package/tools/share/stateserializer.js +3 -0
  42. package/tools/state/globe.d.ts +0 -1
  43. package/tools/state/globe.js +0 -1
  44. package/tools/state/layout.d.ts +62 -0
  45. package/tools/state/layout.js +67 -0
  46. package/tools/state/state.d.ts +2 -0
  47. package/tools/state/state.js +8 -0
  48. package/tools/state/statemanager.d.ts +1 -0
  49. package/tools/state/statemanager.js +8 -0
@@ -4,6 +4,7 @@ import proj4 from 'proj4';
4
4
  import GirafeHTMLElement from '../base/GirafeHTMLElement.js';
5
5
  import Map2dComponent from '../components/map-2d/component.js';
6
6
  import Map3dComponent from '../components/map-3d/component.js';
7
+ import MapGridComponent from '../components/map-grid/component.js';
7
8
  import { register } from 'ol/proj/proj4.js';
8
9
  import GirafeApiContext from './apicontext.js';
9
10
  import BasemapComponent from '../components/basemap/component.js';
@@ -35,10 +36,10 @@ export default class GeoGirafeApi extends GirafeHTMLElement {
35
36
  return uHtml `<style>
36
37
  .hidden{display:none!important}.gg-rotate90{transform:rotate(90deg)}.gg-rotate180{transform:rotate(180deg)}.gg-rotate270{transform:rotate(270deg)}img{filter:var(--svg-filter)}img.legend-image{filter:var(--svg-map-filter);background:var(--svg-legend-bkg)}div{scrollbar-width:thin}a,a:visited{color:var(--link-color)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spin-wait{0%{transform:rotate(0)}7%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.gg-spin{animation-name:spin;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}.gg-spin-wait{animation-name:spin-wait;animation-duration:10s;animation-timing-function:linear;animation-iteration-count:infinite}::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:#999}button,input,select,textarea{font:inherit}.gg-button,.gg-select,.gg-input,.gg-textarea{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);box-sizing:border-box;cursor:pointer;border-radius:3px;outline:0;margin:0;padding:0 0 0 .5rem;display:inline-block}.gg-label{background-color:var(--bkg-color);color:var(--text-color);border:none;align-items:center;margin:0;padding:0;display:flex}.gg-button,.gg-select,.gg-input,.gg-label{min-height:calc(var(--app-standard-height) / 1.5)}.gg-textarea{max-height:initial;resize:vertical;height:6rem;padding:.5rem;line-height:1.3rem}.gg-input{cursor:text}.gg-checkbox{accent-color:var(--text-color);cursor:pointer;width:1.2rem}.gg-range{accent-color:var(--text-color)}.gg-button{padding:0 .5rem}.gg-button.active{border:solid 1px var(--text-color-grad1);background-color:var(--bkg-color-grad2)}.gg-button:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed;border:none}.gg-button:disabled img{filter:opacity(.6)}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed}.gg-button>img{vertical-align:middle}.gg-icon-button{color:var(--text-color);cursor:pointer;background-color:#0000;border:none;flex-direction:column;justify-content:center;align-items:center;padding:0;display:flex}.gg-icon{justify-content:center;align-items:center;display:flex}.gg-big,.gg-big-withtext{min-width:var(--app-standard-height);min-height:var(--app-standard-height);max-height:var(--app-standard-height)}.gg-big img,.gg-big-withtext img{width:calc(var(--app-standard-height) - 1.5rem);margin:0}.gg-big-withtext span{font-variant:small-caps;padding:0 1rem;font-size:.9rem}.gg-medium,.gg-medium-withtext{min-width:calc(var(--app-standard-height) / 1.2);min-height:calc(var(--app-standard-height) / 1.2);max-height:calc(var(--app-standard-height) / 1.2);flex-direction:row}.gg-medium img{width:calc(var(--app-standard-height) / 2.4);margin:0}.gg-medium-withtext img{width:calc(var(--app-standard-height) / 2.4);margin-left:.5rem}.gg-medium-withtext span{padding:0 1rem 0 .5rem;font-size:.9rem}.gg-small,.gg-small-withtext{min-width:calc(var(--app-standard-height) / 2);min-height:calc(var(--app-standard-height) / 2);max-height:calc(var(--app-standard-height) / 2);flex-direction:row}.gg-small img{width:calc(var(--app-standard-height) / 3);margin:0}.gg-small-withtext img{width:calc(var(--app-standard-height) / 3);margin-left:.5rem}.gg-small-withtext span{padding:0 .5rem 0 .3rem;font-size:.9rem}.gg-button:hover:not(:disabled),.gg-select:hover:not(:disabled),.gg-input:hover:not(:disabled),.gg-textarea:hover:not(:disabled),.gg-icon-button:hover:not(:disabled){background-color:var(--bkg-color-grad1)}.gg-opacity{opacity:.5}.gg-opacity:hover{opacity:1;background-color:#0000}.gg-tabs{cursor:pointer;grid-auto-flow:column;padding-bottom:1rem;font-size:1rem;display:grid}.gg-tab{border:none;border-bottom:var(--app-standard-border);cursor:pointer;color:var(--text-color);background:0 0;padding:.5rem}.gg-tab.active{border-bottom:solid 1px var(--text-color)}.girafe-button-big,.girafe-button-large,.girafe-button-small,.girafe-button-tiny{color:var(--text-color);background-color:#0000;border:none;flex-direction:column;display:flex}.girafe-button-big:hover,.girafe-button-large:hover,.girafe-button-small:hover,.girafe-button-tiny:hover{background-color:var(--bkg-color-grad1);cursor:pointer}.girafe-button-big.dark,.girafe-button-large.dark,.girafe-button-small.dark,.girafe-button-tiny.dark{background-color:var(--bkg-color);filter:invert()}.girafe-button-big{width:var(--app-standard-height);height:var(--app-standard-height);align-items:center;padding:1rem}.girafe-button-big img{overflow:hidden}.girafe-button-large{flex-direction:row}.girafe-button-large img{height:2rem;margin:.3rem}.girafe-button-large span{height:2rem;margin:.3rem;line-height:2rem}.girafe-button-small{min-width:calc(var(--app-standard-height) / 2);height:calc(var(--app-standard-height) / 2);align-items:center;padding:.5rem}.girafe-button-small img{overflow:hidden}.girafe-button-small span{text-align:left;text-overflow:ellipsis;width:100%;overflow:hidden}.girafe-button-tiny{align-items:center;width:1rem;height:1rem;padding:0}.girafe-button-tiny img{overflow:hidden}.girafe-onboarding-theme{background-color:var(--bkg-color)!important;color:var(--text-color)!important}.girafe-onboarding-theme button{background-color:var(--bkg-color)!important;color:var(--text-color)!important;text-shadow:none!important}.girafe-onboarding-theme button.driver-popover-close-btn{z-index:10000}.tippy-box{background-color:var(--bkg-color)!important;border:var(--app-standard-border)!important}.tippy-content{color:var(--text-color)!important}.tippy-arrow{color:var(--bkg-color)!important}.tippy-box[data-theme~=error]{background-color:var(--error-color);white-space:pre-line}div.tippy-box[data-theme=image-popup]{border:var(--app-standard-border)!important;& div.tippy-content{background-color:var(--bkg-color)!important;& img{max-width:20rem;max-height:20rem}}& div.tippy-arrow{color:var(--bkg-color)!important}}
37
38
  </style><style>
38
- :host{position:relative}#maps{width:100%;height:100%;position:relative;overflow:hidden}girafe-map-2d{width:100%;height:100%;display:block;position:relative}girafe-map-3d{border-left:3px solid #444;height:100%;display:none;position:absolute;top:0;overflow:hidden}girafe-basemap{display:block;position:absolute;bottom:.5rem;left:.5rem}girafe-search{width:50%;height:2.25rem;display:block;position:absolute;top:.5rem;left:.5rem;box-shadow:0 1px 4px #0000004d}girafe-selection-window{z-index:100;width:400px;height:240px;position:absolute;bottom:1rem;right:1rem}
39
+ :host{position:relative}girafe-map-grid,girafe-map-2d,girafe-map-3d{width:100%;height:100%;display:block;position:relative;overflow:hidden}girafe-basemap{display:block;position:absolute;bottom:.5rem;left:.5rem}girafe-search{width:50%;height:2.25rem;display:block;position:absolute;top:.5rem;left:.5rem;box-shadow:0 1px 4px #0000004d}girafe-selection-window{z-index:100;width:400px;height:240px;position:absolute;bottom:1rem;right:1rem}
39
40
  </style>
40
41
  <style>${this.customStyle}</style>
41
- <div id="maps"><girafe-map-2d></girafe-map-2d><girafe-map-3d></girafe-map-3d></div>
42
+ <girafe-map-grid><girafe-map-2d></girafe-map-2d><girafe-map-3d></girafe-map-3d></girafe-map-grid>
42
43
  ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
43
44
  };
44
45
  isInitialized = false;
@@ -107,6 +108,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
107
108
  if (!customElements.get('girafe-map-2d')) {
108
109
  customElements.define('girafe-map-2d', Map2dComponent);
109
110
  customElements.define('girafe-map-3d', Map3dComponent);
111
+ customElements.define('girafe-map-grid', MapGridComponent);
110
112
  }
111
113
  }
112
114
  manageAttributes() {
package/api/style.css CHANGED
@@ -2,27 +2,21 @@
2
2
  position: relative;
3
3
  }
4
4
 
5
- #maps {
5
+ girafe-map-grid {
6
+ display: block;
6
7
  position: relative;
7
8
  width: 100%;
8
9
  height: 100%;
9
10
  overflow: hidden;
10
11
  }
11
12
 
12
- girafe-map-2d {
13
+ girafe-map-2d,
14
+ girafe-map-3d {
13
15
  display: block;
14
16
  position: relative;
15
17
  width: 100%;
16
18
  height: 100%;
17
- }
18
-
19
- girafe-map-3d {
20
- display: none;
21
- position: absolute;
22
- top: 0;
23
- height: 100%;
24
19
  overflow: hidden;
25
- border-left: solid 3px #444;
26
20
  }
27
21
 
28
22
  girafe-basemap {
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "de": {
3
+ "(empty)": "(leer)",
3
4
  "+{number} more": "+{number} mehr",
4
5
  "2D": "2D",
5
6
  "2D / 3D": "2D / 3D",
7
+ "2D Map": "2D-Karte",
6
8
  "3D": "3D",
9
+ "3D Map": "3D-Karte",
7
10
  "About": "About",
8
11
  "about-panel": "About",
9
12
  "Activate / Deactivate": "Aktivieren / Deaktivieren",
@@ -126,6 +129,7 @@
126
129
  "Discard": "Verwerfen",
127
130
  "Disk": "Kreis",
128
131
  "Display menu": "Menü anzeigen",
132
+ "Displayed components": "Angezeigte Komponenten",
129
133
  "Distance": "Entfernung",
130
134
  "Do you want to delete this bookmark?": "Möchten Sie dieses Lesezeichen löschen?",
131
135
  "Do you want to delete this feature?": "Möchten Sie dieses Objekt löschen?",
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "en": {
3
+ "(empty)": "(empty)",
3
4
  "+{number} more": "+{number} more",
4
5
  "2D": "2D",
5
6
  "2D / 3D": "2D / 3D",
7
+ "2D Map": "2D Map",
6
8
  "3D": "3D",
9
+ "3D Map": "3D Map",
7
10
  "About": "About",
8
11
  "about-panel": "About",
9
12
  "Activate / Deactivate": "Activate / Deactivate",
@@ -127,6 +130,7 @@
127
130
  "Discard": "Discard",
128
131
  "Disk": "Circle",
129
132
  "Display menu": "Display menu",
133
+ "Displayed components": "Displayed components",
130
134
  "Distance": "Distance",
131
135
  "Do you want to delete this bookmark?": "Do you want to delete this bookmark?",
132
136
  "Do you want to delete this feature?": "Do you want to delete this feature?",
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "fr": {
3
+ "(empty)": "(vide)",
3
4
  "+{number} more": "+{number} de plus",
4
5
  "2D": "2D",
5
6
  "2D / 3D": "2D / 3D",
7
+ "2D Map": "Carte 2D",
6
8
  "3D": "3D",
9
+ "3D Map": "Carte 3D",
7
10
  "About": "À propos",
8
11
  "about-panel": "À propos",
9
12
  "Activate / Deactivate": "Activer / Désactiver",
@@ -126,6 +129,7 @@
126
129
  "Discard": "Annuler cette entité",
127
130
  "Disk": "Cercle",
128
131
  "Display menu": "Afficher le menu",
132
+ "Displayed components": "Composants affichés",
129
133
  "Distance": "Distance",
130
134
  "Do you want to delete this bookmark?": "Voulez-vous supprimer ce favori ?",
131
135
  "Do you want to delete this feature?": "Souhaitez-vous supprimer cette entité?",
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "it": {
3
+ "(empty)": "(vuoto)",
3
4
  "+{number} more": "+{number} in più",
4
5
  "2D": "2D",
5
6
  "2D / 3D": "2D / 3D",
7
+ "2D Map": "Mappa 2D",
6
8
  "3D": "3D",
9
+ "3D Map": "Mappa 3D",
7
10
  "About": "Informazioni",
8
11
  "about-panel": "Informazioni",
9
12
  "Activate / Deactivate": "Attiva / Disattiva",
@@ -126,6 +129,7 @@
126
129
  "Discard": "Scartare",
127
130
  "Disk": "Cerchio",
128
131
  "Display menu": "Mostra menu",
132
+ "Displayed components": "Componenti visualizzati",
129
133
  "Distance": "Distanza",
130
134
  "Do you want to delete this bookmark?": "Vuoi eliminare questo segnalibro?",
131
135
  "Do you want to delete this feature?": "Si desidera eliminare questo elemento?",
@@ -13,7 +13,7 @@ class BasemapComponent extends GirafeHTMLElement {
13
13
  .basemap-button{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);box-sizing:border-box;cursor:pointer;box-shadow:var(--bx-shdw);border-width:2px;border-radius:4px;outline:0;margin:0;padding:5px;display:inline-flex}#container{flex-direction:column;width:max-content;display:inline-flex}.basemap-icon{background:var(--svg-legend-bkg);filter:var(--svg-map-filter)}.active-basemap{background-color:var(--bkg-color-grad2)!important}.opacity-slider{flex-direction:row;height:20px;padding:0 5px;display:flex}.opacity-slider img{height:16px;position:relative;top:2px}.opacity-slider input[type=range]{flex-grow:1}.basemap-container{flex-direction:column;display:flex}.basemap-container button{color:var(--text-color);background-color:#0000;border:none;flex-direction:row;flex-grow:1;display:flex}.basemap-container button img{height:2rem;margin:.3rem}.basemap-container button span{height:2rem;margin:.3rem;line-height:2rem}.basemap-container:hover{background-color:var(--bkg-color-grad1)}.basemaps-with-opacity-separator{border-top:1px solid var(--bkg-color-grad2);height:1px;margin:6px 8px}
14
14
  </style>
15
15
  <style>${this.customStyle}</style>
16
- <girafe-menu-button direction="up-right" id="basemap-menu-button"><button slot="menu-button" class="basemap-button" tip="Select basemap"><img class="basemap-icon" alt="menu-icon" src="images/button_basemaps.webp"></button><div slot="menu-content" id="container">${Object.values(this.state.basemaps).filter(basemap => basemap.opacity == -1).map(basemap => uHtmlFor(basemap, basemap.id) `<div class="${this.determineClassnames(basemap)}"><button onclick="${() => this.changeBasemap(basemap)}"><img class="basemap-icon" alt="basemap-icon" src="${basemap.thumbnail}"> <span i18n="${basemap.name}">${basemap.name}</span></button></div>`)}<div class="basemaps-with-opacity-separator"></div>${Object.values(this.state.basemaps).filter(basemap => basemap.opacity != -1).map(basemap => uHtmlFor(basemap, basemap.id) `<div class="${this.determineClassnames(basemap)}"><button onclick="${() => this.changeBasemap(basemap)}"><img class="basemap-icon" alt="basemap-icon" src="${basemap.thumbnail}"> <span i18n="${basemap.name}">${basemap.name}</span></button><div class="opacity-slider"><img alt="Control opacity" src="icons/opacity.svg"> <input type="range" min="0" max="1" step="0.01" class="slider" value="${basemap.opacity}" onkeydown="${(e) => { e.preventDefault(); e.stopPropagation(); }}" onclick="${(e) => { e.preventDefault(); e.stopPropagation(); }}" onchange="${(e) => this.changeBasemapOpacity(basemap, e)}"></div></div>`)}</div></girafe-menu-button>
16
+ <girafe-menu-button direction="up-right" id="basemap-menu-button"><button slot="menu-button" class="basemap-button" tip="Select basemap"><img class="basemap-icon" alt="menu-icon" src="images/button_basemaps.webp"></button><div slot="menu-content" id="container">${Object.values(this.state.basemaps).filter(basemap => basemap.opacity == -1).map(basemap => uHtmlFor(basemap, basemap.id) `<div class="${this.determineClassnames(basemap)}"><button onclick="${() => this.changeBasemap(basemap)}"><img class="basemap-icon" alt="basemap-icon" src="${basemap.thumbnail}"> <span i18n="${basemap.name}">${basemap.name}</span></button></div>`)}<div class="basemaps-with-opacity-separator"></div>${Object.values(this.state.basemaps).filter(basemap => basemap.opacity != -1).map(basemap => uHtmlFor(basemap, basemap.id) `<div class="${this.determineClassnames(basemap)}"><button onclick="${() => this.changeBasemap(basemap)}"><img class="basemap-icon" alt="basemap-icon" src="${basemap.thumbnail}"> <span i18n="${basemap.name}">${basemap.name}</span></button><div class="opacity-slider"><img alt="Control opacity" src="icons/opacity.svg"> <input type="range" min="0" max="1" step="0.01" class="gg-range" value="${basemap.opacity}" onkeydown="${(e) => { e.preventDefault(); e.stopPropagation(); }}" onclick="${(e) => { e.preventDefault(); e.stopPropagation(); }}" onchange="${(e) => this.changeBasemapOpacity(basemap, e)}"></div></div>`)}</div></girafe-menu-button>
17
17
  ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
18
18
  };
19
19
  constructor() {
@@ -1,5 +1,6 @@
1
1
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
2
  import IGirafePanel from '../../tools/state/igirafepanel.js';
3
+ import { CellContent, LayoutType } from '../../tools/state/layout.js';
3
4
  declare class LayoutComponent extends GirafeHTMLElement implements IGirafePanel {
4
5
  protected templateUrl: string | null;
5
6
  protected styleUrls: string[] | null;
@@ -7,13 +8,43 @@ declare class LayoutComponent extends GirafeHTMLElement implements IGirafePanel
7
8
  isPanelVisible: boolean;
8
9
  panelTitle: string;
9
10
  panelTogglePath: string;
11
+ /** The Components the user can choose between for each Cell of the Grid. */
12
+ availableComponents: readonly [{
13
+ readonly id: "map-2d";
14
+ readonly tagName: "girafe-map-2d";
15
+ readonly label: "2D Map";
16
+ }, {
17
+ readonly id: "map-3d";
18
+ readonly tagName: "girafe-map-3d";
19
+ readonly label: "3D Map";
20
+ }];
10
21
  constructor();
22
+ private get layout();
23
+ /** The Layouts the user can choose between, as configured in the application. */
24
+ get allowedLayouts(): LayoutType[];
25
+ /** The Cells of the current Layout, one line of the schema per row of the Grid. */
26
+ get cellRows(): {
27
+ index: number;
28
+ content: CellContent;
29
+ }[][];
11
30
  private registerEvents;
12
31
  togglePanel(visible: boolean): void;
13
- private onLayoutSelect;
14
32
  private onLayoutChanged;
15
- private syncLayoutSelect;
16
- private updateShadowsVisibility;
33
+ /**
34
+ * uHtml only updates the <selected> attribute of the options, which the browser ignores
35
+ * as soon as the user has interacted with a select. The values are therefore applied explicitly,
36
+ * so that the panel also follows the changes made outside of it (shared state, other components).
37
+ */
38
+ private syncSelects;
39
+ private onLayoutSelect;
40
+ /**
41
+ * Places a Component in a Cell of the Grid.
42
+ * A Component can only be displayed once: if it is already displayed in another Cell,
43
+ * both Cells are swapped, so that the Component that was in the target Cell is not lost.
44
+ */
45
+ onCellSelect(event: Event, cellIndex: number): void;
46
+ /** Whether one of the Cells displays a 3D Map, which the shadows options depend on. */
47
+ get is3dDisplayed(): boolean;
17
48
  private onShadowsToggle;
18
49
  private setDateTimePicker;
19
50
  private update3dMapTimestamp;
@@ -1,7 +1,7 @@
1
1
  import { html as uHtml } from 'uhtml';
2
2
  // SPDX-License-Identifier: Apache-2.0
3
3
  import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
4
- const ALLOWED_LAYOUTS = ['2D', '3D', '2D/3D'];
4
+ import { AVAILABLE_CELL_COMPONENTS, distributeCells, getDefaultSizes, getGridSize, isCellComponent, isLayoutType } from '../../tools/state/layout.js';
5
5
  class LayoutComponent extends GirafeHTMLElement {
6
6
  templateUrl = null;
7
7
  styleUrls = null;
@@ -9,59 +9,96 @@ class LayoutComponent extends GirafeHTMLElement {
9
9
  return uHtml `<style>
10
10
  .hidden{display:none!important}.gg-rotate90{transform:rotate(90deg)}.gg-rotate180{transform:rotate(180deg)}.gg-rotate270{transform:rotate(270deg)}img{filter:var(--svg-filter)}img.legend-image{filter:var(--svg-map-filter);background:var(--svg-legend-bkg)}div{scrollbar-width:thin}a,a:visited{color:var(--link-color)}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@keyframes spin-wait{0%{transform:rotate(0)}7%{transform:rotate(360deg)}to{transform:rotate(360deg)}}.gg-spin{animation-name:spin;animation-duration:2s;animation-timing-function:linear;animation-iteration-count:infinite}.gg-spin-wait{animation-name:spin-wait;animation-duration:10s;animation-timing-function:linear;animation-iteration-count:infinite}::-webkit-scrollbar{width:5px}::-webkit-scrollbar-thumb{background:#999}button,input,select,textarea{font:inherit}.gg-button,.gg-select,.gg-input,.gg-textarea{background-color:var(--bkg-color);color:var(--text-color);border:var(--app-standard-border);box-sizing:border-box;cursor:pointer;border-radius:3px;outline:0;margin:0;padding:0 0 0 .5rem;display:inline-block}.gg-label{background-color:var(--bkg-color);color:var(--text-color);border:none;align-items:center;margin:0;padding:0;display:flex}.gg-button,.gg-select,.gg-input,.gg-label{min-height:calc(var(--app-standard-height) / 1.5)}.gg-textarea{max-height:initial;resize:vertical;height:6rem;padding:.5rem;line-height:1.3rem}.gg-input{cursor:text}.gg-checkbox{accent-color:var(--text-color);cursor:pointer;width:1.2rem}.gg-range{accent-color:var(--text-color)}.gg-button{padding:0 .5rem}.gg-button.active{border:solid 1px var(--text-color-grad1);background-color:var(--bkg-color-grad2)}.gg-button:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed;border:none}.gg-button:disabled img{filter:opacity(.6)}.gg-input:disabled,.gg-select:disabled,.gg-textarea:disabled{background-color:var(--bkg-color-grad1);color:var(--text-color-grad2);cursor:not-allowed}.gg-button>img{vertical-align:middle}.gg-icon-button{color:var(--text-color);cursor:pointer;background-color:#0000;border:none;flex-direction:column;justify-content:center;align-items:center;padding:0;display:flex}.gg-icon{justify-content:center;align-items:center;display:flex}.gg-big,.gg-big-withtext{min-width:var(--app-standard-height);min-height:var(--app-standard-height);max-height:var(--app-standard-height)}.gg-big img,.gg-big-withtext img{width:calc(var(--app-standard-height) - 1.5rem);margin:0}.gg-big-withtext span{font-variant:small-caps;padding:0 1rem;font-size:.9rem}.gg-medium,.gg-medium-withtext{min-width:calc(var(--app-standard-height) / 1.2);min-height:calc(var(--app-standard-height) / 1.2);max-height:calc(var(--app-standard-height) / 1.2);flex-direction:row}.gg-medium img{width:calc(var(--app-standard-height) / 2.4);margin:0}.gg-medium-withtext img{width:calc(var(--app-standard-height) / 2.4);margin-left:.5rem}.gg-medium-withtext span{padding:0 1rem 0 .5rem;font-size:.9rem}.gg-small,.gg-small-withtext{min-width:calc(var(--app-standard-height) / 2);min-height:calc(var(--app-standard-height) / 2);max-height:calc(var(--app-standard-height) / 2);flex-direction:row}.gg-small img{width:calc(var(--app-standard-height) / 3);margin:0}.gg-small-withtext img{width:calc(var(--app-standard-height) / 3);margin-left:.5rem}.gg-small-withtext span{padding:0 .5rem 0 .3rem;font-size:.9rem}.gg-button:hover:not(:disabled),.gg-select:hover:not(:disabled),.gg-input:hover:not(:disabled),.gg-textarea:hover:not(:disabled),.gg-icon-button:hover:not(:disabled){background-color:var(--bkg-color-grad1)}.gg-opacity{opacity:.5}.gg-opacity:hover{opacity:1;background-color:#0000}.gg-tabs{cursor:pointer;grid-auto-flow:column;padding-bottom:1rem;font-size:1rem;display:grid}.gg-tab{border:none;border-bottom:var(--app-standard-border);cursor:pointer;color:var(--text-color);background:0 0;padding:.5rem}.gg-tab.active{border-bottom:solid 1px var(--text-color)}.girafe-button-big,.girafe-button-large,.girafe-button-small,.girafe-button-tiny{color:var(--text-color);background-color:#0000;border:none;flex-direction:column;display:flex}.girafe-button-big:hover,.girafe-button-large:hover,.girafe-button-small:hover,.girafe-button-tiny:hover{background-color:var(--bkg-color-grad1);cursor:pointer}.girafe-button-big.dark,.girafe-button-large.dark,.girafe-button-small.dark,.girafe-button-tiny.dark{background-color:var(--bkg-color);filter:invert()}.girafe-button-big{width:var(--app-standard-height);height:var(--app-standard-height);align-items:center;padding:1rem}.girafe-button-big img{overflow:hidden}.girafe-button-large{flex-direction:row}.girafe-button-large img{height:2rem;margin:.3rem}.girafe-button-large span{height:2rem;margin:.3rem;line-height:2rem}.girafe-button-small{min-width:calc(var(--app-standard-height) / 2);height:calc(var(--app-standard-height) / 2);align-items:center;padding:.5rem}.girafe-button-small img{overflow:hidden}.girafe-button-small span{text-align:left;text-overflow:ellipsis;width:100%;overflow:hidden}.girafe-button-tiny{align-items:center;width:1rem;height:1rem;padding:0}.girafe-button-tiny img{overflow:hidden}.girafe-onboarding-theme{background-color:var(--bkg-color)!important;color:var(--text-color)!important}.girafe-onboarding-theme button{background-color:var(--bkg-color)!important;color:var(--text-color)!important;text-shadow:none!important}.girafe-onboarding-theme button.driver-popover-close-btn{z-index:10000}.tippy-box{background-color:var(--bkg-color)!important;border:var(--app-standard-border)!important}.tippy-content{color:var(--text-color)!important}.tippy-arrow{color:var(--bkg-color)!important}.tippy-box[data-theme~=error]{background-color:var(--error-color);white-space:pre-line}div.tippy-box[data-theme=image-popup]{border:var(--app-standard-border)!important;& div.tippy-content{background-color:var(--bkg-color)!important;& img{max-width:20rem;max-height:20rem}}& div.tippy-arrow{color:var(--bkg-color)!important}}
11
11
  </style><style>
12
- #panel{color:var(--text-color)}#panel>div{padding:1rem}.hidden{display:none}fieldset{margin-top:1rem}.option{margin-top:.5rem;display:flex}.option select,.option input{flex-grow:1}.option input.gg-checkbox{flex-grow:0}.option label{margin-right:.5rem}.option label:after{content:" :"}
12
+ #panel{color:var(--text-color)}#panel>div{padding:1rem}.hidden{display:none}fieldset{margin-top:1rem}.cells-row{gap:.25rem;margin-top:.5rem;display:flex}.cell-select{flex:1 1 0;min-width:0}.option{margin-top:.5rem;display:flex}.option select,.option input{flex-grow:1}.option input.gg-checkbox{flex-grow:0}.option label{margin-right:.5rem}.option label:after{content:" :"}
13
13
  </style>
14
14
  <style>${this.customStyle}</style>
15
- <div id="panel"><div><div class="option"><label for="layout" i18n="View" class="gg-label">View</label> <select id="layout" class="gg-select" onchange="${this.onLayoutSelect}"><option i18n="2D" value="2D">2D<option i18n="2D / 3D" value="2D/3D">2D / 3D<option i18n="3D" value="3D">3D</select></div><fieldset class="shadows-group hidden"><legend i18n="Shadows">Shadows</legend><div class="option shadows-option"><label for="shadowsCheckbox" i18n="Enable shadows" class="gg-label">Enable shadows</label> <input id="shadowsCheckbox" type="checkbox" class="gg-checkbox" onchange="${this.onShadowsToggle}"></div><div class="option shadows-date hidden"><label for="shadowsDate" i18n="Date" class="gg-label">Date</label> <input id="shadowsDate" type="datetime-local" class="gg-input"></div></fieldset></div></div>
15
+ <div id="panel"><div><div class="option"><label for="layout" i18n="View" class="gg-label">View</label> <select id="layout" class="gg-select" onchange="${this.onLayoutSelect}">${this.allowedLayouts.map((layout) => uHtml `<option value="${layout}" ?selected="${layout === this.state.layout.type}">${layout}</option>`)}</select></div><fieldset class="cells-group"><legend i18n="Displayed components">Displayed components</legend>${this.cellRows.map((cellRow) => uHtml `<div class="cells-row">${cellRow.map((cell) => uHtml ` <select class="gg-select cell-select" onchange="${(e) => this.onCellSelect(e, cell.index)}"><option i18n="(empty)" value="" ?selected="${cell.content === null}">(empty)</option>${this.availableComponents.map((component) => uHtml `<option i18n="${component.label}" value="${component.id}" ?selected="${cell.content === component.id}">${component.label}</option>`)}</select> `)}</div>`)}</fieldset><fieldset class="${this.is3dDisplayed ? 'shadows-group' : 'shadows-group hidden'}"><legend i18n="Shadows">Shadows</legend><div class="option shadows-option"><label for="shadowsCheckbox" i18n="Enable shadows" class="gg-label">Enable shadows</label> <input id="shadowsCheckbox" type="checkbox" class="gg-checkbox" ?checked="${this.state.globe.shadows}" onchange="${this.onShadowsToggle}"></div><div class="${this.state.globe.shadows ? 'option shadows-date' : 'option shadows-date hidden'}"><label for="shadowsDate" i18n="Date" class="gg-label">Date</label> <input id="shadowsDate" type="datetime-local" class="gg-input"></div></fieldset></div></div>
16
16
  ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
17
17
  };
18
18
  isPanelVisible = false;
19
19
  panelTitle = 'layout-panel';
20
20
  panelTogglePath = 'interface.layoutPanelVisible';
21
+ /** The Components the user can choose between for each Cell of the Grid. */
22
+ availableComponents = AVAILABLE_CELL_COMPONENTS;
21
23
  constructor() {
22
24
  super('layout');
23
25
  this.onLayoutSelect = this.onLayoutSelect.bind(this);
24
26
  this.onShadowsToggle = this.onShadowsToggle.bind(this);
25
27
  }
28
+ get layout() {
29
+ return this.state.layout;
30
+ }
31
+ /** The Layouts the user can choose between, as configured in the application. */
32
+ get allowedLayouts() {
33
+ return this.context.configManager.Config.interface.layouts;
34
+ }
35
+ /** The Cells of the current Layout, one line of the schema per row of the Grid. */
36
+ get cellRows() {
37
+ const { columns, rows } = getGridSize(this.layout.type);
38
+ const cells = this.layout.cells;
39
+ return Array.from({ length: rows }, (_, row) => Array.from({ length: columns }, (_, column) => {
40
+ const index = row * columns + column;
41
+ return { index: index, content: cells[index] ?? null };
42
+ }));
43
+ }
26
44
  registerEvents() {
27
- this.subscribe('globe.display', (_oldValue, newValue) => this.onLayoutChanged(newValue));
45
+ this.subscribe('layout.type', () => this.onLayoutChanged());
46
+ this.subscribe('layout.cells', () => this.onLayoutChanged());
28
47
  }
29
48
  togglePanel(visible) {
30
49
  this.isPanelVisible = visible;
31
50
  this.render();
32
- if (visible) {
33
- const currentLayout = this.state.globe.display;
34
- this.syncLayoutSelect(currentLayout);
35
- this.updateShadowsVisibility(currentLayout);
51
+ }
52
+ onLayoutChanged() {
53
+ if (this.isPanelVisible) {
54
+ this.refreshRender();
55
+ this.syncSelects();
36
56
  }
37
57
  }
58
+ /**
59
+ * uHtml only updates the <selected> attribute of the options, which the browser ignores
60
+ * as soon as the user has interacted with a select. The values are therefore applied explicitly,
61
+ * so that the panel also follows the changes made outside of it (shared state, other components).
62
+ */
63
+ syncSelects() {
64
+ const layoutSelect = this.getById('layout');
65
+ if (layoutSelect) {
66
+ layoutSelect.value = this.layout.type;
67
+ }
68
+ const cells = this.layout.cells;
69
+ this.shadow.querySelectorAll('.cell-select').forEach((select, index) => {
70
+ select.value = cells[index] ?? '';
71
+ });
72
+ }
38
73
  onLayoutSelect(event) {
39
74
  const selectedLayout = event.target?.value;
40
- if (ALLOWED_LAYOUTS.includes(selectedLayout)) {
41
- this.state.globe.display = selectedLayout;
75
+ if (!isLayoutType(selectedLayout) || !this.allowedLayouts.includes(selectedLayout)) {
76
+ console.error(`${selectedLayout} is not a valid layout!`);
42
77
  return;
43
78
  }
44
- console.error(`${selectedLayout} is not a valid layout!`);
45
- }
46
- onLayoutChanged(globe) {
47
- this.syncLayoutSelect(globe);
48
- if (this.isPanelVisible) {
49
- this.updateShadowsVisibility(globe);
50
- }
79
+ this.layout.cells = distributeCells(selectedLayout, this.layout.cells);
80
+ this.layout.sizes = getDefaultSizes(selectedLayout);
81
+ this.layout.type = selectedLayout;
51
82
  }
52
- syncLayoutSelect(layout) {
53
- const layoutSelect = this.shadowRoot?.querySelector('#layout');
54
- if (layoutSelect) {
55
- layoutSelect.value = layout;
83
+ /**
84
+ * Places a Component in a Cell of the Grid.
85
+ * A Component can only be displayed once: if it is already displayed in another Cell,
86
+ * both Cells are swapped, so that the Component that was in the target Cell is not lost.
87
+ */
88
+ onCellSelect(event, cellIndex) {
89
+ const value = event.target?.value;
90
+ const component = isCellComponent(value) ? value : null;
91
+ const cells = [...this.layout.cells];
92
+ const previousIndex = component === null ? -1 : cells.indexOf(component);
93
+ if (previousIndex >= 0) {
94
+ cells[previousIndex] = cells[cellIndex];
56
95
  }
96
+ cells[cellIndex] = component;
97
+ this.layout.cells = cells;
57
98
  }
58
- updateShadowsVisibility(globe) {
59
- const shadowsGroup = this.shadowRoot?.querySelector('.shadows-group');
60
- const shadowsDate = this.shadowRoot?.querySelector('.shadows-date');
61
- const shadowsCheckbox = this.shadowRoot?.querySelector('#shadowsCheckbox');
62
- const has3Dlayout = globe === '2D/3D' || globe === '3D';
63
- shadowsGroup?.classList.toggle('hidden', !has3Dlayout);
64
- shadowsDate?.classList.toggle('hidden', !has3Dlayout || !shadowsCheckbox?.checked);
99
+ /** Whether one of the Cells displays a 3D Map, which the shadows options depend on. */
100
+ get is3dDisplayed() {
101
+ return this.layout.cells.includes('map-3d');
65
102
  }
66
103
  onShadowsToggle(event) {
67
104
  const shadowsDate = this.shadowRoot?.querySelector('.shadows-date');
@@ -93,6 +130,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
93
130
  render() {
94
131
  if (this.isPanelVisible) {
95
132
  super.render();
133
+ this.syncSelects();
96
134
  }
97
135
  else {
98
136
  this.hide();
@@ -15,6 +15,19 @@ fieldset {
15
15
  margin-top: 1rem;
16
16
  }
17
17
 
18
+ /* Schema of the Grid: one row per row of the Grid, one select per Cell. */
19
+ .cells-row {
20
+ display: flex;
21
+ gap: 0.25rem;
22
+ margin-top: 0.5rem;
23
+ }
24
+
25
+ .cell-select {
26
+ flex: 1 1 0;
27
+ /* Prevents the selects from growing over the width of the panel. */
28
+ min-width: 0;
29
+ }
30
+
18
31
  .option {
19
32
  margin-top: 0.5rem;
20
33
  display: flex;
@@ -32,6 +32,7 @@ export { default as LRPanelComponent } from './lr-panel/component.js';
32
32
  export { default as Map2dComponent } from './map-2d/component.js';
33
33
  export { default as DeprecatedMapComponent } from './map-2d/deprecatedmapcomponent.js';
34
34
  export { default as Map3dComponent } from './map-3d/component.js';
35
+ export { default as MapGridComponent } from './map-grid/component.js';
35
36
  export { default as MenuMobile } from './menu-mobile/component.js';
36
37
  export { default as MenuButtonComponent } from './menubutton/component.js';
37
38
  export { default as ModalsComponent } from './modals/component.js';
@@ -31,6 +31,7 @@ export { default as LRPanelComponent } from './lr-panel/component.js';
31
31
  export { default as Map2dComponent } from './map-2d/component.js';
32
32
  export { default as DeprecatedMapComponent } from './map-2d/deprecatedmapcomponent.js';
33
33
  export { default as Map3dComponent } from './map-3d/component.js';
34
+ export { default as MapGridComponent } from './map-grid/component.js';
34
35
  export { default as MenuMobile } from './menu-mobile/component.js';
35
36
  export { default as MenuButtonComponent } from './menubutton/component.js';
36
37
  export { default as ModalsComponent } from './modals/component.js';
@@ -109,12 +109,6 @@ export default class Map2dComponent extends GirafeHTMLElement {
109
109
  private setSelectLayerStyle;
110
110
  private setHighlightLayerStyle;
111
111
  private setCrosshairStyle;
112
- /**
113
- * Resizes the 2D Map depending on the current globe display mode.
114
- * In side-by-side mode the 2D Map only takes the left part of the available space,
115
- * in full 3D mode it is hidden. The 3D Map takes care of its own sizing.
116
- */
117
- onGlobeDisplayChanged(display: string): void;
118
112
  onFeaturesSelected(features: Feature[]): void;
119
113
  onFeatureHighlighted(features: Feature[]): void;
120
114
  zoomToExtent(extent: Extent): void;
@@ -158,7 +158,6 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
158
158
  this.addMarker(marker);
159
159
  }
160
160
  });
161
- this.subscribe('globe.display', (_oldDisplay, newDisplay) => this.onGlobeDisplayChanged(newDisplay));
162
161
  this.subscribe(/layers\.layersList\..*\.activeState/, async (_oldActive, _newActive, layer) => {
163
162
  await this.onLayerToggled(layer);
164
163
  if (layer.active) {
@@ -352,6 +351,9 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
352
351
  this.olMap.updateSize();
353
352
  }, 1000);
354
353
  this.mapTargetResizeObserver = new ResizeObserver(() => {
354
+ // The size of the Map is defined by the Cell of the Grid it is displayed in,
355
+ // and the user can change it at any time by dragging a splitter.
356
+ this.olMap.updateSize();
355
357
  this.updateCloseSwiperPosition();
356
358
  });
357
359
  this.mapTargetResizeObserver.observe(this.mapTarget);
@@ -481,7 +483,6 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
481
483
  this.render();
482
484
  this.listenOpenLayersEvents();
483
485
  this.registerEvents();
484
- this.onGlobeDisplayChanged(this.state.globe.display);
485
486
  }
486
487
  onCustomGirafeEvent(details) {
487
488
  if (details.action === GeoEvents.zoomToExtent) {
@@ -549,26 +550,6 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
549
550
  })
550
551
  }));
551
552
  }
552
- /**
553
- * Resizes the 2D Map depending on the current globe display mode.
554
- * In side-by-side mode the 2D Map only takes the left part of the available space,
555
- * in full 3D mode it is hidden. The 3D Map takes care of its own sizing.
556
- */
557
- onGlobeDisplayChanged(display) {
558
- if (display === '3D') {
559
- this.style.display = 'none';
560
- }
561
- else if (display === '2D/3D') {
562
- this.style.display = 'block';
563
- this.style.width = '45%';
564
- this.olMap.updateSize();
565
- }
566
- else {
567
- this.style.display = 'block';
568
- this.style.width = '100%';
569
- this.olMap.updateSize();
570
- }
571
- }
572
553
  onFeaturesSelected(features) {
573
554
  this.selectedFeaturesCollection.clear();
574
555
  if (features) {
@@ -43,8 +43,10 @@ export default class Map3dComponent extends GirafeHTMLElement {
43
43
  */
44
44
  private createGlobe;
45
45
  /**
46
- * Shows or hides the 3D Map and sizes it depending on the current display mode.
47
- * The 2D Map takes care of its own sizing.
46
+ * Creates and enables the globe when the 3D Map is displayed in a Cell of the Grid,
47
+ * and disables it when it is not displayed anymore.
48
+ * The size and the position of the Map are managed by the Grid, OLCesium takes care
49
+ * of following the size of its container.
48
50
  */
49
51
  onGlobeToggled(): Promise<void>;
50
52
  }
@@ -61,7 +61,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
61
61
  this.map3dTarget = this.shadow.getElementById('cs-map');
62
62
  }
63
63
  registerEvents() {
64
- this.subscribe('globe.display', async () => {
64
+ this.subscribe('layout.cells', async () => {
65
65
  await this.onGlobeToggled();
66
66
  this.globe?.setCamera(this.state.globe.camera);
67
67
  });
@@ -109,30 +109,19 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
109
109
  return this.globeCreation;
110
110
  }
111
111
  /**
112
- * Shows or hides the 3D Map and sizes it depending on the current display mode.
113
- * The 2D Map takes care of its own sizing.
112
+ * Creates and enables the globe when the 3D Map is displayed in a Cell of the Grid,
113
+ * and disables it when it is not displayed anymore.
114
+ * The size and the position of the Map are managed by the Grid, OLCesium takes care
115
+ * of following the size of its container.
114
116
  */
115
117
  async onGlobeToggled() {
116
- if (this.state.globe.display === '3D') {
117
- // Full screen globe has been enabled
118
- this.style.display = 'block';
119
- this.style.left = '0';
120
- this.style.width = '100%';
121
- await this.createGlobe();
122
- this.globe?.setEnabled(true);
123
- }
124
- else if (this.state.globe.display === '2D/3D') {
125
- // Side by side has been enabled
126
- this.style.display = 'block';
127
- this.style.left = '45%';
128
- this.style.width = '55%';
118
+ const isDisplayed = this.state.layout.cells.includes('map-3d');
119
+ if (isDisplayed) {
129
120
  await this.createGlobe();
130
121
  this.globe?.setEnabled(true);
131
122
  }
132
123
  else {
133
- // 3d map is not visible
134
124
  this.globe?.setEnabled(false);
135
- this.style.display = 'none';
136
125
  this.unregisterInteractionListeners('globe.select');
137
126
  }
138
127
  }
@@ -0,0 +1,70 @@
1
+ import GirafeHTMLElement from '../../base/GirafeHTMLElement.js';
2
+ /** A splitter, placed on the boundary between two columns or two rows of the Grid. */
3
+ type Splitter = {
4
+ /** Whether the splitter is dragged left/right (vertical) or up/down (horizontal). */
5
+ orientation: 'vertical' | 'horizontal';
6
+ /** Index of the column (vertical) or of the row (horizontal) placed before the splitter. */
7
+ index: number;
8
+ /** Position of the splitter, in percent of the Grid. */
9
+ position: number;
10
+ };
11
+ /**
12
+ * The Grid in which the Map Components are displayed.
13
+ *
14
+ * The Components are written as children of this Element in the HTML of the application
15
+ * and are moved into the Cells of the Grid, depending on <code>state.layout</code>.
16
+ * A Component that is not placed in any Cell is not slotted, and therefore not displayed.
17
+ */
18
+ declare class MapGridComponent extends GirafeHTMLElement {
19
+ protected templateUrl: string | null;
20
+ protected styleUrls: string[] | null;
21
+ template: () => import("uhtml").Hole;
22
+ /** Index of each Cell of the current Layout, used to render the Cells and their slots. */
23
+ cellIndexes: number[];
24
+ /** The splitters between the Cells of the current Layout. */
25
+ splitters: Splitter[];
26
+ /** The splitter that is currently dragged, if any. */
27
+ private draggedSplitter;
28
+ constructor();
29
+ private get layout();
30
+ private get grid();
31
+ protected connectedCallback(): void;
32
+ private registerEvents;
33
+ /**
34
+ * Rebuilds the Cells and the splitters of the Grid for the current Layout,
35
+ * then places the Components and applies the sizes.
36
+ */
37
+ private applyLayout;
38
+ /**
39
+ * Creates one splitter for each boundary between two columns and between two rows.
40
+ * The splitters go through the whole Grid, so that the columns and the rows keep
41
+ * the same sizes in every Cell of the Grid.
42
+ */
43
+ private createSplitters;
44
+ /**
45
+ * Moves each Component into the Cell it is configured for, by setting the name of the slot
46
+ * it must be displayed in. Components that are not placed in any Cell get no slot,
47
+ * which means that they are not displayed at all.
48
+ */
49
+ private applyCells;
50
+ /** Applies the sizes of the columns and of the rows to the Grid and to the splitters. */
51
+ private applySizes;
52
+ private applySplitterPosition;
53
+ getSplitterId(splitter: Splitter): string;
54
+ onSplitterPointerDown(event: PointerEvent, splitter: Splitter): void;
55
+ /**
56
+ * Resizes the two columns (or the two rows) around the dragged splitter.
57
+ * While dragging, the new sizes are only applied to the DOM: the state is updated
58
+ * at the end of the drag, to prevent flooding it with intermediate values.
59
+ */
60
+ onSplitterPointerMove(event: PointerEvent, splitter: Splitter): void;
61
+ onSplitterPointerUp(event: PointerEvent, splitter: Splitter): void;
62
+ /**
63
+ * Computes the new sizes of the columns (or of the rows) when the splitter is moved
64
+ * to the given position, in percent of the Grid. The two resized columns (or rows) are
65
+ * kept between MIN_CELL_SIZE and MAX_CELL_SIZE, the other ones keep their size.
66
+ */
67
+ private getResizedSizes;
68
+ render(): void;
69
+ }
70
+ export default MapGridComponent;