@geogirafe/lib-geoportal 1.2.0-dev.2836809138 → 1.2.0-dev.2844989778

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 (46) hide show
  1. package/api/apigeogirafeapp.js +4 -2
  2. package/api/style.css +4 -10
  3. package/assets/i18n/de.json +7 -3
  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/layout/component.d.ts +34 -3
  8. package/components/layout/component.js +66 -28
  9. package/components/layout/style.css +13 -0
  10. package/components/main.d.ts +1 -0
  11. package/components/main.js +1 -0
  12. package/components/map-2d/component.d.ts +0 -6
  13. package/components/map-2d/component.js +3 -22
  14. package/components/map-3d/component.d.ts +4 -2
  15. package/components/map-3d/component.js +7 -18
  16. package/components/map-grid/component.d.ts +70 -0
  17. package/components/map-grid/component.js +206 -0
  18. package/components/map-grid/style.css +99 -0
  19. package/components/menu-buttons-mobile/MenuMobile3dButton.js +3 -1
  20. package/package.json +1 -1
  21. package/styles/iframe.css +4 -10
  22. package/styles/index.css +4 -10
  23. package/styles/mobile.css +4 -10
  24. package/templates/iframe.html +2 -2
  25. package/templates/index.html +2 -2
  26. package/templates/mobile.html +2 -2
  27. package/templates/public/about.json +1 -1
  28. package/tools/app/geogirafeapp-mobile.js +2 -0
  29. package/tools/app/geogirafeapp.js +2 -0
  30. package/tools/configuration/girafeconfig.d.ts +4 -0
  31. package/tools/configuration/girafeconfig.js +20 -2
  32. package/tools/main.d.ts +3 -0
  33. package/tools/main.js +2 -0
  34. package/tools/share/serializers/globeserializer.d.ts +2 -0
  35. package/tools/share/serializers/globeserializer.js +26 -4
  36. package/tools/share/serializers/layoutserializer.d.ts +17 -0
  37. package/tools/share/serializers/layoutserializer.js +44 -0
  38. package/tools/share/stateserializer.js +3 -0
  39. package/tools/state/globe.d.ts +0 -1
  40. package/tools/state/globe.js +0 -1
  41. package/tools/state/layout.d.ts +62 -0
  42. package/tools/state/layout.js +67 -0
  43. package/tools/state/state.d.ts +2 -0
  44. package/tools/state/state.js +8 -0
  45. package/tools/state/statemanager.d.ts +1 -0
  46. package/tools/state/statemanager.js +8 -0
