Fonctions
Room est composé de 10 fonctions qui sont exportées dans la version module ECMAScript 6 et accessibles via l’objet Room dans la version non ESM.
Les 10 fonctions sont :
Dans la version ECMAScript de Room, il est possible d’importer les fonctions de Room individuellement comme par exemple avec ce code :
import {elements, createData} from "Room";
const {div} = elements();
createData(0);
Mais il est aussi possible d’importer un objet Room et d’utiliser les fonctions via cet objet comme par exemple ci-dessous :
import Room from "Room";
const {div} = Room.elements();
Room.createData(0);
Ceci permet d’écrire du code qui est le même (à l’exception de l’importation) entre la version ECMAScript et la version non ECMAScript puisque dans cette dernière, l‘utilisation des fonctions de Room ne peut se faire que via l’objet Room.
Cette possibilité a été utilisée dans les différents exemples proposés afin de faire abstraction de la version de Room utilisée.
Si vous développez uniquement des modules ECMAScript, utilisez directement les fonctions importées de Room permet d’avoir un code moins verbeux.
Fonction h()
Cette fonction permet de créer par défaut un élément HTML avec des attributs et des contenus mais peut aussi servir pour créer des éléments SVG ou MathML.
La signature de la fonction est la suivante :
h(tagName, ...contents)
Elle retourne un objet de la classe Element
et plus précisément un HTMLElement
, mais aussi éventuellement un SVGElement
ou un MathMLElement
comme indiqué plus loin.
La fonction
h()
est la fonction de base, mais il est bien plus pratique d’utiliser la fonctionelements()
de Room qui permet d’obtenir des fonctions dont le nom est directement un nom d’élément, l’écriture du code est ainsi plus concise et plus lisible.
Les paramètres attendus par la fonction sont :
tagName
: Une chaîne de caractères contenant un nom d’élément HTML ("div"
,"p"
,"span"
, etc.), ou éventuellement un nom d’élément SVG ("svg"
,"path"
, etc.) ou MathML ("math"
,"mfrac"
, etc.).contents
: Des contenus pour l’élément qui peuvent être ses attributs et des éléments enfants.
La fonction commence par analyser la valeur de this
.
Si la valeur de this
n’est pas définie, null
ou une chaîne de caractères vide, un élément HTML est créé avec la méthode createElement()
en utilisant le paramètre tagName
.
Si la valeur de this
est une chaîne de caractères non vide, cette valeur est considérée comme étant un espace de nom (nameSpace
) et un élément SVG ou MatHML est créé avec la méthode createElementNS()
en utilisant ce nameSpace
et le paramètre tagName
.
S’il y en a, la fonction ajoute ensuite à l’élément les contenus référencés par le paramètre contents
(attributs et enfants) en appelant la fonction append()
de Room et retourne l’élément qui a été créé. Les types utilisables et les contraintes pour le paramètres contents
sont donc identiques à ce qui est décrit dans la fonction append()
.
La fonction h()
produit donc de base des éléments HTML mais peut aussi être utilisée pour produire des éléments SVG ou MathML. Pour cela il suffit de créer une nouvelle fonction via la fonction bind()
de JavaScript en passant à cette dernière un unique paramètre qui doit être un nameSpace
, celui de SVG ou celui de MathML.
Cette possibilité est présentée dans la page Composition avec SVG et MathML.
Fonction elements()
Cette fonction génère un objet Proxy
de la fonction h()
qui permet ensuite d’obtenir des fonctions dont les noms sont des noms d’éléments HTML et éventuellement des noms d’éléments SVG ou MathML.
La signature de la fonction est la suivante :
elements(nameSpace)
Le paramètre nameSpace
n’est pas requis, il n’est pris en compte que s’il contient une chaîne de caractères non vide et est alors utilisé comme espace de nom passé, via this
, à la fonction h()
.
La fonction elements()
retourne un Proxy
de la fonction h()
et s’utilise typiquement avec l’affectation par décomposition de JavaScript pour obtenir des fonctions qui génèrent des objets de la classe Element
dont le type est le nom de la fonction.
Un exemple :
// Récupération des fonctions div(), p() et my_component()
const {div, p, my_component} = Room.elements();
// Création d'un élément <div> de classe "container" et contenant
// un élément <p> et un composant web <my-component>
const container = div({class: "container"},
p("Hello World"),
my_component({text: "Composant de test"})
);
Il est à noter dans cet exemple le cas de la fonction my_component()
qui génère bien un élément <my-component>
, Room remplace automatiquement le caractère _
par un caractère -
.
Des exemples d’utilisation de la fonction
elements()
pour générer des fonctions qui génèrent des éléments SVG ou MathML sont disponibles dans la page Composition avec SVG et MathML.
Fonction append()
Cette fonction permet d’ajouter à un élément des contenus qui peuvent être des attributs pour l’élément ou des éléments enfants.
La signature de la fonction est la suivante :
append(element, ...contents)
Le premier paramètre element
est requis et doit être un objet de la classe Element
, il est retourné par la fonction.
Les paramètres suivants non requis et référencés par contents
sont les contenus à ajouter à l’élément, les objets littéraux étant considérés comme des descripteurs d’attributs, les autres types comme des éléments enfants.
Quelques exemples d’utilisation :
// Récupération des fonction div() et p()
const {div, p} = Room.elements();
// Création d'un élément <div>
const container = div();
// Création d'un élément <p>
const para = p();
// Ajout d'un attribut style et d'un texte à l'élément <p>
Room.append(para, {style: "color: red"}, "[Fin de page]");
// Ajout de l'élément <p> dans l'élément <div>
Room.append(container, para);
// Ajout de l'élément <div> à la fin de la page
Room.append(document.body, container);
// À partir de là, la fin de la page contient le texte [Fin de page] en rouge
En plus d’ajouter les contenus à l’élément, la fonction append()
s’occupe de déterminer les dépendances des éléments aux données observables créées avec la fonction createData()
et utilisées dans les attributs ou les éléments enfants. Le fonctionnement de cette recherche des dépendances est décrite dans la page la partie Réactivité de la page Fonctionnement.
Les attributs
Les attributs d’un élément sont décrits dans un objet littéral passé à la fonction append()
comme un contenu.
Il peut y avoir plusieurs objets littéraux passés à la fonction append()
et l’ordre dans lequel ils sont passés n’a pas d’importance, sinon qu’en cas de doublon d’un attribut, c’est la dernière valeur qui prime.
Les propriétés de l’objet correspondent aux noms des attributs, la valeur des propriétés deviennent les valeurs des attributs.
Les noms des attributs ne sont pas traités au niveau de la casse sauf si l’attribut concerne un gestionnaire d’évènement. Par contre, un traitement est réalisé pour transformer les caractères _
en -
. Ceci permet par exemple d’écrire en JavaScript un attribut data-params
sous la forme data_params
et non sous la forme "data-params"
, le caractère -
n’étant pas utilisable en JavaScript pour les identifiants.
La valeur d’un attribut peut être donnée par une donnée observable d’une primitive JavaScript ou par une fonction, c’est comme cela que vous pouvez rendre l’attribut réactif si la fonction consulte des données observables. Il est à noter que Room appelle la fonction avec un unique paramètre qui est l‘élément sur lequel l’attribut est appliqué.
Une valeur d’attribut à null
ou undefined
supprime l’attribut.
Les gestionnaires d’évènements
Les gestionnaires d’évènements sont renseignés dans les attributs.
Le nom de l’attribut doit commencer par on
, par exemple onClick
, onSubmit
, onChange
, etc. La casse n’a pas d’importance, Room transforme les noms en minuscule, ce qui n’est pas le cas avec les attributs ordinaires.
La valeur de l’attribut doit être une fonction qui reçoit un unique paramètre qui est un objet Event
.
Si cette fonction retourne false
, Room appelle automatiquement les méthodes preventDefault()
et stopPropagation()
de l’évènement. Cette facilité est par exemple intéressante pour un événement de type submit
dans un formulaire.
Cette fonction n’est pas considérée par Room comme étant réactive, vous pouvez néanmoins rendre réactif l’attribut en utilisant comme valeur une donnée observable d’une fonction que vous modifiez ailleurs en fonction du besoin. Cette méthode permet également de supprimer un gestionnaire d’évènement en donnant la valeur
null
à l’attribut.De plus, il n’est pas possible avec Room d’avoir pour un élément, plusieurs gestionnaires d’un même type d’évènement.
Il est tout a fait possible de définir des gestionnaires d’évènements personnalisés, il suffit juste de respecter la contrainte d’écriture avec on
en début du nom de l’attribut, par exemple onHello
pour un évènement personnalisé de type hello
. Vous pouvez ensuite envoyer un évènement de type hello
à l’élément avec la méthode dispatchEvent()
.
Room gère par ailleurs 4 évènements spéciaux qui ne sont pas des évènements standards :
mount
: L’élément est ajouté dans le DOM de la page.unmount
: L’élément est supprimé du DOM de la page.pageShow
: L’élément redevient visible car la page redevient visible.pageHide
: L’élément devient invisible car la page devient invisible.
Ces évènements spéciaux s’utilisent comme les évènements standards dans les attributs donc en commençant le nom par on
, par exemple onMount
, onPageHide
, etc. avec comme valeur une fonction.
Pour gérer les évènements spéciaux mount
et unmount
, Room met en place un MutationObserver
sur l’élément <body>
de la page, les modifications de l’élément <head>
de la page ne sont donc pas pris en compte.
Pour les évènement spéciaux pageShow
et pageHide
, Room met en place un gestionnaire de l’évènement de type visibilityChange
sur le document. Quand cet évènement intervient, suivant l’état de visibilité de la page, l’un ou l’autre des évènements spéciaux est envoyé aux éléments qui sont dans l’élément <body>
de la page (l’élément <head>
est ignoré) et qui ont un gestionnaire du type de l’évènement spécial.
Au chargement d’une page, l’évènement
pageShow
n’est pas envoyé mais l’évènementmount
l’est dés qu’un élément est ajouté dans la page.
À contrario, l’évènementunmount
n’est pas envoyé quand une page est déchargée ou rechargée mais l’évènementpageHide
l’est.
Évènement mount
Cet événement se produit quand un élément devient connecté au DOM de la page (sa propriété isConnected
devient true
).
En définissant une fonction pour cet événement, cela donne l’opportunité de déclencher des traitements (comme charger des données, démarrer un timer, ouvrir une connexion, etc.) au moment du montage de l’élément dans le DOM.
Évènement unmount
Cet évènement se produit quand un élément n’est plus connecté au DOM de la page (sa propriété isConnected
devient false
).
En définissant une fonction pour cet événement, cela donne l’opportunité de contrebalancer des opérations réalisées au montage (par exemple arrêter un timer, fermer des connexions, etc.) mais aussi de sauvegarder des données.
Des exemples d’utilisation de cet événement sont disponibles dans les pages Liste de tâches à faire et Calcul de Pi.
Évènement pageShow
Cet évènement se produit quand la page devient visible.
En définissant une fonction pour cet événement, cela donne l’opportunité de déclencher des traitements comme relancer un traitement ou une vidéo mise en pause lors d’un évènement pageHide
.
Évènement pageHide
Cet évènement se produit quand la page devient invisible.
En définissant une fonction pour cet événement, cela donne l’opportunité de déclencher des traitements, comme mettre en pause un traitement, une vidéo, etc.
C’est le dernier évènement traité en cas de déchargement d’une page (changement de page ou le navigateur quitte), d’un changement d’onglet dans le navigateur ou d’un changement d’application sur un mobile. C’est donc le bon moment pour par exemple faire une sauvegarde de données, ce qui évite d’en faire à chaque changement des données et donc économise les supports physiques et réduit la consommation d’énergie.
Des exemples d’utilisation de cet événement sont disponibles dans les pages Liste de tâches à faire et Calcul de Pi.
Les éléments enfants
Les éléments enfants sont tous les éléments du paramètre contents
de la fonction append()
qui ne sont pas des objets littéraux.
Ils peuvent être des 3 types suivants (les valeurs null
et undefined
sont ignorées) :
- des textes ou des valeurs numériques (automatiquement converties en texte) et qui deviennent un élément
Text
, - des éléments HTML, SVG ou MathML,
- des objets ayant une clé qui est le symbole standard
Symbol.toPrimitive
.
A cela s’ajoute :
- des fonctions qui doivent retourner des données des 3 types précédents,
- des tableaux qui peuvent contenir des données des 3 types ou des fonctions,
- des expressions JavaScript qui donnent un des 3 types, des fonctions ou des tableaux.
Si une fonction ou une expression retourne un objet littéral ou si un tableau contient un objet littéral alors cet objet est considéré comme un descripteur d’attributs.
Les fonctions sont exécutées par Room en passant un seul paramètre qui est une référence à l’élément parent ou sa propriété isConnected
est à false
. Si la fonction consulte des données observables alors l’élément enfant devient réactif et la fonction est rappelée par Room si une des données consultées est modifiée. Mais dans ce cas, l’unique paramètre passé à la fonction est une référence à l’élément enfant ou sa propriété isConnected
est alors à true
.
Les fonctions peuvent retourner des fonctions qui elles même retournent des fonctions, mais à la fin de l’exécution, il faut une donnée des 3 types précédents ou un tableau.
Les fonctions peuvent être des fonctions asynchrones, mais dans ce cas, comme elle retourne une promesse (objet Promise
), il faut utiliser le mot clé await
pour attendre le résultat, sinon la promesse est transformée en un texte du genre [object Promise]
!
Fonction createData()
Cette fonction permet de créer une donnée observable, c’est à dire que lire ou modifier cette donnée peut être suivi pour déclencher une action. C’est avec ce type de donnée que la réactivité est possible dans une interface utilisateur.
La signature de la fonction est la suivante :
createData(data)
Elle prend en entrée un seul paramètre qui peut être une primitive JavaScript (nombre, chaîne de caractère, booléen, null
, etc.), un tableau ou un objet et retourne une donnée observable qui est en fait un objet Proxy
JavaScript.
Un objet Proxy
ne pouvant traiter que des objets ou des tableaux, une primitive JavaScript est transformé en un objet spécial contenant une seule propriété value
dont la valeur initiale est la valeur de la primitive passée à la fonction. Ceci concerne aussi l es fonctions et les objets natifs (Date
, Element
, Map
, Set
, etc.) qui ne sont donc pas traités comme des objets ce qui veut dire que la lecture ou la modification d’une propriété de ces objets ne sont pas détectées.
L’objet spécial pour les primitive JavaScript contient en plus de la propriété value
un symbole Symbole.toPrimitive
qui retourne la valeur de la propriété value
ce qui permet de simplifier l’utilisation de la donnée observable lors de sa lecture.
Les exemples suivants présentent quelques cas d’utilisation avec des primitives JavaScript :
const n1 = createData(1);
++n1.value;
console.log(n1); // Le Proxy de n1 dans la console
console.log(n1.value); // "2" dans la console
const n2 = createData(2);
n1.value = Math.pow(n2, 2) - 2 * n1;
console.log(n1.value); // "0" dans la console
const s = createData("Hello");
s.value += " World";
console.log(s.value); // "Hello World" dans la console
s.value += n1 + n2;
console.log(s.value); // "Hello World2" dans la console
const d = createData(new Date);
console.log(d.value.toLocaleTimeString()); // L'heure dans la console
Les objets littéraux et les tableaux sont traités comme des objets avec un suivi en profondeur, c’est à dire que la lecture ou la modification d’un sous-objet de l’objet, qui n’est pas un objet natif, est suivie. Par ailleurs, la lecture ou la modification d’une propriété de l’objet ou du tableau se fait naturellement et est suivie.
Les exemples suivants présentent quelques cas d’utilisation avec des objets littéraux et des tableaux :
const o = createData({a: 1, b: 2, c: 0});
o.c = o.a + o.b;
console.log(o.c); // "3" dans la console
o.d = {e: 4, f: 5, g: 0};
o.d.g = o.d["e"] + o.d.f;
console.log(o.d.g); // "9" dans la console
const t = createData([1, 2, 3]);
t.push(o);
console.log(t.length); // "4" dans la console
console.log(t[3].d["g"]); // "9" dans la console
o.c = 0;
console.log(t[3].c); // "0" dans la console
t[3].a = "Hello";
console.log(o.a); // "Hello" dans la console
Il est à noter dans cet exemple, que la donnée observable o
est ajoutée à la donnée observable t
et qu’une modification d’une propriété (c
dans l’exemple) de la donnée o
impacte la donnée observable t
et inversement.
Il est également possible d’utiliser des objets qui sont des instances de vos classes.
Appeler la fonction
createData()
en lui donnant une donnée observable retournera la même donnée observable.
Fonction getData()
Cette fonction permet d’obtenir la valeur d’une donnée.
La signature de la fonction est la suivante :
getData(data)
Elle prend en entrée un unique paramètre.
Si la donnée passée en paramètre est une donnée observable d’un objet littéral, d’un tableau ou d’un objet non natif, la valeur retournée est une copie profonde sans Proxy
de la donnée.
Si la donnée passée en paramètre est un donnée observable d’une primitive ou d’un objet natif, la donnée retournée est juste la valeur de la propriété value
.
Si la donnée n’est pas observable, la fonction rend alors la même donnée qu’en entrée. Ceci est notamment intéressant dans un composant qui reçoit une donnée, en utilisant la fonction getData()
la valeur est récupérée de la donnée qu’elle soit observable ou pas.
Il est possible d’appeler la fonction
getData()
avec un unique paramètre qui est une fonction, elle est alors appelée (sans paramètre) et c’est la donnée retournée par cette fonction qui sera traitée par la fonctiongetData()
.
Quelques exemples :
const d2 = createData({a: 1, b: {c: 2, d: 3}});
const d3 = getData(d2);
console.log(d2); // Un proxy dans la console
console.log(d3); // L'objet {a: 1, b: {c: 2, d: 3}} dans la console
d3.b.c = null;
console.log(d2.b.c); // "2" dans la console, d3 est une copie profonde de d2
Fonction setData()
Cette fonction permet de modifier une donnée.
La signature de la fonction est la suivante :
setData(data, value)
Elle prend en entrée 2 paramètres requis, la donnée et la valeur à attribuer à la donnée.
La donnée du premier paramètre ne sera modifiée que si le types des données sont identiques, le contenu d’un objet ne peut pas être remplacé par un tableau (et vice versa) par exemple.
Si la donnée du premier paramètre est une donnée observable d’un objet littéral, d’un tableau ou d’un objet non natif, les nouvelles données viennent remplacer les anciennes et seront observables (ajout d’un Proxy
en profondeur).
Si la donnée du premier paramètre est un donnée observable d’une primitive ou d’un objet natif, la propriété value
est modifiée si la donnée du deuxième paramètre est aussi une donnée observable d’une primitive ou d’un objet natif ou n’est pas une donnée observable.
Si la donnée du premier paramètre n’est pas une donnée observable, les nouvelles données viennent remplacer les anciennes par une copie profonde sans Proxy
.
Quelques exemples :
const d4 = {a: 1, b: {c: 2, d: 3}};
setData(d4, {x: 1, y: 2});
console.log(d4); // L'objet {x: 1, y: 2} dans la console
const t1 = [1, 2, 3, 4];
setData(t1, [4, 3]);
console.log(t1); // Le tableau [4, 3] dans la console
const d5 = createData({a: 1, b: {c: 2, d: 3}});
setData(d5, {x: 1, y: 2})
console.log(d5); //Un Proxy d'un objet {x:1, y:2} dans la console
Fonction createEffect()
Cette fonction ajoute un effet qui est une fonction qui va être appelée à l’ajout de l’effet et dés qu’une donnée dont dépend l’effet est modifiée.
La signature de la fonction est la suivante :
createEffect(effect, ...dependencies)
Elle prend en entrée au minimum un paramètre requis qui doit être une fonction.
Elle exécute cette fonction immédiatement et détermine les dépendances aux données observables utilisées. Si aucune dépendance n’a été trouvée, la fonction ne sera plus jamais appelée. Par contre, si des dépendances ont été trouvées, la fonction est rappelée chaque fois qu’au moins une donnée observable est modifiée.
Il est possible d’indiquer des dépendances supplémentaires en ajoutant à l’appel de la fonction createEffect()
et après le premier paramètre, des paramètres supplémentaires qui doivent être des données observables. Ceci peut être nécessaire par exemple si vous savez que le premier appel à la fonction ne consulte aucune donnée observable mais que les appels suivant le font.
Les appels suivants à la fonction sont réalisées après la modification de toutes les données et ne génère qu’un seul appel si par exemple 2 données observables sont modifiées dans le même traitement.
La fonction createEffect()
retourne le résultat du premier appel à la fonction passée en paramètre. Si cette fonction est asynchrone, createEffect()
retourne la promesse JavaScript (objet Promise
) retournée par la fonction.
Des exemples d’utilisation de cette fonction sont disponibles dans les pages Composant utilisant une animation et Composant utilisant du chargement asynchrone.
Fonction untrack()
Cette fonction exécute la fonction passée en paramètre en suspendant la recherche des dépendances.
La signature de la fonction est la suivante :
untrack(func)
Elle prend en entrée un unique paramètre requis qui doit être une fonction.
Elle exécute cette fonction immédiatement en désactivant la recherche des dépendances pour le traitement en cours mais pas pour les traitements imbriqués suivants.
Les appels à untrack()
peuvent être imbriqués, c’est à dire que l’appeler avec une fonction qui appelle aussi untrack()
ne pose pas de problème.
La fonction untrack()
retourne le résultat de l’appel à la fonction passée en paramètre. Si cette fonction est asynchrone, untrack()
retourne la promesse JavaScript (objet Promise
) retournée par la fonction.
Fonction onError()
Cette fonction permet de définir une fonction qui est appelée en cas d’erreur.
La signature de la fonction est la suivante :
onError(func)
Room utilise une fonction globale et unique appelée en cas d’erreur. Par défaut c’est la fonction console.error()
qui est utilisée, la fonction onError()
permet de remplacer cette fonction.
La fonction onError()
attend en entrée une fonction qui reçoit en paramètre une erreur qui est un objet Error
. Si la fonction onError()
est appelée sans paramètre ou avec un paramètre qui n’est pas une fonction, elle ne fait rien. La fonction onError()
ne retourne rien.
Exemple d’utilisation :
// Une fonction d'erreur affichant le message d'erreur
function errorFunc(error) {
alert(error.message);
}
// La mise en place de la fonction d'erreur
Room.onError(errorFunc);
Une erreur dans votre fonction d’erreur est interceptée et envoyée dans la console d’erreur.
Fonction map()
Cette fonction permet de synchroniser une donnée observable de type tableau ou objet littéral avec sa représentation en éléments dans un container.
La signature de la fonction est la suivante :
map(container, data, func)
Les paramètres attendus par la fonction maps()
sont :
container
: un objet de la classeElement
qui va contenir la représentation des données,data
: une donnée observable d’un tableau ou d’un objet littéral,func
: une fonction qui doit retourner un objet de la classeElement
représentant un élément des données.
La fonction map()
retourne la valeur du paramètre container
. Les éléments enfants du container sont initialement supprimés et il n’est pas possible d’ajouter d’autres éléments dans ce container, son contenu est entièrement géré par la fonction map()
.
La signature de la fonction attendue dans le troisième paramètre est la suivante :
func(value, key, data)
Les paramètres de cette fonction sont donc :
value
: la valeur de l’élément correspondant à la clékey
dans la donnée observabledata
,key
: la clé (pour un objet) ou l’indice (pour un tableau) de l’élément dans la donnée observabledata
,data
: La donnée observable passée à la fonctionmap()
.
Le paramètres key
est en fait une donnée observable d’une primitive JavaScript créée par la fonction map()
avec la fonction createData()
et contenant un entier si la donnée data
est un tableau et une chaine de caractères si la donnée data
est un objet. Pour un tableau c’est donc l’indice de l’élément dans le tableau et pour un objet c’est la clé de l’objet donnant l’élément.
Cette fonction est appelée par la fonction map()
dés qu’elle en a besoin et doit impérativement retourner un objet de la classe Element
.
Attention si vous utilisez un tableau comme donnée et que vous supprimez un élément avec l’opérateur
delete
et non pas avec la méthodesplice()
, un trou est créé dans le tableau. C’est à dire que l’indice existe toujours mais avec la valeur associéeundefined
. Dans ce cas la fonctionmap()
appelle la fonction du paramètrefunc
avec un paramètrevalue
qui estundefined
, à vous alors de néanmoins rendre un objet de la classeElement
.
Vous pouvez utiliser la fonction map()
quand vous voulez représenter une liste d’éléments et que cette liste peut évoluer (ajout, suppression ou permutation d’éléments pour un tableau). Elle n’a pas d’intérêt si la liste n’a pas d’évolution, l’utilisation de la fonction map()
pour un tableau ou entries()
pour un objet est suffisante dans ce cas, même si le contenu des éléments eux évoluent.
Un exemple d’utilisation de cette fonction est disponible dans la page Liste de tâches à faire.
Dernière mise à jour :