React Input avec des mots-clés pouvant être mis en évidence
La première chose qui m'est venue à l'esprit est que quelqu'un a probablement déjà construit un tel composant et que la seule chose que j'ai à faire est d'adapter la solution implémentée. Après une courte recherche, j'ai trouvé un exemple réalisé à l'aide de textarea qui fonctionnait exactement comme textarea avec la possibilité de mettre en surbrillance des mots séparés.
De plus, les réponses sur StackOverflow ont clairement indiqué que ce n'est pas une tâche simple.
Une solution évidente consistait à créer ce composant en deux parties : une balise de saisie de texte devant avec du texte transparent et un caret coloré et une div positionnée de manière absolue à l'arrière, contenant du texte stylé visible. En faisant cela, j'ai déjà un texte entièrement personnalisable.
Je laisserai les styles CSS en dehors de cet article car cela prendra trop de place. Le principe de base est très simple - les propriétés de police, les rembourrages et les tailles doivent être identiques pour l' entrée et la div qui se trouve au verso.
const [value, setValue] = useState('');
const onChange = (e) => setValue(e.target.value);
const format = (text) => // formatted jsx
<div className="container">
<div className="text">{format(value)}</div>
<input type="text" className="field" value={value} onChange={onChange} />
</div>
Lors de la saisie d'une chaîne trop longue, le champ commence à défiler mais le texte dans div reste statique.
Après de longues discussions avec mon collègue, nous avons réalisé que nous pouvions prendre la propriété scroll left de l'entrée et la définir pour l' élément div .
// ...
const textBlockRef = useRef(null);
const [scrollValue, setScrollValue] = useState(0);
// ...
const handleScroll = (e) => setScrollValue(e.currentTarget.scrollLeft);
useEffect(() => {
if (textBlockRef.current) {
textBlockRef.current.scrollLeft = scrollValue;
}
}, [scrollValue]);
<!-- ... -->
<div className="text" ref={textBlockRef}>{format(value)}</div>
<input
type="text"
className="field"
value={value}
onChange={onChange}
onScroll={handleScroll}
/>
<!-- ... -->
Le deuxième problème
Il s'est avéré que je n'ai pas complètement résolu le premier problème et que c'était le deuxième problème.
Safari ne prend pas en charge la propriété onScroll d'un élément d'entrée.
Eh bien, ça change le jeu (pensais-je), et j'ai vraiment commencé à remettre en question mon approche. Mais Dieu merci, j'ai découvert que Safari prend en charge la propriété onScroll d'un élément textarea .
Cela a résolu le problème.
Tout ce que j'avais à faire était de styliser textarea pour qu'il ressemble à un input[type="text"] et c'est tout.
<!-- ... -->
<textarea
className="field"
ref={ref}
value={value}
onChange={onChange}
onScroll={handleScroll}
rows={1}
/>
<!-- ... -->
Lorsque vous appuyez sur la touche Entrée , la zone de texte crée une nouvelle ligne. Il est facile à gérer en créant un gestionnaire onKeyDown et en annulant le comportement par défaut. De plus, si le champ est enveloppé par une balise de formulaire , il est également nécessaire de soumettre la requête.
// ...
const submitHandler = (e) => {
if (e.keyCode === 13 || e.key === "Enter" || e.which === 13) {
if (!e.repeat && e.target.form) {
e.target.form.submit();
}
e.preventDefault();
}
};
// ...
<!-- ... -->
<textarea
className="field"
ref={ref}
value={value}
onChange={onChange}
onScroll={handleScroll}
onKeyDown={submitHandler}
rows={1}
/>
<!-- ... -->
Probablement, plus de problèmes seront trouvés dans cette solution, et je serai heureux si vous partagez dans les commentaires ce que vous trouvez.
![Qu'est-ce qu'une liste liée, de toute façon? [Partie 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)



































