Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GSLUX-601: Style selector #85

Merged
merged 12 commits into from
May 3, 2024
22,800 changes: 11,417 additions & 11,383 deletions bundle/lux.dist.mjs

Large diffs are not rendered by default.

86 changes: 43 additions & 43 deletions bundle/lux.dist.umd.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion bundle/style.css

Large diffs are not rendered by default.

84 changes: 84 additions & 0 deletions cypress/e2e/style-selection.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
describe('Style selector', () => {
beforeEach(() => {
cy.visit('/')
cy.get('[data-cy="myLayersButton"]').click()
cy.get('.fa-pencil').click()
})

it('renders the style selector properly', () => {
cy.get('[data-cy="styleSelector"').should('not.be.hidden')
cy.get('[data-cy="styleSelector"')
.find('button')
.filter(':visible')
.should('have.length', 3)
})

it('has no style value in localStorage', () => {
expect(localStorage.getItem('basemap_2015_global')).to.eq('')
})

describe('Simple style selector', () => {
beforeEach(() => {
cy.get('[data-cy="styleSelector"')
.find('button')
.filter(':visible')
.first()
.as('simple')
cy.get('@simple').click()
})
it('renders the simple style selector properly', () => {
cy.get('@simple')
.siblings()
.find('button')
.filter(':visible')
.should('have.length', 6)
})
it('sets style to 4th style of list', () => {
const newStyle = `{"medium":[{"label":"Roads primary","color":"#f9c50d","lines":["lu_road_trunk_primary","lu_bridge_major","lu_tunnel_major","lu_road_major_motorway"],"visible":true},{"label":"Roads secondary","color":"#ffffff","lines":["lu_road_minor","lu_road_secondary_tertiary","lu_bridge_minor","lu_road_path","lu_bridge_path","lu_bridge_railway case","lu_bridge_path case"],"visible":true},{"label":"Vegetation","color":"#839836","opacity":"1","fills":["lu_landcover_wood","lu_landcover_grass","lu_landuse_stadium","lu_landuse_cemetery"],"visible":true},{"label":"Buildings","color":"#d6d3ce","opacity":"1","fillExtrusions":["lu_building-3d_public","lu_building-3d"],"fills":["lu_building","lu_building_public"],"lines":["lu_bridge_railway","lu_railway","lu_tunnel_railway"],"visible":true},{"label":"Water","color":"#2a5ba8","lines":["lu_waterway","lu_waterway_tunnel","lu_waterway_intermittent"],"fills":["lu_water"],"visible":true},{"label":"Background","color":"#eeeeee","backgrounds":["background"],"visible":true},{"label":"Hillshade","hillshades":["hillshade"],"visible":false}]}`
cy.get('@simple')
.siblings()
.find('button')
.filter(':visible')
.eq(3)
.click()
cy.window().then(win => {
expect(win.localStorage.getItem('basemap_2015_global')).to.eq(newStyle)
})
})
})

describe('Medium style selector', () => {
beforeEach(() => {
cy.get('[data-cy="styleSelector"')
.find('button')
.filter(':visible')
.eq(1)
.as('medium')
cy.get('@medium').click()
})
it('renders the medium style selector properly', () => {
cy.get('@medium')
.siblings()
.find('input[type="checkbox"]')
.filter(':visible')
.should('have.length', 7)
cy.get('@medium')
.siblings()
.find('input[type="color"]')
.filter(':visible')
.should('have.length', 6)
})
it('sets "Roads primary" to "visible":false', () => {
const newStyle = `{"medium":[{"label":"Roads primary","color":"#f7f7f7","lines":["lu_road_trunk_primary","lu_bridge_major","lu_tunnel_major","lu_road_major_motorway"],"visible":false},{"label":"Roads secondary","color":"#f7f7f7","lines":["lu_road_minor","lu_road_secondary_tertiary","lu_bridge_minor","lu_road_path","lu_bridge_path","lu_bridge_railway case","lu_bridge_path case"],"visible":true},{"label":"Vegetation","color":"#B8D293","opacity":"1","fills":["lu_landcover_wood","lu_landcover_grass","lu_landuse_stadium","lu_landuse_cemetery"],"visible":true},{"label":"Buildings","color":"#D6AA85","opacity":"1","fillExtrusions":["lu_building-3d_public","lu_building-3d"],"fills":["lu_building","lu_building_public"],"lines":["lu_bridge_railway","lu_railway","lu_tunnel_railway"],"visible":true},{"label":"Water","color":"#94c1e1","lines":["lu_waterway","lu_waterway_tunnel","lu_waterway_intermittent"],"fills":["lu_water"],"visible":true},{"label":"Background","color":"#e7e7e7","backgrounds":["background"],"visible":true},{"label":"Hillshade","hillshades":["hillshade"],"visible":true}]}`
cy.get('@medium')
.siblings()
.find('input[type="checkbox"]')
.filter(':visible')
.first()
.click()
cy.window().then(win => {
expect(win.localStorage.getItem('basemap_2015_global')).to.eq(newStyle)
})
})
})
})
2 changes: 2 additions & 0 deletions src/bundle/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { statePersistorMyMapService } from '@/services/state-persistor/state-per
import { proxyUrlHelper } from '@/services/proxyurl/proxyurl.helper'
import { themeSelectorService } from '@/components/theme-selector/theme-selector.service'
import MapLibreLayer from '@/lib/ol-mapbox-layer'
import StyleSelector from '@/components/style-selector/style-selector.vue'

