Sort documentation

This commit is contained in:
Eric NGUYEN 2022-10-17 12:19:40 +02:00
parent b64bc8cb6a
commit 3627a9718e
23 changed files with 18 additions and 1 deletions

7
docs/Tutorial/Home.md Normal file
View file

@ -0,0 +1,7 @@
Bienvenue au tutoriel de SVGLayoutDesigner.
Cette documentation a pour objectif de familiariser les nouveaux développeur aux outils du projet
et à apprendre à développer des composants sous React.
Si vous êtes prêt allez sur la page [Pour commencer](Pages/PourCommencer.md)

View file

@ -0,0 +1,251 @@
# Les bases de React
Pour commencer, je vous recommande fortement de regarder cette vidéo qui expliquera mieux que ce document sur comment fonctionne React : https://www.youtube.com/watch?v=Tn6-PIqc4UM
Ce document fera quand même de son mieux pour expliquer les bases de React et permettra d'expliquer comment intégrer un composant dans le projet.
## Qu'est-ce qu'un composant React ?
Un composant React peut être décrite comme une classe (`class component`) ou une fonction (`functional component`) retournant une description de composant en JSX, un langage permettant de combiner du HTML et du JavaScript. Ces composants ont par convention l'extension de fichier `.jsx` (ou `.tsx` avec TypeScript)
Un composant est principalement constituer de `props` (propriétés) et d'un `state` (état). Cela est universel pour tous les composants.
Voici un basique exemple de composant :
```jsx
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
```
> La règle d'or est que le rendu change lorsqu'un props ou un state change.
Selon comment vous écrivez le composant, classe ou fonctionnelle, la manière de changer les props ou state est différente:
Pour les props :
```tsx
// functional
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
// class
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}</h1>;
}
}
```
Pour l'état:
```jsx
// Functionnal
const useTicking = (setDate) => {
React.useEffect(() => {
const timerID = setInterval(() => {
setDate(new Date());
}, 1000);
return () => {
clearInterval(timerID)
}
}, []);
};
export const Clock = (props) => {
const [date, setDate] = React.useState(new Date());
useTicking(setDate);
return (
<div>
{ date.toLocaleTimeString() }
</div>
);
};
// class
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
{this.state.date.toLocaleTimeString()}
</div>
);
}
}
```
Dans ce projet, il est de convention d'écrire en fonctionnel, car cela a pour avantage de fractionner les composants possèdant beaucoup de fonctions (voir par exemple `ContainerOperations.ts`) et de séparer les les propriétés d'états par des `state hooks` (crochet d'états).
La documentation qui suivra utilisera donc l'écriture fonctionnelle. Pour en savoir plus sur l'écriture de classe, vous pouvez essayer [le tutoriel de React](https://reactjs.org/tutorial/tutorial.html).
## Props
Les props sont des données entrer d'un composant. Comme pour un composant html ils décrivent simplement ce que le composant doit représenter.
Exemple :
```tsx
function Welcome(props) {
return <h1>Hello, {props.name}</h1>;
}
```
## State vs Stateless
Un composant possédant un état interne utilise `React.setState(newState)` dans un composant de classe ou `React.useState<Type>(defaultValue)` dans un composant fonctionnel.
Un composant ne possédant pas d'état est dit `stateless`.
Voici donc un exemple de composant React **avec état** :
```tsx
// Clock.tsx
interface IClockProps {
}
const useTicking = (setTime: React.Dispatch<React.SetStateAction<Date>>) => {
React.useEffect(() => {
const timerID = setInterval(() => {
setTime(new Date());
}, 1000);
return () => {
clearInterval(timerID)
}
}, []);
};
export const Clock: React.FC<IClockProps> = (props) => {
const [time, setTime] = React.useState<Date>(new Date());
useTicking(setTime);
return (
<div>
{ time.toLocaleTimeString() }
</div>
);
};
```
Et voici la version **sans état** :
```tsx
// Clock.tsx
interface IClockProps {
time: Date
}
export const Clock: React.FC<IClockProps> = ({ time }) => {
return (
<div>
{ time.toLocaleTimeString() }
</div>
);
};
// Parent.tsx
const useTicking = (setTime: React.Dispatch<React.SetStateAction<Date>>) => {
React.useEffect(() => {
const timerID = setInterval(() => {
setTime(new Date());
}, 1000);
return () => {
clearInterval(timerID);
};
}, []);
};
export const Parent: React.FC<IClockProps> = ({ time }) => {
const [time, setTime] = React.useState<Date>(new Date());
useTicking(setTime);
return (
<Clock time={time}>
);
};
```
Comme vous pouvez le voir, la différence est que la gestion d'état est donnée au composant parent. Cela permet de réduire les mises à jours de rendu en parallèles et de faire le rendu seulement si l'état du composant parent change.
Vous avez probablement remarqué l'usage de `React.useState()` et `React.useEffect()`. Lorsque l'on utilise une fonction avec le mot clé `use` de React, on utilise ce qu'on appelle un `hook` (crochet).
Il existe plusieurs type de hook et chacun ont leurs propre effet. Dans ce projet, nous utilisont principalement `useState` et `useEffect` mais il y a aucune interdiction pour utiliser d'autres types.
### `useState`
`useState` permet de lier à l'état interne du composant une variable.
La fonction nous fourni 2 paramètres dans une liste. La valeur courante et le setter de cette valeur :
```jsx
const [value, setValue] = React.useState(defaultValue);
```
### `useEffect`
`useEffect` permet de lancer une fonction d'effet lors d'un rendu. Il prend en paramètres :
- une fonction callback d'effet lors du rendu qui retourne lui aussi retourne une fonction d'effet lors de la suppression du rendu.
- Il prend aussi optionnellement un liste de dépendance permettant de déterminer les conditions de rendu
Revenons sur l'exemple de `useTicking`:
```tsx
const useTicking = (setTime) => {
React.useEffect(() => {
// componentDidMount
const timerID = setInterval(() => {
setTime(new Date());
}, 1000);
return () => {
// componentWillUnmount
clearInterval(timerID);
};
}, []);
};
```
Décrivons ce quelle fait :
- La callback de `useEffect` appelle `setInterval` afin d'exécuter une fonction `setTime` toute les seconde.
- Cette callback retourne la fonction de suppression de `setInterval` qui sera exécutée si le composant est disposé.
- `useEffect` possède un tableu de dépendance mais elle est vide. Cela veut dire que la callback sera exécuté qu'une seule est unique fois sur toute la durée de vie du composant
Si on avait enlevé le tableau de dépendance, on aurait eu une boucle infinie ! Car rien n'aurait limité l'exécution de la callback à chaque rendu créé par `setTime`.
> Une règle d'or est qu'il faut mettre un tableau de dépendance lorsque l'on utilise un setteur d'état car il y aura a coup sûr une boucle infinie s'il y en a pas
> Contraposée: Il n'est pas nécessaire de mettre un tableau de dépendances s'il n'y pas de setteur d'état dans la callback
> Il est de convention de mettre les fonctions `useEffect` dans une fonction nommée afin de déterminer ce qu'elle fait. Il est possible d'appeler plusieurs fois `useEffect`, donc il n'est pas dangereux de dissocier les effets.
Si vous avez compris, essayons de créer notre [premier composant React](PremierComposantReact.md).

View file

@ -0,0 +1,41 @@
# Heroicon
Cette page est totalement optionnel pour apprendre à faire un composant. Mais elle vous permettra d'apprendre à utiliser [Heroicon](https://heroicons.com/).
Heroicon est un ensemble de SVG utilisable sous forme JSX avec React et Tailwind CSS.
# Continuation de l'exemple de Premier Composant React
L'icône Home peut être importé comme tout composant React.
Il existe plusieurs type de chaque icône de Heroicon : `outline`, `solid`, `mini`. Utilisons donc `outline`.
```tsx
import { HomeIcon } from '@heroicons/react/outline';
...
export function Home({ goHome }: IHomeProps): JSX.Element {
const defaultIndex = Math.floor(Math.random() * (colors.length - 1));
const [index, setIndex] = useState<number>(defaultIndex);
const selectedColor = colors[index];
const className = `${selectedColor}`;
return (
<button
className={className}
type='button'
onClick={goHome}
onMouseEnter={() => {
const newIndex = Math.floor(Math.random() * (colors.length - 1));
setIndex(newIndex);
}}
>
<HomeIcon />
Home
</button>
);
}
```

View file

@ -0,0 +1,43 @@
# Pour commencer
Je vous recommande de lire [README.md](../../../README.md) si ce n'est pas encore fait
et de mettre en place le projet selon ce qui est écrit.
La documentation fera référence à la configuration suivante :
- [VSCode](https://code.visualstudio.com/) pour l'IDE
- [pNPm](https://pnpm.io/fr/) pour le gestionnaire de paquet
- [React Developer Tools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) pour l'outil d'analyse de projet React
- [Typescript React code snippets](https://marketplace.visualstudio.com/items?itemName=infeng.vscode-react-typescript) pour les snippets React
Si vous n'avez pas installé l'un de ces outils, je vous le recommande **fortement**.
# Structure du projet
Commençons par nous familiariser avec la structure du projet: 90% du temps, les modifications que vous allez faire seront dans `src/`.
Si vous souhaitez vous familiariser avec le dossier du projet, lisez [Project_Structure](../../Project_Structure.md).
Le dossier est composé de la manière suivante:
```
./src
├── assets
├── Components
├── dts
├── Enums
├── Events
├── Interfaces
├── test
├── tests
├── utils
├── index.scss
├── main.tsx
└── vite-env.d.ts
```
Le point d'entrer est le `main.tsx`, mais vous aurez rarement le besoin de le modifier sauf pour ajouter des fonctionnalité sur le scope global.
Les dossiers principaux que vous aurez besoin d'utiliser pour développer un nouveau composant seront : `Components`, `Enums`, `Interfaces`, `utils` (et `assets` si vous mettez des images ou polices d'écriture).
Les autres dossiers ne sont pas nécessaire pour faire un composant, mais sont utilisés pour développer d'autres systèmes comme les évents, les définitions typescript et les tests.
Si vous avez compris, allons à la suite et regardons [les bases de React](BasesReact.md).

View file

@ -0,0 +1,297 @@
# Notre premier composant React
Si vous êtes arrivé ici, c'est que vous avez compris ce qu'est un composant React et qu'il est composant de `props` et d'un `state`.
Mettons donc en pratique ce que vous avez vu.
Voici le cahier de charge fonctionnel du composant à implémenter :
Fonction:
- On veut ajouter un bouton `Home` dans l'éditeur pour revenir au menu principal
Contraintes principales :
- Le bouton `home` ne doit pas rafraîchir la page
Contraites secondaires :
- Le bouton est positionné en bas sur la barre
- Le bouton change de couleur à chaque fois que le curseur entre dedans
- Le bouton possède un icône home
## Initialiser un composant
Commençont par créer le composant sans le lier forcément au reste du projet.
Par convention comme il est dit dans la section [Pour commencer](PourCommencer.md), créons le fichier `Home.tsx` dans un dossier `Home/` dans `src/Components`. Il est recommandé de toujours créer un dossier pour le composant pour plus tard si on veut ajouter des styles propres au composant ou si on veut faire des tests.
Maintenant créons le squelette du composant. Pour rappel, on utilise la syntaxe *fonctionnelle* (i.e. une fonction = un composant).
```tsx
// Home.tsx
export function Home(): JSX.Element {
return <></>;
}
```
Note: vous pouvez utiliser le snippet `tsrfc` si vous utilisez l'extension de VSCode.
La convention de nommage indique qu'il faut prioriser les fonctions nommées (donc pas de flèches fonctions).
Mettons le bouton html et mettons du texte dedans :
```tsx
export function Home(): JSX.Element {
return (
<button>
Home
</button>
);
}
```
## Appeler une fonction
Pour que le bouton appelle une fonction nous allons utiliser la propriété `onClick`. Pour lui équiper d'une variable/fonction, on la met entre accolades `prop={var}`.
```tsx
function GoHome(): void {
location.reload();
}
export function Home(): JSX.Element {
return (
<button
onClick={GoHome}
>
Home
</button>
);
}
```
Cela devrait être suffisant pour revenir au menu principal. Appelons-le dans l'editeur.
## Ajouter le composant dans l'editeur
Tout ce qui est UI doit se mettre dans `UI.tsx`.
On sait que l'on doit l'ajouter dans la **barre** à gauche, soit `Bar.tsx`
Commençons par importer le composant dans `Bar.tsx`:
```tsx
// Bar.tsx
import { Home } from '../Home/Home';
...
```
Puis ajoutons-le dans la vue :
```tsx
// Bar.tsx
export function Bar(props: IBarProps): JSX.Element {
return (
<div className='bar'>
...
<Home />
</div>
);
}
```
Testez avec `pnpm dev`. Et ouvrez la page sur `http://localhost:5173`.
Et voilà ! Vous avez fini !
...
Sauf que vous n'avez respecté aucune des contraintes.
Faisons mieux. Utilisons les `props` et le `state` de l'application.
## Props et state
Le composant doit restaurer l'état du menu principal.
Dans le menu principal, lorsque l'on clique sur `Start from scratch`, le composant doit forcément changer un état pour cacher le menu.
Ce que l'on veut faire est donc de renverser cette opération, autrement dit changer l'état qui permet de cacher le menu pour le remontrer.
Allons donc dans le composant `MainMenu.tsx` et analysons le contenu du bouton `Start from scratch`.
```tsx
// MainMenu.tsx
...
default:
return (
<div className='absolute bg-blue-50 p-12 rounded-lg drop-shadow-lg grid grid-cols-1 md:grid-cols-2 gap-8 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2'>
<button type="button" className='mainmenu-btn' onClick={() => {
setWindowState(WindowState.Loading);
props.newEditor();
}}>Start from scratch</button>
<button type="button" className='mainmenu-btn' onClick={() => setWindowState(WindowState.Load)}>Load a configuration file</button>
</div>
);
```
On trouve la fonction `onClick` suivante :
```tsx
// MainMenu.tsx
{
setWindowState(WindowState.Loading);
props.newEditor();
}
```
Aha! On trouve une fonction qui change d'état. Cependant ! On découvre aussi qu'elle appelle une fonction `newEditor`.
Instinctivement, vu que l'on cherche une fonction qui permet de changer d'état, on pourrait croire que c'est `setWindowState` qui gère mais on va vite comprendre que ce n'est pas elle.
Pour rappel `setWindowState` est un setteur d'état, et qu'elle est définie **dans** le composant. Autrement dit, elle ne gère pas l'état de l'application. De plus, un indice indique que le l'état du menu passe de `Main` à `Loading`. Elle ne fait donc que changer sa propre vue pour passer à celle d'une animation de loading que l'on peut voir plus haut dans le `switch`.
`newEditor` étant une fonction donnée par `props`, doit donc correspondre à la fonctionalité que l'on veut renveser.
Regardons donc le composant parent de `MainMenu`: `App`.
```tsx
// App.tsx
...
<MainMenu
newEditor={() => NewEditor(
setEditorState, setLoaded
)}
loadEditor={(files: FileList | null) => LoadEditor(
files,
setEditorState,
setLoaded
)}
/>
...
```
Cette fonction `newEditor` appelle donc une autre fonction `NewEditor`.
```tsx
export function NewEditor(
setEditorState: Dispatch<SetStateAction<IEditorState>>,
setLoaded: Dispatch<SetStateAction<boolean>>
): void {
// Fetch the configuration from the API
FetchConfiguration()
.then((configuration: IConfiguration) => {
// Set the editor from the given properties of the API
const editorState: IEditorState = GetDefaultEditorState(configuration);
setEditorState(editorState);
setLoaded(true);
}, (error) => {
console.debug('[NewEditor] Could not fetch resource from API. Using default.', error);
setLoaded(true);
});
}
```
Cette fonction à l'air assez compliquée, mais on sait qu'elle utilise `setEditorState` et `setLoaded`. Et c'est tout ce qui nous intéresse.
Donc pour renverser l'état de `App` il faut appeler soit `setEditorState` ou soit `setLoaded`. En analysant un peu plus près `App.tsx`, on remarque l'évidante condition permettant d'afficher ou non le menu principal :
```tsx
// App.tsx
...
if (isLoaded) {
return (
<div>
<Editor
(...)
/>
</div>
);
}
return (
<div className='mainmenu-bg'>
<MainMenu
(...)
/>
</div>
```
Ainsi, on sait qu'il faut juste appeler `setLoaded` le setteur d'état de `isLoaded`:
```tsx
// App.tsx
const [isLoaded, setLoaded] = useState<boolean>(false);
```
Cependant comment appeler cette fonction depuis `Home` ? Et bien nous allons utiliser les `props` pour passer la fonction en dessous.
Comme pour `MainMenu`, dans `Home.tsx` nous allons mettre la fonction `GoHome` dans les props :
```tsx
import React from 'react';
interface IHomeProps {
goHome: () => void
}
export function Home({ goHome }: IHomeProps): JSX.Element {
return (
<button
onClick={goHome}
>
Home
</button>
);
}
```
Note: vous pouvez utiliser `Home(props: IHomeProps)` et appeler `props.goHome` comme tout le reste du projet, mais le destructuring d'objet est autorisé.
Home est utilisé dans les composants Bar, UI, et Editor avant d'être appelé dans App. Il faut faire la même chose, ajouter la fonction dans les props et l'appeler dans le composant :
```tsx
// Bar.tsx
interface IBarProps {
goHome: () => void
}
...
export function Bar(props: IBarProps): JSX.Element {
return (
<div className='bar'>
...
<Home
goHome={props.goHome}
/>
</div>
);
}
```
*Faites la même chose pour UI et Editor.*
Enfin dans `App.tsx`, créez la fonction permettant de changer `isLoaded` vers `false`:
```tsx
...
if (isLoaded) {
return (
<div>
<Editor
root={props.root}
configuration={editorState.configuration}
history={editorState.history}
historyCurrentStep={editorState.historyCurrentStep}
goHome={() => {
setLoaded(false);
}}
/>
</div>
);
}
...
```
Félicitation vous avez correctement développé votre premier composant Home !
Essayons maintenant de faire le reste dans le prochain chapitre avec [`TailwindCSS`](Tailwind.md)

View file

@ -0,0 +1,240 @@
# Tailwind CSS
Tailwind CSS est un framework CSS proposant des classes utilitaires comme `flex, pt-4, text-center`.
Ce framework permet d'optimiser notament la génération de css et d'éviter la duplication de code lorsque `Vite` compile le projet.
On peut confirmer cela en regardant le fichier `.css` généré après avoir lancé la commande `pnpm build`.
Il utilise `index.scss` si on veut regrouper les propriétés de tailwind dans une classe.
Il utilise `tailwind.config.cjs` pour configurer et étendre quelques propriétés comme la couleur.
# Continuation de l'exemple de Premier Composant React
Rappelons les contraintes secondaires du bouton Home :
- Le bouton est positionné en bas sur la barre
- Le bouton change de couleur à chaque fois que le curseur entre dedans
- Le bouton possède un icône home
Essayons Tailwind avec React.
Je vous recommande fortement d'avoir [la documentation de Tailwind](https://tailwindcss.com/) sous les yeux pour suivre ce que font les propriétés.
## Positionner avec Tailwind
Pour positionner le bouton tout en bas il faut modifier le layout de la barre
Voici le layout actuel du composant Bar.
```tsx
// Bar.tsx
<div className='bar'>
<BarIcon>(...)</BarIcon>
<BarIcon>(...)</BarIcon>
<BarIcon>(...)</BarIcon>
<BarIcon>(...)</BarIcon>
<Home goHome={props.goHome} />
</div>
```
```scss
// index.scss
.bar {
@apply fixed z-20 flex flex-col top-0 left-0
h-full w-16 bg-slate-100
}
```
Traduisont donc ceci en css:
```css
.bar {
display: flex; /* flex */
flex-direction: column; /* flex-col */
position: fixed; /* fixed */
top: 0; /* top-0 */
left: 0; /* left-0 */
z-index: 20; /* z-20 */
width: 4rem; /* w-16 */
height: 100%; /* h-full */
}
```
On va juste nous interesser sur `flex` et `flex-col` du coup car elles decident du layout des éléments enfants.
Pour espacer deux éléments dans un layout flex, il y a plusieurs manière de le faire :
- On peut rajout un `div` d'espacement avec la propriété `grow`
- On peut wrapper tous les `BarIcon` et utiliser `place-content-between` dans `.bar`
On va utiliser la premiere méthode, mais libre à vous la méthode.
Pour ajouter une classe sur React, on utilise le mot-clé `className`. C'est pour ne pas rentrer en conflit avec le mot-clé `class` servant de classe d'objet en JavaScript que ce mot-clé a été choisi.
```tsx
// Bar.tsx
<div className='bar'>
<BarIcon>(...)</BarIcon>
<BarIcon>(...)</BarIcon>
<BarIcon>(...)</BarIcon>
<BarIcon>(...)</BarIcon>
<div className='grow'></div>
<Home goHome={props.goHome} />
</div>
```
Et voilà ! Aussi simple que cela.
Continuons avec les couleurs avec le curseur. Cette fois ci, on va utiliser React.
# Utiliser Tailwind avec React
Revenons sur le composant Home définie par un simple bouton :
```tsx
// Home.tsx
import React from 'react';
interface IHomeProps {
goHome: () => void
}
export function Home({ goHome }: IHomeProps): JSX.Element {
return (
<button
onClick={goHome}
>
Home
</button>
);
}
```
Dans ce composant, initialisons la liste des couleurs que l'on veut utiliser. Les couleurs de Tailwind sont définies sur cette page : `https://tailwindcss.com/docs/customizing-colors`.
Il nous suffit donc de juste mettre une liste de mots-clés de couleurs :
```tsx
// Home.tsx
...
const colors = [
'bg-state-500',
'bg-gray-500',
'bg-zinc-500',
'bg-neutral-500',
'bg-stone-500',
'bg-red-500',
'bg-orange-500',
'bg-amber-500',
'bg-yellow-500',
'bg-lime-500',
'bg-emerald-500',
'bg-teal-500',
'bg-cyan-500',
'bg-sky-500',
'bg-blue-500',
'bg-indigo-500',
'bg-violet-500',
'bg-purple-500',
'bg-fuchsia-500',
'bg-pink-500',
'bg-rose-500'
];
```
Et d'en sélectionner une au hazard.
```tsx
// Home.tsx
const colors = [
...
];
export function Home({ goHome }: IHomeProps): JSX.Element {
const index = Math.floor(Math.random() * (colors.length - 1));
const selectedColor = colors[index];
const className = `${selectedColor}`;
return (
<button
className={className}
type='button'
onClick={goHome}
>
Home
</button>
);
}
```
Vous vous demandez peut-être pourquoi je n'ai pas juste interpolé une liste de couleurs (`red`, `blue` etc.) au lieux de la *liste de propriétés de couleurs d'arrière-plan* (`bg-red-500`, `bg-blue-500`...).
Cela est expliqué par l'algorithme d'optimisation de fichier CSS de Tailwind.
Tailwind CSS purge les classes non utilisées dans son framework en scannant tous les fichiers, cela permet d'avoir un fichier css final léger mais en contrepartie, les opérations d'interpolation ne fonctionne pas.
Vous pouvez vérifier la liste de type fichier scanné dans `tailwind.config.cjs`.
Note : Cela veut dire que l'on aurait pas créer un fichier de preload appelé `colors.tw` et ajouter ce format `.tw` dans le fichier de config pour qu'il le scanne. Ainsi pouvoir enfin interpoler dans le fichier `.tsx` mais cela revient au même finalement que de faire la liste.
On a donc enfin un arrière-plan qui change mais n'avons nous pas oublié quelque chose ?
Oui ! Il faut changer la couleur quand la souris passe par dessus !
Cette fois-ci, pouvez-vous le faire sans lire la solution ?
Indice: Cela implique l'utilisation de `state` ;)
# Réponse
Suivons donc l'indice et créons un hook d'état afin de sélectionner la couleur et de pouvoir la changer quand la souris passe dans le composant
```tsx
// Home.tsx
export function Home({ goHome }: IHomeProps): JSX.Element {
const defaultIndex = Math.floor(Math.random() * (colors.length - 1));
const [index, setIndex] = useState<number>(defaultIndex);
const selectedColor = colors[index];
const className = `${selectedColor}`;
return (
<button
className={className}
type='button'
onClick={goHome}
>
Home
</button>
);
}
```
Ensuite utilisons la propriété `onMouseEnter` pour jeter l'évent.
```tsx
// Home.tsx
export function Home({ goHome }: IHomeProps): JSX.Element {
const defaultIndex = Math.floor(Math.random() * (colors.length - 1));
const [index, setIndex] = useState<number>(defaultIndex);
const selectedColor = colors[index];
const className = `${selectedColor}`;
return (
<button
className={className}
type='button'
onClick={goHome}
onMouseEnter={() => {
const newIndex = Math.floor(Math.random() * (colors.length - 1));
setIndex(newIndex);
}}
>
Home
</button>
);
}
```
Et voilà vous avez enfin terminé avec Tailwind CSS !
Amusez-vous un peu avec Tailwind CSS pour ajuster le style du bouton (padding, margin, transitions etc.) et découvrir quelques propriétés.
Finissons ce tutoriel avec [Heroicon](Heroicon.md).