Skip to content

Commit 3f18324

Browse files
authored
Merge pull request #1420 from swisstopo/feature/viewer-1369-einstellungen-tab
Feature/viewer 1369 einstellungen tab
2 parents 683ae80 + 06c7973 commit 3f18324

File tree

9 files changed

+302
-104
lines changed

9 files changed

+302
-104
lines changed

ui/src/components/core/core-chip.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import {css, html, LitElement} from 'lit';
2+
import {customElement, property} from 'lit/decorators.js';
3+
import './core-icon';
4+
import {applyTypography} from '../../styles/theme';
5+
import {Variant} from './core-button';
6+
7+
@customElement('ngm-core-chip')
8+
export class CoreChip extends LitElement {
9+
@property({reflect: true})
10+
accessor variant: Variant = 'primary'
11+
12+
readonly render = () => html`
13+
<span>
14+
<slot></slot>
15+
</span>
16+
17+
`;
18+
19+
static readonly styles = css`
20+
21+
:host {
22+
border-radius: 22px;
23+
height: 27px;
24+
padding: 0 10px;
25+
align-content: center;
26+
}
27+
28+
:host([variant="primary"]) {
29+
background-color: var(--color-border--default);
30+
color: var(--color-text--emphasis--high);
31+
${applyTypography('overline')};
32+
33+
}
34+
35+
:host([variant="secondary"]) {
36+
background-color: var(--color-bg--white);
37+
color: var(--color-primary);
38+
border: 1px solid var(--color-primary);
39+
${applyTypography('button')};
40+
41+
}
42+
43+
44+
45+
`;
46+
}

ui/src/components/core/core-slider.ts

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import {css, html, LitElement} from 'lit';
2+
import {customElement, property} from 'lit/decorators.js';
3+
import './core-icon';
4+
5+
@customElement('ngm-core-slider')
6+
export class CoreSlider extends LitElement {
7+
@property({type: Boolean})
8+
accessor isActive: boolean = false;
9+
10+
@property({type: Number})
11+
accessor min: number = 0;
12+
13+
@property({type: Number})
14+
accessor max: number = 10;
15+
16+
@property({type: Number})
17+
accessor step: number = 1;
18+
19+
@property({type: Number})
20+
accessor value: number = 0;
21+
22+
handleInputChange(event: InputEvent) {
23+
this.value = parseInt((event.target as HTMLInputElement).value);
24+
this.dispatchEvent(new CustomEvent<SliderValueChangeEventDetail>('change', {
25+
detail: {
26+
value: this.value
27+
}
28+
}));
29+
}
30+
31+
handlePointerUp() {
32+
this.dispatchEvent(new CustomEvent('pointerup'));
33+
}
34+
35+
readonly render = () => html`
36+
<input
37+
type="range"
38+
class="ngm-slider"
39+
style="--value: ${this.value};"
40+
.min=${this.min} .max=${this.max} .step=${this.step}
41+
.value=${isNaN(this.value) ? 1 : this.value}
42+
@input=${this.handleInputChange}
43+
@pointerup=${this.handlePointerUp}
44+
>
45+
`;
46+
47+
static readonly styles = css`
48+
:host {
49+
--slider-thumb-size: 24px;
50+
--slider-track-height: 4px;
51+
52+
display: flex;
53+
width: 100%;
54+
}
55+
56+
input {
57+
height: 4px;
58+
border-radius: 4px;
59+
}
60+
61+
input[type="range"] {
62+
appearance: none;
63+
background-image: linear-gradient(to right, var(--color-primary--active), var(--color-primary--active) calc(var(--value) * 5%), var(--color-border--default) calc(var(--value) * 5%));
64+
cursor: pointer;
65+
width: 100%;
66+
margin: 0;
67+
}
68+
69+
input[type="range"]::-webkit-slider-runnable-track {
70+
border-radius: 4px;
71+
height: var(--slider-track-height);
72+
}
73+
74+
input[type="range"]::-moz-range-track {
75+
border-radius: 4px;
76+
height: var(--slider-track-height);
77+
}
78+
79+
input[type="range"]::-webkit-slider-thumb {
80+
appearance: none;
81+
width: var(--slider-thumb-size);
82+
height: var(--slider-thumb-size);
83+
background: var(--color-bg--white) 0 0 no-repeat padding-box;
84+
box-shadow: 0 2px 2px #00000029;
85+
border: 3px solid var(--color-primary);
86+
border-radius: 50%;
87+
cursor: pointer;
88+
margin-top: calc((var(--slider-track-height) / 2) - (var(--slider-thumb-size) / 2));
89+
}
90+
91+
input[type="range"]::-moz-range-thumb {
92+
width: var(--slider-thumb-size);
93+
height: var(--slider-thumb-size);
94+
background: var(--color-bg--white) 0 0 no-repeat padding-box;
95+
box-shadow: 0 2px 2px #00000029;
96+
border: 3px solid var(--color-primary);
97+
border-radius: 50%;
98+
cursor: pointer;
99+
}
100+
`;
101+
}
102+
103+
export type SliderValueChangeEvent = CustomEvent<SliderValueChangeEventDetail>
104+
105+
export interface SliderValueChangeEventDetail {
106+
value: number;
107+
}