import i18next from 'i18next'
import backend from 'i18next-http-backend'
Expand Down Expand Up @@ -120,4 +121,5 @@ export {
statePersistorMyMapService,
themeSelectorService,
MapLibreLayer,
StyleSelector,
}
30 changes: 30 additions & 0 deletions src/components/common/expandable-panel.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<script lang="ts" setup>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice refactoring, thanks

defineProps<{
expanded: boolean
title: string
}>()
defineEmits<{
(e: 'togglePanel'): void
}>()
</script>

<template>
<button
@click="$emit('togglePanel')"
class="group w-full text-left flex px-2 py-1.5 uppercase bg-tertiary"
:aria-expanded="expanded"
>
<div class="grow" :class="expanded ? 'text-white' : 'text-secondary'">
{{ title }}
</div>
<div class="leading-6">
<div
class="fa fa-sharp fa-solid group-hover:text-white text-primary"
:class="expanded ? 'fa-caret-up' : 'fa-caret-down'"
></div>
</div>
</button>
<div :class="expanded ? '' : 'hidden'">
<slot></slot>
</div>
</template>
30 changes: 9 additions & 21 deletions src/components/layer-tree/layer-tree-node.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import { useTranslation } from 'i18next-vue'
import { computed } from 'vue'
import { useMetadataStore } from '@/stores/metadata.store'
import ExpandablePanel from '@/components/common/expandable-panel.vue'

import type { LayerTreeNodeModel } from './layer-tree.model'

