@cardinali-studio/adminkit 1.0.0 → 2.1.0-rc.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/api.d.ts +8 -1
- package/dist/api.js +1 -1
- package/dist/app/App.js +8 -3
- package/dist/app/app.css +27 -0
- package/dist/app/lib/pages.d.ts +11 -0
- package/dist/app/lib/pages.js +17 -1
- package/dist/app/routes/PageActions.js +1 -2
- package/dist/app/routes/PageEditor.d.ts +12 -2
- package/dist/app/routes/PageEditor.js +82 -14
- package/dist/app/routes/PageSeoEditor.d.ts +4 -1
- package/dist/app/routes/PageSeoEditor.js +2 -2
- package/dist/app/routes/PagesList.d.ts +5 -1
- package/dist/app/routes/PagesList.js +12 -6
- package/dist/app/site.d.ts +21 -4
- package/dist/app/site.js +3 -0
- package/dist/form/SchemaForm.d.ts +3 -1
- package/dist/form/SchemaForm.js +14 -8
- package/dist/form/fields/objectList.js +9 -6
- package/dist/form/registry.d.ts +2 -0
- package/dist/functions/_shared/media-content.js +10 -2
- package/dist/functions/_shared/translations.js +18 -0
- package/dist/functions/api/pages/get.js +34 -9
- package/dist/functions/api/pages.js +116 -8
- package/dist/functions/site.js +15 -0
- package/dist/i18n.d.ts +26 -0
- package/dist/i18n.js +119 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +1 -1
package/dist/i18n.js
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/* Bilinguismo opzionale, interamente guidato dallo schema.
|
|
2
|
+
*
|
|
3
|
+
* Un campo traducibile non è una seconda forma di contenuto: è lo stesso campo, marcato nello
|
|
4
|
+
* schema. Il file della lingua secondaria resta completo, ma questo modulo ricopia dal modello
|
|
5
|
+
* della lingua predefinita ogni valore che non porta il marchio. Così i template Hugo leggono
|
|
6
|
+
* file ordinari e non devono conoscere alcuna logica di merge.
|
|
7
|
+
*/
|
|
8
|
+
import * as z from "zod";
|
|
9
|
+
/** Registro separato da `ui`: la scelta influenza il Worker e il codec, non solo il form. */
|
|
10
|
+
export const i18n = z.registry();
|
|
11
|
+
/** Marca un campo (anche un array o un oggetto intero) come modificabile in una traduzione. */
|
|
12
|
+
export function translatable(field) {
|
|
13
|
+
// Zod esprime `register()` con un conditional type che non conserva il nostro generico T;
|
|
14
|
+
// il registro è comunque quello tipizzato appena sopra e l'operazione non trasforma il campo.
|
|
15
|
+
field.register(i18n, { translatable: true });
|
|
16
|
+
return field;
|
|
17
|
+
}
|
|
18
|
+
export function isTranslatable(schema) {
|
|
19
|
+
return i18n.get(schema)?.translatable === true;
|
|
20
|
+
}
|
|
21
|
+
function defOf(schema) {
|
|
22
|
+
return schema?._zod?.def ?? schema?._def ?? null;
|
|
23
|
+
}
|
|
24
|
+
function shapeOf(schema) {
|
|
25
|
+
const def = defOf(schema);
|
|
26
|
+
if (def?.type === "object" || def?.typeName === "ZodObject") {
|
|
27
|
+
return (typeof def.shape === "function" ? def.shape() : def.shape) ?? null;
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
function elementOf(schema) {
|
|
32
|
+
const def = defOf(schema);
|
|
33
|
+
if (def?.type === "array" || def?.typeName === "ZodArray")
|
|
34
|
+
return def.element ?? def.type ?? null;
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
function clone(value) {
|
|
38
|
+
if (Array.isArray(value))
|
|
39
|
+
return value.map(clone);
|
|
40
|
+
if (value && typeof value === "object") {
|
|
41
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, clone(v)]));
|
|
42
|
+
}
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
/** Unisce una traduzione al modello predefinito senza lasciarle modificare dati ereditati.
|
|
46
|
+
*
|
|
47
|
+
* Gli array non traducibili mantengono la loro struttura predefinita; i loro elementi possono
|
|
48
|
+
* però contenere campi foglia traducibili. Se un'intera lista deve essere traducibile (titolo
|
|
49
|
+
* spezzato in righe, paragrafi, ecc.), va marcata la lista stessa, non ogni sua stringa. */
|
|
50
|
+
export function mergeTranslation(schema, defaultValue, translationValue) {
|
|
51
|
+
if (isTranslatable(schema)) {
|
|
52
|
+
return clone(translationValue === undefined ? defaultValue : translationValue);
|
|
53
|
+
}
|
|
54
|
+
const shape = shapeOf(schema);
|
|
55
|
+
if (shape) {
|
|
56
|
+
const source = defaultValue && typeof defaultValue === "object" && !Array.isArray(defaultValue)
|
|
57
|
+
? defaultValue : {};
|
|
58
|
+
const translated = translationValue && typeof translationValue === "object" && !Array.isArray(translationValue)
|
|
59
|
+
? translationValue : {};
|
|
60
|
+
return Object.fromEntries(Object.entries(shape).map(([key, child]) => [
|
|
61
|
+
key,
|
|
62
|
+
mergeTranslation(child, source[key], translated[key]),
|
|
63
|
+
]));
|
|
64
|
+
}
|
|
65
|
+
const element = elementOf(schema);
|
|
66
|
+
if (element) {
|
|
67
|
+
const source = Array.isArray(defaultValue) ? defaultValue : [];
|
|
68
|
+
const translated = Array.isArray(translationValue) ? translationValue : [];
|
|
69
|
+
return source.map((value, index) => mergeTranslation(element, value, translated[index]));
|
|
70
|
+
}
|
|
71
|
+
return clone(defaultValue);
|
|
72
|
+
}
|
|
73
|
+
/** Modello completo della traduzione: utile sia al primo salvataggio sia a ogni risalvataggio. */
|
|
74
|
+
export function translationModel(shape, defaultModel, translationModel = {}) {
|
|
75
|
+
return Object.fromEntries(Object.entries(shape).map(([key, schema]) => [
|
|
76
|
+
key,
|
|
77
|
+
mergeTranslation(schema, defaultModel[key], translationModel[key]),
|
|
78
|
+
]));
|
|
79
|
+
}
|
|
80
|
+
/** Percorsi dei valori che saranno sempre ricopiati dalla lingua predefinita. */
|
|
81
|
+
export function inheritedFieldPaths(shape) {
|
|
82
|
+
const visit = (schema, path) => {
|
|
83
|
+
if (isTranslatable(schema))
|
|
84
|
+
return [];
|
|
85
|
+
const nested = shapeOf(schema);
|
|
86
|
+
if (nested)
|
|
87
|
+
return Object.entries(nested).flatMap(([key, child]) => visit(child, `${path}.${key}`));
|
|
88
|
+
const element = elementOf(schema);
|
|
89
|
+
if (element) {
|
|
90
|
+
const child = shapeOf(element);
|
|
91
|
+
return child
|
|
92
|
+
? Object.entries(child).flatMap(([key, nestedSchema]) => visit(nestedSchema, `${path}[].${key}`))
|
|
93
|
+
: [path];
|
|
94
|
+
}
|
|
95
|
+
return [path];
|
|
96
|
+
};
|
|
97
|
+
return Object.entries(shape).flatMap(([key, schema]) => visit(schema, key));
|
|
98
|
+
}
|
|
99
|
+
const HEADER_START = "# adminkit: translation metadata (generated)";
|
|
100
|
+
const HEADER_END = "# adminkit: end translation metadata";
|
|
101
|
+
/** Inserisce (o aggiorna) l'avviso che impedisce modifiche manuali ingannevoli ai dati ereditati. */
|
|
102
|
+
export function withTranslationHeader(markdown, options) {
|
|
103
|
+
if (!markdown.startsWith("---\n"))
|
|
104
|
+
throw new Error("Una traduzione richiede front matter YAML.");
|
|
105
|
+
const inherited = options.inherited.length ? options.inherited.join(", ") : "nessuno";
|
|
106
|
+
const header = [
|
|
107
|
+
HEADER_START,
|
|
108
|
+
`# Lingua: ${options.language}. I campi non traducibili sono ereditati da ${options.defaultLanguage}.`,
|
|
109
|
+
`# Campi ereditati: ${inherited}. Modificarli qui non ha effetto: il pannello li risincronizza.`,
|
|
110
|
+
HEADER_END,
|
|
111
|
+
].join("\n") + "\n";
|
|
112
|
+
let rest = markdown.slice(4);
|
|
113
|
+
if (rest.startsWith(`${HEADER_START}\n`)) {
|
|
114
|
+
const end = rest.indexOf(`${HEADER_END}\n`);
|
|
115
|
+
if (end >= 0)
|
|
116
|
+
rest = rest.slice(end + HEADER_END.length + 1);
|
|
117
|
+
}
|
|
118
|
+
return `---\n${header}${rest}`;
|
|
119
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cardinali-studio/adminkit",
|
|
3
|
-
"version": "1.0.0",
|
|
3
|
+
"version": "2.1.0-rc.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Motore riusabile di un pannello di amministrazione per siti Hugo su Cloudflare Pages: codec del front matter guidato dallo schema, form React, libreria media su R2. NIENTE di specifico di un sito qui dentro.",
|
|
6
6
|
"license": "UNLICENSED",
|