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

front: editor: make LPV editable #4040

Merged
merged 14 commits into from
May 11, 2023
38 changes: 26 additions & 12 deletions front/public/locales/fr/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -300,27 +300,41 @@
"show-more-speed-sections_other": "Voir {{count}} résultats de plus"
},
"speed-edition": {
"label": "Outil \"Vitesses limites\"",
"actions": {
"new-speed-section": "Créer une nouvelle vitesse limite",
"reset-speed-section": "Annuler les modifications en cours"
},
"speed-limits": "Limitations de vitesses",
"main-speed-limit": "Limitation de vitesse par défaut",
"additional-speed-limit": "Limitation de vitesse additionnelles",
"add-new-speed-limit": "Ajouter une nouvelle limitation",
"linked-track-sections": "Sections de lignes liées",
"edit-track-range-start": "Modifier le point de départ",
"add-panel": "Ajouter un panneau de type {{panelType}}",
"add-track-range": "Cliquer pour lier à :",
"additional-speed-limit": "Limitation de vitesse additionnelles",
"edit-track-range-end": "Modifier le point d'arrivée",
"edit-track-range-start": "Modifier le point de départ",
"hovered-panel": "Panneau {{panelType}}",
"label": "Outil \"Vitesses limites\"",
"linked-track-sections": "Sections de lignes liées",
"main-speed-limit": "Limitation de vitesse par défaut",
"move-range-extremity": "Cliquer pour déplacer l'extrémité :",
"new-tag": "Code de composition",
"only-show-n": "Ne montrer que les {{count}} premières sections de lignes",
"show-more-ranges_one": "Voir une section de ligne de plus",
"show-more-ranges_other": "Voir {{count}} sections de lignes de plus",
"add-track-range": "Cliquer pour lier à :",
"panel-angle-geo": "Angle Geo",
"panel-angle-sch": "Angle Sch",
"panel-category": "Panneau(x) {{panelType}}",
"panel-select": "Sélectionner",
"panel-remove": "Supprimer le panneau",
"panel-position": "Position",
"panel-track-id": "Track section id",
"panel-type": "Type",
"panel-side": "Côté",
"panel-value": "Vitesse",
"panels-section-list": "Liste des panneaux de la section",
"remove-track-range": "Cliquer pour détacher de :",
"move-range-extremity": "Cliquer pour déplacer l'extrémité :",
"save-new-speed-section": "Sauver la nouvelle limite de vitesse",
"save-existing-speed-section": "Sauvegarder les modifications",
"new-tag": "Code de composition"
"save-new-speed-section": "Sauvegarder la nouvelle limite de vitesse",
"show-more-ranges_one": "Voir une section de ligne de plus",
"show-more-ranges_other": "Voir {{count}} sections de lignes de plus",
"speed-limits": "Limitations de vitesses",
"toggle-lpv": "Limite permanente de vitesse"
}
}
}
Expand Down
15 changes: 13 additions & 2 deletions front/src/applications/editor/Map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,19 @@ const MapUnplugged: FC<PropsWithChildren<MapProps>> = ({
partialToolState.hovered = null;
}

if (activeTool.onMove && (!nearestResult || !activeTool.onHover)) {
activeTool.onMove(e, extendedContext);
if (activeTool.onMove) {
activeTool.onMove(
nearestResult
? {
...e,
// (don't remove this or TypeScript won't be happy)
preventDefault: e.preventDefault,
// Ensure there is a feature, and the good one:
features: [nearestResult.feature],
}
: e,
extendedContext
);
}

if (!isEmpty(partialToolState)) setToolState(partialToolState);
Expand Down
23 changes: 23 additions & 0 deletions front/src/applications/editor/components/ForceRemount.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React, { FC, ReactNode, useEffect, useState } from 'react';

const ForceRemount: FC<{ fingerprint: string; renderChildren: () => ReactNode }> = ({
fingerprint,
renderChildren,
}) => {
const [skipRender, setSkipRender] = useState(false);

useEffect(() => {
setSkipRender(true);
const timeout = setTimeout(() => setSkipRender(true), 0);
return () => {
clearTimeout(timeout);
};
}, [fingerprint]);

if (skipRender) {
return null;
}
return <>{renderChildren()}</>;
};

export default ForceRemount;
107 changes: 59 additions & 48 deletions front/src/applications/editor/components/LayersModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import switchesIcon from 'assets/pictures/layersicons/switches.svg';
import detectorsIcon from 'assets/pictures/layersicons/detectors.svg';
import trackSectionsIcon from 'assets/pictures/layersicons/layer_adv.svg';
import signalsIcon from 'assets/pictures/layersicons/layer_signal.svg';
import lpvsIcon from 'assets/pictures/layersicons/layer_tivs.svg';

import SwitchSNCF from 'common/BootstrapSNCF/SwitchSNCF/SwitchSNCF';
import { useModal, Modal } from 'common/BootstrapSNCF/ModalSNCF';
Expand All @@ -22,15 +23,16 @@ import { getInfraID } from '../../../reducers/osrdconf/selectors';
import { osrdEditoastApi } from '../../../common/api/osrdEditoastApi';
import { updateLayersSettings } from '../../../reducers/map';

const LAYERS: Array<{ id: LayerType; icon: string | JSX.Element }> = [
{ id: 'track_sections', icon: trackSectionsIcon },
{ id: 'signals', icon: signalsIcon },
{ id: 'buffer_stops', icon: bufferStopIcon },
{ id: 'detectors', icon: detectorsIcon },
{ id: 'switches', icon: switchesIcon },
{ id: 'speed_sections', icon: <MdSpeed style={{ width: '20px' }} className="mx-2" /> },
const LAYERS: Array<{ layers: LayerType[]; icon: string | JSX.Element }> = [
{ layers: ['track_sections'], icon: trackSectionsIcon },
{ layers: ['signals'], icon: signalsIcon },
{ layers: ['buffer_stops'], icon: bufferStopIcon },
{ layers: ['detectors'], icon: detectorsIcon },
{ layers: ['switches'], icon: switchesIcon },
{ layers: ['speed_sections'], icon: <MdSpeed style={{ width: '20px' }} className="mx-2" /> },
{ layers: ['lpv', 'lpv_panels'], icon: lpvsIcon },
{
id: 'errors',
layers: ['errors'],
icon: <BsFillExclamationOctagonFill style={{ width: '20px' }} className="mx-2 text-danger" />,
},
];
Expand Down Expand Up @@ -65,6 +67,8 @@ const LayersModal: FC<LayersModalProps> = ({
() =>
selection
? mapKeys(
// TODO: ATM we don't know if a selected speed section should be considered as SpeedSection or LPV,
// which are two different layers.
mapValues(groupBy(selection, 'objType'), (values) => values.length),
(_values, key) => EDITOAST_TO_LAYER_DICT[key as EditoastType]
)
Expand All @@ -74,8 +78,8 @@ const LayersModal: FC<LayersModalProps> = ({
const unselectCount = useMemo(
() =>
sum(
LAYERS.filter((layer) => !selectedLayers.has(layer.id)).map(
(layer) => selectionCounts[layer.id] || 0
LAYERS.filter((layer) => layer.layers.some((id) => !selectedLayers.has(id))).flatMap(
(layer) => layer.layers.map((id) => selectionCounts[id] || 0)
)
),
[selectedLayers, selectionCounts]
Expand All @@ -88,47 +92,54 @@ const LayersModal: FC<LayersModalProps> = ({
<h4>{t('Editor.nav.osrd-layers')}</h4>
</div>
<div className="row">
{LAYERS.map(({ id, icon }) => (
<div className="col-lg-6" key={id}>
<div className="d-flex align-items-center mt-2">
<SwitchSNCF
type="switch"
onChange={() =>
setSelectedLayers((set) => {
const newSet = new Set(set);
if (newSet.has(id)) newSet.delete(id);
else newSet.add(id);
return newSet;
})
}
name={`editor-layer-${id}`}
id={`editor-layer-${id}`}
checked={selectedLayers.has(id)}
disabled={frozenLayers && frozenLayers.has(id)}
/>
{isString(icon) ? (
<img className="mx-2" src={icon} alt="" height="20" />
) : (
<div>{icon}</div>
)}
<div className="d-flex flex-column">
<div>{t(`Editor.layers.${id}`)}</div>
{!!selectionCounts[id] && (
<div className="small text-muted font-italic">
{t('Editor.layers-modal.layer-selected-items', {
count: selectionCounts[id],
})}
</div>
)}
{frozenLayers && frozenLayers.has(id) && (
<div className="small text-muted font-italic">
{t('Editor.layers-modal.frozen-layer')}
</div>
{LAYERS.map(({ layers, icon }) => {
const layerKey = layers.join('-');
const count = sum(layers.map((id) => selectionCounts[id] || 0));
const disabled = frozenLayers && layers.some((id) => frozenLayers.has(id));
return (
<div className="col-lg-6" key={layerKey}>
<div className="d-flex align-items-center mt-2">
<SwitchSNCF
type="switch"
onChange={() =>
setSelectedLayers((set) => {
const newSet = new Set(set);
layers.forEach((id) => {
if (newSet.has(id)) newSet.delete(id);
else newSet.add(id);
});
return newSet;
})
}
name={`editor-layer-${layerKey}`}
id={`editor-layer-${layerKey}`}
checked={layers.every((id) => selectedLayers.has(id))}
disabled={disabled}
/>
{isString(icon) ? (
<img className="mx-2" src={icon} alt="" height="20" />
) : (
<div>{icon}</div>
)}
<div className="d-flex flex-column">
<div>{t(`Editor.layers.${layerKey}`)}</div>
{!!count && (
<div className="small text-muted font-italic">
{t('Editor.layers-modal.layer-selected-items', {
count,
})}
</div>
)}
{disabled && (
<div className="small text-muted font-italic">
{t('Editor.layers-modal.frozen-layer')}
</div>
)}
</div>
</div>
</div>
</div>
))}
);
})}
</div>
<hr />
<div>
Expand Down
4 changes: 3 additions & 1 deletion front/src/applications/editor/nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,9 @@ const NavButtons: NavButton[][] = [
(setToolState as unknown as (newState: SelectionState) => void)({
...currentState,
selection: currentState.selection.filter((entity) =>
newLayers.has(EDITOAST_TO_LAYER_DICT[entity.objType as EditoastType])
EDITOAST_TO_LAYER_DICT[entity.objType as EditoastType].every((layer) =>
newLayers.has(layer)
)
),
} as SelectionState);
}
Expand Down
Loading