ui/src/components/core/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ import './core-checkbox';
33
import './core-heading';
44
import './core-icon';
55
import './core-modal';
6+
import './core-slider';
7+
import './core-chip';

ui/src/components/layer/options/layer-options.ts

Lines changed: 72 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,14 @@ import {customElement, state} from 'lit/decorators.js';
22
import {LitElementI18n} from '../../../i18n';
33
import {CustomDataSource, DataSource, DataSourceCollection, Viewer} from 'cesium';
44
import MainStore from '../../../store/main';
5-
import {css, html, unsafeCSS} from 'lit';
5+
import {css, html} from 'lit';
66
import i18next from 'i18next';
77
import {debounce} from '../../../utils';
88
import {setExaggeration} from '../../../permalink';
99
import NavToolsStore from '../../../store/navTools';
1010
import {updateExaggerationForKmlDataSource} from '../../../cesiumutils';
11-
import {classMap} from 'lit/directives/class-map.js';
12-
13-
14-
import iconsCss from '../../../style/icons.css';
15-
import layersCss from '../../../style/layers.css';
16-
import sliderCss from '../../../style/ngm-slider.css';
17-
import fomanticTransitionCss from 'fomantic-ui-css/components/transition.css';
11+
import '../../core';
12+
import {SliderValueChangeEvent} from '../../core/core-slider';
1813

1914
@customElement('ngm-layer-options')
2015
export class NgmLayerOptions extends LitElementI18n {
@@ -44,6 +39,18 @@ export class NgmLayerOptions extends LitElementI18n {
4439
});
4540
}
4641

