@lystech/sections 0.1.0 → 0.2.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/dist/index.d.ts CHANGED
@@ -1,108 +1,16 @@
1
- import { JSX } from 'react';
2
- import { ReactNode } from 'react';
3
-
4
- export declare type BlocDeclare = {
5
- blockType: string;
6
- label: string;
7
- description?: string;
8
- fields: ChampDeclare[];
9
- pages?: string[] | null;
10
- maxPerPage?: number | null;
11
- };
12
-
13
- export declare type ChampDeclare = {
14
- key: string;
15
- label: string;
16
- type: TypeDeChamp;
17
- required?: boolean;
18
- help?: string;
19
- options?: string[];
20
- /** Sous-champs d'une `list`. */
21
- of?: ChampDeclare[];
22
- /**
23
- * Champ TRADUIT. La valeur stockée devient { fr: …, en: … } et le paquet
24
- * rend celle de la langue courante.
25
- *
26
- * Le serveur ne connaît pas cette clé — il stocke la déclaration telle
27
- * quelle. C'est voulu : ajouter une langue ne doit pas demander une
28
- * migration côté plateforme.
29
- */
30
- localized?: boolean;
31
- };
32
-
33
- declare type Contexte = {
34
- /** Sous-domaine de l'organisation dans Lystech. */
35
- site: string;
36
- /** Langue courante — décide quelle version d'un champ traduit est rendue. */
37
- langue: string;
38
- sections: SectionPubliee[];
39
- etat: Etat;
40
- recharger: () => void;
41
- };
42
-
43
- declare type Etat = "chargement" | "pret" | "erreur";
44
-
45
- export declare type ResultatSection<T> = {
46
- /** Le contenu à afficher : publié si présent, défaut sinon. */
47
- contenu: T;
48
- /** Vrai quand le contenu vient du serveur, faux quand c'est le défaut. */
49
- publie: boolean;
50
- /** L'identifiant de la section — nécessaire à l'édition. */
51
- UID: string | null;
52
- chargement: boolean;
53
- };
54
-
55
- /** Une section telle qu'elle revient du serveur. */
56
- export declare type SectionPubliee = {
57
- UID: string;
58
- page: string;
59
- blockType: string;
60
- position: number;
61
- data: Record<string, unknown> | null;
62
- };
63
-
64
- export declare function SectionsProvider({ site, page, langue, apiBaseUrl, children, }: {
65
- /** Sous-domaine de l'organisation (ex. « armoise »). */
66
- site: string;
67
- /** La page dont on veut les sections (ex. « accueil »). */
68
- page: string;
69
- langue?: string;
70
- apiBaseUrl?: string;
71
- children: ReactNode;
72
- }): JSX.Element;
73
-
74
- export declare type TypeDeChamp = "text" | "richText" | "image" | "number" | "boolean" | "url" | "date" | "choice" | "list";
75
-
76
- /**
77
- * Le contenu d'un type de bloc sur la page courante.
78
- *
79
- * Le cas visé est celui d'un site DÉJÀ DESSINÉ : un bloc par type et par page,
80
- * dont on veut rendre les mots et les images modifiables — pas un constructeur
81
- * de pages. Pour des blocs répétables, voir `useSectionList`.
82
- */
83
- export declare function useSection<T extends object>(blockType: string, options: {
84
- defaut: T;
85
- }): ResultatSection<T>;
86
-
87
- /** Plusieurs blocs du même type, dans l'ordre choisi par le client. */
88
- export declare function useSectionList<T extends object>(blockType: string, options?: {
89
- defaut?: T[];
90
- }): {
91
- items: Array<T & {
92
- __uid: string;
93
- }>;
94
- publie: boolean;
95
- chargement: boolean;
96
- };
97
-
98
- /**
99
- * Le contexte, ou `null` HORS d'un fournisseur.
100
- *
101
- * On ne lève pas : un composant branché sur les sections doit rester
102
- * utilisable dans une page qui n'en a pas (une maquette, un test, une page
103
- * d'erreur). Il affichera son repli, ce qui est exactement le bon
104
- * comportement.
105
- */
106
- export declare function useSectionsContext(): Contexte | null;
107
-
108
- export { }
1
+ /**
2
+ * @lystech/sections — le contenu d'un site, modifiable par son propriétaire.
3
+ *
4
+ * CE QUI EST EXPORTÉ ICI EST CE QU'UN VISITEUR TÉLÉCHARGE : la lecture, et
5
+ * rien d'autre. L'interface d'édition vivra dans un point d'entrée séparé,
6
+ * chargé à la demande. Un visiteur ne doit pas payer le poids d'un éditeur
7
+ * qu'il ne verra jamais.
8
+ *
9
+ * Le paquet ne dépend PAS de @lystech/core : une clinique qui veut modifier
10
+ * trois textes n'a aucune raison de charger un magasin, un panier et PayPal.
11
+ */
12
+ export { SectionsProvider, useSectionsContext } from './SectionsProvider';
13
+ export { SignatureLystech } from './SignatureLystech';
14
+ export { useSection, useSectionList } from './useSection';
15
+ export type { BlocDeclare, ChampDeclare, SectionPubliee, TypeDeChamp, } from './types';
16
+ export type { AncrageEdition, ResultatSection } from './useSection';
package/dist/index.js CHANGED
@@ -1,107 +1,262 @@
1
- import { jsx as j } from "react/jsx-runtime";
2
- import { createContext as x, useState as l, useRef as C, useEffect as S, useMemo as m, useContext as E } from "react";
3
- const b = x(null), _ = "https://api.lystech.ca/api";
4
- function D({
1
+ import { jsx as h, jsxs as j, Fragment as F } from "react/jsx-runtime";
2
+ import { useState as m, useRef as _, useEffect as A, useCallback as I, useMemo as C, useContext as M, createContext as T } from "react";
3
+ const d = "lystech-edit", U = T(null), $ = "https://api.lystech.ca/api";
4
+ function J() {
5
+ if (typeof window > "u") return null;
6
+ const e = (t) => {
7
+ try {
8
+ window.sessionStorage.setItem(d, t);
9
+ } catch {
10
+ }
11
+ return t;
12
+ };
13
+ try {
14
+ const t = window.location.hash.replace(/^#/, "");
15
+ if (t.includes(d + "=")) {
16
+ const o = new URLSearchParams(t), c = o.get(d);
17
+ if (c) {
18
+ o.delete(d);
19
+ const a = o.toString();
20
+ return window.history.replaceState(
21
+ {},
22
+ "",
23
+ window.location.pathname + window.location.search + (a ? "#" + a : "")
24
+ ), e(c);
25
+ }
26
+ }
27
+ const n = new URL(window.location.href), r = n.searchParams.get(d);
28
+ return r ? (n.searchParams.delete(d), window.history.replaceState({}, "", n.toString()), e(r)) : window.sessionStorage.getItem(d);
29
+ } catch {
30
+ return null;
31
+ }
32
+ }
33
+ function q({
5
34
  site: e,
6
- page: r,
7
- langue: t = "fr",
8
- apiBaseUrl: o = _,
9
- children: n
35
+ page: t,
36
+ langue: n = "fr",
37
+ apiBaseUrl: r = $,
38
+ sectionsInitiales: o,
39
+ children: c
10
40
  }) {
11
- const [c, a] = l([]), [d, f] = l("chargement"), [A, g] = l(0), h = C(null);
12
- S(() => {
13
- if (!e || !r) return;
14
- h.current?.abort();
15
- const u = new AbortController();
16
- return h.current = u, f("chargement"), fetch(
17
- `${o}/site-sections/public/${encodeURIComponent(e)}/${encodeURIComponent(r)}`,
18
- { signal: u.signal }
19
- ).then((s) => s.ok ? s.json() : Promise.reject(new Error(String(s.status)))).then((s) => {
20
- u.signal.aborted || (a(Array.isArray(s) ? s : []), f("pret"));
41
+ const [a, p] = m(
42
+ o ?? []
43
+ ), [u, y] = m(
44
+ o?.length ? "pret" : "chargement"
45
+ ), [g, b] = m(0), [P, z] = m({}), [v, O] = m(!1), D = _(null);
46
+ A(() => {
47
+ if (!e || !t) return;
48
+ D.current?.abort();
49
+ const i = new AbortController();
50
+ D.current = i, y("chargement");
51
+ const f = r + "/site-sections/public/" + encodeURIComponent(e) + "/" + encodeURIComponent(t);
52
+ return fetch(f, { signal: i.signal }).then(
53
+ (s) => s.ok ? s.json() : Promise.reject(new Error(String(s.status)))
54
+ ).then((s) => {
55
+ i.signal.aborted || (p(Array.isArray(s) ? s : []), y("pret"));
21
56
  }).catch((s) => {
22
- u.signal.aborted || s?.name === "AbortError" || f("erreur");
23
- }), () => u.abort();
24
- }, [e, r, o, A]);
25
- const p = m(
57
+ i.signal.aborted || s?.name === "AbortError" || y("erreur");
58
+ }), () => i.abort();
59
+ }, [e, t, r, g]);
60
+ const E = I(() => b((i) => i + 1), []), x = I(
61
+ (i, f) => z((s) => ({ ...s, [i]: f })),
62
+ []
63
+ ), l = _({
64
+ site: e,
65
+ page: t,
66
+ langue: n,
67
+ poserApercu: () => {
68
+ },
69
+ recharger: () => {
70
+ }
71
+ });
72
+ l.current.site = e, l.current.page = t, l.current.langue = n, l.current.poserApercu = x, l.current.recharger = E, A(() => {
73
+ l.current.surChangementDePage?.(t);
74
+ }, [t]), A(() => {
75
+ const i = J();
76
+ if (!i) return;
77
+ let f = !0, s;
78
+ return import("@lystech/sections/editeur").then((w) => {
79
+ f && (O(!0), s = w.monterEditeur({
80
+ jeton: i,
81
+ site: e,
82
+ apiBaseUrl: r,
83
+ pont: l.current
84
+ }));
85
+ }).catch((w) => {
86
+ console.error("[lystech] éditeur indisponible:", w?.message ?? w);
87
+ }), () => {
88
+ f = !1, s?.();
89
+ };
90
+ }, [e, r]);
91
+ const k = C(
26
92
  () => ({
27
93
  site: e,
28
- langue: t,
29
- sections: c,
30
- etat: d,
31
- recharger: () => g((u) => u + 1)
94
+ page: t,
95
+ langue: n,
96
+ apiBaseUrl: r,
97
+ sections: a,
98
+ etat: u,
99
+ recharger: E,
100
+ apercu: P,
101
+ poserApercu: x,
102
+ modeEdition: v
32
103
  }),
33
- [e, t, c, d]
104
+ [
105
+ e,
106
+ t,
107
+ n,
108
+ r,
109
+ a,
110
+ u,
111
+ E,
112
+ P,
113
+ x,
114
+ v
115
+ ]
34
116
  );
35
- return /* @__PURE__ */ j(b.Provider, { value: p, children: n });
117
+ return /* @__PURE__ */ h(U.Provider, { value: k, children: c });
118
+ }
119
+ function R() {
120
+ return M(U);
121
+ }
122
+ const W = "https://api.lystech.ca/api";
123
+ function G() {
124
+ if (typeof window > "u") return null;
125
+ try {
126
+ const e = new URL(window.location.href), t = e.searchParams.get("lystech-acces");
127
+ return t ? (e.searchParams.delete("lystech-acces"), window.history.replaceState({}, "", e.toString()), t) : null;
128
+ } catch {
129
+ return null;
130
+ }
36
131
  }
37
- function y() {
38
- return E(b);
132
+ const L = {
133
+ "sans-acces": "Vous n'avez pas encore accès à la modification de ce site.",
134
+ "demande-envoyee": "Demande envoyée. La personne responsable du site vous répondra.",
135
+ "trop-de-demandes": "Trop de demandes aujourd'hui. Réessayez demain."
136
+ };
137
+ function K({
138
+ site: e,
139
+ apiBaseUrl: t,
140
+ className: n,
141
+ mention: r = "Réalisé avec Lystech",
142
+ connexion: o = "Connexion"
143
+ }) {
144
+ const c = R(), a = e ?? c?.site ?? "", p = t ?? c?.apiBaseUrl ?? W, [u, y] = m(null);
145
+ if (A(() => y(G()), []), !a) return null;
146
+ const g = (b) => p + "/site-sections/edit-entry?site=" + encodeURIComponent(a) + (b ? "&demander=1" : "");
147
+ return /* @__PURE__ */ j(
148
+ "div",
149
+ {
150
+ className: n,
151
+ style: {
152
+ display: "flex",
153
+ flexWrap: "wrap",
154
+ alignItems: "center",
155
+ gap: "0.5em",
156
+ fontSize: "0.85em",
157
+ opacity: 0.7,
158
+ lineHeight: 1.5
159
+ },
160
+ children: [
161
+ /* @__PURE__ */ h(
162
+ "a",
163
+ {
164
+ href: "https://lystech.ca",
165
+ target: "_blank",
166
+ rel: "noreferrer",
167
+ style: { color: "inherit" },
168
+ children: r
169
+ }
170
+ ),
171
+ !c?.modeEdition && !u && /* @__PURE__ */ j(F, { children: [
172
+ /* @__PURE__ */ h("span", { "aria-hidden": "true", children: "·" }),
173
+ /* @__PURE__ */ h("a", { href: g(!1), style: { color: "inherit" }, children: o })
174
+ ] }),
175
+ u && /* @__PURE__ */ j("span", { style: { display: "inline-flex", gap: "0.5em", flexWrap: "wrap" }, children: [
176
+ /* @__PURE__ */ h("span", { "aria-hidden": "true", children: "·" }),
177
+ /* @__PURE__ */ h("span", { children: L[u] ?? L["sans-acces"] }),
178
+ u === "sans-acces" && /* @__PURE__ */ h("a", { href: g(!0), style: { color: "inherit" }, children: "Demander l'accès" })
179
+ ] })
180
+ ]
181
+ }
182
+ );
39
183
  }
40
- function I(e) {
41
- return !!e && typeof e == "object" && !Array.isArray(e) && Object.keys(e).every((r) => /^[a-z]{2}(-[A-Za-z]{2})?$/.test(r));
184
+ function H(e) {
185
+ return !!e && typeof e == "object" && !Array.isArray(e) && Object.keys(e).every((t) => /^[a-z]{2}(-[A-Za-z]{2})?$/.test(t));
42
186
  }
43
- function i(e, r) {
44
- if (Array.isArray(e)) return e.map((t) => i(t, r));
45
- if (I(e)) {
46
- const t = e, o = r.slice(0, 2);
47
- return t[r] ?? t[o] ?? Object.values(t).find((c) => c != null) ?? "";
187
+ function S(e, t) {
188
+ if (Array.isArray(e)) return e.map((n) => S(n, t));
189
+ if (H(e)) {
190
+ const n = e, r = t.slice(0, 2);
191
+ return n[t] ?? n[r] ?? Object.values(n).find((c) => c != null) ?? "";
48
192
  }
49
193
  if (e && typeof e == "object") {
50
- const t = {};
51
- for (const [o, n] of Object.entries(e))
52
- t[o] = i(n, r);
53
- return t;
194
+ const n = {};
195
+ for (const [r, o] of Object.entries(e))
196
+ n[r] = S(o, t);
197
+ return n;
54
198
  }
55
199
  return e;
56
200
  }
57
- function U(e, r) {
58
- if (!r || typeof r != "object" || Array.isArray(r))
201
+ function N(e, t) {
202
+ if (!t || typeof t != "object" || Array.isArray(t))
59
203
  return e;
60
- const t = { ...e };
61
- for (const [o, n] of Object.entries(r))
62
- n != null && (t[o] = n);
63
- return t;
204
+ const n = { ...e };
205
+ for (const [r, o] of Object.entries(t))
206
+ o != null && (n[r] = o);
207
+ return n;
64
208
  }
65
- function O(e, r) {
66
- const t = y();
67
- return m(() => {
68
- const o = t?.sections.find(
69
- (a) => a.blockType === e
70
- ), n = o?.data ?? null, c = n ? i(n, t?.langue ?? "fr") : null;
209
+ function Q(e, t) {
210
+ const n = R(), r = n?.apercu?.[e];
211
+ return C(() => {
212
+ const o = n?.sections.find(
213
+ (p) => p.blockType === e
214
+ ), c = r ?? o?.data ?? null, a = c ? S(c, n?.langue ?? "fr") : null;
71
215
  return {
72
- contenu: U(r.defaut, c),
73
- publie: !!n,
216
+ contenu: N(t.defaut, a),
217
+ publie: !!o?.data,
74
218
  UID: o?.UID ?? null,
75
- chargement: t?.etat === "chargement"
219
+ chargement: n?.etat === "chargement",
220
+ ancrage: { "data-lystech-section": e }
76
221
  };
77
- }, [t?.sections, t?.langue, t?.etat, e, r.defaut]);
222
+ }, [
223
+ n?.sections,
224
+ n?.langue,
225
+ n?.etat,
226
+ r,
227
+ e,
228
+ t.defaut
229
+ ]);
78
230
  }
79
- function P(e, r) {
80
- const t = y();
81
- return m(() => {
82
- const o = (t?.sections ?? []).filter(
83
- (n) => n.blockType === e
231
+ function X(e, t) {
232
+ const n = R();
233
+ return C(() => {
234
+ const r = { "data-lystech-section": e }, o = (n?.sections ?? []).filter(
235
+ (c) => c.blockType === e
84
236
  );
85
237
  return o.length === 0 ? {
86
- items: (r?.defaut ?? []).map((n, c) => ({
87
- ...n,
88
- __uid: `defaut-${c}`
238
+ items: (t?.defaut ?? []).map((c, a) => ({
239
+ ...c,
240
+ __uid: `defaut-${a}`
89
241
  })),
90
242
  publie: !1,
91
- chargement: t?.etat === "chargement"
243
+ chargement: n?.etat === "chargement",
244
+ ancrage: r
92
245
  } : {
93
- items: o.map((n) => ({
94
- ...i(n.data ?? {}, t?.langue ?? "fr"),
95
- __uid: n.UID
246
+ items: o.map((c) => ({
247
+ ...S(c.data ?? {}, n?.langue ?? "fr"),
248
+ __uid: c.UID
96
249
  })),
97
250
  publie: !0,
98
- chargement: t?.etat === "chargement"
251
+ chargement: n?.etat === "chargement",
252
+ ancrage: r
99
253
  };
100
- }, [t?.sections, t?.langue, t?.etat, e, r?.defaut]);
254
+ }, [n?.sections, n?.langue, n?.etat, e, t?.defaut]);
101
255
  }
102
256
  export {
103
- D as SectionsProvider,
104
- O as useSection,
105
- P as useSectionList,
106
- y as useSectionsContext
257
+ q as SectionsProvider,
258
+ K as SignatureLystech,
259
+ Q as useSection,
260
+ X as useSectionList,
261
+ R as useSectionsContext
107
262
  };
@@ -0,0 +1,41 @@
1
+ export type TypeDeChamp = "text" | "richText" | "image" | "number" | "boolean" | "url" | "date" | "choice" | "list";
2
+ export type ChampDeclare = {
3
+ key: string;
4
+ label: string;
5
+ type: TypeDeChamp;
6
+ required?: boolean;
7
+ help?: string;
8
+ options?: string[];
9
+ /** Sous-champs d'une `list`. */
10
+ of?: ChampDeclare[];
11
+ /**
12
+ * Champ TRADUIT. La valeur stockée devient { fr: …, en: … } et le paquet
13
+ * rend celle de la langue courante.
14
+ *
15
+ * Le serveur ne connaît pas cette clé — il stocke la déclaration telle
16
+ * quelle. C'est voulu : ajouter une langue ne doit pas demander une
17
+ * migration côté plateforme.
18
+ */
19
+ localized?: boolean;
20
+ };
21
+ export type BlocDeclare = {
22
+ blockType: string;
23
+ label: string;
24
+ description?: string;
25
+ fields: ChampDeclare[];
26
+ pages?: string[] | null;
27
+ maxPerPage?: number | null;
28
+ };
29
+ /** Une section telle qu'elle revient du serveur. */
30
+ export type SectionPubliee = {
31
+ UID: string;
32
+ page: string;
33
+ blockType: string;
34
+ position: number;
35
+ data: Record<string, unknown> | null;
36
+ };
37
+ export type ValeurTraduite = {
38
+ [langue: string]: unknown;
39
+ };
40
+ /** Un champ traduit est un objet de langues ; on le reconnaît sans schéma. */
41
+ export declare function estTraduite(v: unknown): v is ValeurTraduite;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * LES ATTRIBUTS À POSER SUR LE CONTENEUR DE LA SECTION.
3
+ *
4
+ * L'éditeur doit pouvoir retrouver, DANS LA PAGE, le rectangle qui correspond
5
+ * à un bloc — pour l'encadrer et y poser un bouton. Il n'existe aucun moyen
6
+ * fiable de le deviner : deux sections peuvent afficher les mêmes mots, et un
7
+ * site est libre de sa structure. Le site le dit donc explicitement, en
8
+ * étalant ces attributs sur son élément :
9
+ *
10
+ * const { contenu, ancrage } = useSection("tarifs", { defaut });
11
+ * return <section {...ancrage}>…</section>;
12
+ *
13
+ * Hors mode édition, c'est un attribut `data-` inerte : aucun poids, aucun
14
+ * effet, aucun style.
15
+ */
16
+ export type AncrageEdition = {
17
+ "data-lystech-section": string;
18
+ };
19
+ export type ResultatSection<T> = {
20
+ /** Le contenu à afficher : aperçu, sinon publié, sinon défaut. */
21
+ contenu: T;
22
+ /** Vrai quand le contenu vient du serveur, faux quand c'est le défaut. */
23
+ publie: boolean;
24
+ /** L'identifiant de la section — nécessaire à l'édition. */
25
+ UID: string | null;
26
+ chargement: boolean;
27
+ /** À étaler sur le conteneur pour que l'éditeur puisse le situer. */
28
+ ancrage: AncrageEdition;
29
+ };
30
+ /**
31
+ * Le contenu d'un type de bloc sur la page courante.
32
+ *
33
+ * Le cas visé est celui d'un site DÉJÀ DESSINÉ : un bloc par type et par page,
34
+ * dont on veut rendre les mots et les images modifiables — pas un constructeur
35
+ * de pages. Pour des blocs répétables, voir `useSectionList`.
36
+ */
37
+ export declare function useSection<T extends object>(blockType: string, options: {
38
+ defaut: T;
39
+ }): ResultatSection<T>;
40
+ /** Plusieurs blocs du même type, dans l'ordre choisi par le client. */
41
+ export declare function useSectionList<T extends object>(blockType: string, options?: {
42
+ defaut?: T[];
43
+ }): {
44
+ items: Array<T & {
45
+ __uid: string;
46
+ }>;
47
+ publie: boolean;
48
+ chargement: boolean;
49
+ ancrage: AncrageEdition;
50
+ };
package/package.json CHANGED
@@ -1,18 +1,22 @@
1
1
  {
2
2
  "name": "@lystech/sections",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Contenu de site editable par le client — lecture legere, edition chargee a la demande.",
5
5
  "type": "module",
6
- "main": "./dist/index.umd.cjs",
6
+ "main": "./dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "types": "./dist/index.d.ts",
9
9
  "exports": {
10
10
  ".": {
11
11
  "types": "./dist/index.d.ts",
12
- "import": "./dist/index.js",
13
- "require": "./dist/index.umd.cjs"
12
+ "import": "./dist/index.js"
13
+ },
14
+ "./editeur": {
15
+ "types": "./dist/editeur/index.d.ts",
16
+ "import": "./dist/editeur.js"
14
17
  }
15
18
  },
19
+ "sideEffects": false,
16
20
  "files": ["dist"],
17
21
  "scripts": {
18
22
  "build": "tsc -b && vite build",
@@ -1 +0,0 @@
1
- (function(s,a){typeof exports=="object"&&typeof module<"u"?a(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],a):(s=typeof globalThis<"u"?globalThis:s||self,a(s.LystechSections={},s.jsxRuntime,s.React))})(this,(function(s,a,c){"use strict";const y=c.createContext(null),j="https://api.lystech.ca/api";function A({site:t,page:n,langue:e="fr",apiBaseUrl:o=j,children:r}){const[i,l]=c.useState([]),[S,h]=c.useState("chargement"),[E,P]=c.useState(0),b=c.useRef(null);c.useEffect(()=>{if(!t||!n)return;b.current?.abort();const f=new AbortController;return b.current=f,h("chargement"),fetch(`${o}/site-sections/public/${encodeURIComponent(t)}/${encodeURIComponent(n)}`,{signal:f.signal}).then(u=>u.ok?u.json():Promise.reject(new Error(String(u.status)))).then(u=>{f.signal.aborted||(l(Array.isArray(u)?u:[]),h("pret"))}).catch(u=>{f.signal.aborted||u?.name==="AbortError"||h("erreur")}),()=>f.abort()},[t,n,o,E]);const R=c.useMemo(()=>({site:t,langue:e,sections:i,etat:S,recharger:()=>P(f=>f+1)}),[t,e,i,S]);return a.jsx(y.Provider,{value:R,children:r})}function m(){return c.useContext(y)}function g(t){return!!t&&typeof t=="object"&&!Array.isArray(t)&&Object.keys(t).every(n=>/^[a-z]{2}(-[A-Za-z]{2})?$/.test(n))}function d(t,n){if(Array.isArray(t))return t.map(e=>d(e,n));if(g(t)){const e=t,o=n.slice(0,2);return e[n]??e[o]??Object.values(e).find(i=>i!=null)??""}if(t&&typeof t=="object"){const e={};for(const[o,r]of Object.entries(t))e[o]=d(r,n);return e}return t}function p(t,n){if(!n||typeof n!="object"||Array.isArray(n))return t;const e={...t};for(const[o,r]of Object.entries(n))r!=null&&(e[o]=r);return e}function x(t,n){const e=m();return c.useMemo(()=>{const o=e?.sections.find(l=>l.blockType===t),r=o?.data??null,i=r?d(r,e?.langue??"fr"):null;return{contenu:p(n.defaut,i),publie:!!r,UID:o?.UID??null,chargement:e?.etat==="chargement"}},[e?.sections,e?.langue,e?.etat,t,n.defaut])}function C(t,n){const e=m();return c.useMemo(()=>{const o=(e?.sections??[]).filter(r=>r.blockType===t);return o.length===0?{items:(n?.defaut??[]).map((r,i)=>({...r,__uid:`defaut-${i}`})),publie:!1,chargement:e?.etat==="chargement"}:{items:o.map(r=>({...d(r.data??{},e?.langue??"fr"),__uid:r.UID})),publie:!0,chargement:e?.etat==="chargement"}},[e?.sections,e?.langue,e?.etat,t,n?.defaut])}s.SectionsProvider=A,s.useSection=x,s.useSectionList=C,s.useSectionsContext=m,Object.defineProperty(s,Symbol.toStringTag,{value:"Module"})}));