@deneb-ui/ui 2.0.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/README.md +182 -0
- package/dist/EditableBox.d.ts +56 -0
- package/dist/EditableBox.js +113 -0
- package/dist/EditableCard.d.ts +48 -0
- package/dist/EditableCard.js +85 -0
- package/dist/EditableContactForm.d.ts +11 -0
- package/dist/EditableContactForm.js +146 -0
- package/dist/EditableDialog.d.ts +25 -0
- package/dist/EditableDialog.js +145 -0
- package/dist/EditableFAQAccordion.d.ts +19 -0
- package/dist/EditableFAQAccordion.js +62 -0
- package/dist/EditableFooter.d.ts +31 -0
- package/dist/EditableFooter.js +330 -0
- package/dist/EditableGrid.d.ts +40 -0
- package/dist/EditableGrid.js +39 -0
- package/dist/EditableHero.d.ts +27 -0
- package/dist/EditableHero.js +154 -0
- package/dist/EditableImage.d.ts +13 -0
- package/dist/EditableImage.js +38 -0
- package/dist/EditableList.d.ts +33 -0
- package/dist/EditableList.js +19 -0
- package/dist/EditableNavbar.d.ts +28 -0
- package/dist/EditableNavbar.js +178 -0
- package/dist/EditablePricingCard.d.ts +31 -0
- package/dist/EditablePricingCard.js +93 -0
- package/dist/EditableProductCard.d.ts +60 -0
- package/dist/EditableProductCard.js +116 -0
- package/dist/EditableSection.d.ts +35 -0
- package/dist/EditableSection.js +34 -0
- package/dist/EditableServiceCard.d.ts +38 -0
- package/dist/EditableServiceCard.js +28 -0
- package/dist/EditableTestimonialCard.d.ts +17 -0
- package/dist/EditableTestimonialCard.js +46 -0
- package/dist/EditableText.d.ts +117 -0
- package/dist/EditableText.js +319 -0
- package/dist/SiteDataProvider.d.ts +61 -0
- package/dist/SiteDataProvider.js +316 -0
- package/dist/ThemeStyles.d.ts +46 -0
- package/dist/ThemeStyles.js +181 -0
- package/dist/business/BusinessHours.d.ts +20 -0
- package/dist/business/BusinessHours.js +109 -0
- package/dist/business/index.d.ts +1 -0
- package/dist/business/index.js +17 -0
- package/dist/contact/ContactActions.d.ts +24 -0
- package/dist/contact/ContactActions.js +30 -0
- package/dist/contact/ContactButton.d.ts +15 -0
- package/dist/contact/ContactButton.js +94 -0
- package/dist/contact/EmailButton.d.ts +6 -0
- package/dist/contact/EmailButton.js +12 -0
- package/dist/contact/PhoneButton.d.ts +6 -0
- package/dist/contact/PhoneButton.js +12 -0
- package/dist/contact/WhatsAppButton.d.ts +6 -0
- package/dist/contact/WhatsAppButton.js +13 -0
- package/dist/contact/index.d.ts +5 -0
- package/dist/contact/index.js +21 -0
- package/dist/core/DenebAction.d.ts +19 -0
- package/dist/core/DenebAction.js +107 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +17 -0
- package/dist/index.d.ts +61 -0
- package/dist/index.js +106 -0
- package/dist/location/Address.d.ts +14 -0
- package/dist/location/Address.js +26 -0
- package/dist/location/LocationCard.d.ts +20 -0
- package/dist/location/LocationCard.js +41 -0
- package/dist/location/LocationLink.d.ts +18 -0
- package/dist/location/LocationLink.js +23 -0
- package/dist/location/MapEmbed.d.ts +18 -0
- package/dist/location/MapEmbed.js +30 -0
- package/dist/location/MapLink.d.ts +13 -0
- package/dist/location/MapLink.js +63 -0
- package/dist/location/index.d.ts +5 -0
- package/dist/location/index.js +21 -0
- package/dist/social/SocialButton.d.ts +11 -0
- package/dist/social/SocialButton.js +112 -0
- package/dist/social/SocialLinks.d.ts +14 -0
- package/dist/social/SocialLinks.js +42 -0
- package/dist/social/index.d.ts +2 -0
- package/dist/social/index.js +18 -0
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.js +17 -0
- package/dist/utils/urls.d.ts +32 -0
- package/dist/utils/urls.js +91 -0
- package/package.json +41 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import React, { type ReactNode } from 'react';
|
|
2
|
+
export declare const DENEB_PREVIEW_DATA_MESSAGE = "DENEB_PREVIEW_SITE_DATA";
|
|
3
|
+
export declare const PREVIEW_DATA_MESSAGE = "FIVORA_PREVIEW_SITE_DATA";
|
|
4
|
+
export declare const LEGACY_PREVIEW_DATA_MESSAGE: string;
|
|
5
|
+
export declare const DENEB_PREVIEW_READY_MESSAGE = "DENEB_PREVIEW_READY";
|
|
6
|
+
export declare const PREVIEW_READY_MESSAGE = "FIVORA_PREVIEW_READY";
|
|
7
|
+
export declare const LEGACY_PREVIEW_READY_MESSAGE: string;
|
|
8
|
+
export declare const DENEB_PREVIEW_FOCUS_MESSAGE = "DENEB_PREVIEW_FOCUS_PAGE";
|
|
9
|
+
export declare const PREVIEW_FOCUS_MESSAGE = "FIVORA_PREVIEW_FOCUS_PAGE";
|
|
10
|
+
export declare const LEGACY_PREVIEW_FOCUS_MESSAGE: string;
|
|
11
|
+
export declare const PREVIEW_FIELD_ATTRIBUTE = "data-preview-field-path";
|
|
12
|
+
export type GenericRecord = Record<string, unknown>;
|
|
13
|
+
export type SiteData = {
|
|
14
|
+
project?: {
|
|
15
|
+
id?: string | null;
|
|
16
|
+
} | null;
|
|
17
|
+
merchant?: GenericRecord | null;
|
|
18
|
+
template?: {
|
|
19
|
+
structure?: {
|
|
20
|
+
pages?: string[] | null;
|
|
21
|
+
theme?: GenericRecord | null;
|
|
22
|
+
} | null;
|
|
23
|
+
} | null;
|
|
24
|
+
requirements?: {
|
|
25
|
+
requiredPages?: string[] | null;
|
|
26
|
+
} | null;
|
|
27
|
+
content?: GenericRecord | null;
|
|
28
|
+
[key: string]: unknown;
|
|
29
|
+
};
|
|
30
|
+
export declare const SiteDataContext: React.Context<SiteData>;
|
|
31
|
+
export declare function isRecord(value: unknown): value is GenericRecord;
|
|
32
|
+
export declare function mergeSiteData(current: unknown, incoming: unknown): unknown;
|
|
33
|
+
export declare function normalizeOrigin(value?: string | null, baseOrigin?: string | null): string | null;
|
|
34
|
+
/**
|
|
35
|
+
* Match the injected preview focus bridge: after in-iframe navigations,
|
|
36
|
+
* document.referrer becomes the previous preview page (same origin), so we
|
|
37
|
+
* must retain the real parent origin from the first embed.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveParentOrigin(): string | null;
|
|
40
|
+
export declare function readCachedSiteData<T = SiteData>(): T | null;
|
|
41
|
+
export interface SiteDataProviderProps<T = SiteData> {
|
|
42
|
+
children: ReactNode;
|
|
43
|
+
initialSiteData?: T;
|
|
44
|
+
fallbackSiteData?: T;
|
|
45
|
+
}
|
|
46
|
+
export declare function SiteDataProvider<T extends SiteData = SiteData>({ children, initialSiteData, fallbackSiteData, }: SiteDataProviderProps<T>): React.JSX.Element;
|
|
47
|
+
export declare function useSiteData<T = SiteData>(): T;
|
|
48
|
+
export declare function contentObject(value: unknown): GenericRecord;
|
|
49
|
+
export declare function contentText(value: unknown): string;
|
|
50
|
+
export declare function contentList<Item = unknown>(value: unknown): Item[];
|
|
51
|
+
export declare function parseFieldPath(path: string): Array<string | number>;
|
|
52
|
+
export declare function getFieldStyle(content: unknown, path?: string): GenericRecord | null;
|
|
53
|
+
export declare function useFieldStyle(path?: string): GenericRecord | null;
|
|
54
|
+
/**
|
|
55
|
+
* Official DENEB UI data aliases for modern storefront development.
|
|
56
|
+
* Fully compatible with Fivora visual editing engine and marketplace preview.
|
|
57
|
+
*/
|
|
58
|
+
export declare const DenebDataProvider: typeof SiteDataProvider;
|
|
59
|
+
export declare const useDenebData: typeof useSiteData;
|
|
60
|
+
export declare const DenebDataContext: React.Context<SiteData>;
|
|
61
|
+
export type DenebData = SiteData;
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
'use client';
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.DenebDataContext = exports.useDenebData = exports.DenebDataProvider = exports.SiteDataContext = exports.PREVIEW_FIELD_ATTRIBUTE = exports.LEGACY_PREVIEW_FOCUS_MESSAGE = exports.PREVIEW_FOCUS_MESSAGE = exports.DENEB_PREVIEW_FOCUS_MESSAGE = exports.LEGACY_PREVIEW_READY_MESSAGE = exports.PREVIEW_READY_MESSAGE = exports.DENEB_PREVIEW_READY_MESSAGE = exports.LEGACY_PREVIEW_DATA_MESSAGE = exports.PREVIEW_DATA_MESSAGE = exports.DENEB_PREVIEW_DATA_MESSAGE = void 0;
|
|
5
|
+
exports.isRecord = isRecord;
|
|
6
|
+
exports.mergeSiteData = mergeSiteData;
|
|
7
|
+
exports.normalizeOrigin = normalizeOrigin;
|
|
8
|
+
exports.resolveParentOrigin = resolveParentOrigin;
|
|
9
|
+
exports.readCachedSiteData = readCachedSiteData;
|
|
10
|
+
exports.SiteDataProvider = SiteDataProvider;
|
|
11
|
+
exports.useSiteData = useSiteData;
|
|
12
|
+
exports.contentObject = contentObject;
|
|
13
|
+
exports.contentText = contentText;
|
|
14
|
+
exports.contentList = contentList;
|
|
15
|
+
exports.parseFieldPath = parseFieldPath;
|
|
16
|
+
exports.getFieldStyle = getFieldStyle;
|
|
17
|
+
exports.useFieldStyle = useFieldStyle;
|
|
18
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
19
|
+
const react_1 = require("react");
|
|
20
|
+
exports.DENEB_PREVIEW_DATA_MESSAGE = 'DENEB_PREVIEW_SITE_DATA';
|
|
21
|
+
exports.PREVIEW_DATA_MESSAGE = 'FIVORA_PREVIEW_SITE_DATA';
|
|
22
|
+
const PREVIOUS_PREVIEW_PREFIX = `${['MARKET', 'PLACE'].join('')}_PREVIEW_`;
|
|
23
|
+
const previousPreviewMessage = (suffix) => `${PREVIOUS_PREVIEW_PREFIX}${suffix}`;
|
|
24
|
+
const previousPreviewStorageKey = (suffix) => `__${PREVIOUS_PREVIEW_PREFIX}${suffix}__`;
|
|
25
|
+
exports.LEGACY_PREVIEW_DATA_MESSAGE = previousPreviewMessage('SITE_DATA');
|
|
26
|
+
exports.DENEB_PREVIEW_READY_MESSAGE = 'DENEB_PREVIEW_READY';
|
|
27
|
+
exports.PREVIEW_READY_MESSAGE = 'FIVORA_PREVIEW_READY';
|
|
28
|
+
exports.LEGACY_PREVIEW_READY_MESSAGE = previousPreviewMessage('READY');
|
|
29
|
+
exports.DENEB_PREVIEW_FOCUS_MESSAGE = 'DENEB_PREVIEW_FOCUS_PAGE';
|
|
30
|
+
exports.PREVIEW_FOCUS_MESSAGE = 'FIVORA_PREVIEW_FOCUS_PAGE';
|
|
31
|
+
exports.LEGACY_PREVIEW_FOCUS_MESSAGE = previousPreviewMessage('FOCUS_PAGE');
|
|
32
|
+
exports.PREVIEW_FIELD_ATTRIBUTE = 'data-preview-field-path';
|
|
33
|
+
const PARENT_ORIGIN_KEY = '__FIVORA_PREVIEW_PARENT_ORIGIN__';
|
|
34
|
+
const LEGACY_PARENT_ORIGIN_KEY = previousPreviewStorageKey('PARENT_ORIGIN');
|
|
35
|
+
const SITE_DATA_CACHE_KEY = '__FIVORA_PREVIEW_SITE_DATA_CACHE__';
|
|
36
|
+
const LEGACY_SITE_DATA_CACHE_KEY = previousPreviewStorageKey('SITE_DATA_CACHE');
|
|
37
|
+
const SITE_DATA_GLOBAL_KEY = '__FIVORA_PREVIEW_SITE_DATA__';
|
|
38
|
+
const LEGACY_SITE_DATA_GLOBAL_KEY = previousPreviewStorageKey('SITE_DATA');
|
|
39
|
+
exports.SiteDataContext = (0, react_1.createContext)({});
|
|
40
|
+
function isRecord(value) {
|
|
41
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
42
|
+
}
|
|
43
|
+
function mergeSiteData(current, incoming) {
|
|
44
|
+
if (Array.isArray(incoming))
|
|
45
|
+
return incoming;
|
|
46
|
+
if (!isRecord(incoming))
|
|
47
|
+
return incoming;
|
|
48
|
+
const base = isRecord(current) ? current : {};
|
|
49
|
+
const next = { ...base };
|
|
50
|
+
for (const key of Object.keys(incoming)) {
|
|
51
|
+
// Parent preview always sends complete content/media snapshots. Replacing
|
|
52
|
+
// them wholesale avoids deep-cloning large collections on every keystroke.
|
|
53
|
+
if (key === 'content' || key === 'media') {
|
|
54
|
+
next[key] = incoming[key];
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
next[key] = mergeSiteData(base[key], incoming[key]);
|
|
58
|
+
}
|
|
59
|
+
return next;
|
|
60
|
+
}
|
|
61
|
+
function normalizeOrigin(value, baseOrigin) {
|
|
62
|
+
if (!value || value.trim() === 'null')
|
|
63
|
+
return null;
|
|
64
|
+
try {
|
|
65
|
+
const origin = new URL(value, baseOrigin ?? undefined).origin;
|
|
66
|
+
return origin === 'null' ? null : origin;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function readRememberedParentOrigin() {
|
|
73
|
+
try {
|
|
74
|
+
return (window.sessionStorage.getItem(PARENT_ORIGIN_KEY) ||
|
|
75
|
+
window.sessionStorage.getItem(LEGACY_PARENT_ORIGIN_KEY));
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function rememberParentOrigin(origin) {
|
|
82
|
+
if (!origin)
|
|
83
|
+
return;
|
|
84
|
+
try {
|
|
85
|
+
window.sessionStorage.setItem(PARENT_ORIGIN_KEY, origin);
|
|
86
|
+
window.sessionStorage.setItem(LEGACY_PARENT_ORIGIN_KEY, origin);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// Messaging still works without session storage.
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Match the injected preview focus bridge: after in-iframe navigations,
|
|
94
|
+
* document.referrer becomes the previous preview page (same origin), so we
|
|
95
|
+
* must retain the real parent origin from the first embed.
|
|
96
|
+
*/
|
|
97
|
+
function resolveParentOrigin() {
|
|
98
|
+
if (typeof window === 'undefined')
|
|
99
|
+
return null;
|
|
100
|
+
const currentOrigin = normalizeOrigin(window.location.origin);
|
|
101
|
+
let ancestorOrigin = null;
|
|
102
|
+
try {
|
|
103
|
+
ancestorOrigin = normalizeOrigin(window.location
|
|
104
|
+
.ancestorOrigins?.item(0), currentOrigin);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
ancestorOrigin = null;
|
|
108
|
+
}
|
|
109
|
+
let accessibleParentOrigin = null;
|
|
110
|
+
try {
|
|
111
|
+
accessibleParentOrigin =
|
|
112
|
+
window.parent !== window
|
|
113
|
+
? normalizeOrigin(window.parent.location.origin, currentOrigin)
|
|
114
|
+
: null;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
accessibleParentOrigin = null;
|
|
118
|
+
}
|
|
119
|
+
const referrerOrigin = normalizeOrigin(document.referrer, currentOrigin);
|
|
120
|
+
const rememberedOrigin = normalizeOrigin(readRememberedParentOrigin());
|
|
121
|
+
const resolved = ancestorOrigin ??
|
|
122
|
+
accessibleParentOrigin ??
|
|
123
|
+
(referrerOrigin && referrerOrigin !== currentOrigin
|
|
124
|
+
? referrerOrigin
|
|
125
|
+
: null) ??
|
|
126
|
+
rememberedOrigin ??
|
|
127
|
+
referrerOrigin;
|
|
128
|
+
rememberParentOrigin(resolved);
|
|
129
|
+
return resolved;
|
|
130
|
+
}
|
|
131
|
+
function readCachedSiteData() {
|
|
132
|
+
if (typeof window === 'undefined')
|
|
133
|
+
return null;
|
|
134
|
+
try {
|
|
135
|
+
const globalStore = window;
|
|
136
|
+
const globalData = globalStore[SITE_DATA_GLOBAL_KEY] ??
|
|
137
|
+
globalStore[LEGACY_SITE_DATA_GLOBAL_KEY];
|
|
138
|
+
if (isRecord(globalData)) {
|
|
139
|
+
return globalData;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// Ignore non-extensible window environments.
|
|
144
|
+
}
|
|
145
|
+
try {
|
|
146
|
+
const raw = window.sessionStorage.getItem(SITE_DATA_CACHE_KEY) ||
|
|
147
|
+
window.sessionStorage.getItem(LEGACY_SITE_DATA_CACHE_KEY);
|
|
148
|
+
if (!raw)
|
|
149
|
+
return null;
|
|
150
|
+
const parsed = JSON.parse(raw);
|
|
151
|
+
return isRecord(parsed) ? parsed : null;
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
function SiteDataProvider({ children, initialSiteData, fallbackSiteData, }) {
|
|
158
|
+
// SSR HTML and the first client paint must match. Reading sessionStorage here
|
|
159
|
+
// causes React hydration error #418 when a previous preview left merchant
|
|
160
|
+
// content in the cache. Cache is applied after mount in useEffect instead.
|
|
161
|
+
const [siteData, setSiteData] = (0, react_1.useState)(() => {
|
|
162
|
+
return (initialSiteData ?? fallbackSiteData ?? {});
|
|
163
|
+
});
|
|
164
|
+
(0, react_1.useEffect)(() => {
|
|
165
|
+
let parentOrigin = resolveParentOrigin();
|
|
166
|
+
const applyIncomingSiteData = (incoming) => {
|
|
167
|
+
if (!isRecord(incoming))
|
|
168
|
+
return;
|
|
169
|
+
setSiteData((current) => mergeSiteData(current, incoming));
|
|
170
|
+
try {
|
|
171
|
+
window[SITE_DATA_GLOBAL_KEY] =
|
|
172
|
+
incoming;
|
|
173
|
+
window[LEGACY_SITE_DATA_GLOBAL_KEY] = incoming;
|
|
174
|
+
}
|
|
175
|
+
catch {
|
|
176
|
+
// Ignore non-extensible window environments.
|
|
177
|
+
}
|
|
178
|
+
// Debounce session cache writes — stringify of large templates is costly.
|
|
179
|
+
const win = window;
|
|
180
|
+
window.clearTimeout(win.__fivoraSiteDataPersistTimer);
|
|
181
|
+
win.__fivoraSiteDataPersistTimer = window.setTimeout(() => {
|
|
182
|
+
try {
|
|
183
|
+
const serialized = JSON.stringify(incoming);
|
|
184
|
+
window.sessionStorage.setItem(SITE_DATA_CACHE_KEY, serialized);
|
|
185
|
+
window.sessionStorage.setItem(LEGACY_SITE_DATA_CACHE_KEY, serialized);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
// Cache is best-effort.
|
|
189
|
+
}
|
|
190
|
+
}, 750);
|
|
191
|
+
};
|
|
192
|
+
const onMessage = (event) => {
|
|
193
|
+
if (event.source !== window.parent)
|
|
194
|
+
return;
|
|
195
|
+
if (!isRecord(event.data))
|
|
196
|
+
return;
|
|
197
|
+
const isData = event.data.type === exports.DENEB_PREVIEW_DATA_MESSAGE ||
|
|
198
|
+
event.data.type === exports.PREVIEW_DATA_MESSAGE ||
|
|
199
|
+
event.data.type === exports.LEGACY_PREVIEW_DATA_MESSAGE;
|
|
200
|
+
const isFocus = event.data.type === exports.DENEB_PREVIEW_FOCUS_MESSAGE ||
|
|
201
|
+
event.data.type === exports.PREVIEW_FOCUS_MESSAGE ||
|
|
202
|
+
event.data.type === exports.LEGACY_PREVIEW_FOCUS_MESSAGE;
|
|
203
|
+
if (!isData && !isFocus) {
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
// Pin the first concrete parent origin when referrer was suppressed.
|
|
207
|
+
if (!parentOrigin) {
|
|
208
|
+
try {
|
|
209
|
+
const candidateOrigin = new URL(event.origin).origin;
|
|
210
|
+
if (candidateOrigin !== 'null') {
|
|
211
|
+
parentOrigin = candidateOrigin;
|
|
212
|
+
rememberParentOrigin(candidateOrigin);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
catch {
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
// Accept the remembered/resolved parent origin, or a same-origin relay
|
|
220
|
+
// from the injected focus bridge (used after in-iframe navigations).
|
|
221
|
+
const trustedOrigins = new Set([parentOrigin, window.location.origin].filter(Boolean));
|
|
222
|
+
if (!trustedOrigins.has(event.origin))
|
|
223
|
+
return;
|
|
224
|
+
if (isData && isRecord(event.data.siteData)) {
|
|
225
|
+
applyIncomingSiteData(event.data.siteData);
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (isFocus) {
|
|
229
|
+
const fieldPath = typeof event.data.fieldPath === 'string' ? event.data.fieldPath : '';
|
|
230
|
+
if (!fieldPath)
|
|
231
|
+
return;
|
|
232
|
+
requestAnimationFrame(() => {
|
|
233
|
+
const target = document.querySelector(`[${exports.PREVIEW_FIELD_ATTRIBUTE}="${CSS.escape(fieldPath)}"]`);
|
|
234
|
+
target?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
window.addEventListener('message', onMessage);
|
|
239
|
+
// Re-apply cache after mount in case the bridge published between
|
|
240
|
+
// useState init and listener registration.
|
|
241
|
+
const cached = readCachedSiteData();
|
|
242
|
+
if (cached) {
|
|
243
|
+
applyIncomingSiteData(cached);
|
|
244
|
+
}
|
|
245
|
+
window.parent.postMessage({ type: exports.DENEB_PREVIEW_READY_MESSAGE, pathname: window.location.pathname }, parentOrigin ?? '*');
|
|
246
|
+
window.parent.postMessage({ type: exports.PREVIEW_READY_MESSAGE, pathname: window.location.pathname }, parentOrigin ?? '*');
|
|
247
|
+
window.parent.postMessage({ type: exports.LEGACY_PREVIEW_READY_MESSAGE, pathname: window.location.pathname }, parentOrigin ?? '*');
|
|
248
|
+
return () => window.removeEventListener('message', onMessage);
|
|
249
|
+
}, []);
|
|
250
|
+
const value = (0, react_1.useMemo)(() => siteData, [siteData]);
|
|
251
|
+
return ((0, jsx_runtime_1.jsx)(exports.SiteDataContext.Provider, { value: value, children: children }));
|
|
252
|
+
}
|
|
253
|
+
function useSiteData() {
|
|
254
|
+
return (0, react_1.useContext)(exports.SiteDataContext);
|
|
255
|
+
}
|
|
256
|
+
function contentObject(value) {
|
|
257
|
+
return isRecord(value) ? value : {};
|
|
258
|
+
}
|
|
259
|
+
function contentText(value) {
|
|
260
|
+
return typeof value === 'string' ? value : '';
|
|
261
|
+
}
|
|
262
|
+
function contentList(value) {
|
|
263
|
+
return Array.isArray(value) ? value : [];
|
|
264
|
+
}
|
|
265
|
+
function parseFieldPath(path) {
|
|
266
|
+
if (!path)
|
|
267
|
+
return [];
|
|
268
|
+
return path
|
|
269
|
+
.replace(/\[(\d+)\]/g, ".$1")
|
|
270
|
+
.split(".")
|
|
271
|
+
.filter(Boolean)
|
|
272
|
+
.map((part) => (/^\d+$/.test(part) ? Number(part) : part));
|
|
273
|
+
}
|
|
274
|
+
function getFieldStyle(content, path) {
|
|
275
|
+
if (!content || !path || typeof content !== "object")
|
|
276
|
+
return null;
|
|
277
|
+
const parts = parseFieldPath(path);
|
|
278
|
+
if (parts.length === 0)
|
|
279
|
+
return null;
|
|
280
|
+
const last = parts.pop();
|
|
281
|
+
if (last === undefined)
|
|
282
|
+
return null;
|
|
283
|
+
let current = content;
|
|
284
|
+
for (const part of parts) {
|
|
285
|
+
if (typeof part === "number") {
|
|
286
|
+
if (!Array.isArray(current))
|
|
287
|
+
return null;
|
|
288
|
+
current = current[part];
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
291
|
+
if (!isRecord(current))
|
|
292
|
+
return null;
|
|
293
|
+
current = current[part];
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (!isRecord(current))
|
|
297
|
+
return null;
|
|
298
|
+
const styleKey = `${String(last)}Style`;
|
|
299
|
+
const styleObj = current[styleKey];
|
|
300
|
+
return isRecord(styleObj) ? styleObj : null;
|
|
301
|
+
}
|
|
302
|
+
function useFieldStyle(path) {
|
|
303
|
+
const siteData = useSiteData();
|
|
304
|
+
return (0, react_1.useMemo)(() => {
|
|
305
|
+
if (!path || !siteData?.content)
|
|
306
|
+
return null;
|
|
307
|
+
return getFieldStyle(siteData.content, path);
|
|
308
|
+
}, [siteData?.content, path]);
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Official DENEB UI data aliases for modern storefront development.
|
|
312
|
+
* Fully compatible with Fivora visual editing engine and marketplace preview.
|
|
313
|
+
*/
|
|
314
|
+
exports.DenebDataProvider = SiteDataProvider;
|
|
315
|
+
exports.useDenebData = useSiteData;
|
|
316
|
+
exports.DenebDataContext = exports.SiteDataContext;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface TemplateTheme {
|
|
3
|
+
primaryColor?: string;
|
|
4
|
+
secondaryColor?: string;
|
|
5
|
+
accentColor?: string;
|
|
6
|
+
backgroundColor?: string;
|
|
7
|
+
textColor?: string;
|
|
8
|
+
headingColor?: string;
|
|
9
|
+
mutedTextColor?: string;
|
|
10
|
+
linkColor?: string;
|
|
11
|
+
heroMinHeight?: string;
|
|
12
|
+
sectionPadding?: string;
|
|
13
|
+
baseSize?: string;
|
|
14
|
+
headingFont?: string;
|
|
15
|
+
bodyFont?: string;
|
|
16
|
+
borderRadius?: string;
|
|
17
|
+
align?: 'left' | 'center' | 'right';
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Pre-configured, high-converting theme presets tailored for different business categories.
|
|
22
|
+
* Allows developers and AI agents to instantly generate distinct designs without copy-pasting styles.
|
|
23
|
+
*/
|
|
24
|
+
export declare const THEME_PRESETS: Record<string, TemplateTheme>;
|
|
25
|
+
/**
|
|
26
|
+
* Merges category presets with custom developer/agent overrides.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getCategoryTheme(category: keyof typeof THEME_PRESETS | string, overrides?: Partial<TemplateTheme>): TemplateTheme;
|
|
29
|
+
/**
|
|
30
|
+
* Extracts a typed CSSProperties object containing both standard and custom theme tokens.
|
|
31
|
+
* Any custom property defined by a developer in `theme` (e.g. cardBg: "#111")
|
|
32
|
+
* is automatically converted to a CSS variable (e.g. --card-bg: #111).
|
|
33
|
+
*/
|
|
34
|
+
export declare function getThemeCssProperties(theme?: TemplateTheme | null): React.CSSProperties;
|
|
35
|
+
export interface ThemeStylesProps {
|
|
36
|
+
theme?: TemplateTheme | null;
|
|
37
|
+
defaultPrimary?: string;
|
|
38
|
+
defaultSecondary?: string;
|
|
39
|
+
defaultAccent?: string;
|
|
40
|
+
defaultBg?: string;
|
|
41
|
+
defaultText?: string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Automatically injects standard and custom fivora theme variables into the document.
|
|
45
|
+
*/
|
|
46
|
+
export declare function ThemeStyles({ theme, defaultPrimary, defaultSecondary, defaultAccent, defaultBg, defaultText, }: ThemeStylesProps): React.JSX.Element;
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.THEME_PRESETS = void 0;
|
|
4
|
+
exports.getCategoryTheme = getCategoryTheme;
|
|
5
|
+
exports.getThemeCssProperties = getThemeCssProperties;
|
|
6
|
+
exports.ThemeStyles = ThemeStyles;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
/**
|
|
9
|
+
* Pre-configured, high-converting theme presets tailored for different business categories.
|
|
10
|
+
* Allows developers and AI agents to instantly generate distinct designs without copy-pasting styles.
|
|
11
|
+
*/
|
|
12
|
+
exports.THEME_PRESETS = {
|
|
13
|
+
restaurant: {
|
|
14
|
+
primaryColor: '#b45309', // Warm amber
|
|
15
|
+
secondaryColor: '#1c1917', // Deep charcoal
|
|
16
|
+
accentColor: '#f59e0b', // Golden yellow
|
|
17
|
+
backgroundColor: '#0c0a09', // Dark atmospheric background
|
|
18
|
+
textColor: '#f5f5f4', // Off-white
|
|
19
|
+
headingFont: 'Playfair Display, Georgia, serif',
|
|
20
|
+
bodyFont: 'Inter, sans-serif',
|
|
21
|
+
borderRadius: '12px',
|
|
22
|
+
align: 'left',
|
|
23
|
+
},
|
|
24
|
+
medical: {
|
|
25
|
+
primaryColor: '#0284c7', // Serene ocean blue
|
|
26
|
+
secondaryColor: '#0f172a', // Deep slate
|
|
27
|
+
accentColor: '#0d9488', // Teal accent
|
|
28
|
+
backgroundColor: '#ffffff', // Pure clean white
|
|
29
|
+
textColor: '#334155', // Slate body text
|
|
30
|
+
headingFont: 'Plus Jakarta Sans, sans-serif',
|
|
31
|
+
bodyFont: 'Inter, sans-serif',
|
|
32
|
+
borderRadius: '8px',
|
|
33
|
+
align: 'left',
|
|
34
|
+
},
|
|
35
|
+
luxury: {
|
|
36
|
+
primaryColor: '#d4af37', // Metallic gold
|
|
37
|
+
secondaryColor: '#000000', // Jet black
|
|
38
|
+
accentColor: '#e5e5e5', // Platinum silver
|
|
39
|
+
backgroundColor: '#0a0a0a', // Obsidian dark
|
|
40
|
+
textColor: '#ffffff',
|
|
41
|
+
headingFont: 'Cormorant Garamond, serif',
|
|
42
|
+
bodyFont: 'Montserrat, sans-serif',
|
|
43
|
+
borderRadius: '4px',
|
|
44
|
+
align: 'center',
|
|
45
|
+
},
|
|
46
|
+
tech: {
|
|
47
|
+
primaryColor: '#6366f1', // Electric indigo
|
|
48
|
+
secondaryColor: '#0f172a', // Dark slate
|
|
49
|
+
accentColor: '#06b6d4', // Neon cyan
|
|
50
|
+
backgroundColor: '#030712', // Midnight abyss
|
|
51
|
+
textColor: '#f8fafc',
|
|
52
|
+
headingFont: 'Inter, system-ui, sans-serif',
|
|
53
|
+
bodyFont: 'Inter, sans-serif',
|
|
54
|
+
borderRadius: '16px',
|
|
55
|
+
align: 'left',
|
|
56
|
+
},
|
|
57
|
+
retail: {
|
|
58
|
+
primaryColor: '#e11d48', // Vibrant rose/coral
|
|
59
|
+
secondaryColor: '#18181b', // Zinc
|
|
60
|
+
accentColor: '#fbbf24', // Sunny amber
|
|
61
|
+
backgroundColor: '#ffffff',
|
|
62
|
+
textColor: '#27272a',
|
|
63
|
+
headingFont: 'Outfit, sans-serif',
|
|
64
|
+
bodyFont: 'Inter, sans-serif',
|
|
65
|
+
borderRadius: '12px',
|
|
66
|
+
align: 'left',
|
|
67
|
+
},
|
|
68
|
+
corporate: {
|
|
69
|
+
primaryColor: '#1e3a8a', // Corporate navy
|
|
70
|
+
secondaryColor: '#0f172a', // Slate
|
|
71
|
+
accentColor: '#3b82f6', // Bright cobalt
|
|
72
|
+
backgroundColor: '#f8fafc', // Soft light gray
|
|
73
|
+
textColor: '#1e293b',
|
|
74
|
+
headingFont: 'Merriweather, serif',
|
|
75
|
+
bodyFont: 'Inter, sans-serif',
|
|
76
|
+
borderRadius: '6px',
|
|
77
|
+
align: 'left',
|
|
78
|
+
},
|
|
79
|
+
cyberpunk: {
|
|
80
|
+
primaryColor: '#f43f5e', // Neon crimson
|
|
81
|
+
secondaryColor: '#09090b', // Deep zinc
|
|
82
|
+
accentColor: '#06b6d4', // Electric cyan
|
|
83
|
+
backgroundColor: '#09090b', // Dark void
|
|
84
|
+
textColor: '#f4f4f5',
|
|
85
|
+
headingFont: 'Outfit, sans-serif',
|
|
86
|
+
bodyFont: 'Inter, sans-serif',
|
|
87
|
+
borderRadius: '4px',
|
|
88
|
+
align: 'left',
|
|
89
|
+
},
|
|
90
|
+
minimalDark: {
|
|
91
|
+
primaryColor: '#e2e8f0', // Clean silver
|
|
92
|
+
secondaryColor: '#000000', // Pitch black
|
|
93
|
+
accentColor: '#38bdf8', // Sky accent
|
|
94
|
+
backgroundColor: '#0c0a09', // Dark basalt
|
|
95
|
+
textColor: '#f1f5f9',
|
|
96
|
+
headingFont: 'Plus Jakarta Sans, sans-serif',
|
|
97
|
+
bodyFont: 'Inter, sans-serif',
|
|
98
|
+
borderRadius: '12px',
|
|
99
|
+
align: 'left',
|
|
100
|
+
},
|
|
101
|
+
nordicPastel: {
|
|
102
|
+
primaryColor: '#0f766e', // Nordic pine
|
|
103
|
+
secondaryColor: '#1e293b', // Deep slate
|
|
104
|
+
accentColor: '#f59e0b', // Amber sun
|
|
105
|
+
backgroundColor: '#fafaf9', // Crisp stone
|
|
106
|
+
textColor: '#334155',
|
|
107
|
+
headingFont: 'Playfair Display, serif',
|
|
108
|
+
bodyFont: 'Inter, sans-serif',
|
|
109
|
+
borderRadius: '16px',
|
|
110
|
+
align: 'left',
|
|
111
|
+
},
|
|
112
|
+
emeraldGold: {
|
|
113
|
+
primaryColor: '#059669', // Emerald
|
|
114
|
+
secondaryColor: '#064e3b', // Deep forest
|
|
115
|
+
accentColor: '#d97706', // Imperial gold
|
|
116
|
+
backgroundColor: '#022c22', // Emerald dark
|
|
117
|
+
textColor: '#ecfdf5',
|
|
118
|
+
headingFont: 'Cormorant Garamond, serif',
|
|
119
|
+
bodyFont: 'Montserrat, sans-serif',
|
|
120
|
+
borderRadius: '10px',
|
|
121
|
+
align: 'center',
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Merges category presets with custom developer/agent overrides.
|
|
126
|
+
*/
|
|
127
|
+
function getCategoryTheme(category, overrides) {
|
|
128
|
+
const base = exports.THEME_PRESETS[category] || exports.THEME_PRESETS.tech;
|
|
129
|
+
return { ...base, ...overrides };
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Extracts a typed CSSProperties object containing both standard and custom theme tokens.
|
|
133
|
+
* Any custom property defined by a developer in `theme` (e.g. cardBg: "#111")
|
|
134
|
+
* is automatically converted to a CSS variable (e.g. --card-bg: #111).
|
|
135
|
+
*/
|
|
136
|
+
function getThemeCssProperties(theme) {
|
|
137
|
+
const customVars = {};
|
|
138
|
+
if (theme) {
|
|
139
|
+
for (const [key, val] of Object.entries(theme)) {
|
|
140
|
+
if (typeof val === 'string' || typeof val === 'number') {
|
|
141
|
+
const cssVarName = `--${key.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase()}`;
|
|
142
|
+
customVars[cssVarName] = String(val);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
'--brand-color': theme?.primaryColor || '#2563eb',
|
|
148
|
+
'--brand-secondary': theme?.secondaryColor || '#0f172a',
|
|
149
|
+
'--brand-accent': theme?.accentColor || '#14b8a6',
|
|
150
|
+
'--page-background': theme?.backgroundColor || '#ffffff',
|
|
151
|
+
'--page-text': theme?.textColor || '#0f172a',
|
|
152
|
+
'--heading-color': theme?.headingColor || theme?.secondaryColor || '#0f172a',
|
|
153
|
+
'--muted-text': theme?.mutedTextColor || '#64748b',
|
|
154
|
+
'--link-color': theme?.linkColor || theme?.primaryColor || '#2563eb',
|
|
155
|
+
'--hero-min-height': theme?.heroMinHeight || '72vh',
|
|
156
|
+
'--section-padding': theme?.sectionPadding || '5rem',
|
|
157
|
+
'--base-size': theme?.baseSize || '16px',
|
|
158
|
+
'--heading-font': theme?.headingFont || 'Inter, sans-serif',
|
|
159
|
+
'--body-font': theme?.bodyFont || 'Inter, sans-serif',
|
|
160
|
+
'--border-radius': theme?.borderRadius || '8px',
|
|
161
|
+
'--content-align': theme?.align || 'left',
|
|
162
|
+
fontFamily: theme?.bodyFont || 'Inter, sans-serif',
|
|
163
|
+
...customVars,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Automatically injects standard and custom fivora theme variables into the document.
|
|
168
|
+
*/
|
|
169
|
+
function ThemeStyles({ theme, defaultPrimary = '#2563eb', defaultSecondary = '#0f172a', defaultAccent = '#14b8a6', defaultBg = '#ffffff', defaultText = '#0f172a', }) {
|
|
170
|
+
const styleProps = getThemeCssProperties(theme);
|
|
171
|
+
const cssLines = Object.entries(styleProps)
|
|
172
|
+
.filter(([key]) => key.startsWith('--'))
|
|
173
|
+
.map(([key, value]) => ` ${key}: ${value};`)
|
|
174
|
+
.join('\n');
|
|
175
|
+
const css = `
|
|
176
|
+
:root {
|
|
177
|
+
${cssLines}
|
|
178
|
+
}
|
|
179
|
+
`;
|
|
180
|
+
return (0, jsx_runtime_1.jsx)("style", { dangerouslySetInnerHTML: { __html: css } });
|
|
181
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
export interface DaySchedule {
|
|
3
|
+
open?: string | null;
|
|
4
|
+
close?: string | null;
|
|
5
|
+
closed?: boolean | null;
|
|
6
|
+
}
|
|
7
|
+
export type WeeklyHours = Record<string, DaySchedule | string | null | undefined>;
|
|
8
|
+
export interface BusinessHoursProps {
|
|
9
|
+
hours?: WeeklyHours | null;
|
|
10
|
+
title?: string;
|
|
11
|
+
fieldPath?: string;
|
|
12
|
+
showStatusBadge?: boolean;
|
|
13
|
+
compact?: boolean;
|
|
14
|
+
className?: string;
|
|
15
|
+
style?: React.CSSProperties;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* BusinessHours component displaying weekly operating schedule with live Open/Closed status.
|
|
19
|
+
*/
|
|
20
|
+
export declare function BusinessHours({ hours, title, fieldPath, showStatusBadge, compact, className, style, }: BusinessHoursProps): React.JSX.Element | null;
|