Expand Down Expand Up @@ -32,28 +33,15 @@ function toggleParent(node: LayerTreeNodeModel) {
<template>
<div class="mb-px" v-if="isParent" key="node.id">
<!-- First level parents-->
<button
v-if="node.depth === 1"
class="group node-1 w-full text-left flex px-2 py-1.5 uppercase bg-tertiary"
:aria-expanded="node.expanded"
@click="toggleParent(node)"
:data-cy="`parentLayerLabel-${node.id}`"
>
<div
class="grow"
:class="node.expanded ? 'text-white' : 'text-secondary'"
>
{{ label }}
</div>
<div class="leading-6">
<div
class="fa fa-sharp fa-solid group-hover:text-white text-primary"
:class="node.expanded ? 'fa-caret-up' : 'fa-caret-down'"
></div>
</div>
</button>
<div v-if="node.depth === 1" :data-cy="`parentLayerLabel-${node.id}`">
<expandable-panel
:title="label"
:expanded="node.expanded"
@togglePanel="toggleParent(node)"
/>
</div>

<!-- Other parents-->
<!-- Other parents (with custom panel style)-->
<button
v-else-if="node.depth > 1 && !isMaxDepth"
class="w-full text-left flex px-2 py-1.5 pl-2"
Expand Down
10 changes: 7 additions & 3 deletions src/components/style-selector/expert-style-selector.vue
Original file line number Diff line number Diff line change
Expand Up @@ -67,18 +67,22 @@ function getStyleUrl() {
class="text-white border-2 relative h-[100px] w-[85px] mx-[10px] my-0 mb-[15px] z-5 text-sm after:absolute after:left-[20px] after:top-[10px] after:z-4 after:w-[3.6em] after:text-center after:content-download"
@click="downloadCustomStyleFile()"
>
<span class="absolute top-[70px] w-full text-center text-base">
<span
class="absolute top-[70px] w-full text-center text-base leading-3 font-medium text-white"
>
{{ t('Download style') }}
</span>
</a>
<div
class="text-white border-2 relative h-[100px] w-[85px] mx-[10px] my-0 mb-[15px] text-sm"
>
<label
class="'block z-[5] w-full h-full cursor-pointer after:absolute after:py-[15px] after:px-[15px] after:w-full after:text-center after:content-upload"
class="'block z-[5] w-full h-full cursor-pointer after:absolute after:pt-[15px] after:px-[15px] after:w-full after:text-center after:content-upload"
for="uploadMvtStyle"
>
<span class="absolute top-[70px] w-full text-center text-base">
<span
class="absolute top-[70px] w-full text-center text-base leading-3 font-medium"
>
{{ t('Upload style') }}
</span>
</label>
Expand Down
4 changes: 3 additions & 1 deletion src/components/style-selector/medium-style-item.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@ function updateVisibility(visibilityChangeEvent: Event) {

<template>
<div class="flex w-full items-center">
<label for="colorId" class="w-40">{{ t(style.label) }}</label>
<label for="colorId" class="w-40 m-0 font-medium">{{
t(style.label)
}}</label>
<div class="grow">
<input
v-if="colorEditable && props.style.color"
Expand Down
79 changes: 46 additions & 33 deletions src/components/style-selector/style-selector.vue
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<script setup lang="ts">
import { ref, computed, watch } from 'vue'
import { Ref, ref, computed, watch } from 'vue'
import { storeToRefs } from 'pinia'
import { useTranslation } from 'i18next-vue'

import SimpleStyleSelector from '@/components/style-selector/simple-style-selector.vue'
import MediumStyleSelector from '@/components/style-selector/medium-style-selector.vue'
import ExpertStyleSelector from '@/components/style-selector/expert-style-selector.vue'
import ExpandablePanel from '@/components/common/expandable-panel.vue'
import { useAppStore } from '@/stores/app.store'
import { useMapStore } from '@/stores/map.store'
import { useStyleStore } from '@/stores/style.store'
Expand All @@ -15,6 +16,7 @@ const { t } = useTranslation()
const mapStore = useMapStore()
const appStore = useAppStore()
const styleStore = useStyleStore()
const { bgStyle } = storeToRefs(styleStore)
const { bgLayer } = storeToRefs(mapStore)
const styles = useMvtStyles()

Expand All @@ -28,50 +30,61 @@ watch(bgLayer, bgLayer => {
}
})

let isSimpleStyleOpen = ref(false)
let isMediumStyleOpen = ref(false)
let isAdvancedStyleOpen = ref(false)
let currentOpenPanel: Ref<
undefined | 'simpleStyle' | 'mediumStyle' | 'advancedStyle'
> = ref(undefined)

function resetStyle() {
styleStore.setStyle(null)
}
</script>

<template>
<div v-if="styleCapabilities.isEditable">
<button @click="() => appStore.closeStyleEditorPanel()">X close</button>
<h2 class="h-20 shrink-0 flex justify-between lux-panel-title">
{{ t('Style editor') }}
</h2>
<div v-if="styleCapabilities.hasSimpleStyle">
<button @click="() => (isSimpleStyleOpen = !isSimpleStyleOpen)">
{{ t('Choose a predefined style') }}
</button>
<simple-style-selector :class="isSimpleStyleOpen ? '' : 'hidden'" />
<div v-if="styleCapabilities.isEditable" data-cy="styleSelector">
<div v-if="styleCapabilities.hasSimpleStyle" class="mb-px">
<expandable-panel
:title="t('Simple')"
:expanded="currentOpenPanel === 'simpleStyle'"
@togglePanel="
() =>
(currentOpenPanel =
currentOpenPanel === 'simpleStyle' ? undefined : 'simpleStyle')
"
>
<simple-style-selector
/></expandable-panel>
</div>

<div v-if="styleCapabilities.hasAdvancedStyle">
<button @click="() => (isMediumStyleOpen = !isMediumStyleOpen)">
{{ t('Change main colours') }}
</button>
<medium-style-selector
:class="isMediumStyleOpen ? '' : 'hidden'"
v-if="bgLayer"
:layer="bgLayer"
/>
<div v-if="styleCapabilities.hasAdvancedStyle" class="mb-px">
<expandable-panel
:title="t('Medium')"
:expanded="currentOpenPanel === 'mediumStyle'"
@togglePanel="
() =>
(currentOpenPanel =
currentOpenPanel === 'mediumStyle' ? undefined : 'mediumStyle')
"
>
<medium-style-selector v-if="bgLayer" :layer="bgLayer"
/></expandable-panel>
</div>

<div v-if="styleCapabilities.hasExpertStyle">
<button @click="() => (isAdvancedStyleOpen = !isAdvancedStyleOpen)">
{{ t('Advanced settings') }}
</button>
<expert-style-selector
:class="isAdvancedStyleOpen ? '' : 'hidden'"
v-if="bgLayer"
:layer="bgLayer"
/>
<div v-if="styleCapabilities.hasExpertStyle" class="mb-px">
<expandable-panel
:title="t('Expert (style.json)')"
:expanded="currentOpenPanel === 'advancedStyle'"
@togglePanel="
() =>
(currentOpenPanel =
currentOpenPanel === 'advancedStyle'
? undefined
: 'advancedStyle')
"
>
<expert-style-selector v-if="bgLayer" :layer="bgLayer"
/></expandable-panel>
</div>
<button @click="resetStyle" class="lux-btn">
<button v-if="bgStyle" @click="resetStyle" class="lux-btn my-2">
{{ t('Reset style', { ns: 'client' }) }}
</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/composables/map/ol.composable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ export default function useOpenLayers() {
olMap.addLayer(bgBaseLayer)
}
}
if (oldBgLayerId !== bgLayer?.id) {
if (oldBgLayerId && oldBgLayerId !== bgLayer?.id) {
statePersistorStyleService.restoreStyle(true)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/composables/mvt-styles/mvt-styles.composable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
if (!baseStyles.get(bgLayer.id)) return
const baseStyle: StyleSpecification = JSON.parse(
JSON.stringify(baseStyles.get(bgLayer.id))
) as any

Check warning on line 191 in src/composables/mvt-styles/mvt-styles.composable.ts

View workflow job for this annotation

GitHub Actions / build-lint-test

Unexpected any. Specify a different type
if (!baseStyle) return
if (!baseStyle || !baseStyle.layers) return
if (activeStyle) {
Expand All @@ -197,7 +197,7 @@
for (const styleProperty of stylePropertyTypeList) {
const props = styleItem[`${styleProperty}s`] || []
if (props.includes(layer.id)) {
const basePaint: any = Object.assign(

Check warning on line 200 in src/composables/mvt-styles/mvt-styles.composable.ts

View workflow job for this annotation

GitHub Actions / build-lint-test

Unexpected any. Specify a different type
{},
baseStyle.layers[i].paint
)
Expand Down Expand Up @@ -250,6 +250,7 @@
.then(result => {
return result.id
})
.catch(error => console.warn(error))
})
}

Expand Down
Loading