@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.
Files changed (84) hide show
  1. package/README.md +182 -0
  2. package/dist/EditableBox.d.ts +56 -0
  3. package/dist/EditableBox.js +113 -0
  4. package/dist/EditableCard.d.ts +48 -0
  5. package/dist/EditableCard.js +85 -0
  6. package/dist/EditableContactForm.d.ts +11 -0
  7. package/dist/EditableContactForm.js +146 -0
  8. package/dist/EditableDialog.d.ts +25 -0
  9. package/dist/EditableDialog.js +145 -0
  10. package/dist/EditableFAQAccordion.d.ts +19 -0
  11. package/dist/EditableFAQAccordion.js +62 -0
  12. package/dist/EditableFooter.d.ts +31 -0
  13. package/dist/EditableFooter.js +330 -0
  14. package/dist/EditableGrid.d.ts +40 -0
  15. package/dist/EditableGrid.js +39 -0
  16. package/dist/EditableHero.d.ts +27 -0
  17. package/dist/EditableHero.js +154 -0
  18. package/dist/EditableImage.d.ts +13 -0
  19. package/dist/EditableImage.js +38 -0
  20. package/dist/EditableList.d.ts +33 -0
  21. package/dist/EditableList.js +19 -0
  22. package/dist/EditableNavbar.d.ts +28 -0
  23. package/dist/EditableNavbar.js +178 -0
  24. package/dist/EditablePricingCard.d.ts +31 -0
  25. package/dist/EditablePricingCard.js +93 -0
  26. package/dist/EditableProductCard.d.ts +60 -0
  27. package/dist/EditableProductCard.js +116 -0
  28. package/dist/EditableSection.d.ts +35 -0
  29. package/dist/EditableSection.js +34 -0
  30. package/dist/EditableServiceCard.d.ts +38 -0
  31. package/dist/EditableServiceCard.js +28 -0
  32. package/dist/EditableTestimonialCard.d.ts +17 -0
  33. package/dist/EditableTestimonialCard.js +46 -0
  34. package/dist/EditableText.d.ts +117 -0
  35. package/dist/EditableText.js +319 -0
  36. package/dist/SiteDataProvider.d.ts +61 -0
  37. package/dist/SiteDataProvider.js +316 -0
  38. package/dist/ThemeStyles.d.ts +46 -0
  39. package/dist/ThemeStyles.js +181 -0
  40. package/dist/business/BusinessHours.d.ts +20 -0
  41. package/dist/business/BusinessHours.js +109 -0
  42. package/dist/business/index.d.ts +1 -0
  43. package/dist/business/index.js +17 -0
  44. package/dist/contact/ContactActions.d.ts +24 -0
  45. package/dist/contact/ContactActions.js +30 -0
  46. package/dist/contact/ContactButton.d.ts +15 -0
  47. package/dist/contact/ContactButton.js +94 -0
  48. package/dist/contact/EmailButton.d.ts +6 -0
  49. package/dist/contact/EmailButton.js +12 -0
  50. package/dist/contact/PhoneButton.d.ts +6 -0
  51. package/dist/contact/PhoneButton.js +12 -0
  52. package/dist/contact/WhatsAppButton.d.ts +6 -0
  53. package/dist/contact/WhatsAppButton.js +13 -0
  54. package/dist/contact/index.d.ts +5 -0
  55. package/dist/contact/index.js +21 -0
  56. package/dist/core/DenebAction.d.ts +19 -0
  57. package/dist/core/DenebAction.js +107 -0
  58. package/dist/core/index.d.ts +1 -0
  59. package/dist/core/index.js +17 -0
  60. package/dist/index.d.ts +61 -0
  61. package/dist/index.js +106 -0
  62. package/dist/location/Address.d.ts +14 -0
  63. package/dist/location/Address.js +26 -0
  64. package/dist/location/LocationCard.d.ts +20 -0
  65. package/dist/location/LocationCard.js +41 -0
  66. package/dist/location/LocationLink.d.ts +18 -0
  67. package/dist/location/LocationLink.js +23 -0
  68. package/dist/location/MapEmbed.d.ts +18 -0
  69. package/dist/location/MapEmbed.js +30 -0
  70. package/dist/location/MapLink.d.ts +13 -0
  71. package/dist/location/MapLink.js +63 -0
  72. package/dist/location/index.d.ts +5 -0
  73. package/dist/location/index.js +21 -0
  74. package/dist/social/SocialButton.d.ts +11 -0
  75. package/dist/social/SocialButton.js +112 -0
  76. package/dist/social/SocialLinks.d.ts +14 -0
  77. package/dist/social/SocialLinks.js +42 -0
  78. package/dist/social/index.d.ts +2 -0
  79. package/dist/social/index.js +18 -0
  80. package/dist/utils/index.d.ts +1 -0
  81. package/dist/utils/index.js +17 -0
  82. package/dist/utils/urls.d.ts +32 -0
  83. package/dist/utils/urls.js +91 -0
  84. 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;