@@ -1,3 +1,16 @@
1
+ import LayoutSerializer from './layoutserializer.js';
2
+ /**
3
+ * The layouts the display modes of the previous versions correspond to.
4
+ *
5
+ * <code>globe.display</code> was replaced by <code>state.layout</code>, but the states that were
6
+ * shared before that change still contain it and have to keep working. Such a state never contains
7
+ * a layout, so there is no risk of overwriting a layout that was deserialized before.
8
+ */
9
+ const LEGACY_DISPLAYS = {
10
+ '2D': { type: '1x1', cells: ['map-2d'], sizes: { columns: [100], rows: [100] } },
11
+ '3D': { type: '1x1', cells: ['map-3d'], sizes: { columns: [100], rows: [100] } },
12
+ '2D/3D': { type: '2x1', cells: ['map-2d', 'map-3d'], sizes: { columns: [45, 55], rows: [100] } }
13
+ };
1
14
  export default class GlobeSerializer {
2
15
  context;
3
16
  constructor(context) {
@@ -8,7 +21,6 @@ export default class GlobeSerializer {
8
21
  }
9
22
  brainSerialize(globe) {
10
23
  return JSON.stringify({
11
- display: globe.display,
12
24
  shadows: globe.shadows,
13
25
  shadowsTimestamp: globe.shadowsTimestamp,
14
26
  camera: globe.camera
@@ -17,9 +29,6 @@ export default class GlobeSerializer {
17
29
  brainDeserialize(str) {
18
30
  const parsedGlobe = JSON.parse(str);
19
31
  const globe = this.globe;
20
- if (parsedGlobe.display) {
21
- globe.display = parsedGlobe.display;
22
- }
23
32
  if (parsedGlobe.shadows !== undefined) {
24
33
  globe.shadows = parsedGlobe.shadows;
25
34
  }
@@ -28,5 +37,18 @@ export default class GlobeSerializer {
28
37
  }
29
38
  globe.camera = parsedGlobe.camera ?? null;
30
39
  globe.loaded = false;
40
+ this.deserializeLegacyDisplay(parsedGlobe.display);
41
+ }
42
+ /** Applies the layout a display mode of the previous versions corresponds to. */
43
+ deserializeLegacyDisplay(display) {
44
+ if (display === undefined) {
45
+ return;
46
+ }
47
+ const legacyLayout = LEGACY_DISPLAYS[display];
48
+ if (!legacyLayout) {
49
+ console.warn(`${display} is not a known display mode. The shared display mode will be ignored.`);
50
+ return;
51
+ }
52
+ new LayoutSerializer(this.context).applyLayout(legacyLayout.type, legacyLayout.cells, legacyLayout.sizes);
31
53
  }
32
54
  }
@@ -0,0 +1,17 @@
1
+ import IGirafeContext from '../../context/icontext.js';
2
+ import { IBrainSerializer } from '../../state/brain/serialize.js';
3
+ import LayoutState, { CellContent, LayoutType } from '../../state/layout.js';
4
+ export default class LayoutSerializer implements IBrainSerializer<LayoutState> {
5
+ private readonly context;
6
+ constructor(context: IGirafeContext);
7
+ private get layout();
8
+ brainSerialize(layout: LayoutState): string;
9
+ brainDeserialize(str: string): void;
10
+ /**
11
+ * Applies a layout to the state, ignoring the values that are not valid anymore.
12
+ * A shared state can have been created by another version of the application,
13
+ * in which other components or other layouts were available.
14
+ */
15
+ applyLayout(type: LayoutType, cells?: CellContent[], sizes?: LayoutState['sizes']): void;
16
+ private getValidSizes;
17
+ }
@@ -0,0 +1,44 @@
1
+ import { distributeCells, getDefaultSizes, getGridSize, isCellComponent, isLayoutType } from '../../state/layout.js';
2
+ export default class LayoutSerializer {
3
+ context;
4
+ constructor(context) {
5
+ this.context = context;
6
+ }
7
+ get layout() {
8
+ return this.context.stateManager.state.layout;
9
+ }
10
+ brainSerialize(layout) {
11
+ return JSON.stringify({
12
+ type: layout.type,
13
+ cells: layout.cells,
14
+ sizes: layout.sizes
15
+ });
16
+ }
17
+ brainDeserialize(str) {
18
+ const parsedLayout = JSON.parse(str);
19
+ if (!isLayoutType(parsedLayout.type)) {
20
+ console.warn(`${parsedLayout.type} is not a valid layout. The shared layout will be ignored.`);
21
+ return;
22
+ }
23
+ this.applyLayout(parsedLayout.type, parsedLayout.cells, parsedLayout.sizes);
24
+ }
25
+ /**
26
+ * Applies a layout to the state, ignoring the values that are not valid anymore.
27
+ * A shared state can have been created by another version of the application,
28
+ * in which other components or other layouts were available.
29
+ */
30
+ applyLayout(type, cells, sizes) {
31
+ const layout = this.layout;
32
+ layout.type = type;
33
+ layout.cells = distributeCells(type, (cells ?? []).map((cell) => (isCellComponent(cell) ? cell : null)));
34
+ layout.sizes = this.getValidSizes(type, sizes);
35
+ }
36
+ getValidSizes(type, sizes) {
37
+ const defaultSizes = getDefaultSizes(type);
38
+ const { columns, rows } = getGridSize(type);
39
+ if (sizes?.columns?.length !== columns || sizes?.rows?.length !== rows) {
40
+ return defaultSizes;
41
+ }
42
+ return { columns: sizes.columns, rows: sizes.rows };
43
+ }
44
+ }
@@ -13,6 +13,8 @@ import ObjectSelection from '../state/objectselection.js';
13
13
  import GraphicalInterface from '../state/graphicalInterface.js';
14
14
  import InterfaceSerializer from './serializers/interfaceserializer.js';
15
15
  import GlobeSerializer from './serializers/globeserializer.js';
16
+ import LayoutState from '../state/layout.js';
17
+ import LayoutSerializer from './serializers/layoutserializer.js';
16
18
  class StateSerializer extends GirafeSingleton {
17
19
  brainSerializer = new BrainSerializer();
18
20
  initializeSingleton() {
@@ -22,6 +24,7 @@ class StateSerializer extends GirafeSingleton {
22
24
  this.addSerializer(ObjectSelection, new SelectionSerializer(this.context));
23
25
  this.addSerializer(GraphicalInterface, new InterfaceSerializer(this.context));
24
26
  this.addSerializer(GlobeState, new GlobeSerializer(this.context));
27
+ this.addSerializer(LayoutState, new LayoutSerializer(this.context));
25
28
  }
26
29
  addSerializer(type, serializerData) {
27
30
  this.brainSerializer.addSerializer(type, serializerData);
@@ -4,7 +4,6 @@ export type CameraConfig = {
4
4
  roll: number;
5
5
  };
6
6
  export default class GlobeState {
7
- display: '2D' | '3D' | '2D/3D';
8
7
  loaded: boolean;
9
8
  shadows: boolean;
10
9
  shadowsTimestamp: number;
@@ -1,5 +1,4 @@
1
1
  export default class GlobeState {
2
- display = '2D';
3
2
  loaded = false;
4
3
  shadows = false;
5
4
  shadowsTimestamp = Date.now();
@@ -0,0 +1,62 @@
1
+ /**
2
+ * The available Layouts of the Map Grid.
3
+ * The name of a Layout is <number of columns>x<number of rows>.
4
+ */
5
+ export declare const LAYOUT_TYPES: readonly ["1x1", "2x1", "1x2", "2x2"];
6
+ export type LayoutType = (typeof LAYOUT_TYPES)[number];
7
+ /** Minimal size of a Column or of a Row of the Grid, in percent of the Grid. */
8
+ export declare const MIN_CELL_SIZE = 15;
9
+ /** Maximal size of a Column or of a Row of the Grid, in percent of the Grid. */
10
+ export declare const MAX_CELL_SIZE = 85;
11
+ /**
12
+ * The Components that can be displayed in a Cell of the Grid.
13
+ * To make a new Component available to the user, it is enough to add it to this list
14
+ * (and to add its Element to the Grid in the HTML of the application).
15
+ */
16
+ export declare const AVAILABLE_CELL_COMPONENTS: readonly [{
17
+ readonly id: "map-2d";
18
+ readonly tagName: "girafe-map-2d";
19
+ readonly label: "2D Map";
20
+ }, {
21
+ readonly id: "map-3d";
22
+ readonly tagName: "girafe-map-3d";
23
+ readonly label: "3D Map";
24
+ }];
25
+ export type CellComponent = (typeof AVAILABLE_CELL_COMPONENTS)[number]['id'];
26
+ /** The content of a Cell of the Grid: a Component, or null when the Cell is empty. */
27
+ export type CellContent = CellComponent | null;
28
+ export declare function isLayoutType(value: unknown): value is LayoutType;
29
+ export declare function isCellComponent(value: unknown): value is CellComponent;
30
+ /** The number of columns and of rows of a Layout. */
31
+ export declare function getGridSize(type: LayoutType): {
32
+ columns: number;
33
+ rows: number;
34
+ };
35
+ /** The sizes, in percent, of the equally distributed columns and rows of a Layout. */
36
+ export declare function getDefaultSizes(type: LayoutType): {
37
+ columns: number[];
38
+ rows: number[];
39
+ };
40
+ /**
41
+ * Distributes the Components on the Cells of a Layout.
42
+ * The Components that are already placed keep their Cell as long as it still exists,
43
+ * and the Cells that are added by the new Layout are filled with the Components
44
+ * that are not placed anywhere yet.
45
+ */
46
+ export declare function distributeCells(type: LayoutType, currentCells: CellContent[]): CellContent[];
47
+ /**
48
+ * The Layout of the Map Grid: which Layout is displayed, which Component is displayed
49
+ * in each of its Cells, and the sizes of its columns and rows (in percent),
50
+ * which the user can change by dragging the splitters.
51
+ *
52
+ * The Cells are indexed row by row: in a 2x2 Layout, the Cell 0 is the top-left one,
53
+ * the Cell 1 the top-right one, the Cell 2 the bottom-left one and the Cell 3 the bottom-right one.
54
+ */
55
+ export default class LayoutState {
56
+ type: LayoutType;
57
+ cells: CellContent[];
58
+ sizes: {
59
+ columns: number[];
60
+ rows: number[];
61
+ };
62
+ }
@@ -0,0 +1,67 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * The available Layouts of the Map Grid.
4
+ * The name of a Layout is <number of columns>x<number of rows>.
5
+ */
6
+ export const LAYOUT_TYPES = ['1x1', '2x1', '1x2', '2x2'];
7
+ /** Minimal size of a Column or of a Row of the Grid, in percent of the Grid. */
8
+ export const MIN_CELL_SIZE = 15;
9
+ /** Maximal size of a Column or of a Row of the Grid, in percent of the Grid. */
10
+ export const MAX_CELL_SIZE = 85;
11
+ /**
12
+ * The Components that can be displayed in a Cell of the Grid.
13
+ * To make a new Component available to the user, it is enough to add it to this list
14
+ * (and to add its Element to the Grid in the HTML of the application).
15
+ */
16
+ export const AVAILABLE_CELL_COMPONENTS = [
17
+ { id: 'map-2d', tagName: 'girafe-map-2d', label: '2D Map' },
18
+ { id: 'map-3d', tagName: 'girafe-map-3d', label: '3D Map' }
19
+ ];
20
+ export function isLayoutType(value) {
21
+ return LAYOUT_TYPES.includes(value);
22
+ }
23
+ export function isCellComponent(value) {
24
+ return AVAILABLE_CELL_COMPONENTS.some((component) => component.id === value);
25
+ }
26
+ /** The number of columns and of rows of a Layout. */
27
+ export function getGridSize(type) {
28
+ const [columns, rows] = type.split('x').map(Number);
29
+ return { columns, rows };
30
+ }
31
+ /** The sizes, in percent, of the equally distributed columns and rows of a Layout. */
32
+ export function getDefaultSizes(type) {
33
+ const { columns, rows } = getGridSize(type);
34
+ return {
35
+ columns: new Array(columns).fill(100 / columns),
36
+ rows: new Array(rows).fill(100 / rows)
37
+ };
38
+ }
39
+ /**
40
+ * Distributes the Components on the Cells of a Layout.
41
+ * The Components that are already placed keep their Cell as long as it still exists,
42
+ * and the Cells that are added by the new Layout are filled with the Components
43
+ * that are not placed anywhere yet.
44
+ */
45
+ export function distributeCells(type, currentCells) {
46
+ const { columns, rows } = getGridSize(type);
47
+ const cellCount = columns * rows;
48
+ const cells = currentCells.slice(0, cellCount);
49
+ const unplacedComponents = AVAILABLE_CELL_COMPONENTS.map((component) => component.id).filter((id) => !cells.includes(id));
50
+ while (cells.length < cellCount) {
51
+ cells.push(unplacedComponents.shift() ?? null);
52
+ }
53
+ return cells;
54
+ }
55
+ /**
56
+ * The Layout of the Map Grid: which Layout is displayed, which Component is displayed
57
+ * in each of its Cells, and the sizes of its columns and rows (in percent),
58
+ * which the user can change by dragging the splitters.
59
+ *
60
+ * The Cells are indexed row by row: in a 2x2 Layout, the Cell 0 is the top-left one,
61
+ * the Cell 1 the top-right one, the Cell 2 the bottom-left one and the Cell 3 the bottom-right one.
62
+ */
63
+ export default class LayoutState {
64
+ type = '1x1';
65
+ cells = ['map-2d'];
66
+ sizes = { columns: [100], rows: [100] };
67
+ }
@@ -1,5 +1,6 @@
1
1
  import MapPosition from './mapposition.js';
2
2
  import GlobeState from './globe.js';
3
+ import LayoutState from './layout.js';
3
4
  import type Basemap from '../../models/basemaps/basemap.js';
4
5
  import type ThemeLayer from '../../models/layers/themelayer.js';
5
6
  import type OlGeomLineString from 'ol/geom/LineString.js';
@@ -124,6 +125,7 @@ export default class State {
124
125
  treeview: TreeviewConfig;
125
126
  print: PrintConfig;
126
127
  globe: GlobeState;
128
+ layout: LayoutState;
127
129
  theme: Theme | null;
128
130
  selection: ObjectSelection;
129
131
  infobox: {
@@ -7,6 +7,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
7
7
  // SPDX-License-Identifier: Apache-2.0
8
8
  import MapPosition from './mapposition.js';
9
9
  import GlobeState from './globe.js';
10
+ import LayoutState from './layout.js';
10
11
  import { BrainSerialize } from './brain/decorators.js';
11
12
  import LayersConfig from './layersConfig.js';
12
13
  import ObjectSelection from './objectselection.js';
@@ -90,6 +91,10 @@ export default class State {
90
91
  };
91
92
  // Current 3D-Globe state
92
93
  globe = new GlobeState();
94
+ // Current Layout of the Map Grid
95
+ // Note: it must stay declared after <globe>, so that a legacy globe.display coming from
96
+ // an old shared state is translated into a layout before this one is deserialized.
97
+ layout = new LayoutState();
93
98
  // TODO REG : What is this used for?
94
99
  // Is it the default theme configured in the user preferences?
95
100
  // Do we really need this?
@@ -142,6 +147,9 @@ __decorate([
142
147
  __decorate([
143
148
  BrainSerialize
144
149
  ], State.prototype, "globe", void 0);
150
+ __decorate([
151
+ BrainSerialize
152
+ ], State.prototype, "layout", void 0);
145
153
  __decorate([
146
154
  BrainSerialize
147
155
  ], State.prototype, "selection", void 0);
@@ -10,6 +10,7 @@ declare class StateManager extends GirafeSingleton {
10
10
  constructor(context: IGirafeContext);
11
11
  initializeSingleton(): void;
12
12
  private setDefaultValues;
13
+ private setDefaultLayout;
13
14
  /**
14
15
  * Get the language based on the following priority:
15
16
  * 1. user preferences, 2. browser language, 3. app default language
@@ -4,6 +4,7 @@ import GirafeSingleton from '../../base/GirafeSingleton.js';
4
4
  import State from './state.js';
5
5
  import { getPropertyByPath } from '../utils/pathUtils.js';
6
6
  import Brain from './brain/brain.js';
7
+ import { distributeCells, getDefaultSizes } from './layout.js';
7
8
  class StateManager extends GirafeSingleton {
8
9
  girafeState = new State();
9
10
  stateProxy;
@@ -28,8 +29,15 @@ class StateManager extends GirafeSingleton {
28
29
  this.state.projection = config.map.srid;
29
30
  this.state.language = this.decideLanguage();
30
31
  this.state.interface.selectionComponent = config.interface.defaultSelectionComponent;
32
+ this.setDefaultLayout();
31
33
  }
32
34
  }
35
+ setDefaultLayout() {
36
+ const defaultLayout = this.context.configManager.Config.interface.defaultLayout;
37
+ this.state.layout.type = defaultLayout;
38
+ this.state.layout.cells = distributeCells(defaultLayout, this.state.layout.cells);
39
+ this.state.layout.sizes = getDefaultSizes(defaultLayout);
40
+ }
33
41
  /**
34
42
  * Get the language based on the following priority:
35
43
  * 1. user preferences, 2. browser language, 3. app default language