demarches-normaliennes/app/javascript/components/shared/mapbox/MapStyleControl.jsx

188 lines
5.6 KiB
React
Raw Normal View History

import React, { useMemo, useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { Popover, RadioGroup } from '@headlessui/react';
import { usePopper } from 'react-popper';
import { MapIcon } from '@heroicons/react/outline';
2021-07-01 18:50:35 +02:00
import { Slider } from '@reach/slider';
2021-07-07 14:12:50 +02:00
import { useId } from '@reach/auto-id';
2021-07-01 18:50:35 +02:00
import '@reach/slider/styles.css';
2021-07-01 18:50:35 +02:00
import { getMapStyle, getLayerName, NBS } from './styles';
const STYLES = {
ortho: 'Satellite',
vector: 'Vectoriel',
ign: 'Carte IGN'
};
function optionalLayersMap(optionalLayers) {
return Object.fromEntries(
2021-07-07 14:02:55 +02:00
optionalLayers.map((layer) => [
layer,
{
configurable: layer != 'cadastres',
enabled: true,
opacity: 70,
name: getLayerName(layer)
}
])
);
}
export function useMapStyle(
optionalLayers,
{ onStyleChange, cadastreEnabled }
) {
const [styleId, setStyle] = useState('ortho');
const [layers, setLayers] = useState(() => optionalLayersMap(optionalLayers));
const setLayerEnabled = (layer, enabled) =>
setLayers((optionalLayers) => {
optionalLayers[layer].enabled = enabled;
return { ...optionalLayers };
});
const setLayerOpacity = (layer, opacity) =>
setLayers((optionalLayers) => {
optionalLayers[layer].opacity = opacity;
return { ...optionalLayers };
});
const enabledLayers = Object.entries(layers).filter(
([, { enabled }]) => enabled
);
const layerIds = enabledLayers.map(
([layer, { opacity }]) => `${layer}-${opacity}`
);
const style = useMemo(
() =>
getMapStyle(
styleId,
2021-07-01 18:50:35 +02:00
enabledLayers.map(([layer]) => layer),
Object.fromEntries(
enabledLayers.map(([layer, { opacity }]) => [layer, opacity])
)
),
[styleId, layerIds]
);
useEffect(() => onStyleChange(), [styleId, layerIds, cadastreEnabled]);
return { style, layers, setStyle, setLayerEnabled, setLayerOpacity };
}
2021-07-01 18:50:35 +02:00
function MapStyleControl({
style,
layers,
setStyle,
setLayerEnabled,
setLayerOpacity
}) {
const [buttonElement, setButtonElement] = useState();
const [panelElement, setPanelElement] = useState();
const { styles, attributes } = usePopper(buttonElement, panelElement, {
placement: 'bottom-end'
});
2021-07-07 14:02:55 +02:00
const configurableLayers = Object.entries(layers).filter(
([, { configurable }]) => configurable
);
2021-07-07 14:12:50 +02:00
const mapId = useId();
return (
<div className="form map-style-control mapboxgl-ctrl-group">
<Popover>
<Popover.Button
ref={setButtonElement}
className="map-style-button"
title="Sélectionner les couches cartographiques"
>
<MapIcon className="icon-size" />
</Popover.Button>
<Popover.Panel
className="flex map-style-panel mapboxgl-ctrl-group"
ref={setPanelElement}
style={styles.popper}
{...attributes.popper}
>
<RadioGroup
value={style}
onChange={setStyle}
className="styles-list"
as="ul"
>
{Object.entries(STYLES).map(([style, title]) => (
<RadioGroup.Option
key={style}
value={style}
as="li"
className="flex"
>
{({ checked }) => (
<>
<input
type="radio"
key={`${style}-${checked}`}
defaultChecked={checked}
name="map-style"
className="m-0 p-0 mr-1"
/>
<RadioGroup.Label>
{title.replace(/\s/g, ' ')}
</RadioGroup.Label>
</>
)}
</RadioGroup.Option>
))}
</RadioGroup>
2021-07-07 14:02:55 +02:00
{configurableLayers.length ? (
<ul className="layers-list">
2021-07-07 14:02:55 +02:00
{configurableLayers.map(([layer, { enabled, opacity, name }]) => (
<li key={layer}>
<div className="flex mb-1">
<input
2021-07-07 14:12:50 +02:00
id={`${mapId}-${layer}`}
2021-07-07 14:02:55 +02:00
className="m-0 p-0 mr-1"
type="checkbox"
checked={enabled}
onChange={(event) => {
setLayerEnabled(layer, event.target.checked);
2021-07-01 18:50:35 +02:00
}}
/>
2021-07-07 14:12:50 +02:00
<label className="m-0" htmlFor={`${mapId}-${layer}`}>
{name}
</label>
2021-07-07 14:02:55 +02:00
</div>
<Slider
min={10}
max={100}
step={5}
value={opacity}
onChange={(value) => {
setLayerOpacity(layer, value);
}}
className="mb-1"
title={`Réglage de lopacité de la couche «${NBS}${name}${NBS}»`}
getAriaLabel={() =>
`Réglage de lopacité de la couche «${NBS}${name}${NBS}»`
}
getAriaValueText={(value) =>
`Lopacité de la couche «${NBS}${name}${NBS}» est à ${value}${NBS}%`
}
/>
</li>
))}
</ul>
) : null}
</Popover.Panel>
</Popover>
</div>
);
}
MapStyleControl.propTypes = {
style: PropTypes.string,
layers: PropTypes.object,
setStyle: PropTypes.func,
setLayerEnabled: PropTypes.func,
setLayerOpacity: PropTypes.func
};
export default MapStyleControl;