42+
private toggleExaggerationVisibility() {
43+
if (!this.viewer) {
44+
return;
45+
}
46+
this.hideExaggeration = !this.hideExaggeration;
47+
const exaggeration = this.hideExaggeration ? 1 : this.exaggeration;
48+
this.viewer.scene.verticalExaggeration = exaggeration;
49+
this.updateExaggerationForKmls();
50+
NavToolsStore.exaggerationChanged.next(exaggeration);
51+
this.viewer.scene.requestRender();
52+
}
53+
4754
private updateExaggerationForKmls() {
4855
const exaggeration = this.hideExaggeration ? 1 : this.exaggeration;
4956
MainStore.uploadedKmlNames.forEach(name => {
@@ -54,12 +61,12 @@ export class NgmLayerOptions extends LitElementI18n {
5461
this.viewer?.scene.requestRender();
5562
}
5663

57-
private updateExaggeration(evt: InputEvent) {
64+
private updateExaggeration(event: SliderValueChangeEvent) {
5865
if (this.viewer == null) {
5966
return;
6067
}
6168
this.hideExaggeration = false;
62-
this.exaggeration = Number((<HTMLInputElement>evt.target).value);
69+
this.exaggeration = event.detail.value;
6370
this.viewer.scene.verticalExaggeration = this.exaggeration;
6471
// workaround for billboards positioning
6572
setTimeout(() => this.viewer!.scene.requestRender(), 500);
@@ -68,45 +75,66 @@ export class NgmLayerOptions extends LitElementI18n {
6875
}
6976

7077
readonly render = () => html`
71-
<div class="ngm-base-layer">
72-
<div
73-
title=${!this.hideExaggeration ? i18next.t('dtd_hide_exaggeration') : i18next.t('dtd_show_exaggeration')}
74-
class="ngm-layer-icon ${classMap({
75-
'ngm-visible-icon': !this.hideExaggeration,
76-
'ngm-invisible-icon': this.hideExaggeration,
77-
})}"
78-
@click=${() => {
79-
if (!this.viewer) return;
80-
this.hideExaggeration = !this.hideExaggeration;
81-
const exaggeration = this.hideExaggeration ? 1 : this.exaggeration;
82-
this.viewer.scene.verticalExaggeration = exaggeration;
83-
this.updateExaggerationForKmls();
84-
NavToolsStore.exaggerationChanged.next(exaggeration);
85-
this.viewer.scene.requestRender();
86-
}}>HIDE
78+
<div class="group">
79+
<ngm-core-icon
80+
icon="${this.hideExaggeration ? 'invisible' : 'visible'}"
81+
title=${this.hideExaggeration ? i18next.t('dtd_show_exaggeration') : i18next.t('dtd_hide_exaggeration')}
82+
@click=${this.toggleExaggerationVisibility}
83+
></ngm-core-icon>
84+
<label>${i18next.t('dtd_exaggeration_map')}</label>
8785
</div>
88-
<div class="ngm-displayed-slider ngm-layer-options">
89-
<div>
90-
<label>${i18next.t('dtd_exaggeration_map')}</label>
91-
<label>${(this.exaggeration).toFixed()}x</label>
86+
<hr>
87+
<div class="group">
88+
<ngm-core-slider
89+
.min="${1}"
90+
.max="${20}"
91+
.step="${1}"
92+
.value="${this.exaggeration}"
93+
@change=${this.updateExaggeration}
94+
@pointerup="${debounce(() => this.updateExaggerationForKmls(), 300)}"
95+
></ngm-core-slider>
96+
<div class="chip-container">
97+
<ngm-core-chip>${this.exaggeration.toFixed()}x</ngm-core-chip>
9298
</div>
93-
<input
94-
type="range"
95-
class="ngm-slider"
96-
style="background-image: linear-gradient(to right, var(--ngm-interaction-active), var(--ngm-interaction-active) ${this.exaggeration * 5}%, white ${this.exaggeration * 5}%)"
97-
min=1 max=20 step=1
98-
.value=${!isNaN(this.exaggeration) ? this.exaggeration : 1}
99-
@input=${(evt: InputEvent) => this.updateExaggeration(evt)}
100-
@pointerup=${debounce(() => this.updateExaggerationForKmls(), 300)}
101-
>
10299
</div>
103-
</div>
104100
`;
105101

106102
static readonly styles = css`
107-
${unsafeCSS(fomanticTransitionCss)}
108-
${unsafeCSS(iconsCss)}
109-
${unsafeCSS(layersCss.replaceAll('ngm-layers-item', ':host'))}
110-
${unsafeCSS(sliderCss)}
103+
:host {
104+
display: flex;
105+
flex-direction: column;
106+
justify-content: center;
107+
background-color: var(--color-bg--white);
108+
box-sizing: border-box;
109+
border: 1px solid var(--color-border--default);
110+
border-radius: 4px;
111+
}
112+
113+
ngm-core-icon {
114+
padding: 6px;
115+
color: var(--color-primary);
116+
}
117+
118+
hr {
119+
margin: 0 10px;
120+
height: 1px;
121+
border-width: 0;
122+
color: var(--color-border--default);
123+
background-color: var(--color-border--default);
124+
}
125+
126+
.group {
127+
display: flex;
128+
justify-content: flex-start;
129+
gap: 6px;
130+
align-items: center;
131+
margin: 10px;
132+
}
133+
134+
.chip-container {
135+
min-width: 48px;
136+
display: flex;
137+
justify-content: flex-end;
138+
}
111139
`;
112140
}

ui/src/icons/i_invisible.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import {html} from 'lit';
2+
3+
export const invisibleIcon = html`<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
4+
<path d="M8.94418 4.23033C10.8853 3.999 12.8489 4.40934 14.5349 5.3987C16.221 6.38806 17.5369 7.90206 18.2817 9.70949C18.3511 9.89659 18.3511 10.1024 18.2817 10.2895C17.9754 11.032 17.5707 11.7299 17.0783 12.3645M11.7367 11.7987C11.2652 12.2541 10.6337 12.506 9.97818 12.5003C9.32269 12.4946 8.69565 12.2317 8.23213 11.7682C7.76861 11.3047 7.50569 10.6777 7.5 10.0222C7.4943 9.36667 7.74629 8.73516 8.20168 8.26366M14.5658 14.5828C13.4604 15.2376 12.2271 15.647 10.9495 15.7832C9.6719 15.9193 8.37997 15.779 7.16137 15.3719C5.94277 14.9647 4.82601 14.3002 3.88686 13.4234C2.94771 12.5467 2.20814 11.4781 1.71835 10.2903C1.6489 10.1032 1.6489 9.89742 1.71835 9.71033C2.45721 7.91854 3.75724 6.4147 5.42335 5.42449M1.66668 1.66699L18.3333 18.3337" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
5+
</svg>`;

ui/src/icons/i_visible.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import {html} from 'lit';
2+
3+
export const visibleIcon = html`<svg width="20" height="14" viewBox="0 0 20 14" fill="none" xmlns="http://www.w3.org/2000/svg">
4+
<path d="M1.71835 7.29079C1.6489 7.10369 1.6489 6.89788 1.71835 6.71079C2.39476 5.07067 3.54294 3.66832 5.01732 2.68154C6.4917 1.69475 8.22588 1.16797 10 1.16797C11.7741 1.16797 13.5083 1.69475 14.9827 2.68154C16.4571 3.66832 17.6053 5.07067 18.2817 6.71079C18.3511 6.89788 18.3511 7.10369 18.2817 7.29079C17.6053 8.93091 16.4571 10.3333 14.9827 11.32C13.5083 12.3068 11.7741 12.8336 10 12.8336C8.22588 12.8336 6.4917 12.3068 5.01732 11.32C3.54294 10.3333 2.39476 8.93091 1.71835 7.29079Z" stroke="#337083" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
5+
<path d="M10 9.50079C11.3807 9.50079 12.5 8.3815 12.5 7.00079C12.5 5.62008 11.3807 4.50079 10 4.50079C8.6193 4.50079 7.50001 5.62008 7.50001 7.00079C7.50001 8.3815 8.6193 9.50079 10 9.50079Z" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/>
6+
</svg>`;

ui/src/icons/icons.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {closeIcon} from './i_close';
1212
import {dropdownIcon} from './i_dropdown';
1313
import {uploadIcon} from './i_upload';
1414
import {cesiumIcon} from './i_cesium';
15+
import {visibleIcon} from './i_visible';
16+
import {invisibleIcon} from './i_invisible';
1517

1618
export const icons = {
1719
cesium: cesiumIcon,
@@ -28,6 +30,8 @@ export const icons = {
2830
user: userIcon,
2931
viewAll: viewAllIcon,
3032
viewLess: viewLessIcon,
33+
visible: visibleIcon,
34+
invisible: invisibleIcon
3135
};
3236

3337
export type IconKey = keyof typeof icons;

ui/src/style/layers.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ ngm-layers-item .ngm-checkbox {
4646

4747
ngm-layers-item, .ngm-base-layer {
4848
display: flex;
49-
align-items: end;
50-
flex-direction: row;
49+
flex-direction: column;
50+
width: 100%;
5151
}
5252

5353
.ngm-base-layer > div:first-child {

0 commit comments

Comments
 (0)