app: Généralise addQueryTerm/removeQueryTerm

This commit is contained in:
Mattéo Delabre 2019-12-04 21:46:57 -05:00
parent 650f76c028
commit 8e58ab8c91
Signed by: matteo
GPG Key ID: AE3FBD02DC583ABB
4 changed files with 108 additions and 65 deletions

View File

@ -1,7 +1,7 @@
import React, {useState} from 'react';
import React from 'react';
import TermInput from './TermInput.js';
import DiseaseGraph from './DiseaseGraph.js';
import {useAsync} from '../util.js';
import {useAsync, useQuery} from '../util.js';
import {
diseasesBySymptoms,
exploreSymptoms
@ -9,22 +9,35 @@ import {
const App = () =>
{
const [query, setQuery] = useState([]);
const {
query,
addTerm: addQueryTerm,
removeTerm: removeQueryTerm
} = useQuery();
// Récupération des résultats de la recherche
const diseases = useAsync([], diseasesBySymptoms, query);
const terms = useAsync([], exploreSymptoms, diseases);
// Tous les termes compatibles avec la recherche qui napparaissent pas
// dans la recherche
const availableTerms = terms.filter(({id}) =>
!query.some(({id: termId}) => termId === id)
);
return (
<div className="App">
<TermInput
query={query}
setQuery={setQuery}
availableTerms={terms}
addQueryTerm={addQueryTerm}
removeQueryTerm={removeQueryTerm}
availableTerms={availableTerms}
/>
<DiseaseGraph
terms={terms}
query={query}
setQuery={setQuery}
addQueryTerm={addQueryTerm}
removeQueryTerm={removeQueryTerm}
/>
</div>
);

View File

@ -10,10 +10,15 @@ import {symptomsSubgraph} from '../data/mock';
*
* @prop terms Termes à afficher.
* @prop query Ensemble de termes recherchés par lutilisateur.
* @prop setQuery Fonction de rappel pour ajouter de nouveaux termes de
* recherche.
* @prop addQueryTerm Fonction de rappel pour ajouter un terme à la requête.
* @prop removeQueryTerm Fonction de rappel pour ôter un terme de la requête.
*/
const DiseaseGraph = ({terms, query, setQuery}) =>
const DiseaseGraph = ({
terms,
query,
addQueryTerm,
removeQueryTerm,
}) =>
{
const {nodes, edges} = useAsync({
nodes: {},
@ -40,7 +45,11 @@ const DiseaseGraph = ({terms, query, setQuery}) =>
].join(' ')}
title={
`Cliquez pour plus dinformations sur « ${term.name} »\n`
+ '(Ctrl-clic pour lajouter à la requête)'
+ `(Ctrl-clic pour ${
isInQuery
? 'lenlever de'
: 'lajouter à'
} la requête)`
}
>
{nodes[id].name}
@ -67,15 +76,12 @@ const DiseaseGraph = ({terms, query, setQuery}) =>
else if (queryIndex >= 0)
{
// Ctrl-clic : Retrait dun terme déjà dans la requête
setQuery([
...query.slice(0, queryIndex),
...query.slice(queryIndex + 1)
]);
removeQueryTerm(queryIndex);
}
else
{
// Ctrl-clic : Ajout dun nouveau terme dans la requête
setQuery(query.concat([term]));
addQueryTerm(term);
}
};
@ -93,7 +99,8 @@ const DiseaseGraph = ({terms, query, setQuery}) =>
DiseaseGraph.propTypes = {
terms: PropTypes.arrayOf(Term).isRequired,
query: PropTypes.arrayOf(Term).isRequired,
setQuery: PropTypes.func.isRequired,
addQueryTerm: PropTypes.func.isRequired,
removeQueryTerm: PropTypes.func.isRequired,
};
export default DiseaseGraph;

View File

@ -20,10 +20,16 @@ const keys = Object.assign(Object.create(null), {
* Zone de saisie des termes de recherche.
*
* @prop query Ensemble de termes déjà dans la requête.
* @prop setQuery Fonction de rappel utilisée pour modifier la requête.
* @param availableTerms Termes pouvant être ajoutés par lutilisateur.
* @prop addQueryTerm Fonction de rappel pour ajouter un terme à la requête.
* @prop removeQueryTerm Fonction de rappel pour ôter un terme de la requête.
* @prop availableTerms Termes pouvant être ajoutés par lutilisateur.
*/
const TermInput = ({query, setQuery, availableTerms}) =>
const TermInput = ({
query,
addQueryTerm,
removeQueryTerm,
availableTerms,
}) =>
{
// Valeur actuellement saisie dans le champ de recherche de termes
const [value, setValue] = useState('');
@ -40,19 +46,13 @@ const TermInput = ({query, setQuery, availableTerms}) =>
const inputRef = useRef(null);
/**
* Ajoute un terme à la requête.
* Valide lentrée courante en lajoutant comme terme de la requête.
*
* @param term Terme à ajouter.
* @param id Identifiant du terme à ajouter dans la requête.
*/
const addQueryTerm = term =>
const finalizeInput = id =>
{
if (query.some(({id}) => id === term.id))
{
// Najoute pas les termes déjà sélectionnés
return;
}
setQuery(query.concat([term]));
addQueryTerm(id);
setValue('');
setSuggestions([]);
@ -62,19 +62,6 @@ const TermInput = ({query, setQuery, availableTerms}) =>
}
};
/**
* Supprime un terme de la requête.
*
* @param index Indice du terme à supprimer dans la liste.
*/
const removeQueryTerm = index =>
{
setQuery([
...query.slice(0, index),
...query.slice(index + 1)
]);
};
/**
* Gère lappui sur une touche dans le champ de saisie.
*
@ -89,19 +76,15 @@ const TermInput = ({query, setQuery, availableTerms}) =>
if (focusedSuggestion < suggestions.length)
{
addQueryTerm(suggestions[focusedSuggestion]);
finalizeInput(suggestions[focusedSuggestion]);
}
}
else if (
ev.keyCode === keys.backspace
&& !value
&& query.length !== 0
)
else if (ev.keyCode === keys.backspace && !value)
{
// Touche Retour alors que le champ de saisie est vide :
// retrait du dernier terme de la requête
ev.preventDefault();
setQuery(query.slice(0, -1));
removeQueryTerm(query.length - 1);
}
else if (ev.keyCode === keys.up)
{
@ -134,17 +117,8 @@ const TermInput = ({query, setQuery, availableTerms}) =>
}
else
{
setSuggestions(fuzzy.filterTerms(
// Filtre les termes correspondant à la saisie
nextValue,
// Sélectionne les termes qui nont pas déjà été choisis
availableTerms.filter(({id: suggestionId}) =>
!query.some(({id: termId}) =>
termId === suggestionId
)
)
));
// Filtre les termes correspondant à la saisie
setSuggestions(fuzzy.filterTerms(nextValue, availableTerms));
}
};
@ -180,7 +154,7 @@ const TermInput = ({query, setQuery, availableTerms}) =>
: ''
].join(' ')}
onMouseEnter={setFocusedSuggestion.bind(null, index)}
onClick={addQueryTerm.bind(null, suggestion)}
onClick={finalizeInput.bind(null, suggestion)}
title="Cliquez pour ajouter ce terme à la recherche"
>
{suggestion.name}
@ -206,7 +180,8 @@ const TermInput = ({query, setQuery, availableTerms}) =>
TermInput.propTypes = {
query: PropTypes.arrayOf(Term).isRequired,
setQuery: PropTypes.func.isRequired,
addQueryTerm: PropTypes.func.isRequired,
removeQueryTerm: PropTypes.func.isRequired,
availableTerms: PropTypes.arrayOf(Term).isRequired,
};

View File

@ -32,9 +32,6 @@ export const useAsync = (initial, func, ...args) =>
*
* si la liste nest pas vide, focus [0, taille de la liste].
* sinon, focus = 0.
*
* @return Objet contenant la liste, lindice de lélément ayant le focus ainsi
* que des fonctions de modification.
*/
export const useFocusableList = () =>
{
@ -42,7 +39,14 @@ export const useFocusableList = () =>
const [focus, setFocus] = useState(0);
return {
/**
* Contenu courant de la liste.
*/
list,
/**
* Indice de lélément ayant présentement le focus.
*/
focus,
/**
@ -88,3 +92,47 @@ export const useFocusableList = () =>
}
};
};
/**
* Crée une liste de termes de recherche.
*/
export const useQuery = () =>
{
const [query, setQuery] = useState([]);
return {
/**
* Liste des termes de la requête.
*/
query,
/**
* Ajoute un terme à la requête.
*
* @param term Terme à ajouter.
*/
addTerm(term)
{
if (query.some(({id}) => id === term.id))
{
// Najoute pas les termes déjà sélectionnés
return;
}
setQuery(query.concat([term]));
},
/**
* Supprime un terme de la requête.
*
* @param index Indice du terme à supprimer dans la liste.
*/
removeTerm(index)
{
setQuery([
...query.slice(0, index),
...query.slice(index + 1)
]);
},
};
};