@fraza/core 0.3.10 → 0.3.12
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/CHANGELOG.md +16 -0
- package/changelog.json +43 -0
- package/dist/docs/02-guides/etendre-l-admin.md +2 -2
- package/dist/docs/03-reference/02-features/admin.md +13 -2
- package/dist/docs/03-reference/04-partage/composants.md +2 -0
- package/dist/docs/06-modules/drive/index.md +1 -1
- package/dist/docs/06-modules/forms/02-configuration.md +2 -2
- package/dist/docs/assets/decouverte/tableau-de-bord.dark.webp +0 -0
- package/dist/docs/assets/decouverte/tableau-de-bord.light.webp +0 -0
- package/dist/docs/assets/demarrage/connexion.dark.webp +0 -0
- package/dist/docs/assets/demarrage/connexion.light.webp +0 -0
- package/dist/docs/assets/demarrage/roles.dark.webp +0 -0
- package/dist/docs/assets/demarrage/roles.light.webp +0 -0
- package/dist/docs/assets/demarrage/tableau-de-bord-neuf.dark.webp +0 -0
- package/dist/docs/assets/demarrage/tableau-de-bord-neuf.light.webp +0 -0
- package/dist/docs/assets/modules/booking/agenda.dark.webp +0 -0
- package/dist/docs/assets/modules/booking/agenda.light.webp +0 -0
- package/dist/docs/assets/modules/drive/navigateur.dark.webp +0 -0
- package/dist/docs/assets/modules/drive/navigateur.light.webp +0 -0
- package/dist/docs/assets/modules/facturation/factures.dark.webp +0 -0
- package/dist/docs/assets/modules/facturation/factures.light.webp +0 -0
- package/dist/docs/assets/modules/forms/constructeur.dark.webp +0 -0
- package/dist/docs/assets/modules/forms/constructeur.light.webp +0 -0
- package/dist/docs/assets/modules/medias/mediatheque.dark.webp +0 -0
- package/dist/docs/assets/modules/medias/mediatheque.light.webp +0 -0
- package/dist/docs/assets/modules/pages/liste.dark.webp +0 -0
- package/dist/docs/assets/modules/pages/liste.light.webp +0 -0
- package/dist/docs/assets/modules/posts/liste.dark.webp +0 -0
- package/dist/docs/assets/modules/posts/liste.light.webp +0 -0
- package/dist/docs/assets/reference/liste-compacte.dark.webp +0 -0
- package/dist/docs/assets/reference/liste-compacte.light.webp +0 -0
- package/dist/features/admin/components/dashboard/DashboardGrid.client.js +34 -23
- package/dist/shared/lib/version.js +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,22 @@ 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.12 (2026-10-25)
|
|
8
|
+
|
|
9
|
+
### Documentation
|
|
10
|
+
|
|
11
|
+
- update screenshots for various pages (08afd94)
|
|
12
|
+
|
|
13
|
+
## 0.3.11 (2026-10-25)
|
|
14
|
+
|
|
15
|
+
### Corrections
|
|
16
|
+
|
|
17
|
+
- **admin** : update dashboard grid layout documentation and implementation (8a86eb3)
|
|
18
|
+
|
|
19
|
+
### Documentation
|
|
20
|
+
|
|
21
|
+
- **form** : update section grouping and order in admin configuration (5ae2e37)
|
|
22
|
+
|
|
7
23
|
## 0.3.10 (2026-10-25)
|
|
8
24
|
|
|
9
25
|
### Autres
|
package/changelog.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fraza/core",
|
|
3
3
|
"versions": [
|
|
4
|
+
{
|
|
5
|
+
"version": "0.3.12",
|
|
6
|
+
"date": "2026-10-25",
|
|
7
|
+
"breaking": [],
|
|
8
|
+
"envAdded": [],
|
|
9
|
+
"envRemoved": [],
|
|
10
|
+
"templateChanged": [],
|
|
11
|
+
"action": [],
|
|
12
|
+
"entries": [
|
|
13
|
+
{
|
|
14
|
+
"type": "docs",
|
|
15
|
+
"scope": null,
|
|
16
|
+
"subject": "update screenshots for various pages",
|
|
17
|
+
"hash": "08afd94",
|
|
18
|
+
"breaking": false
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"version": "0.3.11",
|
|
24
|
+
"date": "2026-10-25",
|
|
25
|
+
"breaking": [],
|
|
26
|
+
"envAdded": [],
|
|
27
|
+
"envRemoved": [],
|
|
28
|
+
"templateChanged": [],
|
|
29
|
+
"action": [],
|
|
30
|
+
"entries": [
|
|
31
|
+
{
|
|
32
|
+
"type": "fix",
|
|
33
|
+
"scope": "admin",
|
|
34
|
+
"subject": "update dashboard grid layout documentation and implementation",
|
|
35
|
+
"hash": "8a86eb3",
|
|
36
|
+
"breaking": false
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"type": "docs",
|
|
40
|
+
"scope": "form",
|
|
41
|
+
"subject": "update section grouping and order in admin configuration",
|
|
42
|
+
"hash": "5ae2e37",
|
|
43
|
+
"breaking": false
|
|
44
|
+
}
|
|
45
|
+
]
|
|
46
|
+
},
|
|
4
47
|
{
|
|
5
48
|
"version": "0.3.10",
|
|
6
49
|
"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
|
|
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
|
|
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
|
|
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
|
+

|
|
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
|
-

|
|
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
|
-
|
|
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
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -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
|
|
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
|
|
14
|
-
3: "col-span-1
|
|
15
|
-
4: "col-span-1
|
|
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
|
-
|
|
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 =
|
|
25
|
+
return ((_a = COLUMN_STEPS.find((step) => width >= step.min)) == null ? void 0 : _a.columns) ?? 1;
|
|
31
26
|
}
|
|
32
|
-
function useGridColumns() {
|
|
33
|
-
|
|
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
|
|
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
|
|
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
|
{
|