From f903a8caee4426528754388b58ba61da2ba3b01d Mon Sep 17 00:00:00 2001 From: Damien Date: Mon, 29 Jan 2024 00:08:09 +0100 Subject: [PATCH] Maj doc intro + modules basics --- README.md | 81 +++++++++++- packages/docs/basics/01-basics.mdx | 7 -- packages/docs/basics/01-messages.mdx | 32 +++++ packages/docs/basics/02-fetch.mdx | 56 +++++++++ packages/docs/basics/02-messages.mdx | 13 -- packages/docs/basics/03-fetch.mdx | 20 --- packages/docs/basics/03-translation.mdx | 67 ++++++++++ packages/docs/basics/04-reference.mdx | 118 ++++++++++++------ packages/docs/basics/05-translation.mdx | 9 -- packages/docs/basics/06-user-store.mdx | 7 -- packages/docs/main.mdx | 69 +++++++--- packages/docs/other/01-focus4.mdx | 15 +-- .../{02-legacy.mdx => 02-legacy/01-forms.mdx} | 45 ++----- .../02-legacy/02-routing.mdx} | 6 +- packages/stores/src/reference/store.ts | 1 + packages/toolbox/src/components/font-icon.tsx | 2 +- 16 files changed, 388 insertions(+), 160 deletions(-) delete mode 100644 packages/docs/basics/01-basics.mdx create mode 100644 packages/docs/basics/01-messages.mdx create mode 100644 packages/docs/basics/02-fetch.mdx delete mode 100644 packages/docs/basics/02-messages.mdx delete mode 100644 packages/docs/basics/03-fetch.mdx create mode 100644 packages/docs/basics/03-translation.mdx delete mode 100644 packages/docs/basics/05-translation.mdx delete mode 100644 packages/docs/basics/06-user-store.mdx rename packages/docs/other/{02-legacy.mdx => 02-legacy/01-forms.mdx} (70%) rename packages/docs/{routing/06-legacy.mdx => other/02-legacy/02-routing.mdx} (96%) diff --git a/README.md b/README.md index 4a0c607d..243baae3 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,83 @@ # Présentation -**`Focus v4`** est en ensemble de modules conçu pour essayer de simplifier et généraliser les réponses aux besoins récurrents de réalisation de SPA avec **[React](http://www.reactjs.org)** (bien que certains d'entres-eux pourraient être utilisés avec d'autres frameworks comme Angular ou Vue). Ces modules s'appuient fortement sur **[Typescript](http://www.typescriptlang.org)** et **[MobX](http://mobx.js.org)**. Il est vivement encouragé d'être au moins familier avec ces technologies avant de continuer. +**`Focus`** est un framework **[React](https://react.dev/)** modulaire conçu pour accélérer le développement d'application clientes en mettant à disposition un ensemble de +fonctionnalités évoluées conçues pour fonctionner ensemble. Il essaie de fournir une alternative intégrée crédible aux projets React classiques qui accumulent +un très grand nombre de librairies tierces et essaient tant bien que mal de les faire cohabiter. -`Focus v4` peut être utilisé avec toute sorte de stack technique côté API. Il s'attend néanmoins à un format spécifique pour interpréter les erreurs et nécessite une API spécifique pour utiliser le module de recherche avancée. De plus, il est conseillé de pouvoir générer les différents fichiers de modèle utilisés par les formulaires. [Vertigo](http://www.github.com/KleeGroup/vertigo) et [Kinetix](http://www.github.com/KleeGroup/kinetix-tools) répondent à tous ces besoins (en Java et C#, respectivement), mais il est tout à fait abordable de s'en passer. +Par conception, et à l'inverse de la quasi-totalité des frameworks React récents, `Focus` ne se préoccupe pas de votre serveur et n'essaie pas de s'y +imposer ou de vous forcer à maintenir un serveur Node quelque part. C'est un framework "100% front" qui se package avec n'importe quel bundler comme une +application statique (des fichiers HTML, CSS, JS, des images...) et peut se servir par n'importe quel serveur ou système de stockage (S3, Blob Storage...). +`Focus` pourra néanmoins vous demander d'implémenter quelques APIs spécifiques côté serveur pour utiliser certaines de ces fonctionnalités. -[La documentation est disponible ici](https://klee-contrib.github.io/focus4) +Néanmoins, pour maximiser votre productivité avec `Focus`, il est conseillé d'utiliser un outil de génération de code comme [TopModel](https://klee-contrib.github.io/topmodel). -# Starter Kit +`Focus` utilise extensivement [`TypeScript`](https://www.typescriptlang.org/) et [`MobX`](https://mobx.js.org/) pour réaliser la plupart de ses fonctionnalités. Il est recommandé d'être familier avec ces deux +technologies (et `React` bien sûr) avant de pouvoir commencer avec. -Vous pouvez commencer un projet en utilisant le [starter kit](http://www.github.com/KleeGroup/focus4-starter-kit), qui sert également de démo et présente les usages les plus courants de focus4. +## `focus4` + +Ce framework est la 4ème itération du framework `Focus`, et la seule version maintenue depuis 2017. Il est publié sur [npm](https://www.npmjs.com/package/focus4) sous le nom `focus4` pour le +méta-package et dans le scope `@focus4/xxx` pour les différents modules. + +Le framework lui-même est aujourd'hui dans sa version majeure **11** (`focus4 v11.x`). + +--- + +## Que fait Focus ? + +Focus essaie de simplifier le développement d'une application front en proposant des modules qui adressent les principaux besoins d'une telle application : + +- Gestion des requêtes et des messages +- Mise en page +- Navigation +- Affichage et gestion de listes (avec recherche avancée) +- Composants de saisie basiques +- Gestion des données métiers et formulaires +- Gestion du CSS + +L'usage de toutes ces fonctionnalités est toujours **optionnel**. Vous ferez toujours des composants React et vous aurez toujours MobX sous la main, donc si Focus ne peut pas vous aider à faire ce que vous voulez, vous pouvez toujours revenir vers des choses plus classiques. + +## Les différents modules + +`Focus` est divisé en **7 (+2) modules** NPM, que l'on peut regrouper dans les catégories suivantes : + +### Modules de base + +Ces modules contiennent les éléments de base d'une application Focus, et servent de fondations aux modules plus avancés. + +- **`@focus4/core`** : fonctionnalités de base, utilisées dans les autres modules. +- **`@focus4/styling`** : système de CSS utilisé par les composants de Focus. +- **`@focus4/toolbox`** : composants de base implémentant [Material Design 3](https://m3.material.io/components), utilisé par les autres composants plus avancés. + +### Modules de formulaires + +Ces deux modules permettent de construire des formulaires, et représentent donc le coeur d'une application Focus. C'est avec ces deux modules-là que vous passerez le plus de temps. + +- **`@focus4/stores`** : gestion des stores de formulaires, collections et de référence. +- **`@focus4/forms`** : composants de formulaires. + +### Modules de présentation + +Ces deux modules proposent des composants graphiques de haut niveau qui permettent de structurer la mise en page d'une application Focus, ainsi que l'affichage des listes et de la recherche avancée. + +- **`@focus4/layout`**: composants de mise en page. +- **`@focus4/collections`**\_ : composants de listes et de recherche avancée. + +### Autres modules + +- **`@focus4/legacy`** : repackage des fonctionnalités d'anciennes versions Focus qui n'ont pas d'équivalent direct dans la version actuelle. +- **`focus4`** : méta-package contenant tous les autres (sauf `legacy`). + +### `@focus4/tooling` + +Le module `@focus4/tooling` est lui aussi un méta-package qui contient l'ensemble des outils nécessaires pour packager une application Focus. +En particulier, il inclut [Vite](https://vitejs.dev) et [ESLint](https://eslint.org/) et des configs par défaut à étendre pour ces outils. + +De plus, il contient l'outil de génération de types CSS. + +## Starter Kit + +Vous pouvez commencer un projet en utilisant le [starter kit](http://www.github.com/klee-contrib/focus4-starter-kit), qui sert également de démo et présente les usages les plus courants de Focus. + +La version packagée est également disponible [ici](https://focus4-starter-kit.fly.io). diff --git a/packages/docs/basics/01-basics.mdx b/packages/docs/basics/01-basics.mdx deleted file mode 100644 index 76a40d4b..00000000 --- a/packages/docs/basics/01-basics.mdx +++ /dev/null @@ -1,7 +0,0 @@ -import {Meta} from "@storybook/blocks"; - - - -# Fonctionnalités de base - -Ces fonctionnalités, toutes disponibles dans le module `@focus4/core` (sauf le store de référence dans `@focus4/stores`), sont les briques de base de toute application Focus et seront réutilisées par tous les autres modules. diff --git a/packages/docs/basics/01-messages.mdx b/packages/docs/basics/01-messages.mdx new file mode 100644 index 00000000..a365c582 --- /dev/null +++ b/packages/docs/basics/01-messages.mdx @@ -0,0 +1,32 @@ +import {Meta} from "@storybook/blocks"; + + + +# Gestion des messages + +Les messages dans une application Focus sont gérés par le **`messageStore`**. + +Par défaut, tout message envoyé dans ce store sera transféré au **`MessageCenter`** (posé par le `Layout` du module `@focus4/layout`, qui les affichera dans +une [`Snackbar`](/docs/composants-focus4∕toolbox-snackbar--docs), en bas de l'application, en les dépilant un par un. + +4 types de messages sont préconfigurés : + +- Succès (en vert) +- Erreur (en rouge) +- Avertissement (en jaune) +- Information (en noir/blanc, selon le thème). + +D'autres types de messages arbitraires peuvent être enregistrés, mais ils ne seront pas captés par le `MessageCenter` s'il n'est pas reconfiguré pour. + +## Enregistrer un message + +Le `messageStore` expose une méthode `messageStore.addMessage(type, message)` pour ajouter un message, et une surcharge par type de message +préconfiguré (`addSuccessMessage`, `addErrorMessage`...) + +De plus, la méthode `messageStore.addMessages(messages)` est disponible, ou `messages` est un objet JS dont les clés sont les types de messages et les +valeurs les messages (unitaire ou en liste). Les types de messages récoltés dans cet objet peuvent être configurés via `messageStore.messageTypes`, par +défaut limité aux 4 types préconfigurés `error`, `warning`, `info` et `success`. 4 variantes sur le nom de la clé sont supportées : par exemple pour un type +`error`, on cherchera dans les propriétés `error`, `errors`, `globalError` et `globalErrors`. + +Par défaut, tous les formulaires (du module `@focus4/stores`) envoient des messages de succès lorsqu'une sauvegarde est réalisée avec succès, et toute +[requête](/docs/les-bases-gestion-des-requêtes--docs) en erreur envoie des messages d'erreurs contenant leurs détails. diff --git a/packages/docs/basics/02-fetch.mdx b/packages/docs/basics/02-fetch.mdx new file mode 100644 index 00000000..8ec88309 --- /dev/null +++ b/packages/docs/basics/02-fetch.mdx @@ -0,0 +1,56 @@ +import {Meta} from "@storybook/blocks"; + + + +# Gestion des requêtes + +## `coreFetch` + +Focus propose un wrapper à [`window.fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) appelé **`coreFetch`**, qui a pour vocation de simplifier son usage dans les cas courants et de traiter automatiquement les erreurs. + +Son API est la suivante : + +**`coreFetch(method, url, {body?, query?}, options?)`** + +- **`method`** permet de renseigner le verbe HTTP (GET, POST, PUT, DELETE...) de la requête. +- **`url`** permet de renseigner l'URL que l'on veut appeler. +- **`{body?, query?}`** permettent de renseigner le contenu du _body_ de la requête (pour un POST ou un PUT), ainsi que les _query params_ qui devront être + ajoutés à l'URL. La méthode s'occupera d'inclure les _query params_ à l'URL et gère donc leur caractère optionnel. +- **`options`** est le paramètre d'options de `window.fetch`. Cet objet d'options est prérempli par `coreFetch` pour y inclure ce qu'on a déjà défini (la + méthode et le body en particulier), mais il est surchargeable via ce paramètre. + +Si `coreFetch` reçoit une erreur et que le corps de la réponse est un JSON, alors cette réponse sera envoyée au [`messageStore`](/docs/les-bases-gestion-des-messages--docs) en appelant sa méthode +`addMessages`. Pour assurer une intégration native avec la gestion de messages Focus, les APIs appelées devront renvoyer des réponses de la forme. +`{error: "Message d'erreur"}` ou `{errors: ["Message 1", "Message 2"]}`. + +## `RequestStore` + +Le `RequestStore` est un store dédié au suivi des requêtes en cours dans l'application. + +### Suivi automatique des requêtes + +Par défaut, toute requête faite avec `coreFetch` est automatiquement suivie dans ce store. Les requêtes en cours peuvent se récupérer dans +`requestStore.pending` et la propriété `requestStore.loading` permet de savoir s'il y au moins une requête encore en cours. Cette propriété peut être +utilisée directement pour poser un "spinner" global sur votre application à moindre frais. + +### Suivi personnalisé de services + +Le `requestStore` dispose également d'une API pour suivre le statut de vos propres services (n'importe quelle fonction retournant une `Promise`) via +`requestStore.track` : + +```ts +const id = useId(); // ou v4() du package "uuid" si vous n'est pas dans un composant + +/* ---- */ + +const user = await requestStore.track(id, () => getUser(id)); // Enregistre le service sur cet ID et l'appelle + +/* ---- */ + +requestStore.isLoading(id); // La requête est-elle en cours ? +``` + +**Plusieurs services peuvent être enregistré sur le même ID de suivi**, ce qui permet de récupérer facilement un état de chargement sur plusieurs services à la +fois. + +Ce suivi est automatiquement intégré à `useLoad` et `useFormActions`, qui génèrent un ID de suivi pour leurs services et peuvent y associer d'autres IDs. diff --git a/packages/docs/basics/02-messages.mdx b/packages/docs/basics/02-messages.mdx deleted file mode 100644 index 7ea679e3..00000000 --- a/packages/docs/basics/02-messages.mdx +++ /dev/null @@ -1,13 +0,0 @@ -import {Meta} from "@storybook/blocks"; - - - -# Gestion des messages - -Les messages dans une application Focus sont gérés par le **`messageStore`**. Tout message envoyé dans ce store sera transféré au **`MessageCenter`** (posé par le `Layout` du module `@focus4/layout`, qui les affichera dans une "Snackbar", en bas de l'application. - -Les messages peuvent être des messages de succès (affichés en vert), d'erreur (affichés en rouge), ou des warnings (affichés en jaune). Les couleurs sont personnalisables via le module `@focus4/styling`. - -Par défaut, tous les formulaires (du module `@focus4/stores`) envoient des messages de succès lorsqu'une sauvegarde est réalisée avec succès, et toute requête en erreur (voir paragraphe suivant) envoie des messages d'erreurs contenant leurs détails. - -Plusieurs messages peuvent être envoyés en même temps ou à suivre, ils seront dépilés un par un par le `MessageCenter`. diff --git a/packages/docs/basics/03-fetch.mdx b/packages/docs/basics/03-fetch.mdx deleted file mode 100644 index 7c7751e8..00000000 --- a/packages/docs/basics/03-fetch.mdx +++ /dev/null @@ -1,20 +0,0 @@ -import {Meta} from "@storybook/blocks"; - - - -# Gestion des requêtes - -Focus propose un wrapper à [`window.fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) appelé **`coreFetch`**, qui a pour vocation de simplifier son usage dans les cas courants et de traiter automatiquement les erreurs. - -Son API est la suivante : - -**`coreFetch(method, url, {body?, query?}, options?)`** - -- **`method`** permet de renseigner le verbe HTTP (GET, POST, PUT, DELETE...) de la requête -- **`url`** permet de renseigner l'URL que l'on veut appeler -- **`{body?, query?}`** permettent de renseigner le contenu du _body_ de la requête (pour un POST ou un PUT), ainsi que les _query params_ qui devront être ajoutés à l'URL. La méthode s'occupera d'inclure les _query params_ à l'URL et gère donc leur caractère optionnel -- **`options`** est le paramètre d'options de `window.fetch`. Cet objet d'options est prérempli par `coreFetch` pour y inclure ce qu'on a déjà défini (la méthode et le body en particulier), mais il est surchargeable via ce paramètre. - -Cette méthode est accompagnée du **`requestStore`**, qui permettra de suivre la progression de toutes les requêtes. Il pourra être utilisé pour afficher un "spinner" global dans l'application pour indiquer qu'une requête est en cours, ou à fin de debug. - -Si `coreFetch` reçoit une erreur et que le corps de la réponse est un JSON, alors le contenu des messages inclus dans `globalErrors` (ou `globalWarnings`) sera poussé dans le `messageStore`. diff --git a/packages/docs/basics/03-translation.mdx b/packages/docs/basics/03-translation.mdx new file mode 100644 index 00000000..16b3a91b --- /dev/null +++ b/packages/docs/basics/03-translation.mdx @@ -0,0 +1,67 @@ +import {Meta} from "@storybook/blocks"; + + + +# Libellés et icônes + +Focus utilise [`i18next`](https://www.i18next.com/) pour gérer les libellés et les icônes utilisés à travers ses différents composants. Vous êtes largement encouragés à l'utiliser pour vos +propres libellés également. + +En ce qui concerne Focus, chaque module expose son propre objet de traductions (réexporté dans le méta-package `focus4` en un seul objet), à utiliser dans +l'initialisation d'`i18next`. + +Par exemple : + +```ts +import {translation as focus} from "focus4"; +import i18next from "i18next"; + +i18next.init({lng: "fr", resources: {fr: {translation: {focus}}}}); +``` + +Focus ne dispose (pour l'instant...) que de traductions en français (n'hésitez pas à faire une PR avec au moins la version anglaise... 😉) + +## Surcharge de libellés + +Chaque traduction utilisée dans un composant est récupérée sous la forme `${i18nPrefix}.module.component.label`, ou `i18nPrefix` est une prop du +composant qui vaut `"focus"` par défaut. Le fichier de traduction de Focus contient donc `focus.module.component.label`, et vous pouvez redéfinir cette +traduction en renseignant `custom.module.component.label` dans l'initialisation d'i18n et `custom` comme valeur d'`i18nPrefix`. + +Pour s'assurer de garder toutes les autres traductions par défaut, vous pouvez la définir de cette façon : + +```ts +i18next.init({ + lng: "fr", + resources: { + fr: { + translation: { + focus, + custom: { + ...focus, + module: {...focus.module, component: {...focus.module.component, label: "Ma valeur custom"}} + } + } + } + } +}); + +// Ou bien utiliser `merge` de `lodash` : +custom = merge(focus, {module: {component: {label: "Ma valeur custom"}}}); +``` + +Vous pouvez bien sûr utiliser cette stratégie pour surcharger un libellé de `focus` pour qu'elle s'applique dans toute l'application. + +## Icônes + +Les icônes sont également définies dans les traductions de Focus. Elle sont toutes dans l'objet `icons` qui est à la racine de l'objet `focus` (ou la valeur +de `i18nPrefix`). Par exemple `focus.icons.list.add` correspond à l'icône du bouton "Voir plus" des listes, tandis que le libellé est dans +`focus.list.show.more`. + +Une icône se définit avec un **nom** et une **classe CSS**, qui permet de retrouver la police d'icônes associée, et se pose avec le composant [`FontIcon`](/docs/composants-focus4∕toolbox-fonticon--docs). La classe +CSS est faculative car il existe une classe par défaut, définie dans `config.defaultIconClassName` (et vaut `"material-icons"`). + +La clé i18n devra donc pointer vers un objet `{name, className?}` (avec `className` facultatif). Tout ce qui a été présenté pour la surcharge de libellé +s'applique naturellement aussi pour les icônes. + +_Remarque : la classe CSS de l'icône peut également être un template du nom de l'icône, par exemple `icon-{name}` pour avoir une classe `"icon-home"` +pour `"home"`, ce qui est utile si la police d'icône ne fonctionne pas directement avec le nom de l'icône et qu'il faut une classe CSS dédiée par icône._ diff --git a/packages/docs/basics/04-reference.mdx b/packages/docs/basics/04-reference.mdx index 26004214..781c8375 100644 --- a/packages/docs/basics/04-reference.mdx +++ b/packages/docs/basics/04-reference.mdx @@ -1,64 +1,112 @@ import {Meta} from "@storybook/blocks"; - + -# Store de références +# Store de référence -## Présentation +Focus permet de définir un **store de référence** pour contenir des listes de référence. -Un `ReferenceStore` est construit par la fonction `makeReferenceStore(referenceLoader, refConfig)` : +## Listes de référence -- `referenceLoader` est une fonction qui prend en paramètre un nom de référence et la liste de référence (qui renvoie donc une Promise) -- `refConfig` est un objet dont les propriétés sont des définitions de listes de référence, à priori générés avec le reste du modèle. Ce sont des objets de la forme `{type, valueKey, labelKey}` qui servent à définir totalement comme la référence doit s'utiliser. +Une **liste de référence** correspond (en général) à une table en base de données qui contient un ensemble de lignes fixes (ou qui changent rarement), que l'on +voudra utiliser régulièrement pour résoudre des libellés à partir d'un clé primaire (utilisée comme clé étrangère). -Un store de référence se construit de la manière suivante : +Une liste de référence est généralement décrite de cette manière pour Focus : ```ts -const referenceStore = makeReferenceStore(referenceLoader, { - product, - line +type TypeDroitCode = "ADMIN" | "READ" | "WRITE"; + +interface TypeDroit { + // La clé primaire n'est pas forcément une union (`string` ou `number` sont possibles) + code: TypeDroitCode; + libelle: string; +} + +// `valueKey` est la propriété de l'objet qui représente la clé primaire +// `labelKey` est la propriété de l'objet qui représente le libellé +const typeDroit = {type: {} as TypeDroit, valueKey: "code", labelKey: "libelle"}; +``` + +--- + +## `makeReferenceStore` + +La fonction `makeReferenceStore(referenceLoader, refConfig, referenceClearer?)` permet de créer un **store de listes de référence**, pour contenir +des listes à charger depuis le serveur. Elle prend comme paramètres : + +- `referenceLoader`, une fonction qui prend en paramètre un nom de référence et renvoie la liste de référence (dans une Promise). +- `refConfig`, un objet dont les propriétés sont des définitions de listes de référence, dont le format est vu au dessus (`{type, valueKey, labelKey}`). +- `referenceClearer`, une fonction facultative qui permet de réinitialiser un éventuel cache côté serveur pour une liste de référence. + +Exemple de store de référence : + +```ts +const referenceStore = makeReferenceStore(name => coreFetch("GET", `api/references/${name}`), { + droit: droit, + typeDroit: typeDroit, // C'est le même objet qui a été décrit sur la slide précédente + typeUtilisateur: typeUtilisateur }); ``` -Le `referenceStore` résultant peut être utilisé tel quel dans un composant `observer`: lorsqu'on veut récupérer une liste de références, le store regarde dans le cache et renvoie la valeur s'il la trouve. Sinon, il lance le service de chargement qui mettra à jour le cache et renvoie une liste vide. Une fois la liste chargée, l'observable sera modifiée et les composants mis à jour automatiquement. +Les listes de référence peuvent ensuite être lues via `referenceStore.droit` / `referenceStore.typeDroit` / `referenceStore.typeUtilisateur`. + +**Les listes de référence sont chargées automatiquement lors de leur première lecture** et sont gardées en cache pour la suite. Ce sont des arrays observables +initialement vides qui notifieront la dérivation/réaction qui les utilise une fois que la liste sera chargée. -Exemple d'usage : +Cela s'intègre en particulier nativement dans le rendu d'un composant React : ```tsx -@observer -class View extends Component { - render() { - return ( - - ); - } +function Component() { + return useObserver(() => ( + + )); } ``` -Ce composant sera initialement rendu 3 fois: +Ce composant sera initialement rendu 3 fois : -- La première fois, les deux utilisations de `product` et de `line` vont lancer les appels de service (les deux listes sont vides) +- La première fois, les deux utilisations de `droit` et de `typeDroit` vont lancer les appels de service (les deux listes sont vides). - La deuxième fois, l'une des deux listes aura été chargée et sera affichée. - La troisième fois, l'autre liste aura également été chargée et les deux seront affichées. -Les fois suivantes (dans la mesure que les listes sont toujours en cache), il n'y aura qu'un seul rendu avec les deux listes déjà chargées. +### Autres APIs de `ReferenceStore` + +- `referenceStore.isLoading` permet de savoir s'il y a au moins une liste de référence en cours de chargement. +- `referenceStore.reload(ref?)` permet de forcer le rechargement d'une liste de référence (après une modification par exemple). Cela appellera + également le `referenceClearer` s'il a été défini. +- `referenceStore.get(ref)` permet de récupérer une liste de référence, en appelant le service de chargement si la liste n'est pas chargée. Cette fonction + est asynchrone (car elle peut appeler le serveur) et sert à récupérer une liste de référence de manière certaine en dehors d'une réaction. +- `referenceStore.track(trackingId, ...refNames)` permet d'ajouter un [ID de suivi](/docs/les-bases-gestion-des-requêtes--docs) sur des listes de référence demandées (ou toutes si `refNames` + est vide). Cela permet d'ajouter l'état de chargement de listes de référence à un autre état préexistant. + + `referenceStore.track` renvoie une fonction pour retirer l'ID de suivi des listes de référence. Pour une intégration simplifiée dans un composant React, + utiliser le hook `useReferenceTracking` : + + ```ts + const id = useId(); + useReferenceTracking(id, referenceStore, "droit", "typeDroit"); + const isLoading = requestStore.isLoading(id); + ``` ## `ReferenceList` Une `ReferenceList` est une liste contenue dans un `ReferenceStore`. En plus d'être une liste observable classique, elle a contient aussi : -- Une propriété `$valueKey` qui correspond au nom de la propriété des objets de la liste qui sera utilisée comme valeur -- Une propriété `$labelKey` qui correspond au nom de la propriété des objets de la liste qui sera utilisée comme libellé -- Une fonction `getLabel(value)` qui permet de résoudre une valeur -- Une fonction `filter()` modifiée qui permet de retourner une nouvelle `ReferenceList` avec les mêmes propriétés (au lieu d'un array classique qui n'aurait plus `$valueKey`/`$labelKey`/`getLabel()`); +- Une propriété `$valueKey` qui correspond au nom de la propriété des objets de la liste qui sera utilisée comme valeur. +- Une propriété `$labelKey` qui correspond au nom de la propriété des objets de la liste qui sera utilisée comme libellé. +- Une fonction `getLabel(value)` qui permet de résoudre une valeur. +- Une fonction `filter()` modifiée qui permet de retourner une nouvelle `ReferenceList` avec les mêmes propriétés (au lieu d'un array classique qui + n'aurait plus `$valueKey`/`$labelKey`/`getLabel()`); -## `makeReferenceList(list, {valueKey, labelKey})` +### `makeReferenceList(list, {valueKey, labelKey})` -Cette fonction permet de transformer une liste classique en une liste de référence. Elle contrôlera que `valueKey` et `labelKey` existent. +Cette fonction permet de transformer une liste classique en une liste de référence. Elle contrôlera que `valueKey` et `labelKey` existent, et dans le cas +contraire demandera de les renseigner. Un grand nombre d'APIs et composants Focus demandent des listes de référence en entrée, donc cette fonction pourra +être utilisée pour les utiliser avec des listes classiques (en particulier des listes à ne pas mettre en cache qui seront chargées "normalement"). diff --git a/packages/docs/basics/05-translation.mdx b/packages/docs/basics/05-translation.mdx deleted file mode 100644 index e3265696..00000000 --- a/packages/docs/basics/05-translation.mdx +++ /dev/null @@ -1,9 +0,0 @@ -import {Meta} from "@storybook/blocks"; - - - -# Gestion des libellés - -L'ensemble des libellés de tous les modules de Focus est contenu dans `translation`. Il n'y a qu'une version en français de disponible, n'hésitez pas à ouvrir une PR pour les autres langues (genre l'anglais)... :) - -Toutes les icônes utilisées par Focus sont également décrites dans les fichiers de traduction, ce qui permet de les surcharger. De plus, chaque composant qui utilise des traductions expose une propriété **`i18nPrefix`**, qui est toujours renseignée par défaut à `focus`, qui définit à quel endroit du fichier de traduction il faut chercher les libellés et les icônes. Il est donc possible, pour un composant en particulier, de modifier les libellés et icônes qui y sont utilisées. En général, on remplace quelques entrées, puis on recomplète par les libellés et icônes par défaut. diff --git a/packages/docs/basics/06-user-store.mdx b/packages/docs/basics/06-user-store.mdx deleted file mode 100644 index ca51598b..00000000 --- a/packages/docs/basics/06-user-store.mdx +++ /dev/null @@ -1,7 +0,0 @@ -import {Meta} from "@storybook/blocks"; - - - -# Store d'utilisateur - -Focus propose une base de store utilisateur, pour y stocker les données de la session. La seule fonctionnalité prévue est la gestion des rôles / permissions (avec `roles` et `hasRole()`), et c'est à chaque application d'y rajouter leurs informations métiers pertinentes. diff --git a/packages/docs/main.mdx b/packages/docs/main.mdx index a91bc839..b0aed910 100644 --- a/packages/docs/main.mdx +++ b/packages/docs/main.mdx @@ -4,49 +4,86 @@ import {Meta} from "@storybook/blocks"; # Présentation -**`Focus v4`** est en ensemble de modules conçu pour essayer de simplifier et généraliser les réponses aux besoins récurrents de réalisation de SPA avec **[React](http://www.reactjs.org)** (bien que certains d'entres-eux pourraient être utilisés avec d'autres frameworks comme Angular ou Vue). Ces modules s'appuient fortement sur **[Typescript](http://www.typescriptlang.org)** et **[MobX](http://mobx.js.org)**. Il est vivement encouragé d'être au moins familier avec ces technologies avant de continuer. +**`Focus`** est un framework **[React](https://react.dev/)** modulaire conçu pour accélérer le développement d'application clientes en mettant à disposition un ensemble de +fonctionnalités évoluées conçues pour fonctionner ensemble. Il essaie de fournir une alternative intégrée crédible aux projets React classiques qui accumulent +un très grand nombre de librairies tierces et essaient tant bien que mal de les faire cohabiter. -`Focus v4` peut être utilisé avec toute sorte de stack technique côté API. Il s'attend néanmoins à un format spécifique pour interpréter les erreurs et nécessite une API spécifique pour utiliser le module de recherche avancée. De plus, il est conseillé de pouvoir générer les différents fichiers de modèle utilisés par les formulaires. [Vertigo](http://www.github.com/KleeGroup/vertigo) et [Kinetix](http://www.github.com/KleeGroup/kinetix-tools) répondent à tous ces besoins (en Java et C#, respectivement), mais il est tout à fait abordable de s'en passer. +Par conception, et à l'inverse de la quasi-totalité des frameworks React récents, `Focus` ne se préoccupe pas de votre serveur et n'essaie pas de s'y +imposer ou de vous forcer à maintenir un serveur Node quelque part. C'est un framework "100% front" qui se package avec n'importe quel bundler comme une +application statique (des fichiers HTML, CSS, JS, des images...) et peut se servir par n'importe quel serveur ou système de stockage (S3, Blob Storage...). +`Focus` pourra néanmoins vous demander d'implémenter quelques APIs spécifiques côté serveur pour utiliser certaines de ces fonctionnalités. + +Néanmoins, pour maximiser votre productivité avec `Focus`, il est conseillé d'utiliser un outil de génération de code comme [TopModel](https://klee-contrib.github.io/topmodel). + +`Focus` utilise extensivement [`TypeScript`](https://www.typescriptlang.org/) et [`MobX`](https://mobx.js.org/) pour réaliser la plupart de ses fonctionnalités. Il est recommandé d'être familier avec ces deux +technologies (et `React` bien sûr) avant de pouvoir commencer avec. + +## `focus4` + +Ce framework est la 4ème itération du framework `Focus`, et la seule version maintenue depuis 2017. Il est publié sur [npm](https://www.npmjs.com/package/focus4) sous le nom `focus4` pour le +méta-package et dans le scope `@focus4/xxx` pour les différents modules. + +Le framework lui-même est aujourd'hui dans sa version majeure **11** (`focus4 v11.x`). Cette documentation n'a vocation qu'à documenter la dernière version +de `focus4`. Si vous utilisez une version plus ancienne, vous pouvez quand même vous baser sur cette documentation puisqu'il n'y a pas de différences +fondamentales à signaler (hormis ce qui est documenté dans `@focus4/legacy`). Attendez-vous à avoir des fonctionnalités en moins ou avec des APIs moins +fonctionnelles, tout de même 😁. + +--- + +## Que fait Focus ? + +Focus essaie de simplifier le développement d'une application front en proposant des modules qui adressent les principaux besoins d'une telle application : + +- Gestion des requêtes et des messages +- Mise en page +- Navigation +- Affichage et gestion de listes (avec recherche avancée) +- Composants de saisie basiques +- Gestion des données métiers et formulaires +- Gestion du CSS + +L'usage de toutes ces fonctionnalités est toujours **optionnel**. Vous ferez toujours des composants React et vous aurez toujours MobX sous la main, donc si Focus ne peut pas vous aider à faire ce que vous voulez, vous pouvez toujours revenir vers des choses plus classiques. ## Les différents modules -`Focus v4` est divisé en **7 (+2) modules** NPM, que l'on peut regrouper dans les catégories suivantes (_(R)_ indique que le module a une dépendance à React) : +`Focus` est divisé en **7 (+2) modules** NPM, que l'on peut regrouper dans les catégories suivantes : ### Modules de base Ces modules contiennent les éléments de base d'une application Focus, et servent de fondations aux modules plus avancés. - **`@focus4/core`** : fonctionnalités de base, utilisées dans les autres modules. -- **`@focus4/styling`**_(R)_ : système de CSS utilisé par les composants de Focus. -- **`@focus4/toolbox`**_(R)_ : repackaging de [react-toolbox](http://www.react-toolbox.io), pour une intégration optimale avec le reste des modules. +- **`@focus4/styling`** : système de CSS utilisé par les composants de Focus. +- **`@focus4/toolbox`** : composants de base implémentant [Material Design 3](https://m3.material.io/components), utilisé par les autres composants plus avancés. ### Modules de formulaires Ces deux modules permettent de construire des formulaires, et représentent donc le coeur d'une application Focus. C'est avec ces deux modules-là que vous passerez le plus de temps. - **`@focus4/stores`** : gestion des stores de formulaires, collections et de référence. -- **`@focus4/forms`**_(R)_ : composants de formulaires. +- **`@focus4/forms`** : composants de formulaires. ### Modules de présentation Ces deux modules proposent des composants graphiques de haut niveau qui permettent de structurer la mise en page d'une application Focus, ainsi que l'affichage des listes et de la recherche avancée. -- **`@focus4/layout`**_(R)_ : composants de mise en page. -- **`@focus4/collections`**_(R)_ : composants de listes et de recherche avancée. +- **`@focus4/layout`**: composants de mise en page. +- **`@focus4/collections`**\_ : composants de listes et de recherche avancée. ### Autres modules -- **`@focus4/legacy`**_(R)_ : compatibilité avec d'anciennes versions de Focus ( < 9). -- **`focus4`**_(R)_ : méta-package contenant tous les autres (sauf `legacy`). +- **`@focus4/legacy`** : repackage des fonctionnalités d'anciennes versions Focus qui n'ont pas d'équivalent direct dans la version actuelle. +- **`focus4`** : méta-package contenant tous les autres (sauf `legacy`). -## Starter Kit +### `@focus4/tooling` -Vous pouvez commencer un projet en utilisant le [starter kit](http://www.github.com/KleeGroup/focus4-starter-kit), qui sert également de démo et présente les usages les plus courants de focus4. +Le module `@focus4/tooling` est lui aussi un méta-package qui contient l'ensemble des outils nécessaires pour packager une application Focus. +En particulier, il inclut [Vite](https://vitejs.dev) et [ESLint](https://eslint.org/) et des configs par défaut à étendre pour ces outils. -## Remarque sur les anciennes versions +De plus, il contient l'outil de génération de types CSS. -`Focus v4` est une librairie qui évolue assez fréquemment et n'hésite pas trop à casser la compatibilité avec les versions précédentes. Il est également assez difficile de fixer une version précise pour laquelle effectuer le "support" puisque différentes mises à jour font évoluer différents modules de la librairie, et donc selon où vous vous êtes "arrêté" vous aurez certaines évolutions mais pas d'autres. +## Starter Kit -Il est néanmoins conseillé, quelque soit la version de `Focus v4` (>= 9) que vous utilisez, de vous référer à cette documentation en premier lieu. Les concepts présentés restent identiques et pourront être adaptés aux APIs à votre disposition. N'hésitez pas à parcourir le repository au tag de votre version pour lire la documentation qui s'y trouve et comparer avec la documentation à jour. +Vous pouvez commencer un projet en utilisant le [starter kit](http://www.github.com/klee-contrib/focus4-starter-kit), qui sert également de démo et présente les usages les plus courants de Focus. -Si vous êtes encore en 8, le même conseil concernant l'historique s'applique, mais les concepts derrière les formulaires ont beaucoup changé entre la 8 et la 9. Le module `legacy` sert à faire la correspondance entre les deux. +La version packagée est également disponible [ici](https://focus4-starter-kit.fly.io). diff --git a/packages/docs/other/01-focus4.mdx b/packages/docs/other/01-focus4.mdx index b61d155e..ba706886 100644 --- a/packages/docs/other/01-focus4.mdx +++ b/packages/docs/other/01-focus4.mdx @@ -4,14 +4,11 @@ import {Meta} from "@storybook/blocks"; # `focus4` -Ce méta-module contient l'ensemble des modules de `Focus v4`, à l'exception du module `legacy`. +Ce méta-module contient l'ensemble des modules de `Focus v4`, à l'exception du module `legacy`. Il permet de simplifier la gestion de la dépendance à +Focus dans un projet qui utilisent tous les modules en permettant de ne renseigner que ce "package" dans le `package.json`. -Il permet également de simplifier l'initialisation d'une application via l'import `import "focus4"` qui peut être mis en entête du fichier racine de la SPA. +De plus, il permet également de simplifier l'initialisation d'une application via l'import `import "focus4"` qui peut être mis en entête du fichier racine de votre +application, car il permet d'importer : -Cet import permet d'importer : - -- les polyfills nécessaires -- la police Material Icons -- Le CSS de Focus - -Il réexporte également certaines APIs des différents modules, selon la même API que les versions précédentes (v9.x), qui n'étaient pas divisées en module. Il est en revanche déconseillé de les utiliser puisqu'elles disparaîtront lors de la prochaine version majeure (v11), au profit des APIs de chaque module. Le but initial était de faciliter la migration 9 -> 10. +- Tout le CSS des divers modules de Focus. +- Toutes les [traductions](/docs/les-bases-libellés-et-icônes--docs) des divers modules de Focus. diff --git a/packages/docs/other/02-legacy.mdx b/packages/docs/other/02-legacy/01-forms.mdx similarity index 70% rename from packages/docs/other/02-legacy.mdx rename to packages/docs/other/02-legacy/01-forms.mdx index 6cfca552..39dd3b8d 100644 --- a/packages/docs/other/02-legacy.mdx +++ b/packages/docs/other/02-legacy/01-forms.mdx @@ -1,37 +1,10 @@ import {Meta} from "@storybook/blocks"; - + -# (LEGACY) Module `legacy` +# (LEGACY) AutoForm -Permet d'utiliser les anciens formulaires (pré-v9) à partir du module `stores`. - -## API - -### `displayFor(field, options?)` - -Même chose que `fieldFor` mais avec `isEdit = false`. - -### `fieldFor(field, options?)` - -La fonction `fieldFor`, permettent de créer un champ d'entrée ou d'affichage avec un libellé, en utilisant les composants du domaine ou par défaut. Elle prend comme paramètres : - -- `field`, l'`EntityField` contenant la valeur et les métadonnées du champ à afficher. La plupart du temps, `field` est une propriété dans un `EntityStore`, mais il est également possible de le créer à la volée. Il est également possible de passer directement une valeur s'il n'y a pas de métadonnées associées (elles seront toutes vides du coup). -- `options`, les différentes options à passer au champ. On y retrouve les props du `Field`, comportant entre autres les propriétés `inputProps`, `displayProps` et `labelProps` qui seront les props supplémentaires à passer aux différents composants du `Field`. - -Par défaut, `fieldFor` utilise les composants `InputComponent`, `DisplayComponent` et `LabelComponent` définis dans le domaine. Si ces composants ne sont pas renseignés, alors il utilisera les composants par défaut (en particulier, un `InputText` pour l'input). Il est possible de surcharger localement ces composants en les respécifiant dans les options. Si le typage des domaines et des entités est bien fait, alors `inputProps` et consorts seront bien typés avec les props du composant qui sera utilisé. - -### `selectFor(field, values, options?)` - -La fonction `selectFor` est une version spécialisée de `fieldFor` pour l'affichage de champ avec une liste de référence. Elle utilise un composant `Select` par défaut comme composant d'input et renseigne `options.values` avec le paramètre `values`. - -- `field`, comme `fieldFor`, à la différence près qu'on accepte que des `EntityField`. -- `values`, la liste des valeurs de la liste de référence à utilise pour résoudre le code. -- `options`, comme `fieldfor`. - -`selectFor` vérifie également le type de la liste de référence en fonction du type du champ et de la présence des propriétés de bon type `valueKey` et `labelKey`. Par défaut, il cherche des propriétés `code` et `label`, qui sont surchargeables dans les options. Attention de bien caster `valueKey` et `labelKey` en eux-même (par exemple `{valueKey: "id" as "id}`) pour que l'inférence de type cherche bien la propriété `id` et non toutes les propriétés de l'objet. - -## AutoForm +**Ce composant a été remplacé par `useFormNode` / `useFormActions` et `Form`.** L'`AutoForm` est une classe dont un composant de formulaire doit hériter. C'est le remplacant du `formMixin` de la v2. @@ -45,9 +18,11 @@ C'est un "vestige" de Focus v2 qui fait le travail qu'on lui demande de façon t En somme, **un formulaire**. -Au risque de se répeter, **si vous ne faites pas un écran de formulaire, n'utilisez pas `AutoForm`**. Les fonctions `fieldFor` et co. sont disponibles dans la librairie et utilisables directement avec des stores, les composants sont synchronisés automatiquement avec les stores avec `@observer`, donc tous les cas de "non formulaire" sont gérés de façon beaucoup plus élégante, simple et souple qu'avec l'`AutoForm`. N'oubliez pas que le [starter kit](http://www.github.com/get-focus/focus4-starter-kit) fait également office de démo et présente les cas d'usages les plus courants. +Au risque de se répeter, **si vous ne faites pas un écran de formulaire, n'utilisez pas `AutoForm`**. Les fonctions `fieldFor` et co. sont disponibles dans la librairie et utilisables directement avec des stores, les composants sont synchronisés automatiquement avec les stores avec `@observer`, donc tous les cas de "non formulaire" sont gérés de façon beaucoup plus élégante, simple et souple qu'avec l'`AutoForm`. + +En particulier, **`AutoForm` redéfinit `fieldFor`/`selectFor`/`autocompleteFor` en méthode de classe** pour pouvoir les lier à son état de formulaire. -### Configuration +## Configuration La config d'un formulaire se fait intégralement dans la méthode `init()` dans laquelle il faut appeler la méthode `formInit()`, dont les paramètres sont : @@ -55,7 +30,7 @@ La config d'un formulaire se fait intégralement dans la méthode `init()` dans - `serviceConfig`, qui est un objet contenant **les services** de `load` et de `save` (les actions n'existant plus en tant que telles, on saute l'étape), ainsi que la fonction `getLoadParams()` qui doit retourner les paramètres du `load`. Cette fonction sera appelée pendant `componentWillMount` puis une réaction MobX sera construite sur cette fonction : à chaque fois qu'une des observables utilisées dans la fonction est modifiée et que la valeur retournée à structurellement changée, le formulaire sera rechargé. Cela permet de synchroniser le formulaire sur une autre observable (en particulier un `ViewStore`) et de ne pas avoir à passer par une prop pour charger le formulaire. Rien n'empêche par contre de définir `getLoadParams` comme `() => [this.props.id]` et par conséquent de ne pas bénéficier de la réaction. C'est une moins bonne solution. - `options?`, un objet qui contient des options de configuration secondaires. -### this.entity et ViewModel +## this.entity et ViewModel Le formulaire construit un `ViewModel` qu'il place dans la propriété `this.entity` à partir de `storeData`. C'est une copie conforme de `storeData` et fera office de state interne au formulaire (il est possible de passer son propre `ViewModel` dans les options du constructeur si on veut externaliser le state du formulaire). @@ -69,14 +44,14 @@ L'appel de `cancel()` sur le formulaire appelle simplement `this.entity.reset()` Il est important de noter que puisque les valeurs de stores sont toutes stockées dans un objet `{$entity, value}`, copier cette objet puis modifier `value` va modifier la valeur initiale. C'est très pratique lorsque le contenu du store ne correspond pas à ce qu'on veut afficher, puisqu'il n'y a pas besoin de se soucier de mettre à jour le store lorsque l'on modifier sa transformée. `createViewModel` construit une copie profonde du store, ce qui veut dire que ceci ne s'applique pas de `this.entity` vers `storeData` (heureusement !). -### Autres fonctionnalités +## Autres fonctionnalités - Chaque `Field` gère ses erreurs et expose un champ dérivé `error` qui contient le message d'erreur courant (ou `undefined` du coup s'il n'y en a pas), surchargeable par la prop `error` (passée au `Field` par `this.fieldFor` dans le cas d'une erreur serveur). Pour la validation du formulaire, on parcourt tous les champs (d'où la `ref` passée par `this.fieldFor`) et on regarde s'il y a des erreurs. - `onChange` et `isEdit`, passés aux `Field`s par `this.fieldFor` permettent respectivement de synchroniser `this.entity` avec les valeurs courantes des champs et de spécifier l'état du formulaire. - La suppression des actions à entraîné une migration des fonctionnalités annexes de `l'actionBuilder` et du `CoreStore`, en particulier sur la gestion des erreurs. Le traitement des erreurs de services à été décalé dans `coreFetch` dans le module `network` (ce qui fait que toutes les erreurs de services vont s'enregistrer dans le `MessageCenter`, pas seulement quand on utilise des actions), et le stockage des erreurs de validation a été déplacé dans le formulaire. De même, l'état `isLoading` est porté par le formulaire. - Le formulaire possède également des méthodes à surcharger `onFormLoaded`, `onFormSaved` et `onFormDeleted` pour placer des actions après ces évènements. -### Exemple de formulaire (issu du starter kit) +## Exemple de formulaire (issu de l'ancien starter kit) ```tsx import {AutoForm, i18n, observer, Panel, React} from "focus4"; diff --git a/packages/docs/routing/06-legacy.mdx b/packages/docs/other/02-legacy/02-routing.mdx similarity index 96% rename from packages/docs/routing/06-legacy.mdx rename to packages/docs/other/02-legacy/02-routing.mdx index fb7d95c4..f4bb2a7d 100644 --- a/packages/docs/routing/06-legacy.mdx +++ b/packages/docs/other/02-legacy/02-routing.mdx @@ -1,10 +1,10 @@ import {Meta} from "@storybook/blocks"; - + -# Routeur legacy +# (LEGACY) Routeur -**Ce routeur a été remplacé par le nouveau `router2`**. +**Ce routeur a été remplacé par le nouveau [`router`](/docs/routage-présentation--docs)**. ## Bases diff --git a/packages/stores/src/reference/store.ts b/packages/stores/src/reference/store.ts index d049e25c..88872ac2 100644 --- a/packages/stores/src/reference/store.ts +++ b/packages/stores/src/reference/store.ts @@ -15,6 +15,7 @@ export const referenceTrackingId = v4(); * (Les valeurs données aux différentes listes de références de la config n'importent peu et ne servent que pour le typage) * @param referenceLoader Le service de chargement des listes de référence, par nom. * @param refConfig Un objet dont les propriétés représentent les noms des listes de référence. Le type de chaque objet ne doit pas contenir la liste. + * @param referenceClearer Un service pour réinitialiser une (ou toutes) les listes de référence sur le serveur. */ export function makeReferenceStore>( referenceLoader: (refName: string) => Promise<{}[]>, diff --git a/packages/toolbox/src/components/font-icon.tsx b/packages/toolbox/src/components/font-icon.tsx index c4208375..f2f18db5 100644 --- a/packages/toolbox/src/components/font-icon.tsx +++ b/packages/toolbox/src/components/font-icon.tsx @@ -59,7 +59,7 @@ export interface FontIconProps extends PointerEvents { * La classe CSS sera interprétée comme un template du nom si elle contient `{name}` dans sa définition. * Dans ce cas, le `name` ne sera pas posé en enfant du `` qui définira l'icône. * - * Une icône peut également être définie via une clé i18n, qui devra pointer vers un objet `{name, className?}` représentant l'icône. + * Une icône peut également être définie via une [clé i18n](/docs/les-bases-libellés-et-icônes--docs), qui devra pointer vers un objet `{name, className?}` représentant l'icône. */ export function FontIcon({ icon,