Skip to content

Commit

Permalink
style(PoiMap): update popup controls
Browse files Browse the repository at this point in the history
  • Loading branch information
KevinFabre-ods committed Jan 10, 2024
1 parent 1f61416 commit 1f7c971
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 75 deletions.
16 changes: 14 additions & 2 deletions packages/visualizations-react/stories/Poi/PoiMap.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,20 @@ const layers = [layer1, layer2];

const citiesColorMatch = {
key: 'key',
colors: { Paris: 'blue', Nantes: 'yellow', Bordeaux: 'purple', Marseille: 'lightblue' },
borderColors: { Paris: 'white', Nantes: 'black', Bordeaux: 'white', Marseille: 'black' },
colors: {
Paris: 'blue',
'Paris--duplicate': 'lightblue',
Nantes: 'yellow',
Bordeaux: 'purple',
Marseille: 'lightblue',
},
borderColors: {
Paris: 'white',
'Paris--duplicate': 'white',
Nantes: 'black',
Bordeaux: 'white',
Marseille: 'black',
},
};

const battleImageMatch = {
Expand Down
18 changes: 15 additions & 3 deletions packages/visualizations-react/stories/Poi/sources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,25 @@ const sources : PoiMapData["sources"] = {
type: 'Point',
coordinates: [2.357573,48.837904],
},
properties: {
key: 'Paris--duplicate',
description: 'Same location as Paris'
},
},
{
id: 2,
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [2.357573,48.837904],
},
properties: {
key: 'Paris',
description: 'Officia deserunt commodo enim ea ad veniam enim consectetur aliquip adipisicing duis. Exercitation aute velit pariatur est et ea qui veniam ad duis quis ad aliquip. Ipsum exercitation dolor tempor deserunt sunt amet laborum tempor excepteur est sunt ea quis.'
},
},
{
id: 2,
id: 3,
type: 'Feature',
geometry: {
type: 'Point',
Expand All @@ -32,7 +44,7 @@ const sources : PoiMapData["sources"] = {
},
},
{
id: 3,
id: 4,
type: 'Feature',
geometry: {
type: 'Point',
Expand All @@ -44,7 +56,7 @@ const sources : PoiMapData["sources"] = {
},
},
{
id: 4,
id: 5,
type: 'Feature',
geometry: {
type: 'Point',
Expand Down
90 changes: 52 additions & 38 deletions packages/visualizations/src/components/MapPoi/Map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,14 @@ import maplibregl, {
} from 'maplibre-gl';

import {
POPUP_CLASSNAME,
POPUP_CONTENT,
POPUP_LOADING_CONTENT,
POPUP_DISPLAY_CLASSNAME_MODIFIER,
POPUP_NAVIGATION_CONTROLS_CLASSNAME,
POPUP_NAVIGATION_ARROW_CLASSNAME,
POPUP_NAVIGATION_ARROW_BUTTON_CLASSNAME,
POPUP_NAVIGATION_ARROW_BUTTON_ICON_CLASSNAME,
POPUP_NAVIGATION_CLOSE_BUTTON_CLASSNAME,
POPUP_NAVIGATION_CLOSE_BUTTON_ICON_CLASSNAME,
POPUP_OPTIONS,
POPUP_WIDTH,
} from './constants';
Expand All @@ -38,16 +42,6 @@ type MapFunction = (map: maplibregl.Map) => unknown;

type ActiveFeatureType = MapGeoJSONFeature | null;

function updateFeatureState(
map: maplibregl.Map,
feature: ActiveFeatureType,
stateKey: string,
stateValue: unknown
) {
if (!feature) return;
const { id, source, sourceLayer } = feature;
map.setFeatureState({ id, source, sourceLayer }, { [stateKey]: stateValue });
}
export default class MapPOI {
/** The Map object representing the maplibregl.Map instance. */
private map: maplibregl.Map | null = null;
Expand Down Expand Up @@ -94,6 +88,14 @@ export default class MapPOI {
this.queuedFunctions = [];
}

private updateFeatureState(feature: ActiveFeatureType, stateKey: string, stateValue: unknown) {
if (!feature) return;
const { id, source, sourceLayer } = feature;
this.queue((map) => {
map.setFeatureState({ id, source, sourceLayer }, { [stateKey]: stateValue });
});
}

/** Initialize a resize observer to always fit the map to its container */
private initializeMapResizer(map: maplibregl.Map, container: HTMLElement) {
// Set a resizeObserver to resize map on container size changes
Expand Down Expand Up @@ -170,33 +172,50 @@ export default class MapPOI {
}

private navigateToFeature(direction: number) {
this.updateFeatureState(this.activeFeature, POPUP_FEATURE_STATE_KEY, false);
const activeFeatureIndex = this.availableFeaturesOnClick.indexOf(this.activeFeature);
this.activeFeature = this.availableFeaturesOnClick[activeFeatureIndex + direction];
this.updatePopupContent();
this.updatePopupDisplay();
if (this.activeFeature?.geometry.type === 'Point') {
this.popup.setLngLat(this.activeFeature?.geometry.coordinates as LngLatLike);
}
this.updateFeatureState(this.activeFeature, POPUP_FEATURE_STATE_KEY, true);
}

private renderFeaturesNavigationControls() {
const popupNavigationDiv = document.createElement('div');
const availableFeaturesTotal = this.availableFeaturesOnClick.length;
const activeFeatureHumanIndex =
this.availableFeaturesOnClick.indexOf(this.activeFeature) + 1;
let arrows = '';
if (availableFeaturesTotal > 1) {
const activeFeatureHumanIndex =
this.availableFeaturesOnClick.indexOf(this.activeFeature) + 1;
arrows = `<button class="${POPUP_NAVIGATION_ARROW_BUTTON_CLASSNAME}" id="prevButton" ${
activeFeatureHumanIndex === 1 ? 'disabled' : ''
}><span class="${POPUP_NAVIGATION_ARROW_BUTTON_ICON_CLASSNAME}"></span></button>
<div class="feature-count">${activeFeatureHumanIndex} / ${availableFeaturesTotal}</div>
<button class="${POPUP_NAVIGATION_ARROW_BUTTON_CLASSNAME}" id="nextButton" ${
activeFeatureHumanIndex === availableFeaturesTotal ? 'disabled' : ''
}><span class="${POPUP_NAVIGATION_ARROW_BUTTON_ICON_CLASSNAME}"></span></button>`;
}

popupNavigationDiv.innerHTML = `
<div class="${POPUP_NAVIGATION_CONTROLS_CLASSNAME}">
<button class="${POPUP_NAVIGATION_ARROW_CLASSNAME}" id="prevButton" ${
activeFeatureHumanIndex === 1 ? 'disabled' : ''
}><</button>
<div class="feature-count">${activeFeatureHumanIndex} / ${availableFeaturesTotal}</div>
<button class="${POPUP_NAVIGATION_ARROW_CLASSNAME}" id="nextButton" ${
activeFeatureHumanIndex === availableFeaturesTotal ? 'disabled' : ''
}>></button>
</div>
`;
<div class="${POPUP_NAVIGATION_CONTROLS_CLASSNAME}">
${arrows}
<button class="${POPUP_NAVIGATION_CLOSE_BUTTON_CLASSNAME}"><span class="${POPUP_NAVIGATION_CLOSE_BUTTON_ICON_CLASSNAME}"></span></button>
</div>
`;

const prevButton = popupNavigationDiv.querySelector('#prevButton');
prevButton?.addEventListener('click', () => this.navigateToFeature(-1));

const nextButton = popupNavigationDiv.querySelector('#nextButton');
nextButton?.addEventListener('click', () => this.navigateToFeature(1));

const closeButton = popupNavigationDiv.querySelector(
`.${POPUP_NAVIGATION_CLOSE_BUTTON_CLASSNAME}`
);
closeButton?.addEventListener('click', () => this.popup.remove());
return popupNavigationDiv;
}

Expand All @@ -212,20 +231,17 @@ export default class MapPOI {
if (!popupLayerConfiguration) return;
const { getLoadingContent, getContent } = popupLayerConfiguration;

this.popup.addClassName(`${POPUP_CLASSNAME}--loading`);
this.popup.setHTML(getLoadingContent());
this.popup.setHTML(`<div class="${POPUP_LOADING_CONTENT}">${getLoadingContent()}</div>`);

getContent(id, properties).then((content) => {
const popupContainerDiv = document.createElement('div');
if (this.availableFeaturesOnClick.length > 1) {
popupContainerDiv.appendChild(this.renderFeaturesNavigationControls());
}
const controlsDiv = this.renderFeaturesNavigationControls();

const popupContentDiv = document.createElement('div');
popupContentDiv.innerHTML = `<div class="popup-content">${content}</div>`;
popupContainerDiv.appendChild(popupContentDiv);
popupContentDiv.innerHTML = `<div class="${POPUP_CONTENT}">${content}</div>`;
popupContainerDiv.append(controlsDiv, popupContentDiv);

this.popup.setDOMContent(popupContainerDiv);
this.popup.removeClassName(`${POPUP_CLASSNAME}--loading`);
});
}

Expand Down Expand Up @@ -291,7 +307,7 @@ export default class MapPOI {
});

// Removing feature state for the obsolete active feature.
updateFeatureState(map, this.activeFeature, POPUP_FEATURE_STATE_KEY, false);
this.updateFeatureState(this.activeFeature, POPUP_FEATURE_STATE_KEY, false);
const hasFeatures = !!features.length;
// Current rule: use the first feature to build the popup.
// TO DO: Create a menu to display a list of feature to choose from.
Expand Down Expand Up @@ -324,7 +340,7 @@ export default class MapPOI {

this.updatePopupContent();
this.updatePopupDisplay();
updateFeatureState(map, this.activeFeature, POPUP_FEATURE_STATE_KEY, true);
this.updateFeatureState(this.activeFeature, POPUP_FEATURE_STATE_KEY, true);
}

initialize(
Expand Down Expand Up @@ -430,7 +446,7 @@ export default class MapPOI {
* to reflect the new configuration.
* @param config Popups configuration
*/
setpopupConfigurationByLayers(config: PopupConfigurationByLayers) {
setPopupConfigurationByLayers(config: PopupConfigurationByLayers) {
this.popupConfigurationByLayers = config;
this.updatePopupContent();
this.updatePopupDisplay();
Expand Down Expand Up @@ -504,9 +520,7 @@ export default class MapPOI {

constructor() {
this.popup.on('close', () => {
this.queue((map) => {
updateFeatureState(map, this.activeFeature, POPUP_FEATURE_STATE_KEY, false);
});
this.updateFeatureState(this.activeFeature, POPUP_FEATURE_STATE_KEY, false);
this.onPopupDisplayUpdate(this.activePopupDisplay, null);
this.activePopupDisplay = null;
this.activeFeature = null;
Expand Down
107 changes: 81 additions & 26 deletions packages/visualizations/src/components/MapPoi/MapRender.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
$: map.setMinZoom(minZoom);
$: map.setMaxZoom(maxZoom);
$: map.setSourcesAndLayers(sources, layers);
$: map.setpopupConfigurationByLayers(popupConfigurationByLayers);
$: map.setPopupConfigurationByLayers(popupConfigurationByLayers);
$: map.jumpTo(getCenterZoomOptions({ zoom, center }));
$: map.loadImages(images);
$: cssVarStyles = `--aspect-ratio:${aspectRatio};`;
Expand Down Expand Up @@ -177,60 +177,115 @@
display: flex;
flex-direction: column;
flex-wrap: nowrap;
padding: 13px;
padding: 0px;
border-radius: 6px;
max-height: 100%;
overflow-y: auto;
overflow-x: hidden;
box-sizing: border-box;
box-shadow: 0 6px 13px 0 rgba(0, 0, 0, 0.26);
}
.map-card :global(.poi-map__popup .poi-map__popup-content),
.map-card :global(.poi-map__popup .poi-map__popup-content--loading) {
margin: 13px;
}
/* Add a more opacity and blur effect on map when cooperative gesture is shown */
.map-card :global(.maplibregl-cooperative-gesture-screen) {
background: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(2px);
}
/* --- POPUP CLOSE BUTTON --- */
.map-card :global(.maplibregl-popup-close-button) {
font-size: 16px;
padding: 0;
width: 24px;
height: 24px;
margin-bottom: 13px;
position: relative;
order: -1;
flex-shrink: 0;
left: calc(100% - 26px);
}
.map-card :global(.maplibregl-popup-close-button:hover) {
background-color: transparent;
}
/* Hide close button when content is loading or when its display is as a tooltip */
.map-card :global(.poi-map__popup--loading .maplibregl-popup-close-button),
.map-card :global(.poi-map__popup--as-tooltip .maplibregl-popup-close-button) {
/* Hide close button when its display is as a tooltip */
.map-card :global(.poi-map__popup--as-tooltip .poi-map__popup-navigation-close-button) {
display: none;
}
/* --- POPUP NAVIGATION CONTROLS --- */
.map-card :global(.poi-map__popup-navigation-controls) {
position: relative;
display: flex;
gap: 6px;
justify-content: center;
align-items: center;
margin-bottom: 12px;
font-weight: 400;
margin: 6px 6px 0px 6px;
}
.map-card :global(.poi-map__popup-navigation-arrow) {
margin: 6px;
cursor: pointer;
border: none;
.map-card :global(.poi-map__popup-navigation-arrow-button) {
display: flex;
align-items: center;
justify-content: center;
padding: 0px;
width: 36px;
height: 36px;
background: none;
border: none;
cursor: pointer;
}
.map-card :global(.poi-map__popup-navigation-arrow:disabled) {
.map-card :global(.poi-map__popup-navigation-arrow-button:disabled) {
cursor: not-allowed;
}
.map-card :global(.poi-map__popup-navigation-arrow-button-icon) {
width: 6px;
height: 6px;
border-top: 2px solid;
border-left: 2px solid;
}
.map-card
:global(#prevButton.poi-map__popup-navigation-arrow-button
.poi-map__popup-navigation-arrow-button-icon) {
transform: rotate(-45deg);
}
.map-card
:global(#nextButton.poi-map__popup-navigation-arrow-button
.poi-map__popup-navigation-arrow-button-icon) {
transform: rotate(135deg);
}
.map-card
:global(.poi-map__popup-navigation-arrow-button:disabled
.poi-map__popup-navigation-arrow-button-icon) {
opacity: 0.5;
}
.map-card :global(.poi-map__popup-navigation-close-button) {
display: flex;
align-items: center;
justify-content: center;
width: 36px;
height: 36px;
padding: 0px;
border: none;
background: none;
cursor: pointer;
position: absolute;
top: 0;
bottom: 0;
right: 0;
}
.map-card :global(.poi-map__popup-navigation-close-button-icon) {
position: relative;
display: block;
width: 14px;
height: 14px;
}
.map-card :global(.poi-map__popup-navigation-close-button-icon:before),
.map-card :global(.poi-map__popup-navigation-close-button-icon:after) {
position: absolute;
left: 6px;
content: '';
height: 100%;
width: 2px;
border-radius: 2px;
background-color: #333;
}
.map-card :global(.poi-map__popup-navigation-close-button-icon:before) {
transform: rotate(45deg);
}
.map-card :global(.poi-map__popup-navigation-close-button-icon:after) {
transform: rotate(-45deg);
}
/* --- CONTROLS --- */
.map-card :global(.maplibregl-ctrl.maplibregl-ctrl-group) {
margin-top: 13px;
Expand Down
Loading

0 comments on commit 1f7c971

Please sign in to comment.