@vertebr/react 0.5.0 → 0.6.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/components/admin/AdminContent.d.ts +11 -2
- package/dist/components/admin/AdminContent.d.ts.map +1 -1
- package/dist/components/admin/AdminContent.js +35 -10
- package/dist/components/admin/AdminHeader.d.ts +3 -1
- package/dist/components/admin/AdminHeader.d.ts.map +1 -1
- package/dist/components/admin/AdminHeader.js +12 -3
- package/dist/components/admin/PagePreviewPane.d.ts +14 -0
- package/dist/components/admin/PagePreviewPane.d.ts.map +1 -0
- package/dist/components/admin/PagePreviewPane.js +121 -0
- package/dist/components/admin/VertebrAdmin.d.ts +1 -1
- package/dist/components/admin/VertebrAdmin.d.ts.map +1 -1
- package/dist/components/admin/VertebrAdmin.js +123 -41
- package/dist/components/admin/routes.d.ts +21 -0
- package/dist/components/admin/routes.d.ts.map +1 -0
- package/dist/components/admin/routes.js +170 -0
- package/dist/components/admin/types.d.ts +7 -0
- package/dist/components/admin/types.d.ts.map +1 -1
- package/dist/components/config/VisibilityEye.d.ts +12 -0
- package/dist/components/config/VisibilityEye.d.ts.map +1 -1
- package/dist/components/config/VisibilityEye.js +12 -4
- package/dist/components/config/form/ConfigField.d.ts +5 -3
- package/dist/components/config/form/ConfigField.d.ts.map +1 -1
- package/dist/components/config/form/ConfigField.js +7 -3
- package/dist/components/config/form/ConfigForm.d.ts +1 -1
- package/dist/components/config/form/ConfigForm.d.ts.map +1 -1
- package/dist/components/config/form/ConfigForm.js +5 -1
- package/dist/components/config/form/RecordConfigForm.d.ts +1 -1
- package/dist/components/config/form/RecordConfigForm.d.ts.map +1 -1
- package/dist/components/config/form/RecordConfigForm.js +2 -2
- package/dist/components/config/form/SettingsConfigForm.d.ts +3 -3
- package/dist/components/config/form/SettingsConfigForm.d.ts.map +1 -1
- package/dist/components/config/form/SettingsConfigForm.js +7 -2
- package/dist/components/config/form/helpers/fields.d.ts +2 -1
- package/dist/components/config/form/helpers/fields.d.ts.map +1 -1
- package/dist/components/config/form/helpers/fields.js +4 -0
- package/dist/components/config/form/types.d.ts +6 -1
- package/dist/components/config/form/types.d.ts.map +1 -1
- package/dist/components/fonts/FontFaceInjector.d.ts +9 -0
- package/dist/components/fonts/FontFaceInjector.d.ts.map +1 -0
- package/dist/components/fonts/FontFaceInjector.js +28 -0
- package/dist/components/fonts/FontLibraryBoard.d.ts +15 -0
- package/dist/components/fonts/FontLibraryBoard.d.ts.map +1 -0
- package/dist/components/fonts/FontLibraryBoard.js +41 -0
- package/dist/components/fonts/FontPickerField.d.ts +16 -0
- package/dist/components/fonts/FontPickerField.d.ts.map +1 -0
- package/dist/components/fonts/FontPickerField.js +49 -0
- package/dist/components/fonts/FontPreviewModal.d.ts +18 -0
- package/dist/components/fonts/FontPreviewModal.d.ts.map +1 -0
- package/dist/components/fonts/FontPreviewModal.js +57 -0
- package/dist/components/fonts/FontPreviewSample.d.ts +8 -0
- package/dist/components/fonts/FontPreviewSample.d.ts.map +1 -0
- package/dist/components/fonts/FontPreviewSample.js +13 -0
- package/dist/components/fonts/GoogleFontsBrowser.d.ts +8 -0
- package/dist/components/fonts/GoogleFontsBrowser.d.ts.map +1 -0
- package/dist/components/fonts/GoogleFontsBrowser.js +49 -0
- package/dist/components/resources/ResourceCell.js +3 -2
- package/dist/components/resources/ResourceTable.d.ts +1 -1
- package/dist/components/resources/ResourceTable.d.ts.map +1 -1
- package/dist/components/resources/ResourceTable.js +123 -5
- package/dist/hooks/useVertebrControls.d.ts +10 -1
- package/dist/hooks/useVertebrControls.d.ts.map +1 -1
- package/dist/hooks/useVertebrControls.js +32 -3
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/styles/base.css +3 -3
- package/dist/styles/font-library.css +401 -0
- package/dist/styles/form.css +89 -3
- package/dist/styles/index.css +3 -1
- package/dist/styles/main.css +1 -1
- package/dist/styles/page-preview.css +208 -0
- package/dist/styles/panel.css +7 -2
- package/dist/styles/states.css +6 -3
- package/dist/styles/table.css +18 -0
- package/package.json +1 -1
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { CatalogResponse } from "@vertebr/client";
|
|
2
|
+
import type { Selection } from "./types";
|
|
3
|
+
/**
|
|
4
|
+
* Derive the admin SPA base path from the API base.
|
|
5
|
+
* `/admin/api` → `/admin`, `/custom/api` → `/custom`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function adminBaseFromApiBase(apiBase: string): string;
|
|
8
|
+
/** Relative admin route for a selection (e.g. `/pages/home`). */
|
|
9
|
+
export declare function pathForSelection(selection: Selection, catalog: CatalogResponse): string;
|
|
10
|
+
export declare function hrefForSelection(basePath: string, selection: Selection, catalog: CatalogResponse): string;
|
|
11
|
+
/**
|
|
12
|
+
* Parse the location pathname into a selection.
|
|
13
|
+
* Returns null when the path is empty/unknown (caller picks a catalog default).
|
|
14
|
+
*/
|
|
15
|
+
export declare function selectionFromPath(pathname: string, basePath: string, catalog: CatalogResponse | null): Selection | null;
|
|
16
|
+
/** First valid panel when the URL has no usable selection. */
|
|
17
|
+
export declare function defaultSelection(catalog: CatalogResponse): Selection;
|
|
18
|
+
/** Keep a selection only if it still exists in the catalog. */
|
|
19
|
+
export declare function coerceSelection(selection: Selection, catalog: CatalogResponse): Selection;
|
|
20
|
+
export declare function selectionsEqual(a: Selection, b: Selection): boolean;
|
|
21
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../../src/components/admin/routes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACvD,OAAO,KAAK,EAAa,SAAS,EAAE,MAAM,SAAS,CAAC;AAIpD;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAO5D;AAOD,iEAAiE;AACjE,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,eAAe,GACvB,MAAM,CAuBR;AAED,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,eAAe,GACvB,MAAM,CAIR;AASD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,GAAG,IAAI,GAC9B,SAAS,GAAG,IAAI,CAoClB;AAED,8DAA8D;AAC9D,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,GAAG,SAAS,CAUpE;AAED,+DAA+D;AAC/D,wBAAgB,eAAe,CAC7B,SAAS,EAAE,SAAS,EACpB,OAAO,EAAE,eAAe,GACvB,SAAS,CAyBX;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,SAAS,GAAG,OAAO,CAanE"}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
const EMAIL_TABS = new Set(["compose", "templates", "activity"]);
|
|
2
|
+
/**
|
|
3
|
+
* Derive the admin SPA base path from the API base.
|
|
4
|
+
* `/admin/api` → `/admin`, `/custom/api` → `/custom`.
|
|
5
|
+
*/
|
|
6
|
+
export function adminBaseFromApiBase(apiBase) {
|
|
7
|
+
const trimmed = (apiBase || "/admin/api").replace(/\/$/, "") || "/admin/api";
|
|
8
|
+
if (trimmed.endsWith("/api")) {
|
|
9
|
+
const base = trimmed.slice(0, -"/api".length);
|
|
10
|
+
return base || "/";
|
|
11
|
+
}
|
|
12
|
+
return trimmed;
|
|
13
|
+
}
|
|
14
|
+
function normalizeBase(basePath) {
|
|
15
|
+
if (!basePath || basePath === "/")
|
|
16
|
+
return "";
|
|
17
|
+
return basePath.replace(/\/$/, "");
|
|
18
|
+
}
|
|
19
|
+
/** Relative admin route for a selection (e.g. `/pages/home`). */
|
|
20
|
+
export function pathForSelection(selection, catalog) {
|
|
21
|
+
if (!selection)
|
|
22
|
+
return "/overview";
|
|
23
|
+
switch (selection.kind) {
|
|
24
|
+
case "overview":
|
|
25
|
+
return "/overview";
|
|
26
|
+
case "gallery":
|
|
27
|
+
return "/gallery";
|
|
28
|
+
case "fontLibrary":
|
|
29
|
+
return "/fonts";
|
|
30
|
+
case "emails": {
|
|
31
|
+
const tab = selection.tab ?? "compose";
|
|
32
|
+
return `/emails/${tab}`;
|
|
33
|
+
}
|
|
34
|
+
case "resource":
|
|
35
|
+
return `/resources/${encodeURIComponent(selection.id)}`;
|
|
36
|
+
case "config": {
|
|
37
|
+
const config = catalog.configs.find((item) => item.id === selection.id);
|
|
38
|
+
const bucket = config?.section === "page" ? "pages" : "config";
|
|
39
|
+
return `/${bucket}/${encodeURIComponent(selection.id)}`;
|
|
40
|
+
}
|
|
41
|
+
default:
|
|
42
|
+
return "/overview";
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
export function hrefForSelection(basePath, selection, catalog) {
|
|
46
|
+
const base = normalizeBase(basePath);
|
|
47
|
+
const relative = pathForSelection(selection, catalog);
|
|
48
|
+
return `${base}${relative}` || "/";
|
|
49
|
+
}
|
|
50
|
+
function parseEmailsTab(value) {
|
|
51
|
+
if (value && EMAIL_TABS.has(value)) {
|
|
52
|
+
return value;
|
|
53
|
+
}
|
|
54
|
+
return "compose";
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Parse the location pathname into a selection.
|
|
58
|
+
* Returns null when the path is empty/unknown (caller picks a catalog default).
|
|
59
|
+
*/
|
|
60
|
+
export function selectionFromPath(pathname, basePath, catalog) {
|
|
61
|
+
const base = normalizeBase(basePath);
|
|
62
|
+
let rest = pathname || "/";
|
|
63
|
+
if (base) {
|
|
64
|
+
if (rest === base || rest === `${base}/`)
|
|
65
|
+
return null;
|
|
66
|
+
if (rest.startsWith(`${base}/`))
|
|
67
|
+
rest = rest.slice(base.length);
|
|
68
|
+
else if (rest.startsWith(base))
|
|
69
|
+
rest = rest.slice(base.length);
|
|
70
|
+
}
|
|
71
|
+
const parts = rest.replace(/^\//, "").split("/").filter(Boolean);
|
|
72
|
+
if (parts.length === 0)
|
|
73
|
+
return null;
|
|
74
|
+
const [head, ...tail] = parts;
|
|
75
|
+
if (head === "overview")
|
|
76
|
+
return { kind: "overview" };
|
|
77
|
+
if (head === "gallery")
|
|
78
|
+
return { kind: "gallery" };
|
|
79
|
+
if (head === "fonts")
|
|
80
|
+
return { kind: "fontLibrary" };
|
|
81
|
+
if (head === "emails") {
|
|
82
|
+
return { kind: "emails", tab: parseEmailsTab(tail[0]) };
|
|
83
|
+
}
|
|
84
|
+
if (head === "resources" && tail[0]) {
|
|
85
|
+
const id = decodeURIComponent(tail[0]);
|
|
86
|
+
if (catalog && !catalog.resources.some((item) => item.id === id)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return { kind: "resource", id };
|
|
90
|
+
}
|
|
91
|
+
if ((head === "pages" || head === "config") && tail[0]) {
|
|
92
|
+
const id = decodeURIComponent(tail[0]);
|
|
93
|
+
if (catalog) {
|
|
94
|
+
const config = catalog.configs.find((item) => item.id === id);
|
|
95
|
+
if (!config)
|
|
96
|
+
return null;
|
|
97
|
+
if (head === "pages" && config.section !== "page")
|
|
98
|
+
return null;
|
|
99
|
+
if (head === "config" && config.section === "page")
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
return { kind: "config", id };
|
|
103
|
+
}
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
/** First valid panel when the URL has no usable selection. */
|
|
107
|
+
export function defaultSelection(catalog) {
|
|
108
|
+
if (catalog.metrics)
|
|
109
|
+
return { kind: "overview" };
|
|
110
|
+
const firstResource = catalog.resources[0];
|
|
111
|
+
if (firstResource)
|
|
112
|
+
return { kind: "resource", id: firstResource.id };
|
|
113
|
+
if (catalog.gallery)
|
|
114
|
+
return { kind: "gallery" };
|
|
115
|
+
if (catalog.fontLibrary)
|
|
116
|
+
return { kind: "fontLibrary" };
|
|
117
|
+
if (catalog.emails)
|
|
118
|
+
return { kind: "emails", tab: "compose" };
|
|
119
|
+
const firstConfig = catalog.configs[0];
|
|
120
|
+
if (firstConfig)
|
|
121
|
+
return { kind: "config", id: firstConfig.id };
|
|
122
|
+
return { kind: "overview" };
|
|
123
|
+
}
|
|
124
|
+
/** Keep a selection only if it still exists in the catalog. */
|
|
125
|
+
export function coerceSelection(selection, catalog) {
|
|
126
|
+
if (!selection)
|
|
127
|
+
return defaultSelection(catalog);
|
|
128
|
+
if (selection.kind === "overview") {
|
|
129
|
+
return catalog.metrics ? selection : defaultSelection(catalog);
|
|
130
|
+
}
|
|
131
|
+
if (selection.kind === "gallery") {
|
|
132
|
+
return catalog.gallery ? selection : defaultSelection(catalog);
|
|
133
|
+
}
|
|
134
|
+
if (selection.kind === "fontLibrary") {
|
|
135
|
+
return catalog.fontLibrary ? selection : defaultSelection(catalog);
|
|
136
|
+
}
|
|
137
|
+
if (selection.kind === "emails") {
|
|
138
|
+
return catalog.emails ? selection : defaultSelection(catalog);
|
|
139
|
+
}
|
|
140
|
+
if (selection.kind === "resource") {
|
|
141
|
+
return catalog.resources.some((item) => item.id === selection.id)
|
|
142
|
+
? selection
|
|
143
|
+
: defaultSelection(catalog);
|
|
144
|
+
}
|
|
145
|
+
if (selection.kind === "config") {
|
|
146
|
+
return catalog.configs.some((item) => item.id === selection.id)
|
|
147
|
+
? selection
|
|
148
|
+
: defaultSelection(catalog);
|
|
149
|
+
}
|
|
150
|
+
return defaultSelection(catalog);
|
|
151
|
+
}
|
|
152
|
+
export function selectionsEqual(a, b) {
|
|
153
|
+
if (a === b)
|
|
154
|
+
return true;
|
|
155
|
+
if (!a || !b)
|
|
156
|
+
return a === b;
|
|
157
|
+
if (a.kind !== b.kind)
|
|
158
|
+
return false;
|
|
159
|
+
if (a.kind === "overview" || a.kind === "gallery" || a.kind === "fontLibrary") {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
if (a.kind === "emails" && b.kind === "emails") {
|
|
163
|
+
return (a.tab ?? "compose") === (b.tab ?? "compose");
|
|
164
|
+
}
|
|
165
|
+
if (a.kind === "resource" && b.kind === "resource")
|
|
166
|
+
return a.id === b.id;
|
|
167
|
+
if (a.kind === "config" && b.kind === "config")
|
|
168
|
+
return a.id === b.id;
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
export type VertebrAdminProps = {
|
|
2
2
|
/** Base URL for Vertebr API, e.g. `/admin/api` */
|
|
3
3
|
apiBase?: string;
|
|
4
|
+
/**
|
|
5
|
+
* Admin SPA mount path for deep links (e.g. `/admin`).
|
|
6
|
+
* Defaults from `apiBase` by stripping a trailing `/api`.
|
|
7
|
+
*/
|
|
8
|
+
basePath?: string;
|
|
4
9
|
title?: string;
|
|
5
10
|
/** localStorage key for remembering the selected environment */
|
|
6
11
|
envStorageKey?: string;
|
|
@@ -25,6 +30,8 @@ export type Selection = {
|
|
|
25
30
|
id: string;
|
|
26
31
|
} | {
|
|
27
32
|
kind: "gallery";
|
|
33
|
+
} | {
|
|
34
|
+
kind: "fontLibrary";
|
|
28
35
|
} | {
|
|
29
36
|
kind: "emails";
|
|
30
37
|
tab?: EmailsTab;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/admin/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,oEAAoE;IACpE,cAAc,CAAC,EAAE,MACb,MAAM,GACN,IAAI,GACJ,SAAS,GACT,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACvC,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAE7D,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC9B,IAAI,CAAC;AAET,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;KACtB,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,CAAC"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/components/admin/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,kDAAkD;IAClD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sDAAsD;IACtD,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,oEAAoE;IACpE,cAAc,CAAC,EAAE,MACb,MAAM,GACN,IAAI,GACJ,SAAS,GACT,OAAO,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACvC,iDAAiD;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,UAAU,CAAC;AAE7D,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,GACnB;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,GAAG,CAAC,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,GAC9B,IAAI,CAAC;AAET,MAAM,MAAM,YAAY,GAAG;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;IACrB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,KAAK,CAAC;QACf,GAAG,EAAE,MAAM,CAAC;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,MAAM,IAAI,CAAC;KACtB,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,YAAY,EAAE,CAAC;CACvB,CAAC"}
|
|
@@ -4,5 +4,17 @@ export type VisibilityEyeButtonProps = {
|
|
|
4
4
|
label: string;
|
|
5
5
|
form: UseFormReturn<Record<string, unknown>>;
|
|
6
6
|
};
|
|
7
|
+
export type VisibilityEyeToggleProps = {
|
|
8
|
+
visible: boolean;
|
|
9
|
+
label: string;
|
|
10
|
+
dirty?: boolean;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
onChange: (visible: boolean) => void;
|
|
13
|
+
};
|
|
14
|
+
/** Form-bound eye toggle (record editor). */
|
|
7
15
|
export declare function VisibilityEyeButton({ name, label, form, }: VisibilityEyeButtonProps): import("react").JSX.Element;
|
|
16
|
+
/** Controlled eye toggle (resource list, etc.). */
|
|
17
|
+
export declare function VisibilityEyeToggle({ visible, label, dirty, disabled, onChange, }: VisibilityEyeToggleProps): import("react").JSX.Element;
|
|
18
|
+
export declare function EyeIcon(): import("react").JSX.Element;
|
|
19
|
+
export declare function EyeOffIcon(): import("react").JSX.Element;
|
|
8
20
|
//# sourceMappingURL=VisibilityEye.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VisibilityEye.d.ts","sourceRoot":"","sources":["../../../src/components/config/VisibilityEye.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC9C,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,KAAK,EACL,IAAI,GACL,EAAE,wBAAwB,+
|
|
1
|
+
{"version":3,"file":"VisibilityEye.d.ts","sourceRoot":"","sources":["../../../src/components/config/VisibilityEye.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,MAAM,wBAAwB,GAAG;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CAC9C,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CACtC,CAAC;AAEF,6CAA6C;AAC7C,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,KAAK,EACL,IAAI,GACL,EAAE,wBAAwB,+BAmB1B;AAED,mDAAmD;AACnD,wBAAgB,mBAAmB,CAAC,EAClC,OAAO,EACP,KAAK,EACL,KAAK,EACL,QAAQ,EACR,QAAQ,GACT,EAAE,wBAAwB,+BAoB1B;AAED,wBAAgB,OAAO,gCAiBtB;AAED,wBAAgB,UAAU,gCAmBzB"}
|
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect } from "react";
|
|
3
|
+
/** Form-bound eye toggle (record editor). */
|
|
3
4
|
export function VisibilityEyeButton({ name, label, form, }) {
|
|
4
5
|
const visible = form.watch(name) !== false;
|
|
5
6
|
useEffect(() => {
|
|
6
7
|
form.register(name);
|
|
7
8
|
}, [form, name]);
|
|
8
|
-
return (_jsx(
|
|
9
|
+
return (_jsx(VisibilityEyeToggle, { visible: visible, label: label, onChange: (next) => form.setValue(name, next, {
|
|
9
10
|
shouldDirty: true,
|
|
10
11
|
shouldTouch: true,
|
|
11
|
-
})
|
|
12
|
+
}) }));
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
+
/** Controlled eye toggle (resource list, etc.). */
|
|
15
|
+
export function VisibilityEyeToggle({ visible, label, dirty, disabled, onChange, }) {
|
|
16
|
+
return (_jsx("button", { type: "button", className: "vertebr-visibility-btn", "data-dirty": dirty ? "true" : undefined, "aria-pressed": visible, "aria-label": visible ? `Hide ${label} on storefront` : `Show ${label} on storefront`, title: visible ? "Visible on storefront" : "Hidden on storefront", disabled: disabled, onClick: (event) => {
|
|
17
|
+
event.stopPropagation();
|
|
18
|
+
onChange(!visible);
|
|
19
|
+
}, children: visible ? _jsx(EyeIcon, {}) : _jsx(EyeOffIcon, {}) }));
|
|
20
|
+
}
|
|
21
|
+
export function EyeIcon() {
|
|
14
22
|
return (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" }), _jsx("circle", { cx: "12", cy: "12", r: "3" })] }));
|
|
15
23
|
}
|
|
16
|
-
function EyeOffIcon() {
|
|
24
|
+
export function EyeOffIcon() {
|
|
17
25
|
return (_jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true", children: [_jsx("path", { d: "M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" }), _jsx("path", { d: "M14.084 14.158a3 3 0 0 1-4.242-4.242" }), _jsx("path", { d: "M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" }), _jsx("path", { d: "m2 2 20 20" })] }));
|
|
18
26
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type UseFormReturn } from "react-hook-form";
|
|
2
2
|
import type { FieldSchema, ResourceUploadMeta } from "@vertebr/client";
|
|
3
|
-
import type { ConfigFormVariant, GalleryHandlers } from "./types";
|
|
4
|
-
export type ConfigFieldProps = GalleryHandlers & {
|
|
3
|
+
import type { ConfigFormVariant, FontLibraryHandlers, GalleryHandlers } from "./types";
|
|
4
|
+
export type ConfigFieldProps = GalleryHandlers & FontLibraryHandlers & {
|
|
5
5
|
fieldKey: string;
|
|
6
6
|
field: FieldSchema;
|
|
7
7
|
groupLabel: string;
|
|
@@ -17,6 +17,8 @@ export type ConfigFieldProps = GalleryHandlers & {
|
|
|
17
17
|
hideImagePreview?: boolean;
|
|
18
18
|
hideUpload?: boolean;
|
|
19
19
|
visibilityGroup?: string;
|
|
20
|
+
/** Render boolean fields as rows in a settings list (config forms). */
|
|
21
|
+
settingsRow?: boolean;
|
|
20
22
|
};
|
|
21
|
-
export declare function ConfigField({ fieldKey, field, groupLabel, variant, schema, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, hideImagePreview, hideUpload, visibilityGroup, }: ConfigFieldProps): import("react").JSX.Element;
|
|
23
|
+
export declare function ConfigField({ fieldKey, field, groupLabel, variant, schema, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, hideImagePreview, hideUpload, visibilityGroup, settingsRow, }: ConfigFieldProps): import("react").JSX.Element;
|
|
22
24
|
//# sourceMappingURL=ConfigField.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigField.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigField.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIjE,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"ConfigField.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigField.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAIjE,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAqBvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEvF,MAAM,MAAM,gBAAgB,GAAG,eAAe,GAC5C,mBAAmB,GAAG;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,WAAW,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uEAAuE;IACvE,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,KAAK,EACL,UAAU,EACV,OAAO,EACP,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,KAAK,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,UAAU,EACV,eAAe,EACf,WAAW,GACZ,EAAE,gBAAgB,+BAmSlB"}
|
|
@@ -6,13 +6,14 @@ import { VisibilityEyeButton } from "../VisibilityEye";
|
|
|
6
6
|
import { isDescriptionField } from "../../../lib/description";
|
|
7
7
|
import { collectRecordImages, isImageField, urlsFromValue, } from "../../../lib/images";
|
|
8
8
|
import { isCentsField, isPriceField } from "../../../lib/price";
|
|
9
|
+
import { FontPickerField } from "../../fonts/FontPickerField";
|
|
9
10
|
import { ImageUploadControl } from "../../resources/ImageUploadControl";
|
|
10
11
|
import { DescriptionInput } from "../DescriptionInput";
|
|
11
12
|
import { EnumInput } from "../EnumInput";
|
|
12
13
|
import { EnumListInput } from "../EnumListInput";
|
|
13
14
|
import { PriceInput } from "../PriceInput";
|
|
14
15
|
import { displayLabel, firstImageUrl, isMultilineField, fieldVisibilityKey, spanFor, } from "./helpers";
|
|
15
|
-
export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, hideImagePreview, hideUpload, visibilityGroup, }) {
|
|
16
|
+
export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, hideImagePreview, hideUpload, visibilityGroup, settingsRow, }) {
|
|
16
17
|
const label = displayLabel(fieldKey, field, groupLabel);
|
|
17
18
|
const visibilityKey = fieldVisibilityKey(fieldKey, schema, visibilityGroup ?? groupLabel);
|
|
18
19
|
const visibilityOn = !visibilityKey || watched?.[visibilityKey] !== false;
|
|
@@ -21,7 +22,10 @@ export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watc
|
|
|
21
22
|
isImageField(fieldKey, field) &&
|
|
22
23
|
Boolean(firstImageUrl(watched?.[fieldKey]));
|
|
23
24
|
if (field.type === "boolean") {
|
|
24
|
-
|
|
25
|
+
if (settingsRow) {
|
|
26
|
+
return (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsxs("li", { className: "vertebr-settings-list-row", "data-on": Boolean(controllerField.value) ? "true" : "false", children: [_jsxs("div", { className: "vertebr-settings-list-copy", children: [_jsx(Label.Root, { htmlFor: fieldKey, children: label }), field.description ? (_jsx("p", { className: "vertebr-settings-list-desc", children: field.description })) : null] }), _jsx(Switch.Root, { className: "vertebr-switch", id: fieldKey, checked: Boolean(controllerField.value), onCheckedChange: controllerField.onChange, children: _jsx(Switch.Thumb, { className: "vertebr-switch-thumb" }) })] })) }));
|
|
27
|
+
}
|
|
28
|
+
return (_jsxs("div", { className: "vertebr-switch-row", children: [_jsxs("div", { className: "vertebr-switch-row-copy", children: [_jsx(Label.Root, { htmlFor: fieldKey, children: label }), field.description ? (_jsx("p", { className: "vertebr-switch-row-desc", children: field.description })) : null] }), _jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(Switch.Root, { className: "vertebr-switch", id: fieldKey, checked: Boolean(controllerField.value), onCheckedChange: controllerField.onChange, children: _jsx(Switch.Thumb, { className: "vertebr-switch-thumb" }) })) })] }));
|
|
25
29
|
}
|
|
26
30
|
return (_jsxs("div", { className: "vertebr-field", "data-span": spanFor(fieldKey, field, variant), "data-kind": isImageField(fieldKey, field) ? "image" : undefined, "data-storefront": visibilityKey ? (visibilityOn ? "on" : "off") : undefined, children: [_jsxs("div", { className: "vertebr-field-head", children: [_jsx(Label.Root, { htmlFor: fieldKey, children: label }), visibilityKey ? (_jsx(VisibilityEyeButton, { name: visibilityKey, label: label, form: form })) : null] }), showInlinePreview ? (_jsxs("div", { className: "vertebr-field-media", children: [_jsx("img", { src: firstImageUrl(watched[fieldKey]) ?? "", alt: "" }), _jsx("input", { id: fieldKey, type: "text", readOnly: field.readOnly, ...form.register(fieldKey) })] })) : field.type === "richtext" || isDescriptionField(fieldKey, field) ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(DescriptionInput, { id: fieldKey, name: controllerField.name, inputRef: controllerField.ref, value: typeof controllerField.value === "string"
|
|
27
31
|
? controllerField.value
|
|
@@ -34,7 +38,7 @@ export function ConfigField({ fieldKey, field, groupLabel, variant, schema, watc
|
|
|
34
38
|
: "", onBlur: controllerField.onBlur, onChange: (event) => {
|
|
35
39
|
const next = event.target.value;
|
|
36
40
|
controllerField.onChange(next === "" ? undefined : Number(next));
|
|
37
|
-
}, name: controllerField.name, ref: controllerField.ref })) })) : field.type === "enum" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : field.type === "enum[]" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumListInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : isMultilineField(fieldKey, field) ? (_jsx("textarea", { id: fieldKey, rows: 5, readOnly: field.readOnly, ...form.register(fieldKey) })) : (_jsx("input", { id: fieldKey, type: "text", readOnly: field.readOnly, ...form.register(fieldKey) })), !hideUpload && uploads?.[fieldKey] && onUpload && !field.readOnly ? (_jsx(ImageUploadControl, { meta: uploads[fieldKey], multiple: field.type === "file[]", onUpload: async (files) => {
|
|
41
|
+
}, name: controllerField.name, ref: controllerField.ref })) })) : field.type === "enum" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : field.type === "enum[]" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(EnumListInput, { id: fieldKey, value: controllerField.value, field: field, readOnly: field.readOnly, onChange: controllerField.onChange })) })) : field.type === "font" ? (_jsx(Controller, { control: form.control, name: fieldKey, render: ({ field: controllerField }) => (_jsx(FontPickerField, { id: fieldKey, value: controllerField.value, label: field.label, description: field.description, readOnly: field.readOnly, fonts: fonts ?? [], loading: fontsLoading, onChange: controllerField.onChange, onRefreshFonts: onRefreshFonts })) })) : isMultilineField(fieldKey, field) ? (_jsx("textarea", { id: fieldKey, rows: 5, readOnly: field.readOnly, ...form.register(fieldKey) })) : (_jsx("input", { id: fieldKey, type: "text", readOnly: field.readOnly, ...form.register(fieldKey) })), !hideUpload && uploads?.[fieldKey] && onUpload && !field.readOnly ? (_jsx(ImageUploadControl, { meta: uploads[fieldKey], multiple: field.type === "file[]", onUpload: async (files) => {
|
|
38
42
|
const uploaded = await onUpload(fieldKey, files);
|
|
39
43
|
const urls = uploaded
|
|
40
44
|
.map((item) => item.url)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import type { ConfigFormProps } from "./types";
|
|
2
2
|
export type { ConfigFormProps, ConfigFormVariant } from "./types";
|
|
3
|
-
export declare function ConfigForm({ schema, value, onSave, submitLabel, variant, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onChange, hideActions, }: ConfigFormProps): import("react").JSX.Element;
|
|
3
|
+
export declare function ConfigForm({ schema, value, onSave, submitLabel, variant, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, onChange, hideActions, }: ConfigFormProps): import("react").JSX.Element;
|
|
4
4
|
//# sourceMappingURL=ConfigForm.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigForm.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAElE,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,KAAK,EACL,MAAM,EACN,WAAW,EACX,OAAkB,EAClB,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,QAAQ,EACR,WAAW,GACZ,EAAE,eAAe,+
|
|
1
|
+
{"version":3,"file":"ConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/ConfigForm.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE/C,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAElE,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,KAAK,EACL,MAAM,EACN,WAAW,EACX,OAAkB,EAClB,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,KAAK,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,QAAQ,EACR,WAAW,GACZ,EAAE,eAAe,+BAkGjB"}
|
|
@@ -5,7 +5,7 @@ import { zodResolver } from "@hookform/resolvers/zod";
|
|
|
5
5
|
import { buildZodSchema, groupFields, withSpecVisibility, withSpecVisibilityValues, } from "./helpers";
|
|
6
6
|
import { RecordConfigForm } from "./RecordConfigForm";
|
|
7
7
|
import { SettingsConfigForm } from "./SettingsConfigForm";
|
|
8
|
-
export function ConfigForm({ schema, value, onSave, submitLabel, variant = "config", uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onChange, hideActions, }) {
|
|
8
|
+
export function ConfigForm({ schema, value, onSave, submitLabel, variant = "config", uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, onChange, hideActions, }) {
|
|
9
9
|
const editorSchema = useMemo(() => (variant === "record" ? withSpecVisibility(schema) : schema), [schema, variant]);
|
|
10
10
|
const editorValue = useMemo(() => variant === "record"
|
|
11
11
|
? withSpecVisibilityValues(editorSchema, value)
|
|
@@ -64,6 +64,10 @@ export function ConfigForm({ schema, value, onSave, submitLabel, variant = "conf
|
|
|
64
64
|
galleryUpload,
|
|
65
65
|
onListGallery,
|
|
66
66
|
onUploadGallery,
|
|
67
|
+
fonts,
|
|
68
|
+
fontsLoading,
|
|
69
|
+
onListFonts,
|
|
70
|
+
onRefreshFonts,
|
|
67
71
|
onSubmit,
|
|
68
72
|
isDirty,
|
|
69
73
|
status,
|
|
@@ -19,5 +19,5 @@ export type RecordConfigFormProps = GalleryHandlers & {
|
|
|
19
19
|
submitLabel?: string;
|
|
20
20
|
hideActions?: boolean;
|
|
21
21
|
};
|
|
22
|
-
export declare function RecordConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onSubmit, isDirty, status, tone, onDiscard, submitLabel, }: RecordConfigFormProps): import("react").JSX.Element;
|
|
22
|
+
export declare function RecordConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onSubmit, isDirty, status, tone, onDiscard, submitLabel, hideActions, }: RecordConfigFormProps): import("react").JSX.Element;
|
|
23
23
|
//# sourceMappingURL=RecordConfigForm.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RecordConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/RecordConfigForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAYvE,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE3D,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG;IACpD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IACvB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,QAAQ,EACR,OAAO,EACP,MAAM,EACN,IAAI,EACJ,SAAS,EACT,WAAW,GACZ,EAAE,qBAAqB,+
|
|
1
|
+
{"version":3,"file":"RecordConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/RecordConfigForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAYvE,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE3D,MAAM,MAAM,qBAAqB,GAAG,eAAe,GAAG;IACpD,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IACvB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAgB,gBAAgB,CAAC,EAC/B,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,QAAQ,EACR,OAAO,EACP,MAAM,EACN,IAAI,EACJ,SAAS,EACT,WAAW,EACX,WAAW,GACZ,EAAE,qBAAqB,+BAsJvB"}
|
|
@@ -5,7 +5,7 @@ import { ConfigField } from "./ConfigField";
|
|
|
5
5
|
import { ConfigFormActions } from "./ConfigFormActions";
|
|
6
6
|
import { VisibilityEyeButton } from "../VisibilityEye";
|
|
7
7
|
import { findPrimaryImageKey, firstImageUrl, groupVisibilityKey, isStockField, } from "./helpers";
|
|
8
|
-
export function RecordConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onSubmit, isDirty, status, tone, onDiscard, submitLabel, }) {
|
|
8
|
+
export function RecordConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onSubmit, isDirty, status, tone, onDiscard, submitLabel, hideActions, }) {
|
|
9
9
|
const imageKey = findPrimaryImageKey(schema, watched);
|
|
10
10
|
const imageValue = imageKey ? firstImageUrl(watched?.[imageKey]) ?? "" : "";
|
|
11
11
|
const asideGroups = groups.filter((group) => /^(Media|Display|Status)$/i.test(group.id));
|
|
@@ -37,7 +37,7 @@ export function RecordConfigForm({ schema, groups, watched, form, uploads, onUpl
|
|
|
37
37
|
const sectionKey = groupVisibilityKey(group.id, schema);
|
|
38
38
|
const sectionOn = !sectionKey || watched?.[sectionKey] !== false;
|
|
39
39
|
return (_jsxs("div", { className: "vertebr-record-block", "data-layout": inventory ? "chips" : specs ? "specs" : undefined, "data-storefront": sectionKey ? (sectionOn ? "on" : "off") : undefined, children: [_jsx(RecordBlockLabel, { groupId: group.id, label: group.label, schema: schema, form: form }), _jsx("div", { className: "vertebr-form-fields", "data-layout": inventory ? "chips" : specs ? "specs" : undefined, children: group.fields.map(([key, field]) => (_jsx(ConfigField, { fieldKey: key, field: field, groupLabel: group.label, visibilityGroup: group.id, ...fieldProps }, key))) })] }, group.id));
|
|
40
|
-
}) })] }) }), _jsx(ConfigFormActions, { form: form, isDirty: isDirty, status: status, tone: tone, onDiscard: onDiscard, submitLabel: submitLabel })] }));
|
|
40
|
+
}) })] }) }), hideActions ? null : (_jsx(ConfigFormActions, { form: form, isDirty: isDirty, status: status, tone: tone, onDiscard: onDiscard, submitLabel: submitLabel }))] }));
|
|
41
41
|
}
|
|
42
42
|
function RecordBlockLabel({ groupId, label, schema, form, }) {
|
|
43
43
|
const visibilityKey = groupVisibilityKey(groupId, schema);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { UseFormReturn } from "react-hook-form";
|
|
2
2
|
import type { FieldSchema, ResourceUploadMeta } from "@vertebr/client";
|
|
3
|
-
import type { FieldGroup, GalleryHandlers } from "./types";
|
|
4
|
-
export type SettingsConfigFormProps = GalleryHandlers & {
|
|
3
|
+
import type { FieldGroup, FontLibraryHandlers, GalleryHandlers } from "./types";
|
|
4
|
+
export type SettingsConfigFormProps = GalleryHandlers & FontLibraryHandlers & {
|
|
5
5
|
schema: Record<string, FieldSchema>;
|
|
6
6
|
groups: FieldGroup[];
|
|
7
7
|
watched: Record<string, unknown>;
|
|
@@ -19,5 +19,5 @@ export type SettingsConfigFormProps = GalleryHandlers & {
|
|
|
19
19
|
submitLabel?: string;
|
|
20
20
|
hideActions?: boolean;
|
|
21
21
|
};
|
|
22
|
-
export declare function SettingsConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, onSubmit, isDirty, status, tone, onDiscard, submitLabel, hideActions, }: SettingsConfigFormProps): import("react").JSX.Element;
|
|
22
|
+
export declare function SettingsConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, onSubmit, isDirty, status, tone, onDiscard, submitLabel, hideActions, }: SettingsConfigFormProps): import("react").JSX.Element;
|
|
23
23
|
//# sourceMappingURL=SettingsConfigForm.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SettingsConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/SettingsConfigForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"SettingsConfigForm.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/SettingsConfigForm.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAIvE,OAAO,KAAK,EAAE,UAAU,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAEhF,MAAM,MAAM,uBAAuB,GAAG,eAAe,GACnD,mBAAmB,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAC;IAC5D,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,IAAI,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC;IACvB,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EACN,MAAM,EACN,OAAO,EACP,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,aAAa,EACb,aAAa,EACb,eAAe,EACf,KAAK,EACL,YAAY,EACZ,WAAW,EACX,cAAc,EACd,QAAQ,EACR,OAAO,EACP,MAAM,EACN,IAAI,EACJ,SAAS,EACT,WAAW,EACX,WAAW,GACZ,EAAE,uBAAuB,+BA8FzB"}
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { ConfigField } from "./ConfigField";
|
|
3
3
|
import { ConfigFormActions } from "./ConfigFormActions";
|
|
4
|
-
|
|
4
|
+
import { isBooleanOnlyGroup } from "./helpers/fields";
|
|
5
|
+
export function SettingsConfigForm({ schema, groups, watched, form, uploads, onUpload, galleryUpload, onListGallery, onUploadGallery, fonts, fontsLoading, onListFonts, onRefreshFonts, onSubmit, isDirty, status, tone, onDiscard, submitLabel, hideActions, }) {
|
|
5
6
|
const singleGroup = groups.length === 1;
|
|
6
|
-
return (_jsxs("form", { className: "vertebr-form", "data-variant": "config", onSubmit: onSubmit, children: [_jsx("div", { className: "vertebr-form-scroll", children: groups.map((group) =>
|
|
7
|
+
return (_jsxs("form", { className: "vertebr-form", "data-variant": "config", onSubmit: onSubmit, children: [_jsx("div", { className: "vertebr-form-scroll", children: groups.map((group) => {
|
|
8
|
+
const toggleList = isBooleanOnlyGroup(group.fields);
|
|
9
|
+
const showSectionHead = !singleGroup || (group.id !== "General" && group.fields.length > 0);
|
|
10
|
+
return (_jsxs("div", { className: "vertebr-form-section", "data-plain": singleGroup && !showSectionHead ? "true" : undefined, children: [showSectionHead ? (_jsxs("div", { className: "vertebr-form-section-head", children: [_jsx("h3", { children: group.label }), _jsx("span", { className: "vertebr-form-section-count", children: group.fields.length })] })) : null, _jsx("div", { className: "vertebr-form-fields", "data-layout": toggleList ? "settings" : undefined, children: toggleList ? (_jsx("ul", { className: "vertebr-settings-list", children: group.fields.map(([key, field]) => (_jsx(ConfigField, { fieldKey: key, field: field, groupLabel: group.label, variant: "config", schema: schema, watched: watched, form: form, uploads: uploads, onUpload: onUpload, galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery, fonts: fonts, fontsLoading: fontsLoading, onListFonts: onListFonts, onRefreshFonts: onRefreshFonts, settingsRow: true }, key))) })) : (group.fields.map(([key, field]) => (_jsx(ConfigField, { fieldKey: key, field: field, groupLabel: group.label, variant: "config", schema: schema, watched: watched, form: form, uploads: uploads, onUpload: onUpload, galleryUpload: galleryUpload, onListGallery: onListGallery, onUploadGallery: onUploadGallery, fonts: fonts, fontsLoading: fontsLoading, onListFonts: onListFonts, onRefreshFonts: onRefreshFonts }, key)))) })] }, group.id));
|
|
11
|
+
}) }), hideActions ? null : (_jsx(ConfigFormActions, { form: form, isDirty: isDirty, status: status, tone: tone, onDiscard: onDiscard, submitLabel: submitLabel }))] }));
|
|
7
12
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { FieldSchema } from "@vertebr/client";
|
|
2
|
-
import type { ConfigFormVariant } from "../types";
|
|
2
|
+
import type { ConfigFormVariant, FieldEntry } from "../types";
|
|
3
|
+
export declare function isBooleanOnlyGroup(fields: FieldEntry[]): boolean;
|
|
3
4
|
export declare function isStockField(key: string, field?: FieldSchema): boolean;
|
|
4
5
|
export declare function isSpecsField(key: string, field?: FieldSchema): boolean;
|
|
5
6
|
export declare function isMultilineField(key: string, field?: FieldSchema): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../../src/components/config/form/helpers/fields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../../../../src/components/config/form/helpers/fields.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAGnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAE9D,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,GAAG,OAAO,CAKhE;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAGtE;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAKtE;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,OAAO,CAG1E;AAED,wBAAgB,OAAO,CACrB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE,iBAAiB,GACzB,MAAM,GAAG,MAAM,GAAG,SAAS,CAO7B"}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { isDescriptionField } from "../../../../lib/description";
|
|
2
2
|
import { isImageField } from "../../../../lib/images";
|
|
3
|
+
export function isBooleanOnlyGroup(fields) {
|
|
4
|
+
return (fields.length > 0 &&
|
|
5
|
+
fields.every(([, field]) => field.type === "boolean"));
|
|
6
|
+
}
|
|
3
7
|
export function isStockField(key, field) {
|
|
4
8
|
const hay = `${key} ${field?.label ?? ""}`;
|
|
5
9
|
return /(stock|inventory|qty|quantity)/i.test(hay);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FieldSchema, GalleryItem, ResourceUploadMeta } from "@vertebr/client";
|
|
1
|
+
import type { FieldSchema, FontLibraryItem, GalleryItem, ResourceUploadMeta } from "@vertebr/client";
|
|
2
2
|
export type ConfigFormVariant = "config" | "record";
|
|
3
3
|
export type ConfigFormProps = {
|
|
4
4
|
schema: Record<string, FieldSchema>;
|
|
@@ -18,6 +18,10 @@ export type ConfigFormProps = {
|
|
|
18
18
|
galleryUpload?: Pick<ResourceUploadMeta, "accept" | "maxBytes">;
|
|
19
19
|
onListGallery?: () => Promise<GalleryItem[]>;
|
|
20
20
|
onUploadGallery?: (files: File[]) => Promise<GalleryItem[]>;
|
|
21
|
+
fonts?: FontLibraryItem[];
|
|
22
|
+
fontsLoading?: boolean;
|
|
23
|
+
onListFonts?: () => Promise<FontLibraryItem[]>;
|
|
24
|
+
onRefreshFonts?: () => Promise<void>;
|
|
21
25
|
/** Live field values (e.g. mail preview). */
|
|
22
26
|
onChange?: (value: Record<string, unknown>) => void;
|
|
23
27
|
/** Hide Save / Discard — parent owns submit. */
|
|
@@ -30,4 +34,5 @@ export type FieldGroup = {
|
|
|
30
34
|
fields: FieldEntry[];
|
|
31
35
|
};
|
|
32
36
|
export type GalleryHandlers = Pick<ConfigFormProps, "galleryUpload" | "onListGallery" | "onUploadGallery">;
|
|
37
|
+
export type FontLibraryHandlers = Pick<ConfigFormProps, "fonts" | "fontsLoading" | "onListFonts" | "onRefreshFonts">;
|
|
33
38
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/components/config/form/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErG,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEpD,MAAM,MAAM,eAAe,GAAG;IAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACpC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,2DAA2D;IAC3D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE,iBAAiB,CAAC;IAC5B,8DAA8D;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC7C,sDAAsD;IACtD,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,IAAI,EAAE,KACV,OAAO,CAAC,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IACpD,aAAa,CAAC,EAAE,IAAI,CAAC,kBAAkB,EAAE,QAAQ,GAAG,UAAU,CAAC,CAAC;IAChE,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC7C,eAAe,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5D,KAAK,CAAC,EAAE,eAAe,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,EAAE,CAAC,CAAC;IAC/C,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;IACpD,gDAAgD;IAChD,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;AAE/C,MAAM,MAAM,UAAU,GAAG;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,UAAU,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG,IAAI,CAChC,eAAe,EACf,eAAe,GAAG,eAAe,GAAG,iBAAiB,CACtD,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG,IAAI,CACpC,eAAe,EACf,OAAO,GAAG,cAAc,GAAG,aAAa,GAAG,gBAAgB,CAC5D,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type FontLibraryItem } from "@vertebr/client";
|
|
2
|
+
export type FontFaceInjectorProps = {
|
|
3
|
+
items: FontLibraryItem[];
|
|
4
|
+
/** API base for same-origin font proxy URLs. Defaults to `/admin/api`. */
|
|
5
|
+
apiBase?: string;
|
|
6
|
+
};
|
|
7
|
+
/** Inject @font-face rules for library items so admin previews render correctly. */
|
|
8
|
+
export declare function FontFaceInjector({ items, apiBase, }: FontFaceInjectorProps): null;
|
|
9
|
+
//# sourceMappingURL=FontFaceInjector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FontFaceInjector.d.ts","sourceRoot":"","sources":["../../../src/components/fonts/FontFaceInjector.tsx"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,eAAe,EAAE,CAAC;IACzB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,oFAAoF;AACpF,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,OAAsB,GACvB,EAAE,qBAAqB,QA+BvB"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useEffect, useId, useMemo } from "react";
|
|
2
|
+
import { buildFontFaceCss, rewriteFontLibraryUrls, } from "@vertebr/client";
|
|
3
|
+
/** Inject @font-face rules for library items so admin previews render correctly. */
|
|
4
|
+
export function FontFaceInjector({ items, apiBase = "/admin/api", }) {
|
|
5
|
+
const id = useId().replace(/:/g, "");
|
|
6
|
+
const resolved = useMemo(() => rewriteFontLibraryUrls(items, apiBase), [items, apiBase]);
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
const css = resolved.map((item) => buildFontFaceCss(item)).join("");
|
|
9
|
+
if (!css)
|
|
10
|
+
return;
|
|
11
|
+
const sampleUrl = css.match(/url\("([^"]+)"\)/)?.[1] ?? null;
|
|
12
|
+
console.info("[mm:fonts] FontFaceInjector", {
|
|
13
|
+
items: resolved.length,
|
|
14
|
+
cssRules: (css.match(/@font-face/g) ?? []).length,
|
|
15
|
+
apiBase,
|
|
16
|
+
sampleUrl,
|
|
17
|
+
usesFirebase: css.includes("firebasestorage.googleapis.com"),
|
|
18
|
+
});
|
|
19
|
+
const style = document.createElement("style");
|
|
20
|
+
style.id = `vertebr-font-faces-${id}`;
|
|
21
|
+
style.textContent = css;
|
|
22
|
+
document.head.appendChild(style);
|
|
23
|
+
return () => {
|
|
24
|
+
style.remove();
|
|
25
|
+
};
|
|
26
|
+
}, [resolved, id]);
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FontLibraryResponse, GoogleFontImportRequest } from "@vertebr/client";
|
|
2
|
+
export type FontLibraryBoardProps = {
|
|
3
|
+
label?: string;
|
|
4
|
+
data: FontLibraryResponse;
|
|
5
|
+
loading?: boolean;
|
|
6
|
+
onRefresh?: () => void;
|
|
7
|
+
onUpload: (files: File[], meta?: {
|
|
8
|
+
family?: string;
|
|
9
|
+
weight?: number;
|
|
10
|
+
}) => Promise<void>;
|
|
11
|
+
onImportGoogle?: (payload: GoogleFontImportRequest) => Promise<void>;
|
|
12
|
+
onDelete?: (id: string) => Promise<void>;
|
|
13
|
+
};
|
|
14
|
+
export declare function FontLibraryBoard({ label, data, loading, onRefresh, onUpload, onImportGoogle, onDelete, }: FontLibraryBoardProps): import("react").JSX.Element;
|
|
15
|
+
//# sourceMappingURL=FontLibraryBoard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FontLibraryBoard.d.ts","sourceRoot":"","sources":["../../../src/components/fonts/FontLibraryBoard.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAEV,mBAAmB,EACnB,uBAAuB,EAExB,MAAM,iBAAiB,CAAC;AAMzB,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,mBAAmB,CAAC;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,QAAQ,EAAE,CACR,KAAK,EAAE,IAAI,EAAE,EACb,IAAI,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KACxC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,uBAAuB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACrE,QAAQ,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C,CAAC;AAUF,wBAAgB,gBAAgB,CAAC,EAC/B,KAAsB,EACtB,IAAI,EACJ,OAAO,EACP,SAAS,EACT,QAAQ,EACR,cAAc,EACd,QAAQ,GACT,EAAE,qBAAqB,+BAqIvB"}
|