@fraza/core 0.3.10 → 0.3.13

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/changelog.json +53 -0
  3. package/dist/docs/02-guides/etendre-l-admin.md +2 -2
  4. package/dist/docs/03-reference/02-features/admin.md +13 -2
  5. package/dist/docs/03-reference/04-partage/composants.md +2 -0
  6. package/dist/docs/06-modules/drive/index.md +1 -1
  7. package/dist/docs/06-modules/forms/02-configuration.md +2 -2
  8. package/dist/docs/assets/decouverte/tableau-de-bord.dark.webp +0 -0
  9. package/dist/docs/assets/decouverte/tableau-de-bord.light.webp +0 -0
  10. package/dist/docs/assets/demarrage/connexion.dark.webp +0 -0
  11. package/dist/docs/assets/demarrage/connexion.light.webp +0 -0
  12. package/dist/docs/assets/demarrage/roles.dark.webp +0 -0
  13. package/dist/docs/assets/demarrage/roles.light.webp +0 -0
  14. package/dist/docs/assets/demarrage/tableau-de-bord-neuf.dark.webp +0 -0
  15. package/dist/docs/assets/demarrage/tableau-de-bord-neuf.light.webp +0 -0
  16. package/dist/docs/assets/modules/booking/agenda.dark.webp +0 -0
  17. package/dist/docs/assets/modules/booking/agenda.light.webp +0 -0
  18. package/dist/docs/assets/modules/drive/navigateur.dark.webp +0 -0
  19. package/dist/docs/assets/modules/drive/navigateur.light.webp +0 -0
  20. package/dist/docs/assets/modules/facturation/factures.dark.webp +0 -0
  21. package/dist/docs/assets/modules/facturation/factures.light.webp +0 -0
  22. package/dist/docs/assets/modules/forms/constructeur.dark.webp +0 -0
  23. package/dist/docs/assets/modules/forms/constructeur.light.webp +0 -0
  24. package/dist/docs/assets/modules/medias/mediatheque.dark.webp +0 -0
  25. package/dist/docs/assets/modules/medias/mediatheque.light.webp +0 -0
  26. package/dist/docs/assets/modules/pages/liste.dark.webp +0 -0
  27. package/dist/docs/assets/modules/pages/liste.light.webp +0 -0
  28. package/dist/docs/assets/modules/posts/liste.dark.webp +0 -0
  29. package/dist/docs/assets/modules/posts/liste.light.webp +0 -0
  30. package/dist/docs/assets/reference/liste-compacte.dark.webp +0 -0
  31. package/dist/docs/assets/reference/liste-compacte.light.webp +0 -0
  32. package/dist/features/admin/components/dashboard/DashboardGrid.client.js +34 -23
  33. package/dist/shared/lib/version.js +1 -1
  34. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -4,6 +4,26 @@ Historique des versions publiées de `@fraza/core`.
4
4
 
5
5
  Ce fichier est généré par `fraza-module changelog` à partir des messages de commit. Ne pas le modifier à la main.
6
6
 
7
+ ## 0.3.13 (2026-10-25)
8
+
9
+ Maintenance interne, aucun changement de comportement.
10
+
11
+ ## 0.3.12 (2026-10-25)
12
+
13
+ ### Documentation
14
+
15
+ - update screenshots for various pages (08afd94)
16
+
17
+ ## 0.3.11 (2026-10-25)
18
+
19
+ ### Corrections
20
+
21
+ - **admin** : update dashboard grid layout documentation and implementation (8a86eb3)
22
+
23
+ ### Documentation
24
+
25
+ - **form** : update section grouping and order in admin configuration (5ae2e37)
26
+
7
27
  ## 0.3.10 (2026-10-25)
8
28
 
9
29
  ### Autres
