Releases: klee-contrib/focus4
v11.2
Petite release pour rattraper le retard sur les différentes versions de dépendances.
En particulier, react-dnd
, eslint
, typescript
, postcss
et framer-motion
ont reçu des mises à jour majeures mais qui n'ont à priori qu'un impact très limité. Il est possible qu'ESLint soit plus regardant sur la version de Node installée, n'hésitez pas à vous mettre à jour sur la dernière LTS (et mettre à jour npm aussi).
Cette release fix aussi une régression sur les pluriels (qui ont changé avec la dernière mise à jour d'i18next
), ainsi que quelques bugs sur l'autocomplete qui empêchaient de surcharger correctement sa query
(pour un autocomplete de suggestion uniquement, par exemple).
v11.1
Pliage des groupes, defaultFoldedFacets
et defaultFoldedGroups
Dans la recherche avancée (ou simplement dans l'affichage des résultats), les groupes peuvent maintenant être pliés de la même façon que les facettes. Il n'y en revanche pas d'action globale pour plier tous les groupes (principalement par absence d'endroit évident où la mettre).
Le bouton pour plier est posé par le header de groupe par défaut, donc tout header personnalisé ne sera pliable que si le pliage est implémenté (comme pour un composant personnalisé de facette).
Pour compléter cette fonctionnalité, les propriétés defaultFoldedFacets
et defaultFoldedGroups
permettent de spécifier, comme leur nom le suggère, des facettes (codes) et des groupes (groupingKey + codes) qui devront être initialement fermés (tout est ouvert par défaut, facettes comme groupes)
Je pense qu'on peut dire que cette issue #112 peut (enfin !) être fermée avec ça.
Améliorations CollectionStore
local avec des entités
Il est désormais possible de filtrer, facetter et grouper par des champs d'entité dans un CollectionStore
local pour plus de flexibilité.
Par exemple :
const node = buildNode([MyEntity]);
const listStore = new CollectionStore<typeof node>({facetDefinitions: [{code: "myField", label: "My field", fieldName: "myField"}]);
// ....
listStore.list = node;
listStore.addFacetValue("myField", "myValue", "selected");
listStore.groupingKey = "myField";
fonctionnera comme attendu (auparavant ce n'était pas le cas puisque le CollectionStore
n'allait pas lire myField.value
).
v10.9.x
Cette release reporte les fonctionnalités suivantes de la v11 sur la v10 :
- Retrait des imports en
"focus4/*"
(mais conserve le"focus4"
), pour avoir les auto-imports qui fonctionnent avec les bonnes peer dependencies - Retrait du type
NodeToErrors
, pour le gain en performance - Retour du
replace
auactions.save
etreset
avec unreplace(initialData)
+set(sourceNode)
v11
Comme annoncé dans #184, voici, enfin, la v11 de focus4. Comme la v10 avant elle, cette version majeure n'inclus aucune nouvelle fonctionnalité, mais est une version de transition sur laquelle on se permet d'opérer des changements structurants pour la suite.
L'objectif principal de cette nouvelle version est de construire une version de Focus pour 2021 (et le futur), libre de toute dépendance obsolète ou dépréciée. Cette version va aussi au bout des chantiers commencés en v10.
Ces évolutions vont représenter des changements importants pour un projet existant. Tout est évidemment possible, mais il est clair qu'une mise à jour vers la v11 n'est pas anodine et devra être préparée correctement, donc il me semble plus sage de réserver cette version à de nouveaux projets.
Sans plus attendre, voici les nouveaux principales :
Retrait du support d'IE 11
Focus4 ne supporte plus nativement IE11 à partir de la v11. C'est fini, il n'y a vraiment plus de raison de le faire en 2021, Microsoft eux-même ne le supporteront même plus sur Office 365 à partir du 17 août et il sera désinstallé de force en juin 2022.
Focus est désormais compilé en ES2020 (entièrement supporté par Chromium/Firefox/Safari depuis plus d'un an) et utilise par défaut des fonctionnalités qui n'étaient pas émulables sur IE. Certains ajustements spécifiques pour IE (genre sur le layout) ont été retirées aussi, ainsi que tous les polyfills.
La décision n'est pas irréversible, il reste possible de recompiler Focus derrière pour cibler des environnements plus anciens (pas forcément jusqu'à l'ES 5 d'IE 11), d'ajouter des polyfills qui pourraient manquer et d'activer les modes de compatibilités des librairies incompatibles (voir plus bas). Focus ne le fait juste plus, et ça sera donc à votre charge de le faire si le besoin devrait se faire sentir.
De même, l'environnement de dev par défaut suppose d'avoir Node 14 pour que tout fonctionne sans encombres.
Refonte des variables CSS
Si on arrête de supporter IE, ça veut dire qu'on peut utiliser les variables CSS nativement dans le navigateur. La config de build par défaut ne compile donc plus les variables, l'export de Focus pour générer le css-variables.js
n'existe plus, et il suffit juste d'importer le CSS normalement. Les variables peuvent être surchargées où vous voulez (elles sont toujours globales en revanche), il n'y a plus besoin d'importer partout les fichiers de variables pour que ça compile (enfin, il faut le faire une fois quand même). On peut utiliser des variables dans le style inline si besoin, on peut modifier les variables en JS... bref ce sont des variables CSS natives.
Attention cependant ! Focus (enfin, surtout react-toolbox
) n'était pas prévu pour fonctionner sans compiler les variables auparavant, donc il a fallu faire quelques ajustements sur les définitions de variables. En particulier, toutes les variables --palette--
et --color
sont désormais décrites sans rgb()
. Cela veut dire que, par exemple, --color-black
est désormais égal à 0, 0, 0
et non rgb(0, 0, 0)
, et qu'il faut décrire toutes vos couleurs en décimal. Le rgb()
devra être posé à l'usage, comme ça par exemple rgb(var(--color-black))
. La raison pour ça est très simple, c'est parce que ça permet d'écrire rgba(var(--color-black), 0.5)
, qui est un cas d'usage récurrent et il n'est pas possible de l'écrire autrement à partir de variables.
Outre une simplicité de mise en oeuvre simplifiée, l'utilisation de variables natives permet d'écrire très facilement des surcharges CSS pour changer le thème d'une application, par exemple pour mettre au point un thème sombre, un thème par environnement, un thème modifiable par un utilisateur...
Par exemple, ce thème a été fait en modifiant une dizaine de variables, la grande majorité étant l'inversion des nuances de gris :
CSS du module toolbox
react-toolbox
a été complètement intégré à focus dans la version précédente (10.9), mais aucun ajustement de CSS n'avait été réalisé pour ne pas imposer de breaking change aux utilisateurs. Avec une nouvelle majeure, on peut se permettre de le faire.
Le CSS du module toolbox
suit désormais les conventions de nommages du CSS des autres modules, pour pouvoir utiliser pleinement les fonctionnalités de CSS mises en place en 10.2 et les conventions de nommage "(B)EM".
Par exemple, le Button
propose désormais les classes CSS suivantes :
"button"
"button--accent"
"button--flat"
"button--floating"
"button--inverse"
"button--mini"
"button--neutral"
"button--primary"
"button--raised"
"button--solid"
"button--squared"
"icon"
"rippleWrapper"
Le CSS a été également un peu revenu pour simplifier certaines règles un peu trop spécifiques (les :not([disabled])
sur le dit Button
par exemple). Le CSS du Button
et du IconButton
sont désormais séparés, ainsi que ceux des InputDate
, Calendar
, InputTime
et Clock
, là ou ça pouvait être un peu fouilli auparavant.
Le CSS de ces composants devrait donc être désormais beaucoup plus clair et simple.
Migration vers MobX 6
MobX 6 est un breaking change plus ou moins violent selon le fait que vous utilisez des classes ou non. Si non, comme recommandé depuis un moment par React et possible avec Focus depuis la 10.1, l'impact sera faible. La principale différence vient du fait que MobX a changé le fonctionnement des décorateurs (@observable
, @computed
...) pour se caler sur une implémentation qui est compatible avec la future implémentation des champs de classes en JS, incompatible avec ce que tout le monde à toujours fait jusqu'à présent... Cette implémentation doit s'activer avec un flag Typescript (c'est donc aussi un breaking change pour Typescript).
Pour MobX, la nouvelle spec des champs de classes nécessite un appel à makeObservable(this)
dans le constructeur de toute classe avec un décorateur MobX (et le flag TS activé). Vu que même l'implémentation à base de décorateurs nécessite un appel dans le constructeur, MobX ne propose plus d'utiliser les décorateurs par défaut et propose de lister les annotations dans le deuxième paramètre de makeObservable
. Le détail de la migration et de ce qui change est décrit ici.
Il y a un outil mobx-undecorate
qui permet de faire la migration assez facilement et qui est bien foutu, et qui permet de garder les décorateurs ou non. La migration sur Focus et sur mes projets s'est fait finalement fait sans encombres avec. Pour la suite, c'est simplement une raison de plus pour arrêter de faire des classes.
Pour le reste, il y a quelques autres changements, en particulier autour de la gestion de l'héritage dans les classes avec des propriétés observables qui est mieux définie avec une annotation dédiée obligatoire (@)override
(ce qui est un autre breaking change), mais c'est plutôt très mineur en comparaison.
La vrai nouveauté sur MobX finalement, c'est qu'en laissant tomber IE, on peut utiliser l'implémentation de MobX qui se base sur des Proxy, et que par conséquent MobX utilise des vrais objets JS natifs au lieu de "faux" objets, ce qui veut dire que Array.isArray(observable([])) === true
, enfin, et qu'il n'y aura plus besoin de faire autant de .slice()
qu'avant.
Consolidation des exports
Avant, il n'y avait qu'un seul module focus4
. Avec la v10, Focus a été divisé en plusieurs modules dans le scope @focus4
, mais les anciens exports en focus4/xxx
ont été gardés pour simplifier la migration. La v11 termine le chemin en supprimant tous les exports du module focus4
(ou presque). Tous les exports doivent désormais se faire depuis leurs modules respectif.
Pour que la fonctionnalité d'auto-import de Typescript fonctionne correctement et propose les bons imports (aujourd'hui elle ne propose que les imports de "focus4" parce que c'est le seul qui est dans le package.json), il "suffit" de lister en "peerDependency" tous les packages de Focus que vous voulez utiliser dans votre projet, que ça soit les @focus4/*
, mobx
, react
, etc. La liste exhaustive est dans le starter-kit.
Si vous cherchez à migrer, le plus simple est de supprimer tous les imports de focus4
(ils seront en erreur de toute façon), et de tout résoudre avec le fix de typescript "Add all missing imports". Puisque tous les composants ne sont exportés qu'une seule fois et au bon endroit, tous les imports résolus automatiquement seront toujours les bons.
Il y a eu aussi quelques déplacements entre modules :
- les annotations de réaction (
@classAutorun
...) ainsi que l'ancien routeur ont été déplacés dans le modulelegacy
. (par conséquentmakeRouter2
devientmakeRouter
) - chaque package porte désormais ses propres traductions, au lieu d'être toutes regroupées dans
core
.focus4
exporte une version consolidée équivalente à l'ancienne, donc ça revient au même au final une fois l'import changé, mais c'est plus logique comme ça. - de façon plus anecdotique, les variables CSS sont aussi définies dans chaque package au lieu d'être regroupées dans
styling
, mais ça ne change rien puisque chaque package a toujours exporté son fichier CSS qui est importé avecimport "focus4"
- de façon moins anecdotique, **Focus ne package plus sa v...
v10.9
Fin de la migration react-toolbox
Cette version termine la migration des composants de react-toolbox
vers @focus4/toolbox
. Il n'y a donc plus de dépendance à react-toolbox
et tout a été intégré. Le CSS des composants n'a pas été mis à jour pour être conforme au reste du CSS de Focus, ce sera en revanche inclus dans la future v11 (c'est un breaking change assez important).
Comme pour les autres composants déjà migrés, ils ont été réécrits en React moderne et ils sont désormais proprement typés, donc il peut y avoir quelques changements à la marge simples à corriger à ce niveau là.
Pour la reprise du CSS, les noms de fichiers ont été changés, donc le nom des classes générées aussi. A priori, il ne faudrait pas s'en servir directement, mais ça pourrait être impactant si c'était le cas (ça l'est probablement...). Tous les fichiers s'appellaient "theme.css" avant, maintenant ils ont le nom du composant. Par exemple, une classe de bouton theme_button_14sdd
s'appellerait maintenant button_button_41s8f
.
Aussi, le CSS des "ripples" et "tooltips" sont désormais passés par des props spécifiques rippleTheme
et tooltipTheme
au lieu d'être mélangés avec le reste du theme
du composant.
v10.8
node.load()
et useLoad
Désormais, lorsqu'on utilise des FormActions (avec useFormActions
ou makeFormActions
dans les classes), la fonction de chargement est enregistrée sur le StoreNode
est peut être appelée n'importe où via node.load()
. node.load()
n'aura évidemment l'effet escompté que si le composant de formulaire qui a posé le load est affiché sur le page : l'enregistrement est annulé quand le composant est démonté. Si on appelle node.load()
et qu'il n'y aucun service enregistré, ça vide le store.
On ne peut enregistrer une fonction de chargement que sur un StoreNode
(= pas sur un FormNode
). useFormActions
l'enregistre sur formNode.sourceNode
, et formNode.load()
appelle formNode.sourceNode.load()
.
De plus, onexpose aussi désormais useLoad
qui extrait de useFormActions
la partie qui ne concerne que le chargement (et l'enregistrement de node.load()
donc). La méthode utilitaire registerLoad
permet de reproduire le même comportement dans une classe, on prenant soin de bien nettoyer la réaction au démontage du composant.
Ca s'utilise comme ça :
const isLoading = useLoad(store.node, a => a.params(() => router.state.id).load(loadData));
Ca remplace tous les trucs "moches" à base de useEffect(() => autorun(() => ....)) et on a un isLoading
+ node.load()
en bonus.
Début de migration react-toolbox
Comme indiqué dans #184 à propos de l'inévitable v11, il va falloir se séparer une bonne fois pour toute de react-toolbox. Cette version commence la migration en intégrant dans Focus les composants suivants :
- Input (celui utilisé dans 99% des composants est un wrapper Focus, qui lui est inchangé)
- Autocomplete (celui utilisé dans 99% des composants est un wrapper Focus, qui lui est inchangé)
- Dropdown
- Slider
- Avatar/Chip
- FontIcon
- ButtonMenu/IconMenu (mais pas les boutons)
- ProgressBar
- SnackBar
On se sépare également des composants dont on ne se sert pas car remplacés par des composants plus spécifiques dans collections
ou layout
(les listes par exemple), et qui ne seront jamais intégrés.
Puisque tous les composants sont déjà importés depuis @focus4/toolbox
, cela ne nécessite pas d'adaption particulière. Les composants ont été réécrits en React "moderne", avec des définitions de props précises. Cela peut demander quelques ajustements de votre côté pour se conformer aux types plus exigeants. De plus, certaines fonctionnalités obscures n'ont pas nécessairement survécu à la réécriture car elles étaient compliquées à gérer proprement pour pas grand chose. Ces composants ont vocation à être le plus possible compatibles avec l'existant, donc n'hésitez pas à signaler la moindre régression.
Ces travaux continueront dans les prochaines mises à jour. La migration finale sera faite en v11 : c'est en particulier à ce moment-là qu'on se séparera de leur CSS (que ces composants réécrits utilisent toujours).
SearchStore "local"
Les facettes en mode "local" du SearchStore gèrent désormais des champs avec plusieurs valeurs (= un array).
v10.7
v10.6
Nouveau routeur (makeRouter2
)
Cette release s'accompagne d'un nouveau routeur, qui a pour vocation de remplacer l'existant dans les nouvelles applications Focus et pour ceux qui auront le volonté et le courage de faire la migration. Les deux routeurs continueront à être disponibles.
Le routeur existant, s'il met à en œuvre des concepts puissants comme la synchronisation entre l'URL et l'état de stores MobX, n'a jamais apporté entière satisfaction. Le découpage en "ViewStores", distinct du routeur en lui même, n'est pas très pratique à manipuler, en particulier quand il faut passer de l'un à l'autre, et synchroniser une URL avec plusieurs stores (dont un seul peut être actif en même temps), à toujours été une source de problèmes. De plus, puisqu'on cherchait à définir des ViewStores et non des routes, on se retrouvait à définir, dans chaque store, une suite de paramètres "molle", qui pouvaient correspondre à un peu ce qu'on veut, et pour lesquels il était difficile de définir et utiliser des relations entre les différentes valeurs. Avec un ViewStore défini comme {page: "operation" | "projet", id: number}
, le paramètre id
dépend de la valeur de page
et à un sens différent selon. Pour vérifier que id
est bien un id de projet, il fallait donc vérifier également que page
était bien égal à "projet"
, en plus de vérifier que id
était bien renseigné. Et même en faisant attention, on se retrouvait quand même régulièrement dans des cas aux limites où cela posait encore des problèmes.
Le nouveau routeur supprime la notion de "ViewStore", au profit d'un routeur central dans lequel on définit toutes les routes de manière statique. Ce routeur central dispose d'un objet observable qui détient la valeur de tous les paramètres, dans un arbre qui correspond à l'arbre de routes possibles, dans lequel il n'y a donc jamais ambiguïté dans la valeur d'un paramètre. Ce routeur expose des méthodes pour remplacer les usages qu'on pouvait avoir des paramètres de type page
, une méthode de navigation, ainsi qu'une méthode permettant de construire des vues qui correspondent à des sous-sections, pour reconstruire un équivalent de "ViewStore", mais à la volée, au niveau que l'on veut, et avec l'ensemble de l'API du routeur. Il bien évidemment entièrement observable, comme son prédécesseur, donc il en garde tous les avantages tout en adressant ses limitations.
Il est entièrement documenté ici
Stores/Forms
-
Le type de
sourceNode
est désormais distinct de celui duFormNode
pour prendre en compte les transformations réalisées à la construction du formulaire (par exemple : les champs ajoutés dans le formulaire n'apparaitront plus danssourceNode
). Cela ne fonctionne en revanche que pour le noeud racine. -
Nouvelles APIs
remove
etremoveAllBut
surFormNodeBuilder
, qui permettent de supprimer des champs dans un formulaire, par exemple si on ne veut mettre que certains champs d'une entité dans un formulaire. Grâce à la modification précédente, les champs supprimés apparaissent toujours danssourceNode
.
Attention : Puisqu'on peut construire des formulaires "partiels", on s'attend également à avoir des services de sauvegarde "partiels", qui en particulier ne vont envoyer au serveur qu'une partie des champs qui ont été chargés. D'ailleurs, le type du service de chargement dans FormActions est désormais celui du noeud source alors que ceux de sauvegarde utilisent toujours le type du formulaire. Par conséquent, pour prendre en compte le fait que le serveur puisse ne retourner qu'une partie de l'entité chargée, on appelle désormais set
au lieu de replace
avec le retour d'un service de sauvegarde. Cela ne devrait pas poser de problème en pratique, hormis peut être pour le rare cas où une valeur envoyée par le client est vidée par le serveur : dans ce cas elle ne sera plus vidée en retour côté client.
Collections
- Il est désormais possible de surcharger le composant utilisé par
addItemHandler
dans les listes et la recherche avancée, via la propAddItemComponent
. - Le composant de Timeline permet également de spécifier un
AddItemComponent
, en plus de pouvoir profiter aussi deEmptyComponent
etLoadingComponent
comme la liste. De fait, la timeline affiche désormais par défaut un "Aucun élément" et un "Chargement...", comme la liste. - Le composant de Table (
Table
/tableFor
) peut désormais afficher les cases de sélection comme la liste via la prophasSelection
.
v10.5
Fusion des stores de collections (ListStore et SearchStore) en un store unique "CollectionStore".
Le CollectionStore peut être configuré pour gérer des facettes et des groupes côté client (le tri et le filtrage par champ texte étant déjà disponibles), ce qui justifie la fusion des deux stores puisqu'ils ont maintenant les mêmes fonctionnalités. Enfin, le CollectionStore "local" reste tout de même une implémentation simpliste et doit être réservée à de petites volumétries, mais il est désormais possible, pour des besoins simples, d'utiliser une recherche avancée complète 100% client.
v10.4
- Refonte makeField pour utiliser la même API que add/patch + distinguer un field en lecture seule avec api simplifiée (partagée avec fromField)
- Ajout de champs non calculés dans les formNode + clarification toFlatValues
- Génération avec des types d'entités explicites
A préciser...