@opensite/ui 2.5.0 → 2.5.2

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.
@@ -1,16 +1,15 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var React = require('react');
4
+ var React2 = require('react');
5
5
  var clsx = require('clsx');
6
6
  var tailwindMerge = require('tailwind-merge');
7
- var classVarianceAuthority = require('class-variance-authority');
8
7
  var jsxRuntime = require('react/jsx-runtime');
9
- var img = require('@page-speed/img');
8
+ var classVarianceAuthority = require('class-variance-authority');
10
9
  var icon = require('@page-speed/icon');
11
10
  var usePlatformFromUrl = require('@opensite/hooks/usePlatformFromUrl');
12
- var forms = require('@page-speed/forms');
13
11
  var integration = require('@page-speed/forms/integration');
12
+ var img = require('@page-speed/img');
14
13
 
15
14
  function _interopNamespace(e) {
16
15
  if (e && e.__esModule) return e;
@@ -30,593 +29,104 @@ function _interopNamespace(e) {
30
29
  return Object.freeze(n);
31
30
  }
32
31
 
33
- var React__namespace = /*#__PURE__*/_interopNamespace(React);
32
+ var React2__namespace = /*#__PURE__*/_interopNamespace(React2);
34
33
 
35
34
  // components/blocks/footers/footer-social-newsletter.tsx
36
35
  function cn(...inputs) {
37
36
  return tailwindMerge.twMerge(clsx.clsx(inputs));
38
37
  }
39
- function normalizePhoneNumber(input) {
40
- const trimmed = input.trim();
41
- if (trimmed.toLowerCase().startsWith("tel:")) {
42
- return trimmed;
43
- }
44
- const match = trimmed.match(/^[\s\+\-\(\)]*(\d[\d\s\-\(\)\.]*\d)[\s\-]*(x|ext\.?|extension)?[\s\-]*(\d+)?$/i);
45
- if (match) {
46
- const mainNumber = match[1].replace(/[\s\-\(\)\.]/g, "");
47
- const extension = match[3];
48
- const normalized = mainNumber.length >= 10 && !trimmed.startsWith("+") ? `+${mainNumber}` : mainNumber;
49
- const withExtension = extension ? `${normalized};ext=${extension}` : normalized;
50
- return `tel:${withExtension}`;
51
- }
52
- const cleaned = trimmed.replace(/[\s\-\(\)\.]/g, "");
53
- return `tel:${cleaned}`;
54
- }
55
- function normalizeEmail(input) {
56
- const trimmed = input.trim();
57
- if (trimmed.toLowerCase().startsWith("mailto:")) {
58
- return trimmed;
59
- }
60
- return `mailto:${trimmed}`;
61
- }
62
- function isEmail(input) {
63
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
64
- return emailRegex.test(input.trim());
65
- }
66
- function isPhoneNumber(input) {
67
- const trimmed = input.trim();
68
- if (trimmed.toLowerCase().startsWith("tel:")) {
69
- return true;
70
- }
71
- const phoneRegex = /^[\s\+\-\(\)]*\d[\d\s\-\(\)\.]*\d[\s\-]*(x|ext\.?|extension)?[\s\-]*\d*$/i;
72
- return phoneRegex.test(trimmed);
73
- }
74
- function isInternalUrl(href) {
75
- if (typeof window === "undefined") {
76
- return href.startsWith("/") && !href.startsWith("//");
77
- }
78
- const trimmed = href.trim();
79
- if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
80
- return true;
81
- }
82
- try {
83
- const url = new URL(trimmed, window.location.href);
84
- const currentOrigin = window.location.origin;
85
- const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
86
- return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);
87
- } catch {
88
- return false;
89
- }
90
- }
91
- function toRelativePath(href) {
92
- if (typeof window === "undefined") {
93
- return href;
94
- }
95
- const trimmed = href.trim();
96
- if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
97
- return trimmed;
98
- }
99
- try {
100
- const url = new URL(trimmed, window.location.href);
101
- const currentOrigin = window.location.origin;
102
- const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
103
- if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {
104
- return url.pathname + url.search + url.hash;
105
- }
106
- } catch {
38
+ function FooterCopyright({
39
+ copyright,
40
+ className
41
+ }) {
42
+ const currentYear = React2.useMemo(() => (/* @__PURE__ */ new Date()).getFullYear(), []);
43
+ if (!copyright) {
44
+ return null;
107
45
  }
108
- return trimmed;
46
+ return /* @__PURE__ */ jsxRuntime.jsxs("p", { className: cn(className), children: [
47
+ "\xA9 ",
48
+ currentYear,
49
+ " ",
50
+ copyright,
51
+ " All Rights Reserved."
52
+ ] });
109
53
  }
110
- function useNavigation({
111
- href,
112
- onClick
113
- } = {}) {
114
- const linkType = React__namespace.useMemo(() => {
115
- if (!href || href.trim() === "") {
116
- return onClick ? "none" : "none";
117
- }
118
- const trimmed = href.trim();
119
- if (trimmed.toLowerCase().startsWith("mailto:") || isEmail(trimmed)) {
120
- return "mailto";
121
- }
122
- if (trimmed.toLowerCase().startsWith("tel:") || isPhoneNumber(trimmed)) {
123
- return "tel";
124
- }
125
- if (isInternalUrl(trimmed)) {
126
- return "internal";
127
- }
128
- try {
129
- new URL(trimmed, typeof window !== "undefined" ? window.location.href : "http://localhost");
130
- return "external";
131
- } catch {
132
- return "internal";
133
- }
134
- }, [href, onClick]);
135
- const normalizedHref = React__namespace.useMemo(() => {
136
- if (!href || href.trim() === "") {
137
- return void 0;
138
- }
139
- const trimmed = href.trim();
140
- switch (linkType) {
141
- case "tel":
142
- return normalizePhoneNumber(trimmed);
143
- case "mailto":
144
- return normalizeEmail(trimmed);
145
- case "internal":
146
- return toRelativePath(trimmed);
147
- case "external":
148
- return trimmed;
149
- default:
150
- return trimmed;
151
- }
152
- }, [href, linkType]);
153
- const target = React__namespace.useMemo(() => {
154
- switch (linkType) {
155
- case "external":
156
- return "_blank";
157
- case "internal":
158
- return "_self";
159
- case "mailto":
160
- case "tel":
161
- return void 0;
162
- default:
163
- return void 0;
164
- }
165
- }, [linkType]);
166
- const rel = React__namespace.useMemo(() => {
167
- if (linkType === "external") {
168
- return "noopener noreferrer";
169
- }
170
- return void 0;
171
- }, [linkType]);
172
- const isExternal = linkType === "external";
173
- const isInternal = linkType === "internal";
174
- const shouldUseRouter = isInternal && typeof normalizedHref === "string" && normalizedHref.startsWith("/");
175
- const handleClick = React__namespace.useCallback(
176
- (event) => {
177
- if (onClick) {
178
- try {
179
- onClick(event);
180
- } catch (error) {
181
- console.error("Error in user onClick handler:", error);
182
- }
183
- }
184
- if (event.defaultPrevented) {
185
- return;
186
- }
187
- if (shouldUseRouter && normalizedHref && event.button === 0 && // left-click only
188
- !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey) {
189
- if (typeof window !== "undefined") {
190
- const handler = window.__opensiteNavigationHandler;
191
- if (typeof handler === "function") {
192
- try {
193
- const handled = handler(normalizedHref, event.nativeEvent || event);
194
- if (handled !== false) {
195
- event.preventDefault();
196
- }
197
- } catch (error) {
198
- console.error("Error in navigation handler:", error);
199
- }
200
- }
201
- }
54
+ var brandAttributionOptions = [
55
+ {
56
+ brand: "open_site_ai",
57
+ options: [
58
+ {
59
+ prefix: "Powered by ",
60
+ anchorText: "AI Website Builder",
61
+ href: "https://opensite.ai",
62
+ suffix: ""
63
+ },
64
+ {
65
+ prefix: "",
66
+ anchorText: "AI-Powered Web Design",
67
+ href: "https://opensite.ai",
68
+ suffix: " by OpenSite"
69
+ },
70
+ {
71
+ prefix: "Built with ",
72
+ anchorText: "OpenSite AI",
73
+ href: "https://opensite.ai"
74
+ },
75
+ {
76
+ prefix: "Website by ",
77
+ anchorText: "Intelligent Site Builder",
78
+ href: "https://opensite.ai"
79
+ },
80
+ {
81
+ prefix: "Designed using ",
82
+ anchorText: "AI Website Platform",
83
+ href: "https://opensite.ai"
84
+ },
85
+ {
86
+ prefix: "",
87
+ anchorText: "Automated Website Design",
88
+ href: "https://opensite.ai",
89
+ suffix: " \u2013 OpenSite"
90
+ },
91
+ {
92
+ prefix: "Created with ",
93
+ anchorText: "AI Digital Presence Solutions",
94
+ href: "https://opensite.ai"
95
+ },
96
+ {
97
+ prefix: "",
98
+ anchorText: "Smart Website Builder",
99
+ href: "https://opensite.ai",
100
+ suffix: " by OpenSite AI"
101
+ },
102
+ {
103
+ prefix: "Powered by ",
104
+ anchorText: "Industry-Specific AI Web Platform",
105
+ href: "https://opensite.ai"
106
+ },
107
+ {
108
+ prefix: "",
109
+ anchorText: "AI Site Generator",
110
+ href: "https://opensite.ai",
111
+ suffix: " \u2013 OpenSite"
202
112
  }
203
- },
204
- [onClick, shouldUseRouter, normalizedHref]
205
- );
206
- return {
207
- linkType,
208
- normalizedHref,
209
- target,
210
- rel,
211
- isExternal,
212
- isInternal,
213
- shouldUseRouter,
214
- handleClick
215
- };
216
- }
217
- var baseStyles = [
218
- // Layout
219
- "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
220
- // Typography - using CSS variables with sensible defaults
221
- "font-[var(--button-font-family,inherit)]",
222
- "font-[var(--button-font-weight,500)]",
223
- "tracking-[var(--button-letter-spacing,0)]",
224
- "leading-[var(--button-line-height,1.25)]",
225
- "[text-transform:var(--button-text-transform,none)]",
226
- "text-sm",
227
- // Border radius
228
- "rounded-[var(--button-radius,var(--radius,0.375rem))]",
229
- // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)
230
- "[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
231
- // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows
232
- "[box-shadow:var(--button-shadow,none)]",
233
- "hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]",
234
- // Disabled state
235
- "disabled:pointer-events-none disabled:opacity-50",
236
- // SVG handling
237
- "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
238
- // Focus styles
239
- "outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
240
- // Invalid state
241
- "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
242
- ].join(" ");
243
- var buttonVariants = classVarianceAuthority.cva(baseStyles, {
244
- variants: {
245
- variant: {
246
- // Default (Primary) variant - full customization
247
- default: [
248
- "bg-[var(--button-default-bg,hsl(var(--primary)))]",
249
- "text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
250
- "border-[length:var(--button-default-border-width,0px)]",
251
- "border-[color:var(--button-default-border,transparent)]",
252
- "[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]",
253
- "hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
254
- "hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
255
- "hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
256
- "hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
257
- ].join(" "),
258
- // Destructive variant - full customization
259
- destructive: [
260
- "bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
261
- "text-[var(--button-destructive-fg,white)]",
262
- "border-[length:var(--button-destructive-border-width,0px)]",
263
- "border-[color:var(--button-destructive-border,transparent)]",
264
- "[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]",
265
- "hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
266
- "hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
267
- "hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
268
- "hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
269
- "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
270
- "dark:bg-destructive/60"
271
- ].join(" "),
272
- // Outline variant - full customization with proper border handling
273
- outline: [
274
- "bg-[var(--button-outline-bg,hsl(var(--background)))]",
275
- "text-[var(--button-outline-fg,inherit)]",
276
- "border-[length:var(--button-outline-border-width,1px)]",
277
- "border-[color:var(--button-outline-border,hsl(var(--border)))]",
278
- "[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
279
- "hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
280
- "hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
281
- "hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
282
- "hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
283
- "dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
284
- ].join(" "),
285
- // Secondary variant - full customization
286
- secondary: [
287
- "bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
288
- "text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
289
- "border-[length:var(--button-secondary-border-width,0px)]",
290
- "border-[color:var(--button-secondary-border,transparent)]",
291
- "[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]",
292
- "hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
293
- "hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
294
- "hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
295
- "hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
296
- ].join(" "),
297
- // Ghost variant - full customization
298
- ghost: [
299
- "bg-[var(--button-ghost-bg,transparent)]",
300
- "text-[var(--button-ghost-fg,inherit)]",
301
- "border-[length:var(--button-ghost-border-width,0px)]",
302
- "border-[color:var(--button-ghost-border,transparent)]",
303
- "[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]",
304
- "hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
305
- "hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
306
- "hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
307
- "hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
308
- "dark:hover:bg-accent/50"
309
- ].join(" "),
310
- // Link variant - full customization
311
- link: [
312
- "bg-[var(--button-link-bg,transparent)]",
313
- "text-[var(--button-link-fg,hsl(var(--primary)))]",
314
- "border-[length:var(--button-link-border-width,0px)]",
315
- "border-[color:var(--button-link-border,transparent)]",
316
- "[box-shadow:var(--button-link-shadow,none)]",
317
- "hover:bg-[var(--button-link-hover-bg,transparent)]",
318
- "hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
319
- "hover:[box-shadow:var(--button-link-shadow-hover,none)]",
320
- "underline-offset-4 hover:underline"
321
- ].join(" ")
322
- },
323
- size: {
324
- default: [
325
- "h-[var(--button-height-md,2.25rem)]",
326
- "px-[var(--button-padding-x-md,1rem)]",
327
- "py-[var(--button-padding-y-md,0.5rem)]",
328
- "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
329
- ].join(" "),
330
- sm: [
331
- "h-[var(--button-height-sm,2rem)]",
332
- "px-[var(--button-padding-x-sm,0.75rem)]",
333
- "py-[var(--button-padding-y-sm,0.25rem)]",
334
- "gap-1.5",
335
- "has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
336
- ].join(" "),
337
- md: [
338
- "h-[var(--button-height-md,2.25rem)]",
339
- "px-[var(--button-padding-x-md,1rem)]",
340
- "py-[var(--button-padding-y-md,0.5rem)]",
341
- "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
342
- ].join(" "),
343
- lg: [
344
- "h-[var(--button-height-lg,2.5rem)]",
345
- "px-[var(--button-padding-x-lg,1.5rem)]",
346
- "py-[var(--button-padding-y-lg,0.5rem)]",
347
- "has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
348
- ].join(" "),
349
- icon: "size-[var(--button-height-md,2.25rem)]",
350
- "icon-sm": "size-[var(--button-height-sm,2rem)]",
351
- "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
352
- }
113
+ ]
353
114
  },
354
- defaultVariants: {
355
- variant: "default",
356
- size: "default"
357
- }
358
- });
359
- var Pressable = React__namespace.forwardRef(
360
- ({
361
- children,
362
- className,
363
- href,
364
- onClick,
365
- variant,
366
- size,
367
- asButton = false,
368
- fallbackComponentType = "span",
369
- componentType,
370
- "aria-label": ariaLabel,
371
- "aria-describedby": ariaDescribedby,
372
- id,
373
- ...props
374
- }, ref) => {
375
- const navigation = useNavigation({ href, onClick });
376
- const {
377
- normalizedHref,
378
- target,
379
- rel,
380
- linkType,
381
- isInternal,
382
- handleClick
383
- } = navigation;
384
- const shouldRenderLink = normalizedHref && linkType !== "none";
385
- const shouldRenderButton = !shouldRenderLink && onClick;
386
- const effectiveComponentType = componentType || (shouldRenderLink ? "a" : shouldRenderButton ? "button" : fallbackComponentType);
387
- const finalComponentType = isInternal && shouldRenderLink ? "a" : effectiveComponentType;
388
- const shouldApplyButtonStyles = asButton || variant || size;
389
- const combinedClassName = cn(
390
- shouldApplyButtonStyles && buttonVariants({ variant, size }),
391
- className
392
- );
393
- const dataProps = Object.fromEntries(
394
- Object.entries(props).filter(([key]) => key.startsWith("data-"))
395
- );
396
- const buttonDataAttributes = shouldApplyButtonStyles ? {
397
- "data-slot": "button",
398
- "data-variant": variant ?? "default",
399
- "data-size": size ?? "default"
400
- } : {};
401
- const commonProps = {
402
- className: combinedClassName,
403
- onClick: handleClick,
404
- "aria-label": ariaLabel,
405
- "aria-describedby": ariaDescribedby,
406
- id,
407
- ...dataProps,
408
- ...buttonDataAttributes
409
- };
410
- if (finalComponentType === "a" && shouldRenderLink) {
411
- return /* @__PURE__ */ jsxRuntime.jsx(
412
- "a",
413
- {
414
- ref,
415
- href: normalizedHref,
416
- target,
417
- rel,
418
- ...commonProps,
419
- ...props,
420
- children
421
- }
422
- );
423
- }
424
- if (finalComponentType === "button") {
425
- return /* @__PURE__ */ jsxRuntime.jsx(
426
- "button",
427
- {
428
- ref,
429
- type: props.type || "button",
430
- ...commonProps,
431
- ...props,
432
- children
433
- }
434
- );
435
- }
436
- if (finalComponentType === "div") {
437
- return /* @__PURE__ */ jsxRuntime.jsx(
438
- "div",
439
- {
440
- ref,
441
- ...commonProps,
442
- children
443
- }
444
- );
445
- }
446
- return /* @__PURE__ */ jsxRuntime.jsx(
447
- "span",
115
+ {
116
+ brand: "dashtrack",
117
+ options: [
448
118
  {
449
- ref,
450
- ...commonProps,
451
- children
452
- }
453
- );
454
- }
455
- );
456
- Pressable.displayName = "Pressable";
457
- function isThemedLogo(logo) {
458
- return "light" in logo || "dark" in logo;
459
- }
460
- var FooterLogo = ({
461
- logo,
462
- logoSlot,
463
- logoClassName,
464
- logoImageClassName,
465
- logoTitleClassName,
466
- optixFlowConfig
467
- }) => {
468
- if (logoSlot) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: logoSlot });
469
- if (!logo) return null;
470
- const hasThemedSources = isThemedLogo(logo) && (logo.light || logo.dark);
471
- const hasStandardSource = !isThemedLogo(logo) && logo.src;
472
- const logoContent = hasThemedSources ? (
473
- // Themed logo with light/dark mode sources
474
- /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
475
- logo.light && /* @__PURE__ */ jsxRuntime.jsx(
476
- img.Img,
477
- {
478
- src: logo.light,
479
- alt: logo.alt || "Logo",
480
- className: cn(
481
- "h-8 w-auto object-contain dark:hidden md:h-10",
482
- logoImageClassName
483
- ),
484
- optixFlowConfig
485
- }
486
- ),
487
- logo.dark && /* @__PURE__ */ jsxRuntime.jsx(
488
- img.Img,
489
- {
490
- src: logo.dark,
491
- alt: logo.alt || "Logo",
492
- className: cn(
493
- "hidden h-8 w-auto object-contain dark:block md:h-10",
494
- logoImageClassName
495
- ),
496
- optixFlowConfig
497
- }
498
- )
499
- ] })
500
- ) : hasStandardSource ? (
501
- // Standard single logo image
502
- /* @__PURE__ */ jsxRuntime.jsx(
503
- img.Img,
119
+ prefix: "",
120
+ anchorText: "Restaurant Website Builder",
121
+ href: "https://dashtrack.com",
122
+ suffix: " by DashTrack"
123
+ },
504
124
  {
505
- src: logo.src,
506
- alt: logo.alt || "Logo",
507
- className: cn("h-8 w-auto object-contain md:h-10", logoImageClassName),
508
- optixFlowConfig
509
- }
510
- )
511
- ) : logo.title ? (
512
- // Text-based logo fallback
513
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: cn("text-lg font-semibold md:text-xl", logoTitleClassName), children: logo.title })
514
- ) : null;
515
- if (!logoContent) return null;
516
- if (logo.url) {
517
- return /* @__PURE__ */ jsxRuntime.jsx(
518
- Pressable,
519
- {
520
- href: logo.url,
521
- className: cn("inline-flex items-center", logoClassName),
522
- children: logoContent
523
- }
524
- );
525
- }
526
- return /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("inline-flex items-center", logoClassName), children: logoContent });
527
- };
528
- function FooterCopyright({
529
- copyright,
530
- className
531
- }) {
532
- const currentYear = React.useMemo(() => (/* @__PURE__ */ new Date()).getFullYear(), []);
533
- if (!copyright) {
534
- return null;
535
- }
536
- return /* @__PURE__ */ jsxRuntime.jsxs("p", { className: cn(className), children: [
537
- "\xA9 ",
538
- currentYear,
539
- " ",
540
- copyright,
541
- " All Rights Reserved."
542
- ] });
543
- }
544
- var brandAttributionOptions = [
545
- {
546
- brand: "open_site_ai",
547
- options: [
548
- {
549
- prefix: "Powered by ",
550
- anchorText: "AI Website Builder",
551
- href: "https://opensite.ai",
552
- suffix: ""
553
- },
554
- {
555
- prefix: "",
556
- anchorText: "AI-Powered Web Design",
557
- href: "https://opensite.ai",
558
- suffix: " by OpenSite"
559
- },
560
- {
561
- prefix: "Built with ",
562
- anchorText: "OpenSite AI",
563
- href: "https://opensite.ai"
564
- },
565
- {
566
- prefix: "Website by ",
567
- anchorText: "Intelligent Site Builder",
568
- href: "https://opensite.ai"
569
- },
570
- {
571
- prefix: "Designed using ",
572
- anchorText: "AI Website Platform",
573
- href: "https://opensite.ai"
574
- },
575
- {
576
- prefix: "",
577
- anchorText: "Automated Website Design",
578
- href: "https://opensite.ai",
579
- suffix: " \u2013 OpenSite"
580
- },
581
- {
582
- prefix: "Created with ",
583
- anchorText: "AI Digital Presence Solutions",
584
- href: "https://opensite.ai"
585
- },
586
- {
587
- prefix: "",
588
- anchorText: "Smart Website Builder",
589
- href: "https://opensite.ai",
590
- suffix: " by OpenSite AI"
591
- },
592
- {
593
- prefix: "Powered by ",
594
- anchorText: "Industry-Specific AI Web Platform",
595
- href: "https://opensite.ai"
596
- },
597
- {
598
- prefix: "",
599
- anchorText: "AI Site Generator",
600
- href: "https://opensite.ai",
601
- suffix: " \u2013 OpenSite"
602
- }
603
- ]
604
- },
605
- {
606
- brand: "dashtrack",
607
- options: [
608
- {
609
- prefix: "",
610
- anchorText: "Restaurant Website Builder",
611
- href: "https://dashtrack.com",
612
- suffix: " by DashTrack"
613
- },
614
- {
615
- prefix: "Powered by ",
616
- anchorText: "DashTrack",
617
- href: "https://dashtrack.com",
618
- suffix: " Restaurant Platform"
619
- },
125
+ prefix: "Powered by ",
126
+ anchorText: "DashTrack",
127
+ href: "https://dashtrack.com",
128
+ suffix: " Restaurant Platform"
129
+ },
620
130
  {
621
131
  prefix: "",
622
132
  anchorText: "Bar & Restaurant Marketing Platform",
@@ -1002,64 +512,482 @@ var brandAttributionMap = brandAttributionOptions.reduce(
1002
512
  acc[entry.brand] = entry.options;
1003
513
  return acc;
1004
514
  },
1005
- {}
1006
- );
1007
- function buildTrackedHref(baseHref) {
1008
- if (typeof window === "undefined") {
1009
- return baseHref;
515
+ {}
516
+ );
517
+ function buildTrackedHref(baseHref) {
518
+ if (typeof window === "undefined") {
519
+ return baseHref;
520
+ }
521
+ try {
522
+ const url = new URL(baseHref);
523
+ const source = window.location.host;
524
+ const sourceRef = window.location.href;
525
+ url.searchParams.set("source", source);
526
+ url.searchParams.set("sourceRef", sourceRef);
527
+ return url.toString();
528
+ } catch {
529
+ try {
530
+ const url = new URL(baseHref, window.location.origin);
531
+ const source = window.location.host;
532
+ const sourceRef = window.location.href;
533
+ url.searchParams.set("source", source);
534
+ url.searchParams.set("sourceRef", sourceRef);
535
+ return url.toString();
536
+ } catch {
537
+ return baseHref;
538
+ }
539
+ }
540
+ }
541
+ var BrandAttribution = ({
542
+ variant = "span",
543
+ containerClassName,
544
+ linkClassName,
545
+ internalBrandSlug,
546
+ optionIndex
547
+ }) => {
548
+ const options = brandAttributionMap[internalBrandSlug];
549
+ if (!options || optionIndex < 0 || optionIndex >= options.length) {
550
+ return null;
551
+ }
552
+ const { prefix = "", anchorText, href, suffix = "" } = options[optionIndex];
553
+ const ContainerEl = variant;
554
+ const [trackedHref, setTrackedHref] = React2__namespace.useState(href);
555
+ React2__namespace.useEffect(() => {
556
+ setTrackedHref(buildTrackedHref(href));
557
+ }, [href]);
558
+ return /* @__PURE__ */ jsxRuntime.jsxs(ContainerEl, { className: containerClassName, children: [
559
+ prefix,
560
+ /* @__PURE__ */ jsxRuntime.jsx(
561
+ "a",
562
+ {
563
+ href: trackedHref,
564
+ target: "_blank",
565
+ rel: "noreferrer noopener",
566
+ className: linkClassName,
567
+ children: anchorText
568
+ }
569
+ ),
570
+ suffix
571
+ ] });
572
+ };
573
+ function normalizePhoneNumber(input) {
574
+ const trimmed = input.trim();
575
+ if (trimmed.toLowerCase().startsWith("tel:")) {
576
+ return trimmed;
577
+ }
578
+ const match = trimmed.match(/^[\s\+\-\(\)]*(\d[\d\s\-\(\)\.]*\d)[\s\-]*(x|ext\.?|extension)?[\s\-]*(\d+)?$/i);
579
+ if (match) {
580
+ const mainNumber = match[1].replace(/[\s\-\(\)\.]/g, "");
581
+ const extension = match[3];
582
+ const normalized = mainNumber.length >= 10 && !trimmed.startsWith("+") ? `+${mainNumber}` : mainNumber;
583
+ const withExtension = extension ? `${normalized};ext=${extension}` : normalized;
584
+ return `tel:${withExtension}`;
585
+ }
586
+ const cleaned = trimmed.replace(/[\s\-\(\)\.]/g, "");
587
+ return `tel:${cleaned}`;
588
+ }
589
+ function normalizeEmail(input) {
590
+ const trimmed = input.trim();
591
+ if (trimmed.toLowerCase().startsWith("mailto:")) {
592
+ return trimmed;
593
+ }
594
+ return `mailto:${trimmed}`;
595
+ }
596
+ function isEmail(input) {
597
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
598
+ return emailRegex.test(input.trim());
599
+ }
600
+ function isPhoneNumber(input) {
601
+ const trimmed = input.trim();
602
+ if (trimmed.toLowerCase().startsWith("tel:")) {
603
+ return true;
604
+ }
605
+ const phoneRegex = /^[\s\+\-\(\)]*\d[\d\s\-\(\)\.]*\d[\s\-]*(x|ext\.?|extension)?[\s\-]*\d*$/i;
606
+ return phoneRegex.test(trimmed);
607
+ }
608
+ function isInternalUrl(href) {
609
+ if (typeof window === "undefined") {
610
+ return href.startsWith("/") && !href.startsWith("//");
611
+ }
612
+ const trimmed = href.trim();
613
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
614
+ return true;
615
+ }
616
+ try {
617
+ const url = new URL(trimmed, window.location.href);
618
+ const currentOrigin = window.location.origin;
619
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
620
+ return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);
621
+ } catch {
622
+ return false;
623
+ }
624
+ }
625
+ function toRelativePath(href) {
626
+ if (typeof window === "undefined") {
627
+ return href;
628
+ }
629
+ const trimmed = href.trim();
630
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
631
+ return trimmed;
632
+ }
633
+ try {
634
+ const url = new URL(trimmed, window.location.href);
635
+ const currentOrigin = window.location.origin;
636
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
637
+ if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {
638
+ return url.pathname + url.search + url.hash;
639
+ }
640
+ } catch {
641
+ }
642
+ return trimmed;
643
+ }
644
+ function useNavigation({
645
+ href,
646
+ onClick
647
+ } = {}) {
648
+ const linkType = React2__namespace.useMemo(() => {
649
+ if (!href || href.trim() === "") {
650
+ return onClick ? "none" : "none";
651
+ }
652
+ const trimmed = href.trim();
653
+ if (trimmed.toLowerCase().startsWith("mailto:") || isEmail(trimmed)) {
654
+ return "mailto";
655
+ }
656
+ if (trimmed.toLowerCase().startsWith("tel:") || isPhoneNumber(trimmed)) {
657
+ return "tel";
658
+ }
659
+ if (isInternalUrl(trimmed)) {
660
+ return "internal";
661
+ }
662
+ try {
663
+ new URL(trimmed, typeof window !== "undefined" ? window.location.href : "http://localhost");
664
+ return "external";
665
+ } catch {
666
+ return "internal";
667
+ }
668
+ }, [href, onClick]);
669
+ const normalizedHref = React2__namespace.useMemo(() => {
670
+ if (!href || href.trim() === "") {
671
+ return void 0;
672
+ }
673
+ const trimmed = href.trim();
674
+ switch (linkType) {
675
+ case "tel":
676
+ return normalizePhoneNumber(trimmed);
677
+ case "mailto":
678
+ return normalizeEmail(trimmed);
679
+ case "internal":
680
+ return toRelativePath(trimmed);
681
+ case "external":
682
+ return trimmed;
683
+ default:
684
+ return trimmed;
685
+ }
686
+ }, [href, linkType]);
687
+ const target = React2__namespace.useMemo(() => {
688
+ switch (linkType) {
689
+ case "external":
690
+ return "_blank";
691
+ case "internal":
692
+ return "_self";
693
+ case "mailto":
694
+ case "tel":
695
+ return void 0;
696
+ default:
697
+ return void 0;
698
+ }
699
+ }, [linkType]);
700
+ const rel = React2__namespace.useMemo(() => {
701
+ if (linkType === "external") {
702
+ return "noopener noreferrer";
703
+ }
704
+ return void 0;
705
+ }, [linkType]);
706
+ const isExternal = linkType === "external";
707
+ const isInternal = linkType === "internal";
708
+ const shouldUseRouter = isInternal && typeof normalizedHref === "string" && normalizedHref.startsWith("/");
709
+ const handleClick = React2__namespace.useCallback(
710
+ (event) => {
711
+ if (onClick) {
712
+ try {
713
+ onClick(event);
714
+ } catch (error) {
715
+ console.error("Error in user onClick handler:", error);
716
+ }
717
+ }
718
+ if (event.defaultPrevented) {
719
+ return;
720
+ }
721
+ if (shouldUseRouter && normalizedHref && event.button === 0 && // left-click only
722
+ !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey) {
723
+ if (typeof window !== "undefined") {
724
+ const handler = window.__opensiteNavigationHandler;
725
+ if (typeof handler === "function") {
726
+ try {
727
+ const handled = handler(normalizedHref, event.nativeEvent || event);
728
+ if (handled !== false) {
729
+ event.preventDefault();
730
+ }
731
+ } catch (error) {
732
+ console.error("Error in navigation handler:", error);
733
+ }
734
+ }
735
+ }
736
+ }
737
+ },
738
+ [onClick, shouldUseRouter, normalizedHref]
739
+ );
740
+ return {
741
+ linkType,
742
+ normalizedHref,
743
+ target,
744
+ rel,
745
+ isExternal,
746
+ isInternal,
747
+ shouldUseRouter,
748
+ handleClick
749
+ };
750
+ }
751
+ var baseStyles = [
752
+ // Layout
753
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
754
+ // Typography - using CSS variables with sensible defaults
755
+ "font-[var(--button-font-family,inherit)]",
756
+ "font-[var(--button-font-weight,500)]",
757
+ "tracking-[var(--button-letter-spacing,0)]",
758
+ "leading-[var(--button-line-height,1.25)]",
759
+ "[text-transform:var(--button-text-transform,none)]",
760
+ "text-sm",
761
+ // Border radius
762
+ "rounded-[var(--button-radius,var(--radius,0.375rem))]",
763
+ // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)
764
+ "[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
765
+ // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows
766
+ "[box-shadow:var(--button-shadow,none)]",
767
+ "hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]",
768
+ // Disabled state
769
+ "disabled:pointer-events-none disabled:opacity-50",
770
+ // SVG handling
771
+ "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
772
+ // Focus styles
773
+ "outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
774
+ // Invalid state
775
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
776
+ ].join(" ");
777
+ var buttonVariants = classVarianceAuthority.cva(baseStyles, {
778
+ variants: {
779
+ variant: {
780
+ // Default (Primary) variant - full customization
781
+ default: [
782
+ "bg-[var(--button-default-bg,hsl(var(--primary)))]",
783
+ "text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
784
+ "border-[length:var(--button-default-border-width,0px)]",
785
+ "border-[color:var(--button-default-border,transparent)]",
786
+ "[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]",
787
+ "hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
788
+ "hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
789
+ "hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
790
+ "hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
791
+ ].join(" "),
792
+ // Destructive variant - full customization
793
+ destructive: [
794
+ "bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
795
+ "text-[var(--button-destructive-fg,white)]",
796
+ "border-[length:var(--button-destructive-border-width,0px)]",
797
+ "border-[color:var(--button-destructive-border,transparent)]",
798
+ "[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]",
799
+ "hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
800
+ "hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
801
+ "hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
802
+ "hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
803
+ "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
804
+ "dark:bg-destructive/60"
805
+ ].join(" "),
806
+ // Outline variant - full customization with proper border handling
807
+ outline: [
808
+ "bg-[var(--button-outline-bg,hsl(var(--background)))]",
809
+ "text-[var(--button-outline-fg,inherit)]",
810
+ "border-[length:var(--button-outline-border-width,1px)]",
811
+ "border-[color:var(--button-outline-border,hsl(var(--border)))]",
812
+ "[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
813
+ "hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
814
+ "hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
815
+ "hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
816
+ "hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
817
+ "dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
818
+ ].join(" "),
819
+ // Secondary variant - full customization
820
+ secondary: [
821
+ "bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
822
+ "text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
823
+ "border-[length:var(--button-secondary-border-width,0px)]",
824
+ "border-[color:var(--button-secondary-border,transparent)]",
825
+ "[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]",
826
+ "hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
827
+ "hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
828
+ "hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
829
+ "hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
830
+ ].join(" "),
831
+ // Ghost variant - full customization
832
+ ghost: [
833
+ "bg-[var(--button-ghost-bg,transparent)]",
834
+ "text-[var(--button-ghost-fg,inherit)]",
835
+ "border-[length:var(--button-ghost-border-width,0px)]",
836
+ "border-[color:var(--button-ghost-border,transparent)]",
837
+ "[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]",
838
+ "hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
839
+ "hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
840
+ "hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
841
+ "hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
842
+ "dark:hover:bg-accent/50"
843
+ ].join(" "),
844
+ // Link variant - full customization
845
+ link: [
846
+ "bg-[var(--button-link-bg,transparent)]",
847
+ "text-[var(--button-link-fg,hsl(var(--primary)))]",
848
+ "border-[length:var(--button-link-border-width,0px)]",
849
+ "border-[color:var(--button-link-border,transparent)]",
850
+ "[box-shadow:var(--button-link-shadow,none)]",
851
+ "hover:bg-[var(--button-link-hover-bg,transparent)]",
852
+ "hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
853
+ "hover:[box-shadow:var(--button-link-shadow-hover,none)]",
854
+ "underline-offset-4 hover:underline"
855
+ ].join(" ")
856
+ },
857
+ size: {
858
+ default: [
859
+ "h-[var(--button-height-md,2.25rem)]",
860
+ "px-[var(--button-padding-x-md,1rem)]",
861
+ "py-[var(--button-padding-y-md,0.5rem)]",
862
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
863
+ ].join(" "),
864
+ sm: [
865
+ "h-[var(--button-height-sm,2rem)]",
866
+ "px-[var(--button-padding-x-sm,0.75rem)]",
867
+ "py-[var(--button-padding-y-sm,0.25rem)]",
868
+ "gap-1.5",
869
+ "has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
870
+ ].join(" "),
871
+ md: [
872
+ "h-[var(--button-height-md,2.25rem)]",
873
+ "px-[var(--button-padding-x-md,1rem)]",
874
+ "py-[var(--button-padding-y-md,0.5rem)]",
875
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
876
+ ].join(" "),
877
+ lg: [
878
+ "h-[var(--button-height-lg,2.5rem)]",
879
+ "px-[var(--button-padding-x-lg,1.5rem)]",
880
+ "py-[var(--button-padding-y-lg,0.5rem)]",
881
+ "has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
882
+ ].join(" "),
883
+ icon: "size-[var(--button-height-md,2.25rem)]",
884
+ "icon-sm": "size-[var(--button-height-sm,2rem)]",
885
+ "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
886
+ }
887
+ },
888
+ defaultVariants: {
889
+ variant: "default",
890
+ size: "default"
1010
891
  }
1011
- try {
1012
- const url = new URL(baseHref);
1013
- const source = window.location.host;
1014
- const sourceRef = window.location.href;
1015
- url.searchParams.set("source", source);
1016
- url.searchParams.set("sourceRef", sourceRef);
1017
- return url.toString();
1018
- } catch {
1019
- try {
1020
- const url = new URL(baseHref, window.location.origin);
1021
- const source = window.location.host;
1022
- const sourceRef = window.location.href;
1023
- url.searchParams.set("source", source);
1024
- url.searchParams.set("sourceRef", sourceRef);
1025
- return url.toString();
1026
- } catch {
1027
- return baseHref;
892
+ });
893
+ var Pressable = React2__namespace.forwardRef(
894
+ ({
895
+ children,
896
+ className,
897
+ href,
898
+ onClick,
899
+ variant,
900
+ size,
901
+ asButton = false,
902
+ fallbackComponentType = "span",
903
+ componentType,
904
+ "aria-label": ariaLabel,
905
+ "aria-describedby": ariaDescribedby,
906
+ id,
907
+ ...props
908
+ }, ref) => {
909
+ const navigation = useNavigation({ href, onClick });
910
+ const {
911
+ normalizedHref,
912
+ target,
913
+ rel,
914
+ linkType,
915
+ isInternal,
916
+ handleClick
917
+ } = navigation;
918
+ const shouldRenderLink = normalizedHref && linkType !== "none";
919
+ const shouldRenderButton = !shouldRenderLink && onClick;
920
+ const effectiveComponentType = componentType || (shouldRenderLink ? "a" : shouldRenderButton ? "button" : fallbackComponentType);
921
+ const finalComponentType = isInternal && shouldRenderLink ? "a" : effectiveComponentType;
922
+ const shouldApplyButtonStyles = asButton || variant || size;
923
+ const combinedClassName = cn(
924
+ shouldApplyButtonStyles && buttonVariants({ variant, size }),
925
+ className
926
+ );
927
+ const dataProps = Object.fromEntries(
928
+ Object.entries(props).filter(([key]) => key.startsWith("data-"))
929
+ );
930
+ const buttonDataAttributes = shouldApplyButtonStyles ? {
931
+ "data-slot": "button",
932
+ "data-variant": variant ?? "default",
933
+ "data-size": size ?? "default"
934
+ } : {};
935
+ const commonProps = {
936
+ className: combinedClassName,
937
+ onClick: handleClick,
938
+ "aria-label": ariaLabel,
939
+ "aria-describedby": ariaDescribedby,
940
+ id,
941
+ ...dataProps,
942
+ ...buttonDataAttributes
943
+ };
944
+ if (finalComponentType === "a" && shouldRenderLink) {
945
+ return /* @__PURE__ */ jsxRuntime.jsx(
946
+ "a",
947
+ {
948
+ ref,
949
+ href: normalizedHref,
950
+ target,
951
+ rel,
952
+ ...commonProps,
953
+ ...props,
954
+ children
955
+ }
956
+ );
1028
957
  }
1029
- }
1030
- }
1031
- var BrandAttribution = ({
1032
- variant = "span",
1033
- containerClassName,
1034
- linkClassName,
1035
- internalBrandSlug,
1036
- optionIndex
1037
- }) => {
1038
- const options = brandAttributionMap[internalBrandSlug];
1039
- if (!options || optionIndex < 0 || optionIndex >= options.length) {
1040
- return null;
1041
- }
1042
- const { prefix = "", anchorText, href, suffix = "" } = options[optionIndex];
1043
- const ContainerEl = variant;
1044
- const [trackedHref, setTrackedHref] = React__namespace.useState(href);
1045
- React__namespace.useEffect(() => {
1046
- setTrackedHref(buildTrackedHref(href));
1047
- }, [href]);
1048
- return /* @__PURE__ */ jsxRuntime.jsxs(ContainerEl, { className: containerClassName, children: [
1049
- prefix,
1050
- /* @__PURE__ */ jsxRuntime.jsx(
1051
- "a",
958
+ if (finalComponentType === "button") {
959
+ return /* @__PURE__ */ jsxRuntime.jsx(
960
+ "button",
961
+ {
962
+ ref,
963
+ type: props.type || "button",
964
+ ...commonProps,
965
+ ...props,
966
+ children
967
+ }
968
+ );
969
+ }
970
+ if (finalComponentType === "div") {
971
+ return /* @__PURE__ */ jsxRuntime.jsx(
972
+ "div",
973
+ {
974
+ ref,
975
+ ...commonProps,
976
+ children
977
+ }
978
+ );
979
+ }
980
+ return /* @__PURE__ */ jsxRuntime.jsx(
981
+ "span",
1052
982
  {
1053
- href: trackedHref,
1054
- target: "_blank",
1055
- rel: "noreferrer noopener",
1056
- className: linkClassName,
1057
- children: anchorText
983
+ ref,
984
+ ...commonProps,
985
+ children
1058
986
  }
1059
- ),
1060
- suffix
1061
- ] });
1062
- };
987
+ );
988
+ }
989
+ );
990
+ Pressable.displayName = "Pressable";
1063
991
  var maxWidthStyles = {
1064
992
  sm: "max-w-screen-sm",
1065
993
  md: "max-w-screen-md",
@@ -1069,7 +997,7 @@ var maxWidthStyles = {
1069
997
  "4xl": "max-w-[1536px]",
1070
998
  full: "max-w-full"
1071
999
  };
1072
- var Container = React__namespace.default.forwardRef(
1000
+ var Container = React2__namespace.default.forwardRef(
1073
1001
  ({ children, maxWidth = "xl", className, as = "div", ...props }, ref) => {
1074
1002
  const Component = as;
1075
1003
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1375,7 +1303,7 @@ var spacingStyles = {
1375
1303
  };
1376
1304
  var predefinedSpacings = ["none", "sm", "md", "lg", "xl"];
1377
1305
  var isPredefinedSpacing = (spacing) => predefinedSpacings.includes(spacing);
1378
- var Section = React__namespace.default.forwardRef(
1306
+ var Section = React2__namespace.default.forwardRef(
1379
1307
  ({
1380
1308
  id,
1381
1309
  title,
@@ -1473,7 +1401,7 @@ var platformIconMap = {
1473
1401
  dribbble: "cib/dribbble",
1474
1402
  unknown: "icon-park-solid/circular-connection"
1475
1403
  };
1476
- var SocialLinkIcon = React__namespace.forwardRef(
1404
+ var SocialLinkIcon = React2__namespace.forwardRef(
1477
1405
  ({
1478
1406
  platformName,
1479
1407
  label,
@@ -1486,13 +1414,13 @@ var SocialLinkIcon = React__namespace.forwardRef(
1486
1414
  ...pressableProps
1487
1415
  }, ref) => {
1488
1416
  const platform = usePlatformFromUrl.usePlatformFromUrl(href);
1489
- const smartPlatformName = React__namespace.useMemo(() => {
1417
+ const smartPlatformName = React2__namespace.useMemo(() => {
1490
1418
  return platform || platformName;
1491
1419
  }, [platform, platformName]);
1492
- const iconName = React__namespace.useMemo(() => {
1420
+ const iconName = React2__namespace.useMemo(() => {
1493
1421
  return iconNameOverride || platformIconMap[smartPlatformName];
1494
1422
  }, [iconNameOverride, smartPlatformName]);
1495
- const accessibleLabel = React__namespace.useMemo(() => {
1423
+ const accessibleLabel = React2__namespace.useMemo(() => {
1496
1424
  return label || platformName;
1497
1425
  }, [label, platformName]);
1498
1426
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -1521,12 +1449,18 @@ var SocialLinkIcon = React__namespace.forwardRef(
1521
1449
  }
1522
1450
  );
1523
1451
  SocialLinkIcon.displayName = "SocialLinkIcon";
1452
+ var DEFAULT_STYLE_RULES = {
1453
+ formContainer: "flex items-stretch w-full",
1454
+ fieldsContainer: "",
1455
+ fieldClassName: "",
1456
+ formClassName: ""
1457
+ };
1524
1458
  var DEFAULT_FORM_FIELDS = [
1525
1459
  {
1526
1460
  name: "email",
1527
1461
  type: "email",
1528
1462
  label: "Email Address",
1529
- placeholder: "Enter your email",
1463
+ placeholder: "Email Address",
1530
1464
  required: true,
1531
1465
  columnSpan: 12
1532
1466
  }
@@ -1539,7 +1473,6 @@ function FooterSocialNewsletter({
1539
1473
  className,
1540
1474
  contentClassName,
1541
1475
  logoWrapperClassName,
1542
- logoClassName,
1543
1476
  gridClassName,
1544
1477
  navSectionClassName,
1545
1478
  navTitleClassName,
@@ -1548,16 +1481,7 @@ function FooterSocialNewsletter({
1548
1481
  socialColumnClassName,
1549
1482
  socialLinksClassName,
1550
1483
  socialLinkClassName,
1551
- formFields = DEFAULT_FORM_FIELDS,
1552
- formConfig,
1553
- onSubmit,
1554
- onSuccess,
1555
- onError,
1556
- successMessage,
1557
- buttonAction,
1558
- helperText,
1559
- formSlot,
1560
- privacyClassName,
1484
+ formEngineSetup,
1561
1485
  bottomClassName,
1562
1486
  copyrightClassName,
1563
1487
  background,
@@ -1567,104 +1491,36 @@ function FooterSocialNewsletter({
1567
1491
  patternOpacity,
1568
1492
  optixFlowConfig
1569
1493
  }) {
1570
- const {
1571
- uploadTokens,
1572
- uploadProgress,
1573
- isUploading,
1574
- uploadFiles,
1575
- removeFile,
1576
- resetUpload
1577
- } = integration.useFileUpload({ onError });
1578
- const { form, submissionError, formMethod, resetSubmissionState } = integration.useContactForm({
1579
- formFields,
1580
- formConfig,
1581
- onSubmit,
1582
- onSuccess: (data) => {
1583
- resetUpload();
1584
- onSuccess?.(data);
1585
- },
1586
- onError,
1587
- uploadTokens
1588
- });
1589
- const renderForm = React__namespace.useMemo(() => {
1590
- if (formSlot) return formSlot;
1591
- const defaultButtonAction = {
1592
- label: "Subscribe",
1494
+ const renderForm = React2__namespace.useMemo(() => {
1495
+ if (!formEngineSetup) return null;
1496
+ const action = {
1593
1497
  variant: "default",
1594
- className: "h-12"
1498
+ icon: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/arrow-right", size: 16 })
1595
1499
  };
1596
- const action = buttonAction || defaultButtonAction;
1597
- return /* @__PURE__ */ jsxRuntime.jsxs(
1598
- forms.Form,
1599
- {
1600
- form,
1601
- fields: formFields,
1602
- notificationConfig: {
1603
- submissionError,
1604
- successMessage
1605
- },
1606
- formConfig: {
1607
- endpoint: formConfig?.endpoint,
1608
- method: formMethod,
1609
- submissionConfig: formConfig?.submissionConfig,
1610
- formLayout: "button-group",
1611
- buttonGroupSize: "sm",
1612
- submitLabel: action.label,
1613
- submitVariant: action.variant || "default",
1614
- submitIconComponent: action.icon || /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/send" })
1615
- },
1616
- onNewSubmission: () => {
1617
- resetUpload();
1618
- resetSubmissionState();
1500
+ return /* @__PURE__ */ jsxRuntime.jsx(
1501
+ integration.FormEngine,
1502
+ {
1503
+ formEngineSetup: {
1504
+ ...formEngineSetup,
1505
+ formLayoutSettings: {
1506
+ ...formEngineSetup.formLayoutSettings,
1507
+ formLayout: "button-group",
1508
+ buttonGroupSetup: {
1509
+ ...formEngineSetup.formLayoutSettings?.buttonGroupSetup,
1510
+ size: "default",
1511
+ submitLabel: action.icon || action.label,
1512
+ submitVariant: action.variant
1513
+ }
1514
+ }
1619
1515
  },
1620
- children: [
1621
- /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-4 sm:flex-row", children: [
1622
- formFields.map((field) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsxRuntime.jsx(
1623
- integration.DynamicFormField,
1624
- {
1625
- field,
1626
- uploadProgress,
1627
- onFileUpload: uploadFiles,
1628
- onFileRemove: removeFile,
1629
- isUploading
1630
- }
1631
- ) }, field.name)),
1632
- /* @__PURE__ */ jsxRuntime.jsxs(
1633
- Pressable,
1634
- {
1635
- onClick: form.handleSubmit,
1636
- asButton: true,
1637
- variant: action.variant,
1638
- className: cn("h-12", action.className),
1639
- disabled: form.isSubmitting,
1640
- children: [
1641
- action.label,
1642
- action.iconAfter
1643
- ]
1644
- }
1645
- )
1646
- ] }),
1647
- helperText && (typeof helperText === "string" ? /* @__PURE__ */ jsxRuntime.jsx("p", { className: cn("text-sm mt-2"), children: helperText }) : helperText)
1648
- ]
1516
+ defaultFields: DEFAULT_FORM_FIELDS,
1517
+ defaultStyleRules: {
1518
+ ...DEFAULT_STYLE_RULES,
1519
+ formContainer: cn(DEFAULT_STYLE_RULES.formContainer)
1520
+ }
1649
1521
  }
1650
1522
  );
1651
- }, [
1652
- formSlot,
1653
- formFields,
1654
- form,
1655
- formConfig,
1656
- formMethod,
1657
- buttonAction,
1658
- uploadProgress,
1659
- uploadFiles,
1660
- removeFile,
1661
- isUploading,
1662
- submissionError,
1663
- successMessage,
1664
- helperText,
1665
- resetUpload,
1666
- resetSubmissionState
1667
- ]);
1523
+ }, [formEngineSetup]);
1668
1524
  return /* @__PURE__ */ jsxRuntime.jsx(
1669
1525
  Section,
1670
1526
  {
@@ -1672,20 +1528,26 @@ function FooterSocialNewsletter({
1672
1528
  spacing,
1673
1529
  pattern,
1674
1530
  patternOpacity,
1675
- className: cn(className),
1531
+ className,
1676
1532
  containerClassName,
1677
1533
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn(contentClassName), children: /* @__PURE__ */ jsxRuntime.jsxs("footer", { children: [
1678
1534
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-20", children: [
1679
1535
  logo && /* @__PURE__ */ jsxRuntime.jsx(
1680
- FooterLogo,
1536
+ Pressable,
1681
1537
  {
1682
- logo,
1683
- logoClassName: cn(
1684
- "flex items-center gap-2",
1685
- logoWrapperClassName
1686
- ),
1687
- logoImageClassName: cn("h-10", logoClassName),
1688
- optixFlowConfig
1538
+ href: logo.url || "/",
1539
+ className: cn("block mb-8 md:mb-12", logoWrapperClassName),
1540
+ children: /* @__PURE__ */ jsxRuntime.jsx(
1541
+ img.Img,
1542
+ {
1543
+ src: logo.src,
1544
+ className: cn(
1545
+ "h-16 object-contain w-auto max-w-full",
1546
+ logo.className
1547
+ ),
1548
+ optixFlowConfig
1549
+ }
1550
+ )
1689
1551
  }
1690
1552
  ),
1691
1553
  socialLinks && socialLinks.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("mt-6", socialColumnClassName), children: /* @__PURE__ */ jsxRuntime.jsx(
@@ -1698,10 +1560,9 @@ function FooterSocialNewsletter({
1698
1560
  children: socialLinks.map((social, idx) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
1699
1561
  SocialLinkIcon,
1700
1562
  {
1701
- href: social.href,
1702
- label: social.label,
1703
- iconNameOverride: social.iconNameOverride,
1704
- iconSize: 20,
1563
+ size: "icon-lg",
1564
+ variant: "ghost",
1565
+ ...social,
1705
1566
  className: cn(
1706
1567
  "opacity-80 transition-colors hover:opacity-100",
1707
1568
  socialLinkClassName