@deneb-ui/ui 2.0.38 → 2.0.39
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/SiteDataProvider.d.ts +65 -4
- package/dist/SiteDataProvider.js +152 -1
- package/package.json +2 -2
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import React, { type ReactNode } from 'react';
|
|
2
|
+
import type { ProductItem } from './EditableProductCard';
|
|
3
|
+
import type { ServiceItem } from './EditableServiceCard';
|
|
2
4
|
export declare const DENEB_PREVIEW_DATA_MESSAGE = "DENEB_PREVIEW_SITE_DATA";
|
|
3
5
|
export declare const PREVIEW_DATA_MESSAGE = "FIVORA_PREVIEW_SITE_DATA";
|
|
4
6
|
export declare const LEGACY_PREVIEW_DATA_MESSAGE: string;
|
|
@@ -11,10 +13,34 @@ export declare const LEGACY_PREVIEW_FOCUS_MESSAGE: string;
|
|
|
11
13
|
export declare const PREVIEW_FIELD_ATTRIBUTE = "data-preview-field-path";
|
|
12
14
|
export { STYLE_PATCH_MESSAGE, DENEB_STYLE_PATCH_MESSAGE } from '@deneb-ui/core';
|
|
13
15
|
export type GenericRecord = Record<string, any>;
|
|
16
|
+
export interface SiteDataApiConfig {
|
|
17
|
+
baseUrl?: string | null;
|
|
18
|
+
catalogUrl?: string | null;
|
|
19
|
+
contactUrl?: string | null;
|
|
20
|
+
analyticsUrl?: string | null;
|
|
21
|
+
[key: string]: unknown;
|
|
22
|
+
}
|
|
23
|
+
export interface SiteDataProject {
|
|
24
|
+
id?: string | null;
|
|
25
|
+
slug?: string | null;
|
|
26
|
+
title?: string | null;
|
|
27
|
+
status?: string | null;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
}
|
|
30
|
+
export interface SiteInstanceData {
|
|
31
|
+
id?: string | null;
|
|
32
|
+
slug?: string | null;
|
|
33
|
+
domain?: string | null;
|
|
34
|
+
subdomain?: string | null;
|
|
35
|
+
customDomain?: string | null;
|
|
36
|
+
liveUrl?: string | null;
|
|
37
|
+
[key: string]: unknown;
|
|
38
|
+
}
|
|
14
39
|
export type SiteData = {
|
|
15
|
-
project?:
|
|
16
|
-
|
|
17
|
-
|
|
40
|
+
project?: SiteDataProject | null;
|
|
41
|
+
siteInstance?: SiteInstanceData | null;
|
|
42
|
+
api?: SiteDataApiConfig | null;
|
|
43
|
+
shop?: GenericRecord | null;
|
|
18
44
|
merchant?: GenericRecord | null;
|
|
19
45
|
template?: {
|
|
20
46
|
structure?: {
|
|
@@ -26,6 +52,8 @@ export type SiteData = {
|
|
|
26
52
|
requiredPages?: string[] | null;
|
|
27
53
|
} | null;
|
|
28
54
|
content?: GenericRecord | null;
|
|
55
|
+
media?: Record<string, string[]> | null;
|
|
56
|
+
seo?: GenericRecord | null;
|
|
29
57
|
styles?: GenericRecord | null;
|
|
30
58
|
[key: string]: unknown;
|
|
31
59
|
};
|
|
@@ -44,8 +72,17 @@ export interface SiteDataProviderProps<T = SiteData> {
|
|
|
44
72
|
children: ReactNode;
|
|
45
73
|
initialSiteData?: T;
|
|
46
74
|
fallbackSiteData?: T;
|
|
75
|
+
/**
|
|
76
|
+
* Optional custom live catalog endpoint.
|
|
77
|
+
* If omitted, defaults to `/api/site-catalog/${slug}/live-data`.
|
|
78
|
+
*/
|
|
79
|
+
liveCatalogEndpoint?: string;
|
|
80
|
+
/**
|
|
81
|
+
* Optional site slug for live rehydration. If omitted, resolved from initialSiteData.
|
|
82
|
+
*/
|
|
83
|
+
siteSlug?: string;
|
|
47
84
|
}
|
|
48
|
-
export declare function SiteDataProvider<T extends SiteData = SiteData>({ children, initialSiteData, fallbackSiteData, }: SiteDataProviderProps<T>): React.JSX.Element;
|
|
85
|
+
export declare function SiteDataProvider<T extends SiteData = SiteData>({ children, initialSiteData, fallbackSiteData, liveCatalogEndpoint, siteSlug, }: SiteDataProviderProps<T>): React.JSX.Element;
|
|
49
86
|
export declare function useSiteData<T = SiteData>(): T;
|
|
50
87
|
export declare function contentObject(value: unknown): GenericRecord;
|
|
51
88
|
export declare function contentText(value: unknown): string;
|
|
@@ -62,3 +99,27 @@ export declare const DenebDataProvider: typeof SiteDataProvider;
|
|
|
62
99
|
export declare const useDenebData: typeof useSiteData;
|
|
63
100
|
export declare const DenebDataContext: React.Context<SiteData>;
|
|
64
101
|
export type DenebData = SiteData;
|
|
102
|
+
/**
|
|
103
|
+
* Hook to retrieve products cleanly from SiteData, supporting both
|
|
104
|
+
* top-level content.products and nested content.home.products.
|
|
105
|
+
*/
|
|
106
|
+
export declare function useProducts(fallback?: ProductItem[]): ProductItem[];
|
|
107
|
+
/**
|
|
108
|
+
* Hook to retrieve services cleanly from SiteData, supporting both
|
|
109
|
+
* top-level content.services and nested content.home.services.
|
|
110
|
+
*/
|
|
111
|
+
export declare function useServices(fallback?: ServiceItem[]): ServiceItem[];
|
|
112
|
+
/**
|
|
113
|
+
* Hook to access official Fivora backend API endpoints (catalogUrl, contactUrl, analyticsUrl).
|
|
114
|
+
*/
|
|
115
|
+
export declare function useSiteApi(): SiteDataApiConfig | null;
|
|
116
|
+
/**
|
|
117
|
+
* Hook to access full catalog metadata and live status.
|
|
118
|
+
*/
|
|
119
|
+
export declare function useSiteCatalog(): {
|
|
120
|
+
products: ProductItem[];
|
|
121
|
+
services: ServiceItem[];
|
|
122
|
+
project: SiteDataProject | null;
|
|
123
|
+
siteInstance: SiteInstanceData | null;
|
|
124
|
+
api: SiteDataApiConfig | null;
|
|
125
|
+
};
|
package/dist/SiteDataProvider.js
CHANGED
|
@@ -16,6 +16,10 @@ exports.contentNumber = contentNumber;
|
|
|
16
16
|
exports.parseFieldPath = parseFieldPath;
|
|
17
17
|
exports.getFieldStyle = getFieldStyle;
|
|
18
18
|
exports.useFieldStyle = useFieldStyle;
|
|
19
|
+
exports.useProducts = useProducts;
|
|
20
|
+
exports.useServices = useServices;
|
|
21
|
+
exports.useSiteApi = useSiteApi;
|
|
22
|
+
exports.useSiteCatalog = useSiteCatalog;
|
|
19
23
|
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
20
24
|
const react_1 = require("react");
|
|
21
25
|
const core_1 = require("@deneb-ui/core");
|
|
@@ -167,13 +171,92 @@ function readCachedSiteData() {
|
|
|
167
171
|
return null;
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
|
-
function SiteDataProvider({ children, initialSiteData, fallbackSiteData, }) {
|
|
174
|
+
function SiteDataProvider({ children, initialSiteData, fallbackSiteData, liveCatalogEndpoint, siteSlug, }) {
|
|
171
175
|
// SSR HTML and the first client paint must match. Reading sessionStorage here
|
|
172
176
|
// causes React hydration error #418 when a previous preview left merchant
|
|
173
177
|
// content in the cache. Cache is applied after mount in useEffect instead.
|
|
174
178
|
const [siteData, setSiteData] = (0, react_1.useState)(() => {
|
|
175
179
|
return (initialSiteData ?? fallbackSiteData ?? {});
|
|
176
180
|
});
|
|
181
|
+
// Real-time catalog & theme hydration for standalone live sites
|
|
182
|
+
(0, react_1.useEffect)(() => {
|
|
183
|
+
if (typeof window === "undefined" || window.parent !== window)
|
|
184
|
+
return;
|
|
185
|
+
const candidateSlug = siteSlug ||
|
|
186
|
+
initialSiteData?.siteInstance?.slug ||
|
|
187
|
+
initialSiteData?.project?.slug ||
|
|
188
|
+
initialSiteData?.project?.id;
|
|
189
|
+
const endpoint = liveCatalogEndpoint ||
|
|
190
|
+
initialSiteData?.api?.catalogUrl ||
|
|
191
|
+
(candidateSlug && initialSiteData?.api?.baseUrl
|
|
192
|
+
? `${initialSiteData.api.baseUrl.replace(/\/+$/, '')}/site-catalog/${candidateSlug}/live-data`
|
|
193
|
+
: candidateSlug
|
|
194
|
+
? `/site-catalog/${candidateSlug}/live-data`
|
|
195
|
+
: null);
|
|
196
|
+
if (!endpoint)
|
|
197
|
+
return;
|
|
198
|
+
const controller = new AbortController();
|
|
199
|
+
fetch(endpoint, {
|
|
200
|
+
signal: controller.signal,
|
|
201
|
+
headers: { Accept: "application/json" },
|
|
202
|
+
})
|
|
203
|
+
.then((res) => (res.ok ? res.json() : null))
|
|
204
|
+
.then((live) => {
|
|
205
|
+
if (!live || !isRecord(live))
|
|
206
|
+
return;
|
|
207
|
+
setSiteData((current) => {
|
|
208
|
+
const currentContent = isRecord(current.content) ? current.content : {};
|
|
209
|
+
const currentHome = isRecord(currentContent.home) ? currentContent.home : null;
|
|
210
|
+
const nextProducts = Array.isArray(live.products)
|
|
211
|
+
? live.products
|
|
212
|
+
: currentContent.products;
|
|
213
|
+
const nextServices = Array.isArray(live.services)
|
|
214
|
+
? live.services
|
|
215
|
+
: currentContent.services;
|
|
216
|
+
return {
|
|
217
|
+
...current,
|
|
218
|
+
content: {
|
|
219
|
+
...currentContent,
|
|
220
|
+
...(nextProducts !== undefined ? { products: nextProducts } : {}),
|
|
221
|
+
...(nextServices !== undefined ? { services: nextServices } : {}),
|
|
222
|
+
...(currentHome
|
|
223
|
+
? {
|
|
224
|
+
home: {
|
|
225
|
+
...currentHome,
|
|
226
|
+
...(nextProducts !== undefined && 'products' in currentHome
|
|
227
|
+
? { products: nextProducts }
|
|
228
|
+
: {}),
|
|
229
|
+
...(nextServices !== undefined && 'services' in currentHome
|
|
230
|
+
? { services: nextServices }
|
|
231
|
+
: {}),
|
|
232
|
+
...(nextProducts !== undefined && 'featuredProducts' in currentHome
|
|
233
|
+
? { featuredProducts: nextProducts }
|
|
234
|
+
: {}),
|
|
235
|
+
},
|
|
236
|
+
}
|
|
237
|
+
: {}),
|
|
238
|
+
},
|
|
239
|
+
};
|
|
240
|
+
});
|
|
241
|
+
// Dynamic theme variable injection for instant color updates
|
|
242
|
+
if (isRecord(live.theme)) {
|
|
243
|
+
const rootStyle = document.documentElement.style;
|
|
244
|
+
if (typeof live.theme.primaryColor === "string") {
|
|
245
|
+
rootStyle.setProperty("--brand-primary", live.theme.primaryColor);
|
|
246
|
+
}
|
|
247
|
+
if (typeof live.theme.secondaryColor === "string") {
|
|
248
|
+
rootStyle.setProperty("--brand-secondary", live.theme.secondaryColor);
|
|
249
|
+
}
|
|
250
|
+
if (typeof live.theme.accentColor === "string") {
|
|
251
|
+
rootStyle.setProperty("--brand-accent", live.theme.accentColor);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
})
|
|
255
|
+
.catch(() => {
|
|
256
|
+
// Gracefully keep pre-rendered static fallback if live API is unreachable
|
|
257
|
+
});
|
|
258
|
+
return () => controller.abort();
|
|
259
|
+
}, [liveCatalogEndpoint, siteSlug, initialSiteData]);
|
|
177
260
|
(0, react_1.useEffect)(() => {
|
|
178
261
|
let parentOrigin = resolveParentOrigin();
|
|
179
262
|
const applyIncomingSiteData = (incoming) => {
|
|
@@ -342,3 +425,71 @@ function useFieldStyle(path) {
|
|
|
342
425
|
exports.DenebDataProvider = SiteDataProvider;
|
|
343
426
|
exports.useDenebData = useSiteData;
|
|
344
427
|
exports.DenebDataContext = exports.SiteDataContext;
|
|
428
|
+
/**
|
|
429
|
+
* Hook to retrieve products cleanly from SiteData, supporting both
|
|
430
|
+
* top-level content.products and nested content.home.products.
|
|
431
|
+
*/
|
|
432
|
+
function useProducts(fallback = []) {
|
|
433
|
+
const siteData = useSiteData();
|
|
434
|
+
const content = isRecord(siteData?.content) ? siteData.content : null;
|
|
435
|
+
if (!content)
|
|
436
|
+
return fallback;
|
|
437
|
+
if (Array.isArray(content.products) && content.products.length > 0) {
|
|
438
|
+
return content.products;
|
|
439
|
+
}
|
|
440
|
+
const home = isRecord(content.home) ? content.home : null;
|
|
441
|
+
if (home) {
|
|
442
|
+
if (Array.isArray(home.products) && home.products.length > 0) {
|
|
443
|
+
return home.products;
|
|
444
|
+
}
|
|
445
|
+
if (Array.isArray(home.featuredProducts) && home.featuredProducts.length > 0) {
|
|
446
|
+
return home.featuredProducts;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return fallback;
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Hook to retrieve services cleanly from SiteData, supporting both
|
|
453
|
+
* top-level content.services and nested content.home.services.
|
|
454
|
+
*/
|
|
455
|
+
function useServices(fallback = []) {
|
|
456
|
+
const siteData = useSiteData();
|
|
457
|
+
const content = isRecord(siteData?.content) ? siteData.content : null;
|
|
458
|
+
if (!content)
|
|
459
|
+
return fallback;
|
|
460
|
+
if (Array.isArray(content.services) && content.services.length > 0) {
|
|
461
|
+
return content.services;
|
|
462
|
+
}
|
|
463
|
+
const home = isRecord(content.home) ? content.home : null;
|
|
464
|
+
if (home) {
|
|
465
|
+
if (Array.isArray(home.services) && home.services.length > 0) {
|
|
466
|
+
return home.services;
|
|
467
|
+
}
|
|
468
|
+
if (Array.isArray(home.featuredServices) && home.featuredServices.length > 0) {
|
|
469
|
+
return home.featuredServices;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return fallback;
|
|
473
|
+
}
|
|
474
|
+
/**
|
|
475
|
+
* Hook to access official Fivora backend API endpoints (catalogUrl, contactUrl, analyticsUrl).
|
|
476
|
+
*/
|
|
477
|
+
function useSiteApi() {
|
|
478
|
+
const siteData = useSiteData();
|
|
479
|
+
return siteData?.api ?? null;
|
|
480
|
+
}
|
|
481
|
+
/**
|
|
482
|
+
* Hook to access full catalog metadata and live status.
|
|
483
|
+
*/
|
|
484
|
+
function useSiteCatalog() {
|
|
485
|
+
const products = useProducts();
|
|
486
|
+
const services = useServices();
|
|
487
|
+
const siteData = useSiteData();
|
|
488
|
+
return {
|
|
489
|
+
products,
|
|
490
|
+
services,
|
|
491
|
+
project: siteData?.project ?? null,
|
|
492
|
+
siteInstance: siteData?.siteInstance ?? null,
|
|
493
|
+
api: siteData?.api ?? null,
|
|
494
|
+
};
|
|
495
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deneb-ui/ui",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.39",
|
|
4
4
|
"description": "Visual-first React component library for editable commerce storefronts. Built for Next.js and Fivora.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
],
|
|
50
50
|
"license": "MIT",
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@deneb-ui/core": "^2.0.
|
|
52
|
+
"@deneb-ui/core": "^2.0.39"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"react": "^18.0.0 || ^19.0.0",
|