app: Implémentation d’une recherche de test
This commit is contained in:
parent
78afbbb00e
commit
8b77ed5435
3
.babelrc
3
.babelrc
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"]
|
||||
"presets": ["@babel/preset-env", "@babel/preset-react"],
|
||||
"plugins": ["@babel/plugin-transform-runtime"]
|
||||
}
|
||||
|
|
|
@ -9,6 +9,6 @@
|
|||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script src="index.js"></script>
|
||||
<script src="src/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import React, {useState} from 'react';
|
||||
import TermInput from './TermInput.js';
|
||||
import ResultsGraph from './ResultsGraph.js';
|
||||
import SearchResults from './SearchResults.js';
|
||||
|
||||
const App = () =>
|
||||
{
|
||||
|
@ -9,7 +9,7 @@ const App = () =>
|
|||
return (
|
||||
<div className="App">
|
||||
<TermInput terms={terms} setTerms={setTerms} />
|
||||
<ResultsGraph />
|
||||
<SearchResults terms={terms} />
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -191,7 +191,6 @@ const Graph = ({nodes, edges, render}) =>
|
|||
|
||||
return () =>
|
||||
{
|
||||
layout.stop();
|
||||
graphParent.current.removeEventListener('mousedown', mouseDown);
|
||||
document.body.removeEventListener('mousemove', mouseMove);
|
||||
document.body.removeEventListener('mouseup', mouseUp);
|
|
@ -0,0 +1,13 @@
|
|||
import React from 'react';
|
||||
import Graph from './Graph.js';
|
||||
|
||||
|
||||
const ResultsGraph = () => (
|
||||
<Graph
|
||||
nodes={Object.keys(nodes)}
|
||||
edges={edges}
|
||||
render={id => nodes[id].name}
|
||||
/>
|
||||
);
|
||||
|
||||
export default ResultsGraph;
|
|
@ -0,0 +1,37 @@
|
|||
import React, {useState, useEffect} from 'react';
|
||||
import Graph from './Graph.js';
|
||||
import {searchTerms} from './fetch.js';
|
||||
|
||||
const useResults = terms =>
|
||||
{
|
||||
const [results, setResults] = useState({
|
||||
nodes: {},
|
||||
edges: []
|
||||
});
|
||||
|
||||
useEffect(() =>
|
||||
{
|
||||
const fetch = async () =>
|
||||
{
|
||||
setResults(await searchTerms(terms));
|
||||
};
|
||||
|
||||
fetch();
|
||||
}, [terms]);
|
||||
|
||||
return results;
|
||||
};
|
||||
|
||||
const SearchResults = ({terms}) =>
|
||||
{
|
||||
const {nodes, edges} = useResults(terms);
|
||||
return (
|
||||
<Graph
|
||||
nodes={Object.keys(nodes)}
|
||||
edges={edges}
|
||||
render={id => nodes[id].name}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchResults;
|
|
@ -1,6 +1,7 @@
|
|||
import React, {useState} from 'react';
|
||||
|
||||
const enterKey = 13;
|
||||
const backspaceKey = 8;
|
||||
|
||||
const TermInput = ({terms, setTerms}) =>
|
||||
{
|
||||
|
@ -8,7 +9,7 @@ const TermInput = ({terms, setTerms}) =>
|
|||
|
||||
const handleKeyDown = ev =>
|
||||
{
|
||||
if (ev.keyCode === enterKey)
|
||||
if (ev.keyCode === enterKey && value)
|
||||
{
|
||||
ev.preventDefault();
|
||||
|
||||
|
@ -18,6 +19,14 @@ const TermInput = ({terms, setTerms}) =>
|
|||
}
|
||||
|
||||
setValue('');
|
||||
return;
|
||||
}
|
||||
|
||||
if (ev.keyCode === backspaceKey && !value)
|
||||
{
|
||||
ev.preventDefault();
|
||||
setTerms(terms.slice(0, -1));
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -42,7 +51,7 @@ const TermInput = ({terms, setTerms}) =>
|
|||
>{term}</span>
|
||||
)}
|
||||
<input
|
||||
autofocus="true" type="text" className="TermInput_input"
|
||||
autoFocus={true} type="text" className="TermInput_input"
|
||||
placeholder="Rechercher un symptôme, un signe ou une maladie…"
|
||||
value={value}
|
||||
onChange={handleChange} onKeyDown={handleKeyDown} />
|
|
@ -1,21 +1,4 @@
|
|||
import React from 'react';
|
||||
import Graph from './Graph.js';
|
||||
|
||||
// const config = {
|
||||
// automaticRearrangeAfterDropNode: true,
|
||||
// node: {
|
||||
// color: 'lightgreen',
|
||||
// size: 240,
|
||||
// fontSize: 14,
|
||||
// highlightStrokeColor: 'blue',
|
||||
// labelProperty: 'name'
|
||||
// },
|
||||
// link: {
|
||||
// highlightColor: 'lightblue'
|
||||
// }
|
||||
// };
|
||||
|
||||
const nodes = {
|
||||
const mockNodes = {
|
||||
Q2840: {
|
||||
id: 'Q2840',
|
||||
name: 'Grippe',
|
||||
|
@ -127,7 +110,7 @@ const nodes = {
|
|||
}
|
||||
};
|
||||
|
||||
const edges = [
|
||||
const mockEdges = [
|
||||
['Q2840', 'Q38933'],
|
||||
['Q2840', 'Q1115038'],
|
||||
['Q2840', 'Q474959'],
|
||||
|
@ -165,12 +148,83 @@ const edges = [
|
|||
['Q133780', 'Q160796']
|
||||
];
|
||||
|
||||
const ResultsGraph = () => (
|
||||
<Graph
|
||||
nodes={Object.keys(nodes)}
|
||||
edges={edges}
|
||||
render={id => nodes[id].name}
|
||||
/>
|
||||
);
|
||||
/**
|
||||
* Vérifie si une liste d’arêtes contient une arête donnée, dans un sens ou
|
||||
* dans l’autre.
|
||||
*
|
||||
* @param list Liste d’arêtes.
|
||||
* @param from Premier nœud de l’arête.
|
||||
* @param to Second nœud de l’arête.
|
||||
* @return Vrai si et seulement si l’arête existe.
|
||||
*/
|
||||
const includesEdge = (list, from, to) =>
|
||||
list.some(([source, target]) => (
|
||||
(source === from && target === to)
|
||||
|| (source === to && target === from)
|
||||
));
|
||||
|
||||
export default ResultsGraph;
|
||||
export const searchTerms = terms => new Promise((res, rej) =>
|
||||
{
|
||||
// Fait attendre artificiellement pour simuler une requête
|
||||
setTimeout(() =>
|
||||
{
|
||||
const nodes = {};
|
||||
const edges = [];
|
||||
|
||||
// Récupération des identifiants correspondant aux termes de la requête
|
||||
const termIds = terms.map(term =>
|
||||
Object.keys(mockNodes)
|
||||
.find(id => mockNodes[id].name === term)
|
||||
);
|
||||
|
||||
// Si l’un des termes est inconnu, aucun résultat
|
||||
if (!termIds.includes(undefined))
|
||||
{
|
||||
// Sélection des termes de la requête
|
||||
for (let termId of termIds)
|
||||
{
|
||||
nodes[termId] = mockNodes[termId];
|
||||
}
|
||||
|
||||
// Sélection des nœuds liés à tous les éléments de la requête
|
||||
const resultIds = [];
|
||||
|
||||
for (let [id, data] of Object.entries(mockNodes))
|
||||
{
|
||||
if (termIds.every(
|
||||
termId => includesEdge(mockEdges, id, termId)
|
||||
))
|
||||
{
|
||||
nodes[id] = data;
|
||||
resultIds.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Sélection des voisins des résultats de la requête
|
||||
for (let [id, data] of Object.entries(mockNodes))
|
||||
{
|
||||
for (let resultId of resultIds)
|
||||
{
|
||||
if (includesEdge(mockEdges, resultId, id))
|
||||
{
|
||||
nodes[id] = data;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sélection des arêtes liant les nœuds sélectionnés
|
||||
for (let [from, to] of mockEdges)
|
||||
{
|
||||
if (
|
||||
(from in nodes && to in nodes)
|
||||
&& !includesEdge(edges, from, to)
|
||||
)
|
||||
{
|
||||
edges.push([from, to]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res({nodes, edges});
|
||||
}, 500);
|
||||
});
|
|
@ -104,7 +104,7 @@ input
|
|||
color: var(--color-light);
|
||||
|
||||
border-radius: 2px;
|
||||
padding: calc(.2 * var(--base-size));
|
||||
padding: calc(.25 * var(--base-size)) calc(.5 * var(--base-size));
|
||||
margin-left: calc(.3 * var(--base-size));
|
||||
cursor: pointer;
|
||||
|
||||
|
|
|
@ -730,6 +730,40 @@
|
|||
"@babel/helper-plugin-utils": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-runtime": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.7.4.tgz",
|
||||
"integrity": "sha512-O8kSkS5fP74Ad/8pfsCMGa8sBRdLxYoSReaARRNSz3FbFQj3z/QUvoUmJ28gn9BO93YfnXc3j+Xyaqe8cKDNBQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-module-imports": "^7.7.4",
|
||||
"@babel/helper-plugin-utils": "^7.0.0",
|
||||
"resolve": "^1.8.1",
|
||||
"semver": "^5.5.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.7.4.tgz",
|
||||
"integrity": "sha512-dGcrX6K9l8258WFjyDLJwuVKxR4XZfU0/vTUgOQYWEnRD8mgr+p4d6fCUMq/ys0h4CCt/S5JhbvtyErjWouAUQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.7.4"
|
||||
}
|
||||
},
|
||||
"@babel/types": {
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.7.4.tgz",
|
||||
"integrity": "sha512-cz5Ji23KCi4T+YIE/BolWosrJuSmoZeN1EFnRtBwF+KKLi8GG/Z2c2hOJJeCXPk4mwk4QFvTmwIodJowXgttRA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esutils": "^2.0.2",
|
||||
"lodash": "^4.17.13",
|
||||
"to-fast-properties": "^2.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-shorthand-properties": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.2.0.tgz",
|
||||
|
@ -860,10 +894,9 @@
|
|||
}
|
||||
},
|
||||
"@babel/runtime": {
|
||||
"version": "7.7.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.2.tgz",
|
||||
"integrity": "sha512-JONRbXbTXc9WQE2mAZd1p0Z3DZ/6vaQIkgYMSTP3KjRCyd7rCZCcfhCyX+YjwcKxcZ82UrxbRD358bpExNgrjw==",
|
||||
"dev": true,
|
||||
"version": "7.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz",
|
||||
"integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==",
|
||||
"requires": {
|
||||
"regenerator-runtime": "^0.13.2"
|
||||
}
|
||||
|
@ -5954,8 +5987,7 @@
|
|||
"regenerator-runtime": {
|
||||
"version": "0.13.3",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.3.tgz",
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw==",
|
||||
"dev": true
|
||||
"integrity": "sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw=="
|
||||
},
|
||||
"regenerator-transform": {
|
||||
"version": "0.14.1",
|
||||
|
|
|
@ -5,11 +5,13 @@
|
|||
"main": "index.js",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.7.2",
|
||||
"@babel/plugin-transform-runtime": "^7.7.4",
|
||||
"@babel/preset-env": "^7.7.1",
|
||||
"@babel/preset-react": "^7.7.0",
|
||||
"parcel-bundler": "^1.12.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.7.4",
|
||||
"react": "^16.12.0",
|
||||
"react-dom": "^16.12.0",
|
||||
"springy": "^2.8.0"
|
||||
|
|
Loading…
Reference in New Issue