@lystech/sections 0.6.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +188 -1
- package/dist/Audience.d.ts +14 -0
- package/dist/BoutonEdition.d.ts +13 -0
- package/dist/BoutonEditionCorps.d.ts +12 -0
- package/dist/SectionsProvider.d.ts +62 -2
- package/dist/catalogue.d.ts +30 -0
- package/dist/constantes.d.ts +38 -0
- package/dist/editeur/ApercuMobile.d.ts +7 -0
- package/dist/editeur/ChoixAudience.d.ts +6 -0
- package/dist/editeur/MarquesBoutique.d.ts +26 -0
- package/dist/editeur/api.d.ts +16 -1
- package/dist/editeur/boutique/Collections.d.ts +8 -0
- package/dist/editeur/boutique/FicheCollection.d.ts +23 -0
- package/dist/editeur/boutique/FicheProduit.d.ts +23 -0
- package/dist/editeur/boutique/ListeVirtuelle.d.ts +13 -0
- package/dist/editeur/boutique/ModuleBoutique.d.ts +44 -0
- package/dist/editeur/boutique/Publier.d.ts +15 -0
- package/dist/editeur/boutique/Selecteur.d.ts +10 -0
- package/dist/editeur/boutique/api.d.ts +131 -0
- package/dist/editeur/boutique/commun.d.ts +19 -0
- package/dist/editeur/boutique/photos.d.ts +9 -0
- package/dist/editeur/boutique/styles.d.ts +1 -0
- package/dist/editeur/pont.d.ts +7 -0
- package/dist/editeur/styles.d.ts +1 -1
- package/dist/editeur.js +6 -1736
- package/dist/index.d.ts +9 -2
- package/dist/index.js +351 -209
- package/dist/morceaux/ApercuMobile-CSoo4dpM.js +63 -0
- package/dist/morceaux/BoutonEditionCorps-B57eJtJn.js +112 -0
- package/dist/morceaux/ModuleBoutique-CgqcQjIb.js +1509 -0
- package/dist/morceaux/constantes-BAdpe1ss.js +24 -0
- package/dist/morceaux/index-C_uFDnBO.js +2217 -0
- package/dist/types.d.ts +30 -0
- package/dist/useSection.d.ts +15 -0
- package/package.json +46 -46
- package/dist/morceaux/constantes-BPZxUYge.js +0 -19
package/README.md
CHANGED
|
@@ -59,7 +59,7 @@ une version anglaise oubliée vaut mieux affichée en français qu'absente.
|
|
|
59
59
|
|
|
60
60
|
La personne qui édite ouvre SON site avec un jeton dans l'adresse. Le paquet le
|
|
61
61
|
détecte, le retire aussitôt de la barre d'adresse, et charge l'interface
|
|
62
|
-
d'édition —
|
|
62
|
+
d'édition — ~27 Ko compressés qu'aucun visiteur ne télécharge jamais, parce que le
|
|
63
63
|
`import()` qui y mène n'est atteint que s'il y a un jeton.
|
|
64
64
|
|
|
65
65
|
Elle voit alors son site, ses polices, sa mise en page, avec chaque bloc
|
|
@@ -147,6 +147,193 @@ qui se rendent hors de l'arbre :
|
|
|
147
147
|
`<SignatureLystech mention={null} />` ne garde que le bouton « Connexion » —
|
|
148
148
|
pour un pied de page qui porte déjà sa propre mention de Lystech.
|
|
149
149
|
|
|
150
|
+
## La boutique dans l'éditeur (0.7)
|
|
151
|
+
|
|
152
|
+
Une personne qui **administre** la boutique gère, depuis son propre site,
|
|
153
|
+
ses produits, leurs photos et ses collections — au bureau comme au
|
|
154
|
+
téléphone. Maquette : `docs/maquette-editeur.html`.
|
|
155
|
+
|
|
156
|
+
### Ce que voit la cliente
|
|
157
|
+
|
|
158
|
+
- un bouton **Boutique** dans la barre d'édition : la liste de ses produits
|
|
159
|
+
(recherche, filtres En vente / Masqués / Épuisés / Sans collection) et ses
|
|
160
|
+
collections ;
|
|
161
|
+
- un **crayon** sur chaque carte produit de la page, qui ouvre sa fiche ;
|
|
162
|
+
- une tuile **+ Ajouter** juste après la dernière carte de la grille ;
|
|
163
|
+
- la **fiche** : photos (glisser-déposer, ou « Prendre une photo » qui ouvre
|
|
164
|
+
la caméra du téléphone), nom, prix, prix avant rabais, stock en trois mots
|
|
165
|
+
(pièce unique / quantité / sans limite), visible ou masqué, collections,
|
|
166
|
+
description. Panneau latéral au bureau, feuille glissante au téléphone ;
|
|
167
|
+
- un bouton **Mobile** : la page courante dans un cadre de 390 px, brouillons
|
|
168
|
+
de textes compris.
|
|
169
|
+
|
|
170
|
+
**Deux rythmes, dits à l'écran.** Les textes passent par brouillon puis
|
|
171
|
+
« Publier ». Le catalogue, lui, est en ligne dès « Enregistrer » ; un bijou
|
|
172
|
+
pas prêt reste « Masqué ».
|
|
173
|
+
|
|
174
|
+
**Rien ne disparaît d'un clic.** Masquer est réversible. « Supprimer pour
|
|
175
|
+
toujours » n'existe que sur un produit déjà masqué, après une confirmation
|
|
176
|
+
explicite, et le serveur refuse un produit déjà vendu. Retirer une photo la
|
|
177
|
+
détache : elle reste dans la librairie d'images.
|
|
178
|
+
|
|
179
|
+
### Ce que le site doit faire : deux lignes
|
|
180
|
+
|
|
181
|
+
**1. Marquer ses cartes produit** — sans ce marquage, pas de crayon ni de
|
|
182
|
+
tuile (le module reste accessible par la barre) :
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
import { marqueProduit } from "@lystech/sections";
|
|
186
|
+
|
|
187
|
+
<div className="carte" {...marqueProduit(produit.UID)}>…</div>
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
L'attribut posé est `data-lystech-produit="<UID>"`, inerte hors édition.
|
|
191
|
+
Crayons et tuile vivent dans le Shadow DOM de l'éditeur, posés par-dessus :
|
|
192
|
+
rien n'est inséré dans l'arbre React du site. La tuile se place d'après les
|
|
193
|
+
rectangles réels des cartes (grille CSS, flexbox, 2 ou 4 colonnes).
|
|
194
|
+
|
|
195
|
+
**2. Relire ses produits quand l'éditeur en change un.** Après chaque
|
|
196
|
+
enregistrement, l'éditeur émet `lystech:catalogue-modifie` sur `window`
|
|
197
|
+
(détail : `{ quoi, UID, action }`). Avec React Query :
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
import { useCatalogueModifie } from "@lystech/sections";
|
|
201
|
+
|
|
202
|
+
const qc = useQueryClient();
|
|
203
|
+
useCatalogueModifie(() => qc.invalidateQueries());
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
L'aperçu mobile relaie l'événement dans son cadre : le site s'y met à jour
|
|
207
|
+
de la même façon.
|
|
208
|
+
|
|
209
|
+
Un site peut aussi ouvrir le module lui-même — `useSectionsContext()
|
|
210
|
+
?.ouvrirBoutique(UID | "nouveau")` rend faux si la personne n'y a pas droit.
|
|
211
|
+
|
|
212
|
+
### Qui y a droit
|
|
213
|
+
|
|
214
|
+
Routes `/site-sections/boutique/*` du serveur. Le jeton d'édition reste
|
|
215
|
+
plafonné à l'éditeur partout ailleurs ; ici, le serveur relit à chaque
|
|
216
|
+
requête le **rôle réel** de la personne et exige **administrateur** (un
|
|
217
|
+
éditeur reçoit 403 : il garde l'édition des textes, le bouton Boutique ne
|
|
218
|
+
s'affiche pas). Organisation = celle du jeton, jamais un en-tête. Aucune
|
|
219
|
+
route de commandes, clients ou paiements.
|
|
220
|
+
|
|
221
|
+
Le module (≈12 Ko compressés) et l'aperçu mobile sont deux fichiers à part,
|
|
222
|
+
chargés au premier clic.
|
|
223
|
+
|
|
224
|
+
### L'aperçu mobile
|
|
225
|
+
|
|
226
|
+
Un cadre nommé `lystech-apercu` charge la même page ; le fournisseur qui s'y
|
|
227
|
+
trouve le reconnaît, ne monte pas l'éditeur et reçoit les brouillons par
|
|
228
|
+
`postMessage` (même origine seulement). `useSectionsContext()?.modeApercu`
|
|
229
|
+
permet à un site de couper ce qui n'a pas de sens dans un aperçu.
|
|
230
|
+
|
|
231
|
+
## La grille, les collections, les brouillons (0.9)
|
|
232
|
+
|
|
233
|
+
### Une carte « + » dans la grille
|
|
234
|
+
|
|
235
|
+
En mode édition, pour une personne qui administre la boutique, une carte
|
|
236
|
+
**« + Ajouter un bijou »** apparaît **au bout de la grille de produits**, à la
|
|
237
|
+
taille exacte d'une carte. Rien à poser côté site : l'éditeur trouve la grille
|
|
238
|
+
à partir des cartes marquées par `marqueProduit`. Un visiteur, ou un éditeur
|
|
239
|
+
de textes sans droit sur le catalogue, n'a jamais cette carte dans sa page.
|
|
240
|
+
|
|
241
|
+
Au clic : **« Ajouter un bijou existant »** (recherche, vignettes, plusieurs à
|
|
242
|
+
la fois, ceux déjà présents marqués) ou **« Créer un nouveau bijou »**, rangé
|
|
243
|
+
d'office dans la collection affichée. Hors d'une collection : la création
|
|
244
|
+
seule.
|
|
245
|
+
|
|
246
|
+
### Marquer la collection affichée : `marqueCollection`
|
|
247
|
+
|
|
248
|
+
```tsx
|
|
249
|
+
import { marqueCollection } from "@lystech/sections";
|
|
250
|
+
|
|
251
|
+
<div className="collection" {...marqueCollection(c.UID, c.UID === choisie?.UID)}>…</div>
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Le deuxième argument dit « c'est elle que la grille montre ». L'éditeur y pose
|
|
255
|
+
un **crayon** (nom, description, qui peut commander, ses bijoux) et un **« + »**
|
|
256
|
+
(y ajouter des bijoux), et la carte « + » de la grille la garnit. La barre de
|
|
257
|
+
l'éditeur gagne une entrée **Collections**.
|
|
258
|
+
|
|
259
|
+
### Des brouillons, publiés quand on veut
|
|
260
|
+
|
|
261
|
+
Un **nouveau** bijou est rangé **en brouillon** — jamais visible, jamais
|
|
262
|
+
vendable, gardé sur le serveur (il survit au navigateur fermé et suit
|
|
263
|
+
l'artisane d'un appareil à l'autre). « Enregistrer et suivant » vide la fiche
|
|
264
|
+
pour le bijou d'après sans fermer le panneau ; les photos partent en
|
|
265
|
+
arrière-plan. Une nouvelle collection naît aussi en brouillon : on la nomme, on
|
|
266
|
+
la garnit (bijoux existants et nouveaux), sans qu'elle apparaisse.
|
|
267
|
+
|
|
268
|
+
**Publier (N)**, dans la barre, liste les brouillons (et les textes modifiés) :
|
|
269
|
+
on décoche ce qui n'est pas prêt ; ce qui ne peut pas partir (nom de moins de 3
|
|
270
|
+
lettres, pas de prix, pas de photo) le dit sur sa ligne. Chaque élément est
|
|
271
|
+
publié pour lui-même : un échec n'annule pas les autres, un double clic ne crée
|
|
272
|
+
pas de doublon. Pour une collection, on choisit à ce moment-là comment
|
|
273
|
+
l'ouvrir. Corriger un bijou **déjà en ligne** reste, lui, immédiat.
|
|
274
|
+
|
|
275
|
+
### Le crayon flottant : `BoutonEdition` et `connexionPropre`
|
|
276
|
+
|
|
277
|
+
Un site qui a **sa propre connexion** (une boutique @lystech/core) n'a pas
|
|
278
|
+
besoin de la fenêtre « Connexion » du pied de page :
|
|
279
|
+
|
|
280
|
+
```tsx
|
|
281
|
+
<SectionsProvider site="bijouxunyk" page={page} connexionPropre session={{ cle, entetes }}>
|
|
282
|
+
…
|
|
283
|
+
<BoutonEdition connecte={isLoggedIn} />
|
|
284
|
+
</SectionsProvider>
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
`connexionPropre` retire « Connexion » de `SignatureLystech` (défaut : non,
|
|
288
|
+
rien ne change pour les autres sites). `BoutonEdition` ne rend **rien** tant
|
|
289
|
+
que personne n'est connecté — ni code téléchargé, ni requête. Connecté, une
|
|
290
|
+
question au serveur (« puis-je éditer CE site ? ») ; si oui, un crayon rond de
|
|
291
|
+
56 px en bas de l'écran (`coin="gauche"` par défaut, à l'opposé du panier). Au
|
|
292
|
+
clic, le serveur délivre un jeton d'édition **à la session déjà ouverte**, pour
|
|
293
|
+
l'organisation de ce site seulement, et l'éditeur se monte sur la page, sans
|
|
294
|
+
recharger. En mode édition, le crayon disparaît.
|
|
295
|
+
|
|
296
|
+
## Sections par audience (0.8)
|
|
297
|
+
|
|
298
|
+
Une section peut viser une **audience** : tout le monde, clients connectés,
|
|
299
|
+
non-membres, membres (n'importe quel plan actif) ou des plans précis. Le choix
|
|
300
|
+
se fait dans l'éditeur (« Visible pour », en tête du panneau d'un bloc) et
|
|
301
|
+
s'applique tout de suite.
|
|
302
|
+
|
|
303
|
+
**Le filtrage est fait par le serveur, jamais par la page :**
|
|
304
|
+
|
|
305
|
+
- la route publique (`/site-sections/public/...`) — donc aussi le HTML
|
|
306
|
+
prérendu, fabriqué en la lisant — ne contient QUE les sections « tout le
|
|
307
|
+
monde » ;
|
|
308
|
+
- le fournisseur lit d'abord `/site-sections/membre/...`, où le serveur
|
|
309
|
+
reconnaît le visiteur (session, adhésion) et ne livre que ce à quoi il a
|
|
310
|
+
droit. Une section non permise arrive en coquille vide (`hidden`) : le site
|
|
311
|
+
sait qu'elle existe et n'affiche PAS son repli à la place.
|
|
312
|
+
|
|
313
|
+
```tsx
|
|
314
|
+
const { contenu, visible } = useSection("offre-membres", { defaut });
|
|
315
|
+
if (!visible) return null;
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
**Règle :** un contenu réservé ne va jamais dans le `defaut` du code — le code
|
|
319
|
+
d'un site est public. Il s'écrit dans l'éditeur.
|
|
320
|
+
|
|
321
|
+
Session : par défaut le cookie de l'API est envoyé (`credentials: "include"`).
|
|
322
|
+
Un site branché sur `@lystech/core` passe la sienne :
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
<SectionsProvider site="unyk" page="accueil" session={useSectionsSession()}>
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
Pour un bloc codé en dur (un bouton, un mot d'accueil), `<Audience>` décide de
|
|
329
|
+
l'**affichage** d'après le visiteur reconnu par le serveur — jamais pour un
|
|
330
|
+
contenu sensible :
|
|
331
|
+
|
|
332
|
+
```tsx
|
|
333
|
+
<Audience show="nonMembers"><BoutonDevenirMembre /></Audience>
|
|
334
|
+
<Audience show="plans" plans={[uidPlanOr]} fallback={<Teaser />}>…</Audience>
|
|
335
|
+
```
|
|
336
|
+
|
|
150
337
|
## Sites prérendus
|
|
151
338
|
|
|
152
339
|
Un site généré au build affiche les replis du code dans son HTML, puis bascule
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { Visiteur } from './types';
|
|
3
|
+
export type AudienceProps = {
|
|
4
|
+
show: "everyone" | "loggedIn" | "members" | "nonMembers" | "plans";
|
|
5
|
+
/** UID des plans, pour `show="plans"`. */
|
|
6
|
+
plans?: string[] | undefined;
|
|
7
|
+
/** Ce qu'on rend à la place (défaut : rien). */
|
|
8
|
+
fallback?: ReactNode;
|
|
9
|
+
children: ReactNode;
|
|
10
|
+
};
|
|
11
|
+
/** Le visiteur tel que le serveur l'a reconnu, ou `null` tant qu'on ne sait pas. */
|
|
12
|
+
export declare function useVisiteur(): Visiteur | null;
|
|
13
|
+
export declare function visiteurCorrespond(v: Visiteur | null, show: AudienceProps["show"], plans?: string[]): boolean;
|
|
14
|
+
export declare function Audience({ show, plans, fallback, children }: AudienceProps): import("react").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type BoutonEditionProps = {
|
|
2
|
+
/** La personne est connectée par la connexion du site (ex. `useAuth().isLoggedIn`). */
|
|
3
|
+
connecte: boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Si le site le sait déjà (rôles chargés), on n'interroge pas le serveur.
|
|
6
|
+
* Indicatif seulement : c'est le serveur qui délivre (ou non) le jeton.
|
|
7
|
+
*/
|
|
8
|
+
peutEditer?: boolean | undefined;
|
|
9
|
+
/** Le coin du bas où se poser — l'opposé du panier ou du bouton d'aide. */
|
|
10
|
+
coin?: "gauche" | "droite" | undefined;
|
|
11
|
+
libelle?: string | undefined;
|
|
12
|
+
};
|
|
13
|
+
export declare function BoutonEdition(props: BoutonEditionProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export default function BoutonEditionCorps({ connecte, peutEditer, coin, libelle, }: {
|
|
2
|
+
/** La personne est connectée par la connexion du site (ex. `useAuth().isLoggedIn`). */
|
|
3
|
+
connecte: boolean;
|
|
4
|
+
/**
|
|
5
|
+
* Si le site le sait déjà (rôles chargés), on n'interroge pas le serveur.
|
|
6
|
+
* Indicatif seulement : c'est le serveur qui délivre (ou non) le jeton.
|
|
7
|
+
*/
|
|
8
|
+
peutEditer?: boolean | undefined;
|
|
9
|
+
/** Le coin du bas où se poser — l'opposé du panier ou du bouton d'aide. */
|
|
10
|
+
coin?: "gauche" | "droite" | undefined;
|
|
11
|
+
libelle?: string | undefined;
|
|
12
|
+
}): import("react").JSX.Element | null;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { SourceTexte } from './sourceTexte';
|
|
3
|
-
import { SectionPubliee } from './types';
|
|
3
|
+
import { SectionPubliee, Visiteur } from './types';
|
|
4
4
|
type Etat = "chargement" | "pret" | "erreur";
|
|
5
5
|
type Contexte = {
|
|
6
6
|
/** Sous-domaine de l'organisation dans Lystech. */
|
|
@@ -54,8 +54,57 @@ type Contexte = {
|
|
|
54
54
|
* Hors mode édition, l'appel échoue sans rien envoyer.
|
|
55
55
|
*/
|
|
56
56
|
appelEdition: (chemin: string, init?: RequestInit) => Promise<Response>;
|
|
57
|
+
/**
|
|
58
|
+
* OUVRIR LE MODULE BOUTIQUE de l'éditeur, depuis un bouton du site : sur la
|
|
59
|
+
* fiche d'un produit (son UID), une fiche vierge (« nouveau »), ou la liste
|
|
60
|
+
* (sans argument). Rend faux hors édition, ou si la personne connectée
|
|
61
|
+
* n'administre pas la boutique — le site peut alors cacher son bouton.
|
|
62
|
+
*/
|
|
63
|
+
ouvrirBoutique: (cible?: string | null) => boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Vrai dans l'APERÇU MOBILE de l'éditeur (la page affichée dans un cadre de
|
|
66
|
+
* 390 px). L'éditeur n'y est pas monté ; un site peut s'en servir pour
|
|
67
|
+
* couper ce qui n'a pas de sens dans un aperçu (une fenêtre surgissante).
|
|
68
|
+
*/
|
|
69
|
+
modeApercu: boolean;
|
|
70
|
+
/**
|
|
71
|
+
* LE VISITEUR, tel que le serveur l'a reconnu (session + adhésion), ou
|
|
72
|
+
* `null` tant qu'on ne sait pas (premier rendu, serveur ancien, réseau
|
|
73
|
+
* coupé). Sert à `<Audience>` ; le contenu réservé, lui, est déjà filtré
|
|
74
|
+
* par le serveur.
|
|
75
|
+
*/
|
|
76
|
+
visiteur: Visiteur | null;
|
|
77
|
+
/**
|
|
78
|
+
* ENTRER EN MODE ÉDITION avec un jeton obtenu par le site (le crayon
|
|
79
|
+
* flottant, `BoutonEdition`) : le jeton est rangé comme après une
|
|
80
|
+
* connexion, et l'éditeur se monte sur la page courante, SANS recharger.
|
|
81
|
+
*/
|
|
82
|
+
entrerEnEdition: (jeton: string) => void;
|
|
83
|
+
/**
|
|
84
|
+
* Le site a SA PROPRE connexion (une boutique @lystech/core) : on n'y
|
|
85
|
+
* propose pas la fenêtre de connexion de l'éditeur (« Connexion » de
|
|
86
|
+
* `SignatureLystech`) — le crayon flottant prend le relais.
|
|
87
|
+
*/
|
|
88
|
+
connexionPropre: boolean;
|
|
89
|
+
/**
|
|
90
|
+
* APPELER L'API AU NOM DU VISITEUR CONNECTÉ (sa session : cookie, et les
|
|
91
|
+
* en-têtes de `session`). Seuls les chemins de l'API sont acceptés.
|
|
92
|
+
*/
|
|
93
|
+
appelSession: (chemin: string, init?: RequestInit) => Promise<Response>;
|
|
94
|
+
};
|
|
95
|
+
/**
|
|
96
|
+
* LA SESSION DU VISITEUR, pour que le serveur reconnaisse un membre.
|
|
97
|
+
*
|
|
98
|
+
* Par défaut, le cookie de session de l'API est envoyé (`credentials:
|
|
99
|
+
* "include"`). Un site qui s'authentifie autrement (en-tête posé par
|
|
100
|
+
* @lystech/core) passe ses en-têtes ici. `cle` change quand la personne se
|
|
101
|
+
* connecte ou se déconnecte : le contenu est alors relu.
|
|
102
|
+
*/
|
|
103
|
+
export type SessionSections = {
|
|
104
|
+
cle?: string | null | undefined;
|
|
105
|
+
entetes?: Record<string, string> | undefined;
|
|
57
106
|
};
|
|
58
|
-
export declare function SectionsProvider({ site, page, langue, apiBaseUrl, sectionsInitiales, sourceTexte, pagesPartagees, children, }: {
|
|
107
|
+
export declare function SectionsProvider({ site, page, langue, apiBaseUrl, sectionsInitiales, sourceTexte, pagesPartagees, session, connexionPropre, children, }: {
|
|
59
108
|
/** Sous-domaine de l'organisation (ex. « armoise »). */
|
|
60
109
|
site: string;
|
|
61
110
|
/** La page dont on veut les sections (ex. « accueil »). */
|
|
@@ -93,6 +142,17 @@ export declare function SectionsProvider({ site, page, langue, apiBaseUrl, secti
|
|
|
93
142
|
* sur la page courante, jamais dans le commun par accident.
|
|
94
143
|
*/
|
|
95
144
|
pagesPartagees?: string[] | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* Qui regarde (voir `SessionSections`). Facultatif : sans elle, le cookie
|
|
147
|
+
* de session suffit quand le site et l'API le partagent.
|
|
148
|
+
*/
|
|
149
|
+
session?: SessionSections | undefined;
|
|
150
|
+
/**
|
|
151
|
+
* Le site a sa propre connexion (défaut : non). Vrai : la fenêtre de
|
|
152
|
+
* connexion de l'éditeur n'est plus offerte dans le pied de page ; on entre
|
|
153
|
+
* en édition par le crayon flottant (`BoutonEdition`) une fois connecté.
|
|
154
|
+
*/
|
|
155
|
+
connexionPropre?: boolean | undefined;
|
|
96
156
|
children: ReactNode;
|
|
97
157
|
}): import("react").JSX.Element;
|
|
98
158
|
/**
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export type MarqueProduit = {
|
|
2
|
+
"data-lystech-produit": string;
|
|
3
|
+
};
|
|
4
|
+
/** Les attributs à poser sur la carte d'un produit. */
|
|
5
|
+
export declare function marqueProduit(UID: string): MarqueProduit;
|
|
6
|
+
export type MarqueCollection = {
|
|
7
|
+
"data-lystech-collection": string;
|
|
8
|
+
"data-lystech-collection-affichee"?: "";
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Les attributs à poser sur l'élément qui montre une collection (un onglet du
|
|
12
|
+
* bandeau, l'en-tête de sa page). `affichee` : vrai pour celle dont la grille
|
|
13
|
+
* de la page montre les bijoux — la carte « + » de la grille y ajoute.
|
|
14
|
+
*
|
|
15
|
+
* <div className="collection" {...marqueCollection(c.UID, c.UID === choisie?.UID)}>
|
|
16
|
+
*/
|
|
17
|
+
export declare function marqueCollection(UID: string, affichee?: boolean): MarqueCollection;
|
|
18
|
+
/** Ce que l'éditeur dit d'un changement — pour relire juste ce qu'il faut, si on veut. */
|
|
19
|
+
export type ChangementCatalogue = {
|
|
20
|
+
quoi: "produit" | "photo" | "collection";
|
|
21
|
+
/** L'UID du produit ou de la collection touché. */
|
|
22
|
+
UID: string | null;
|
|
23
|
+
action: "cree" | "modifie" | "masque" | "visible" | "supprime" | "ajoute" | "retire";
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* Appelle `rappel` à chaque changement du catalogue fait par l'éditeur.
|
|
27
|
+
* La dernière version du rappel est toujours celle appelée : pas besoin de le
|
|
28
|
+
* mémoriser avec useCallback.
|
|
29
|
+
*/
|
|
30
|
+
export declare function useCatalogueModifie(rappel: (c: ChangementCatalogue) => void): void;
|
package/dist/constantes.d.ts
CHANGED
|
@@ -12,3 +12,41 @@
|
|
|
12
12
|
* général qu'un nom de classe (`rootClassName`).
|
|
13
13
|
*/
|
|
14
14
|
export declare const CLASSE_HORS_EDITION = "lystech-hors-edition";
|
|
15
|
+
/**
|
|
16
|
+
* LE MARQUAGE D'UNE CARTE PRODUIT : `data-lystech-produit="<UID>"`.
|
|
17
|
+
*
|
|
18
|
+
* Posé par le site sur l'élément qui encadre UN produit (sa carte). En mode
|
|
19
|
+
* édition, l'éditeur y pose un crayon qui ouvre la fiche du produit, et
|
|
20
|
+
* ajoute une tuile « + Ajouter » au bout de la grille. Hors édition, c'est un
|
|
21
|
+
* attribut inerte. `marqueProduit(UID)` l'écrit pour vous.
|
|
22
|
+
*/
|
|
23
|
+
export declare const ATTR_PRODUIT = "data-lystech-produit";
|
|
24
|
+
/**
|
|
25
|
+
* LE MARQUAGE D'UNE COLLECTION : `data-lystech-collection="<UID>"`, posé par
|
|
26
|
+
* le site sur l'élément qui la montre (l'onglet du bandeau, l'en-tête d'une
|
|
27
|
+
* page de collection). En mode édition, l'éditeur y pose un crayon (modifier
|
|
28
|
+
* la collection) et un « + » (y ajouter des bijoux). `marqueCollection(UID,
|
|
29
|
+
* affichee)` l'écrit pour vous.
|
|
30
|
+
*/
|
|
31
|
+
export declare const ATTR_COLLECTION = "data-lystech-collection";
|
|
32
|
+
/**
|
|
33
|
+
* « CETTE COLLECTION EST CELLE QUI S'AFFICHE » : `data-lystech-collection-affichee`,
|
|
34
|
+
* posé avec la marque quand la grille de la page montre ses bijoux. C'est
|
|
35
|
+
* elle que la carte « + » de la grille garnit ; sans elle (une page qui n'est
|
|
36
|
+
* pas une collection), la carte ne propose que de créer un bijou.
|
|
37
|
+
*/
|
|
38
|
+
export declare const ATTR_COLLECTION_AFFICHEE = "data-lystech-collection-affichee";
|
|
39
|
+
/**
|
|
40
|
+
* L'ÉVÉNEMENT QUI DIT « LE CATALOGUE A CHANGÉ » — émis sur `window` par
|
|
41
|
+
* l'éditeur après chaque enregistrement d'un produit, d'une photo ou d'une
|
|
42
|
+
* collection. Le site (ou @lystech/core) l'écoute pour relire ses produits :
|
|
43
|
+
* le catalogue s'écrit en direct, sans brouillon, et la page doit le montrer.
|
|
44
|
+
*/
|
|
45
|
+
export declare const EVENEMENT_CATALOGUE = "lystech:catalogue-modifie";
|
|
46
|
+
/**
|
|
47
|
+
* LE NOM DE LA FENÊTRE D'APERÇU MOBILE. L'éditeur affiche la page dans un
|
|
48
|
+
* cadre de 390 px portant ce nom ; le fournisseur qui s'y charge le
|
|
49
|
+
* reconnaît et NE monte PAS l'éditeur dedans. Le nom d'un cadre survit à ses
|
|
50
|
+
* navigations — un paramètre d'adresse, non.
|
|
51
|
+
*/
|
|
52
|
+
export declare const FENETRE_APERCU = "lystech-apercu";
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export declare const ATTR_AJOUT = "data-lystech-ajout";
|
|
2
|
+
/**
|
|
3
|
+
* LA GRILLE d'un ensemble de cartes : on remonte depuis chaque carte jusqu'au
|
|
4
|
+
* premier parent qui en contient une autre. Ce parent est la grille ; l'enfant
|
|
5
|
+
* par lequel on y est arrivé est la « case » de la carte (la carte elle-même,
|
|
6
|
+
* ou l'enveloppe que le site met autour). On garde la grille qui montre le
|
|
7
|
+
* plus de cartes : celle du catalogue, pas un « vous aimerez aussi ».
|
|
8
|
+
*/
|
|
9
|
+
export declare function trouverGrille(cartes: HTMLElement[]): {
|
|
10
|
+
grille: HTMLElement;
|
|
11
|
+
caseModele: HTMLElement;
|
|
12
|
+
} | null;
|
|
13
|
+
export type CibleCollection = {
|
|
14
|
+
UID: string;
|
|
15
|
+
nom: string;
|
|
16
|
+
} | null;
|
|
17
|
+
export declare const MarquesBoutique: import('react').NamedExoticComponent<{
|
|
18
|
+
/** Crayons sur les cartes et la collection (aucun panneau ouvert). */
|
|
19
|
+
crayons: boolean;
|
|
20
|
+
/** La carte « + » dans la grille (droit catalogue + mode « modifier »). */
|
|
21
|
+
carte: boolean;
|
|
22
|
+
ouvrirProduit: (UID: string) => void;
|
|
23
|
+
/** Le « + » : ajouter à la collection affichée (ou créer, sans collection). */
|
|
24
|
+
ouvrirAjout: (collection: CibleCollection) => void;
|
|
25
|
+
ouvrirCollection: (UID: string) => void;
|
|
26
|
+
}>;
|
package/dist/editeur/api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BlocDeclare } from '../types';
|
|
1
|
+
import { AudienceSection, BlocDeclare } from '../types';
|
|
2
2
|
/** Une section telle que l'éditeur la voit — brouillon compris. */
|
|
3
3
|
export type SectionEditeur = {
|
|
4
4
|
UID: string;
|
|
@@ -9,6 +9,8 @@ export type SectionEditeur = {
|
|
|
9
9
|
data: Record<string, unknown> | null;
|
|
10
10
|
draft: Record<string, unknown> | null;
|
|
11
11
|
publishedOn: string | null;
|
|
12
|
+
/** Qui peut la voir (null = tout le monde). */
|
|
13
|
+
audience?: AudienceSection | null;
|
|
12
14
|
};
|
|
13
15
|
export type Api = ReturnType<typeof creerApi>;
|
|
14
16
|
export declare function creerApi({ jeton, site, apiBaseUrl, surJetonRefuse, }: {
|
|
@@ -25,6 +27,7 @@ export declare function creerApi({ jeton, site, apiBaseUrl, surJetonRefuse, }: {
|
|
|
25
27
|
*/
|
|
26
28
|
surJetonRefuse: () => void;
|
|
27
29
|
}): {
|
|
30
|
+
brut: (chemin: string, init?: RequestInit) => Promise<Response>;
|
|
28
31
|
/** Les blocs que le site a déclaré savoir afficher. Route publique. */
|
|
29
32
|
schemas: () => Promise<BlocDeclare[]>;
|
|
30
33
|
/** Ce qui existe sur une page, brouillons compris. */
|
|
@@ -36,4 +39,16 @@ export declare function creerApi({ jeton, site, apiBaseUrl, surJetonRefuse, }: {
|
|
|
36
39
|
publier: (UID: string) => Promise<any>;
|
|
37
40
|
/** Ramène le brouillon à ce qui est en ligne. */
|
|
38
41
|
annuler: (UID: string) => Promise<any>;
|
|
42
|
+
/** Qui peut voir la section. S'applique TOUT DE SUITE (pas de brouillon). */
|
|
43
|
+
audience: (UID: string, audience: AudienceSection) => Promise<{
|
|
44
|
+
success: boolean;
|
|
45
|
+
audience: AudienceSection;
|
|
46
|
+
}>;
|
|
47
|
+
/** Les plans d'abonnement de la boutique, pour « Visible pour : plans… ». */
|
|
48
|
+
optionsAudience: () => Promise<{
|
|
49
|
+
plans: Array<{
|
|
50
|
+
UID: string;
|
|
51
|
+
name: string | null;
|
|
52
|
+
}>;
|
|
53
|
+
}>;
|
|
39
54
|
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ApiBoutique, Collection, CollectionBrouillon } from './api';
|
|
2
|
+
export declare function Collections({ api, collections, brouillons, surOuvrir, surCree, }: {
|
|
3
|
+
api: ApiBoutique;
|
|
4
|
+
collections: Collection[];
|
|
5
|
+
brouillons: CollectionBrouillon[];
|
|
6
|
+
surOuvrir: (UID: string) => void;
|
|
7
|
+
surCree: (c: CollectionBrouillon) => void;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ChangementCatalogue } from '../../catalogue';
|
|
2
|
+
import { ApiBoutique, Collection, CollectionBrouillon, NiveauAcces, Produit, ProduitBrouillon } from './api';
|
|
3
|
+
export declare const NIVEAUX: Array<{
|
|
4
|
+
acces: NiveauAcces;
|
|
5
|
+
titre: string;
|
|
6
|
+
aide: string;
|
|
7
|
+
}>;
|
|
8
|
+
export declare function FicheCollection({ api, collection, produits, brouillonsProduits, signaler, surRetour, majCollection, majProduits, majBrouillons, surAjouterExistants, surCreer, surOuvrirProduit, annonce, }: {
|
|
9
|
+
/** Un message à montrer en ouvrant la fiche (« 3 bijoux ajoutés »). */
|
|
10
|
+
annonce?: string | null;
|
|
11
|
+
api: ApiBoutique;
|
|
12
|
+
collection: Collection | CollectionBrouillon;
|
|
13
|
+
produits: Produit[] | null;
|
|
14
|
+
brouillonsProduits: ProduitBrouillon[];
|
|
15
|
+
signaler: (c: ChangementCatalogue) => void;
|
|
16
|
+
surRetour: () => void;
|
|
17
|
+
majCollection: (c: Collection | CollectionBrouillon) => void;
|
|
18
|
+
majProduits: () => void;
|
|
19
|
+
majBrouillons: () => void;
|
|
20
|
+
surAjouterExistants: () => void;
|
|
21
|
+
surCreer: () => void;
|
|
22
|
+
surOuvrirProduit: (UID: string) => void;
|
|
23
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { ChangementCatalogue } from '../../catalogue';
|
|
2
|
+
import { ApiBoutique, Produit, ProduitBrouillon } from './api';
|
|
3
|
+
import { CollectionChoix } from './commun';
|
|
4
|
+
export declare function FicheProduit({ api, produit, collections, collectionDefaut, surEnregistre, surSupprime, surRetour, surFermer, envoyerEnArrierePlan, photosEnCours, compteBrouillons, signaler, annonce, }: {
|
|
5
|
+
api: ApiBoutique;
|
|
6
|
+
/** null = nouveau bijou (en brouillon, en rafale). */
|
|
7
|
+
produit: Produit | ProduitBrouillon | null;
|
|
8
|
+
collections: CollectionChoix[];
|
|
9
|
+
/** La collection où ranger d'office les nouveaux bijoux (celle de la page). */
|
|
10
|
+
collectionDefaut?: string | null;
|
|
11
|
+
surEnregistre: (p: Produit | ProduitBrouillon) => void;
|
|
12
|
+
surSupprime: (UID: string) => void;
|
|
13
|
+
surRetour: () => void;
|
|
14
|
+
surFermer?: () => void;
|
|
15
|
+
/** Envoyer des photos à un brouillon sans bloquer la fiche (rafale). */
|
|
16
|
+
envoyerEnArrierePlan?: (UID: string, fichiers: File[]) => void;
|
|
17
|
+
/** Photos encore en route (toutes fiches confondues). */
|
|
18
|
+
photosEnCours?: number;
|
|
19
|
+
compteBrouillons?: number;
|
|
20
|
+
signaler: (c: ChangementCatalogue) => void;
|
|
21
|
+
/** Un message à afficher en ouvrant la fiche (« Bijou créé »). */
|
|
22
|
+
annonce?: string | null;
|
|
23
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ReactNode, RefObject } from 'react';
|
|
2
|
+
export declare function ListeVirtuelle<T>({ items, hauteur, conteneur, cle, rendu, surplus, etiquette, }: {
|
|
3
|
+
items: T[];
|
|
4
|
+
/** Hauteur d'une rangée, marge comprise, en px. */
|
|
5
|
+
hauteur: number;
|
|
6
|
+
/** L'élément qui défile (le corps du panneau). */
|
|
7
|
+
conteneur: RefObject<HTMLElement | null>;
|
|
8
|
+
cle: (item: T) => string;
|
|
9
|
+
rendu: (item: T, index: number) => ReactNode;
|
|
10
|
+
surplus?: number;
|
|
11
|
+
/** Nom de la liste pour les lecteurs d'écran. */
|
|
12
|
+
etiquette?: string;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { ChangementCatalogue } from '../../catalogue';
|
|
2
|
+
import { ApiBoutique } from './api';
|
|
3
|
+
export type CibleCollection = {
|
|
4
|
+
UID: string;
|
|
5
|
+
nom: string;
|
|
6
|
+
} | null;
|
|
7
|
+
/** Ce que l'éditeur demande d'ouvrir. `n` change à chaque demande, même identique. */
|
|
8
|
+
export type DemandeBoutique = {
|
|
9
|
+
n: number;
|
|
10
|
+
vue: "liste";
|
|
11
|
+
onglet?: "bijoux" | "collections";
|
|
12
|
+
} | {
|
|
13
|
+
n: number;
|
|
14
|
+
vue: "fiche";
|
|
15
|
+
UID: string;
|
|
16
|
+
} | {
|
|
17
|
+
n: number;
|
|
18
|
+
vue: "nouveau";
|
|
19
|
+
collection: string | null;
|
|
20
|
+
} | {
|
|
21
|
+
n: number;
|
|
22
|
+
vue: "ajout";
|
|
23
|
+
collection: CibleCollection;
|
|
24
|
+
} | {
|
|
25
|
+
n: number;
|
|
26
|
+
vue: "collection";
|
|
27
|
+
UID: string;
|
|
28
|
+
} | {
|
|
29
|
+
n: number;
|
|
30
|
+
vue: "publier";
|
|
31
|
+
};
|
|
32
|
+
declare function ModuleBoutique({ api, demande, surFermer, signaler, textes, publierTextes, surCompteBrouillons, }: {
|
|
33
|
+
api: ApiBoutique;
|
|
34
|
+
demande: DemandeBoutique;
|
|
35
|
+
surFermer: () => void;
|
|
36
|
+
signaler: (c: ChangementCatalogue) => void;
|
|
37
|
+
/** Textes du site en attente de publication (pour « Publier »). */
|
|
38
|
+
textes?: number;
|
|
39
|
+
publierTextes?: () => Promise<boolean>;
|
|
40
|
+
/** Le nombre de brouillons, pour la pastille de la barre de l'éditeur. */
|
|
41
|
+
surCompteBrouillons?: (n: number) => void;
|
|
42
|
+
}): import("react").JSX.Element;
|
|
43
|
+
declare const _default: import('react').MemoExoticComponent<typeof ModuleBoutique>;
|
|
44
|
+
export default _default;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { ApiBoutique, Brouillons, ResultatPublication } from './api';
|
|
2
|
+
export declare function Publier({ api, brouillons: frais, noms, textes, publierTextes, surPublie, surOuvrir, surRetour, }: {
|
|
3
|
+
api: ApiBoutique;
|
|
4
|
+
brouillons: Brouillons;
|
|
5
|
+
/** Noms des collections (en ligne et en brouillon), par UID. */
|
|
6
|
+
noms: Map<string, string>;
|
|
7
|
+
/** Textes du site modifiés, en attente (0 = aucun). */
|
|
8
|
+
textes: number;
|
|
9
|
+
publierTextes: () => Promise<boolean>;
|
|
10
|
+
/** Après publication : relire catalogue et brouillons. */
|
|
11
|
+
surPublie: (r: ResultatPublication[]) => void;
|
|
12
|
+
/** Ouvrir la fiche d'un brouillon à compléter. */
|
|
13
|
+
surOuvrir: (UID: string, type: "produit" | "collection") => void;
|
|
14
|
+
surRetour: () => void;
|
|
15
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Produit } from './api';
|
|
2
|
+
export declare function Selecteur({ produits, deja, nomCollection, occupe, surValider, surAnnuler, }: {
|
|
3
|
+
produits: Produit[] | null;
|
|
4
|
+
/** UIDs déjà dans la collection. */
|
|
5
|
+
deja: Set<string>;
|
|
6
|
+
nomCollection: string;
|
|
7
|
+
occupe: boolean;
|
|
8
|
+
surValider: (UIDs: string[]) => void;
|
|
9
|
+
surAnnuler: () => void;
|
|
10
|
+
}): import("react").JSX.Element;
|