@lystech/sections
v0.6.0
Published
Contenu de site editable par le client — lecture legere, edition chargee a la demande.
Readme
@lystech/sections
Le contenu d'un site, modifiable par son propriétaire — sans reconstruction ni déploiement.
Pourquoi un paquet à part de @lystech/core
core exige React 18 et embarque antd, Mantine et React Query : 2,4 Mo de
JavaScript et 1,2 Mo de CSS. Une clinique qui veut changer trois textes n'a
aucune raison de charger un magasin, un panier et PayPal — et beaucoup de sites
clients sont en React 19.
Ce paquet pèse 11 Ko en lecture (4,4 Ko compressé) et ne dépend que de
React. core le réexporte, pour qu'une boutique qui l'a déjà n'ait rien de
plus à installer.
La plateforme fixe l'alphabet, pas les mots
Le serveur ne connaît aucun type de bloc. « plat-du-jour », « praticien »,
« tarifs » appartiennent au site, qui les déclare. Ce que la plateforme
connaît, ce sont les types de champs — text, richText, image, number,
boolean, url, date, choice, list — vrais dans tous les métiers, et
suffisants pour fabriquer un formulaire pour un bloc jamais vu.
Usage
import { SectionsProvider, useSection } from "@lystech/sections";
<SectionsProvider site="armoise" page="massage" langue={i18n.language}>
<MaPage />
</SectionsProvider>;
// Dans un composant déjà écrit :
const { contenu: tarifs } = useSection("tarifs-massotherapie", {
defaut: SITE.tarifs.massotherapie, // ce que le site affichait déjà
});Le repli n'est pas une option
defaut porte les valeurs actuelles du site. Tant que rien n'est publié — ou
si l'API ne répond pas — ce sont elles qui s'affichent. Brancher un site
existant ne peut donc rien casser : au pire il continue d'afficher ce qu'il
affichait hier.
Publié ≠ brouillon
Le site public ne lit QUE le contenu publié. Un brouillon enregistré ne change rien pour les visiteurs tant que personne n'a cliqué « Publier ».
Champs traduits
Un champ déclaré localized: true stocke { fr: …, en: … } ; le paquet rend
la langue courante, avec repli sur une autre langue plutôt que sur du vide —
une version anglaise oubliée vaut mieux affichée en français qu'absente.
L'éditeur en place
La personne qui édite ouvre SON site avec un jeton dans l'adresse. Le paquet le
détecte, le retire aussitôt de la barre d'adresse, et charge l'interface
d'édition — 24 Ko compressés qu'aucun visiteur ne télécharge jamais, parce que le
import() qui y mène n'est atteint que s'il y a un jeton.
Elle voit alors son site, ses polices, sa mise en page, avec chaque bloc modifiable encadré. Ce qu'elle tape apparaît dedans à mesure qu'elle le tape.
Marquer les blocs dans la page
L'éditeur doit savoir quel rectangle de la page correspond à quel bloc. Aucun moyen fiable de le deviner : le site le dit.
const { contenu, ancrage } = useSection("tarifs", { defaut });
return <section {...ancrage}>…</section>;Hors édition, c'est un attribut data- inerte : aucun poids, aucun effet.
Corriger un prix sur place : champ
Un texte traduit se retrouve tout seul dans la page. Un prix, non : « 85 $ » ne
dit pas que ce 85 est le prix de la séance. Le site pose champ(…) sur
l'élément qui ne contient QUE la valeur :
const { contenu, champ } = useSection("tarifs", { defaut });
return <p><span {...champ("seance")}>{contenu.seance}</span> $</p>;En mode « modifier », un clic sur la valeur ouvre une saisie posée dessus :
Entrée enregistre le brouillon, Échap annule, et la page montre aussitôt la
nouvelle valeur. Un champ qui ne se saisit pas sur une ligne (image, liste,
texte riche) ouvre plutôt le panneau de son bloc. Si le format vient d'une
traduction (« {{amount}} $ »), découpez-le autour de la valeur au lieu de
rendre la phrase d'un bloc : seul le nombre doit porter champ.
Trois états, jamais confondus
| | Qui le voit | |---|---| | Aperçu | la personne qui édite, dans son navigateur | | Brouillon | elle seule, enregistré sur le serveur | | Publié | les visiteurs |
Le jeton
Délivré par POST /site-sections/edit-token depuis la console Lystech. Il vaut
quelques heures, pour UNE organisation, et il est plafonné à l'éditeur même
si la personne est administratrice : un lien qui fuite ne peut pas ouvrir la
console. Le droit est relu dans les rôles à chaque requête, donc retirer un
accès ferme les jetons déjà émis sur-le-champ.
Des gestes propres au site : appelEdition
Un site peut offrir, à la personne connectée, des gestes que l'éditeur générique ne connaît pas — retirer un résultat, masquer un évènement. Le contexte fournit un appel qui porte le jeton, sans jamais en livrer la valeur :
const ctx = useSectionsContext();
if (ctx?.modeEdition) {
const r = await ctx.appelEdition("/results/site/droits");
}Seuls les chemins de l'API sont acceptés. Un 401 fait sortir du mode édition
(jeton expiré) ; un 403 revient tel quel. C'est la ROUTE qui décide de ce que
le jeton ouvre : plafonné à l'éditeur partout, il n'obtient plus qu'en de rares
routes qui le disent explicitement (ex. /results/site, admins seulement).
Des boutons qui restent des boutons
En mode « modifier », cliquer un texte le corrige au lieu de l'actionner — mais
seulement un texte qui a un bloc déclaré où s'enregistrer. Pour qu'une zone
reste actionnable quoi qu'il arrive (une suppression et sa confirmation), posez
CLASSE_HORS_EDITION dessus — et sur rootClassName des fenêtres surgissantes,
qui se rendent hors de l'arbre :
<section className={CLASSE_HORS_EDITION}>…</section>
<Popconfirm rootClassName={CLASSE_HORS_EDITION} …>Un site qui signe déjà
<SignatureLystech mention={null} /> ne garde que le bouton « Connexion » —
pour un pied de page qui porte déjà sa propre mention de Lystech.
Sites prérendus
Un site généré au build affiche les replis du code dans son HTML, puis bascule sur le contenu publié une fois le JavaScript chargé — l'ancien prix clignote vers le nouveau, et les moteurs de recherche n'indexent que l'ancien.
Passez le contenu au premier rendu pour supprimer les deux problèmes :
<SectionsProvider site="armoise" page="massage" sectionsInitiales={dejaLu}>où dejaLu vient d'un appel, dans le script de prérendu, à
GET /site-sections/public/<site>/<page>.