package/changelog.json CHANGED
@@ -1,6 +1,59 @@
1
1
  {
2
2
  "name": "@fraza/core",
3
3
  "versions": [
4
+ {
5
+ "version": "0.3.13",
6
+ "date": "2026-10-25",
7
+ "breaking": [],
8
+ "envAdded": [],
9
+ "envRemoved": [],
10
+ "templateChanged": [],
11
+ "action": [],
12
+ "entries": []
13
+ },
14
+ {
15
+ "version": "0.3.12",
16
+ "date": "2026-10-25",
17
+ "breaking": [],
18
+ "envAdded": [],
19
+ "envRemoved": [],
20
+ "templateChanged": [],
21
+ "action": [],
22
+ "entries": [
23
+ {
24
+ "type": "docs",
25
+ "scope": null,
26
+ "subject": "update screenshots for various pages",
27
+ "hash": "08afd94",
28
+ "breaking": false
29
+ }
30
+ ]
31
+ },
32
+ {
33
+ "version": "0.3.11",
34
+ "date": "2026-10-25",
35
+ "breaking": [],
36
+ "envAdded": [],
37
+ "envRemoved": [],
38
+ "templateChanged": [],
39
+ "action": [],
40
+ "entries": [
41
+ {
42
+ "type": "fix",
43
+ "scope": "admin",
44
+ "subject": "update dashboard grid layout documentation and implementation",
45
+ "hash": "8a86eb3",
46
+ "breaking": false
47
+ },
48
+ {
49
+ "type": "docs",
50
+ "scope": "form",
51
+ "subject": "update section grouping and order in admin configuration",
52
+ "hash": "5ae2e37",
53
+ "breaking": false
54
+ }
55
+ ]
56
+ },
4
57
  {
5
58
  "version": "0.3.10",
6
59
  "date": "2026-10-25",
@@ -189,12 +189,12 @@ Le composant reçoit `data`, le retour du fetcher, `loading` et `size`. **Si le
189
189
 
190
190
  ### Largeur d'une tuile
191
191
 
192
- La grille compte 4 colonnes sur un écran large. `size` déclare la largeur d'une tuile en colonnes : `default` à l'arrivée, `min` et `max` pour borner ce que l'utilisateur peut choisir. Sans `size`, une tuile fait une colonne et peut s'étendre à quatre.
192
+ La grille compte jusqu'à 4 colonnes, selon la place dont elle dispose : 3 sur un portable de 1440 px, une sur un téléphone. `size` déclare la largeur d'une tuile en colonnes : `default` à l'arrivée, `min` et `max` pour borner ce que l'utilisateur peut choisir. Sans `size`, une tuile fait une colonne et peut s'étendre à quatre.
193
193
 
194
194
  ```jsx
195
195
  function OrdersWidget({ data, loading, size }) {
196
196
  // `size` est la largeur réellement occupée : 1 sur un téléphone, au plus 2
197
- // sur un écran moyen, la largeur choisie sur un grand écran.
197
+ // sur un écran moyen, 3 sur un portable, la largeur choisie sur un grand écran.
198
198
  const wide = size >= 2;
199
199
  return (
200
200
  <Card padding="none" spacing="none" className="px-5 py-4">
@@ -210,7 +210,7 @@ Le composant reçoit trois props :
210
210
  |---|---|
211
211
  | `data` | Retour du fetcher, ou `null` si le fetcher a échoué ou n'existe pas. |
212
212
  | `loading` | `true` tant que la donnée n'est pas là. |
213
- | `size` | Largeur réellement occupée, en colonnes : la largeur choisie, bornée par les colonnes de l'écran. |
213
+ | `size` | Largeur réellement occupée, en colonnes : la largeur choisie, bornée par les colonnes de la grille. |
214
214
 
215
215
  `size` sert à adapter le rendu. La tuile de facturation pose son graphique à côté de ses chiffres à partir de deux colonnes, et dessous sur une seule. Sur un téléphone, `size` vaut toujours 1.
216
216
 
@@ -222,7 +222,18 @@ Une tuile `devkit` est écartée par le tableau de bord, pas par le registre. `F
222
222
 
223
223
  ## Disposition du tableau de bord
224
224
 
225
- Les tuiles sont posées dans une **grille de 4 colonnes** à partir de 80rem de large, de 2 colonnes à partir de 48rem, d'une seule en dessous. Une tuile occupe de 1 à 4 colonnes, bornées par celles de l'écran, et sa hauteur suit son contenu.
225
+ Les tuiles sont posées dans une grille de 1 à 4 colonnes. Le nombre suit la **largeur de la grille elle-même**, pas celle de la fenêtre : la barre latérale prend déjà le quart d'un écran de portable, et quatre colonnes dans le reste donnaient des tuiles trop étroites pour se lire.
226
+
227
+ | Largeur de la grille | Colonnes | Avec la barre latérale |
228
+ |---|---|---|
229
+ | 80rem (1280 px) et plus | 4 | fenêtre d'environ 1510 px et plus |
230
+ | de 64rem (1024 px) à 80rem | 3 | de 1250 à 1510 px environ, dont le portable de 1440 px |
231
+ | de 42rem (672 px) à 64rem | 2 | de 900 à 1250 px environ |
232
+ | moins de 42rem | 1 | téléphone |
233
+
234
+ Une tuile occupe de 1 à 4 colonnes, bornées par celles de la grille : une tuile de 4 colonnes en prend 3 sur un portable, soit toute la largeur. Sa hauteur suit son contenu.
235
+
236
+ Les colonnes sont des requêtes de conteneur Tailwind (`@container`, puis `@2xl:`, `@5xl:` et `@7xl:`) : le bon nombre est déjà dans le HTML du serveur, sans saut au chargement. Le composant mesure aussi la largeur de la grille, avec les mêmes seuils, pour donner à chaque tuile son `size` et borner le redimensionnement en mode édition.
226
237
 
227
238
  La grille se tasse d'elle-même. Ses rangées font 4 px, et chaque tuile en couvre autant que sa hauteur mesurée l'exige, espacement compris : un `ResizeObserver` suit la hauteur de chaque tuile. Avec `grid-auto-flow: dense`, une tuile courte remonte dans le vide laissé à côté d'une tuile haute. Le résultat est celui d'une maçonnerie, avec en plus des tuiles qui s'étendent sur plusieurs colonnes. Avant la mesure (rendu serveur, puis premier affichage) la grille garde des rangées ordinaires : une tuile peut y laisser un vide sous elle, jamais en chevaucher une autre.
228
239
 
@@ -663,6 +663,8 @@ Un clic qui part d'un contrôle de la ligne lui appartient : un bouton, un lien,
663
663
 
664
664
  Sous 1024 px de large, `Table` ne rend plus le tableau mais une liste : une ligne par élément, deux lignes de texte au plus. Une carte par élément, qui reprenait toutes les colonnes en grille, faisait défiler trois éléments par écran de téléphone. La liste en montre une dizaine, et le détail reste à un toucher.
665
665
 
666
+ ![La liste des factures sur un téléphone : une ligne par facture, le client et le numéro à gauche, le montant et le statut à droite](../../assets/reference/liste-compacte.light.webp)
667
+
666
668
  ```
667
669
  ┌────────────────────────────────────────────────────┐
668
670
  │ ☐ Boréalis Café inc. 1 345,21 $ … │
@@ -25,7 +25,7 @@ L'activation, la création des tables et le réglage derrière un proxy sont dan
25
25
 
26
26
  C'est ce qui le distingue d'un Nextcloud. Il n'y a ni client de synchronisation, ni édition en ligne, ni commentaires. Il y a un navigateur de dossiers, des liens que l'on règle finement, et un historique qui répond à « qui a touché à ce fichier ».
27
27
 
28
- ![Le navigateur de fichiers, à la racine de l'espace partagé](../../assets/modules/drive/navigateur.light.webp)
28
+ ![Le navigateur de fichiers, dans le dossier d'un client de l'espace partagé](../../assets/modules/drive/navigateur.light.webp)
29
29
 
30
30
  Le navigateur ouvre à la racine de l'espace partagé, ce que rappelle la pastille sous le titre. La barre latérale donne la surface entière du module : l'espace partagé, l'espace personnel et les favoris quand ils sont activés, ce qui a été partagé avec soi, les liens émis, l'historique et la corbeille.
31
31
 
@@ -93,11 +93,11 @@ Le module enregistre une section contenant deux items.
93
93
 
94
94
  | Élément | Valeur |
95
95
  |---|---|
96
- | Section | `Formulaires`, icône `ChecklistIcon`, ordre 75. |
96
+ | Section | `Formulaires`, groupe `content`, icône `ChecklistIcon`, ordre 75. |
97
97
  | Item 1 | `Formulaires`, `/admin/forms`, icône `ChecklistIcon`, permission `forms.view`. |
98
98
  | Item 2 | `Soumissions`, `/admin/forms/submissions`, icône `InboxIcon`, permission `forms.view`. |
99
99
 
100
- L'ordre 75 place la section après les modules de contenu. Voir [Étendre l'admin](../../02-guides/etendre-l-admin.md).
100
+ Le groupe `content` range la section dans la partie Contenu de la barre latérale, et l'ordre 75 la place après les pages. Voir [Étendre l'admin](../../02-guides/etendre-l-admin.md).
101
101
 
102
102
  ## Notes
103
103
 
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
- import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
3
+ import { useCallback, useEffect, useId, useLayoutEffect, useRef, useState } from "react";
4
4
  import AdminErrorBoundary from "../../AdminErrorBoundary.client.js";
5
5
  import Button from "../../../../shared/components/Button.js";
6
6
  import { GripVerticalIcon } from "@fraza/icons/GripVerticalIcon";
@@ -8,29 +8,39 @@ import { CloseIcon } from "@fraza/icons/CloseIcon";
8
8
  import { GRID_COLUMNS, clampWidth } from "../../dashboard-layout.js";
9
9
  const ROW_HEIGHT = 4;
10
10
  const GAP = 16;
11
+ const COLUMN_STEPS = [
12
+ { min: 1280, columns: GRID_COLUMNS },
13
+ { min: 1024, columns: 3 },
14
+ { min: 672, columns: 2 }
15
+ ];
16
+ const GRID_CLASS = "grid-cols-1 @2xl:grid-cols-2 @5xl:grid-cols-3 @7xl:grid-cols-4";
11
17
  const SPAN_CLASS = {
12
18
  1: "col-span-1",
13
- 2: "col-span-1 md:col-span-2",
14
- 3: "col-span-1 md:col-span-2 xl:col-span-3",
15
- 4: "col-span-1 md:col-span-2 xl:col-span-4"
19
+ 2: "col-span-1 @2xl:col-span-2",
20
+ 3: "col-span-1 @2xl:col-span-2 @5xl:col-span-3",
21
+ 4: "col-span-1 @2xl:col-span-2 @5xl:col-span-3 @7xl:col-span-4"
16
22
  };
17
- const QUERIES = [
18
- { query: "(min-width: 80rem)", columns: GRID_COLUMNS },
19
- { query: "(min-width: 48rem)", columns: 2 }
20
- ];
21
- function subscribeColumns(onChange) {
22
- const lists = QUERIES.map(({ query }) => window.matchMedia(query));
23
- for (const list of lists) list.addEventListener("change", onChange);
24
- return () => {
25
- for (const list of lists) list.removeEventListener("change", onChange);
26
- };
27
- }
28
- function readColumns() {
23
+ function columnsFor(width) {
29
24
  var _a;
30
- return ((_a = QUERIES.find(({ query }) => window.matchMedia(query).matches)) == null ? void 0 : _a.columns) ?? 1;
25
+ return ((_a = COLUMN_STEPS.find((step) => width >= step.min)) == null ? void 0 : _a.columns) ?? 1;
31
26
  }
32
- function useGridColumns() {
33
- return useSyncExternalStore(subscribeColumns, readColumns, () => GRID_COLUMNS);
27
+ function useGridColumns(ref) {
28
+ const [columns, setColumns] = useState(GRID_COLUMNS);
29
+ useLayoutEffect(() => {
30
+ const node = ref.current;
31
+ if (!node) return void 0;
32
+ const update = (width) => setColumns(columnsFor(width));
33
+ update(node.getBoundingClientRect().width);
34
+ if (typeof ResizeObserver === "undefined") return void 0;
35
+ const observer = new ResizeObserver(([entry]) => {
36
+ var _a;
37
+ const box = (_a = entry.contentBoxSize) == null ? void 0 : _a[0];
38
+ update(box ? box.inlineSize : entry.target.getBoundingClientRect().width);
39
+ });
40
+ observer.observe(node);
41
+ return () => observer.disconnect();
42
+ }, [ref]);
43
+ return columns;
34
44
  }
35
45
  function WidgetError({ reset }) {
36
46
  return /* @__PURE__ */ jsxs("div", { role: "alert", className: "bg-surface border border-line rounded-card p-5 flex flex-col items-start gap-3", children: [
@@ -153,7 +163,8 @@ const move = (items, from, to) => {
153
163
  return next;
154
164
  };
155
165
  function DashboardGrid({ items, widgets, stats, loading, editing, onChange }) {
156
- const columns = useGridColumns();
166
+ const containerRef = useRef(null);
167
+ const columns = useGridColumns(containerRef);
157
168
  const gridRef = useRef(null);
158
169
  const instructionsId = useId();
159
170
  const [measured, setMeasured] = useState(false);
@@ -324,11 +335,11 @@ function DashboardGrid({ items, widgets, stats, loading, editing, onChange }) {
324
335
  return /* @__PURE__ */ jsxs(Fragment, { children: [
325
336
  editing && /* @__PURE__ */ jsx("p", { id: instructionsId, className: "sr-only", children: "Glisser la poign\xE9e pour d\xE9placer le widget. Au clavier, les fl\xE8ches le d\xE9placent et Maj avec les fl\xE8ches gauche et droite change sa largeur." }),
326
337
  /* @__PURE__ */ jsx("div", { "aria-live": "polite", className: "sr-only", children: editing ? announcement : "" }),
327
- /* @__PURE__ */ jsx(
338
+ /* @__PURE__ */ jsx("div", { ref: containerRef, className: "@container", children: /* @__PURE__ */ jsx(
328
339
  "div",
329
340
  {
330
341
  ref: gridRef,
331
- className: `grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 grid-flow-row-dense gap-x-4 ${editing ? "pt-1" : ""}`,
342
+ className: `grid ${GRID_CLASS} grid-flow-row-dense gap-x-4 ${editing ? "pt-1" : ""}`,
332
343
  style: measured ? { gridAutoRows: `${ROW_HEIGHT}px`, rowGap: 0 } : { rowGap: GAP },
333
344
  children: items.map((item) => {
334
345
  const widget = widgets.get(item.id);
@@ -350,7 +361,7 @@ function DashboardGrid({ items, widgets, stats, loading, editing, onChange }) {
350
361
  );
351
362
  })
352
363
  }
353
- ),
364
+ ) }),
354
365
  ghost && dragId && /* @__PURE__ */ jsx(
355
366
  "div",
356
367
  {
@@ -1,4 +1,4 @@
1
- const CORE_VERSION = "0.3.10";
1
+ const CORE_VERSION = "0.3.13";
2
2
  export {
3
3
  CORE_VERSION
4
4
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fraza/core",
3
- "version": "0.3.10",
3
+ "version": "0.3.13",
4
4
  "description": "Starter kit Next.js avec un back-office modulaire, conçu au Québec.",
5
5
  "publishConfig": {
6
6
  "access": "public"