@opensite/ui 2.1.9 → 2.2.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,458 +1,21 @@
1
1
  "use client";
2
2
  import * as React from 'react';
3
- import React__default, { useMemo } from 'react';
4
- import { Form } from '@page-speed/forms';
5
- import { useFileUpload, useContactForm, getColumnSpanClass, DynamicFormField } from '@page-speed/forms/integration';
3
+ import React__default from 'react';
4
+ import { motion } from 'framer-motion';
5
+ import { FormEngine } from '@page-speed/forms/integration';
6
6
  import { clsx } from 'clsx';
7
7
  import { twMerge } from 'tailwind-merge';
8
- import { cva } from 'class-variance-authority';
9
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
10
8
  import { Img } from '@page-speed/img';
9
+ import { Icon } from '@page-speed/icon';
10
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
11
11
 
12
12
  // components/blocks/contact/contact-image.tsx
13
13
  function cn(...inputs) {
14
14
  return twMerge(clsx(inputs));
15
15
  }
16
- function normalizePhoneNumber(input) {
17
- const trimmed = input.trim();
18
- if (trimmed.toLowerCase().startsWith("tel:")) {
19
- return trimmed;
20
- }
21
- const match = trimmed.match(/^[\s\+\-\(\)]*(\d[\d\s\-\(\)\.]*\d)[\s\-]*(x|ext\.?|extension)?[\s\-]*(\d+)?$/i);
22
- if (match) {
23
- const mainNumber = match[1].replace(/[\s\-\(\)\.]/g, "");
24
- const extension = match[3];
25
- const normalized = mainNumber.length >= 10 && !trimmed.startsWith("+") ? `+${mainNumber}` : mainNumber;
26
- const withExtension = extension ? `${normalized};ext=${extension}` : normalized;
27
- return `tel:${withExtension}`;
28
- }
29
- const cleaned = trimmed.replace(/[\s\-\(\)\.]/g, "");
30
- return `tel:${cleaned}`;
31
- }
32
- function normalizeEmail(input) {
33
- const trimmed = input.trim();
34
- if (trimmed.toLowerCase().startsWith("mailto:")) {
35
- return trimmed;
36
- }
37
- return `mailto:${trimmed}`;
38
- }
39
- function isEmail(input) {
40
- const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
41
- return emailRegex.test(input.trim());
42
- }
43
- function isPhoneNumber(input) {
44
- const trimmed = input.trim();
45
- if (trimmed.toLowerCase().startsWith("tel:")) {
46
- return true;
47
- }
48
- const phoneRegex = /^[\s\+\-\(\)]*\d[\d\s\-\(\)\.]*\d[\s\-]*(x|ext\.?|extension)?[\s\-]*\d*$/i;
49
- return phoneRegex.test(trimmed);
50
- }
51
- function isInternalUrl(href) {
52
- if (typeof window === "undefined") {
53
- return href.startsWith("/") && !href.startsWith("//");
54
- }
55
- const trimmed = href.trim();
56
- if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
57
- return true;
58
- }
59
- try {
60
- const url = new URL(trimmed, window.location.href);
61
- const currentOrigin = window.location.origin;
62
- const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
63
- return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);
64
- } catch {
65
- return false;
66
- }
67
- }
68
- function toRelativePath(href) {
69
- if (typeof window === "undefined") {
70
- return href;
71
- }
72
- const trimmed = href.trim();
73
- if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
74
- return trimmed;
75
- }
76
- try {
77
- const url = new URL(trimmed, window.location.href);
78
- const currentOrigin = window.location.origin;
79
- const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
80
- if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {
81
- return url.pathname + url.search + url.hash;
82
- }
83
- } catch {
84
- }
85
- return trimmed;
86
- }
87
- function useNavigation({
88
- href,
89
- onClick
90
- } = {}) {
91
- const linkType = React.useMemo(() => {
92
- if (!href || href.trim() === "") {
93
- return onClick ? "none" : "none";
94
- }
95
- const trimmed = href.trim();
96
- if (trimmed.toLowerCase().startsWith("mailto:") || isEmail(trimmed)) {
97
- return "mailto";
98
- }
99
- if (trimmed.toLowerCase().startsWith("tel:") || isPhoneNumber(trimmed)) {
100
- return "tel";
101
- }
102
- if (isInternalUrl(trimmed)) {
103
- return "internal";
104
- }
105
- try {
106
- new URL(trimmed, typeof window !== "undefined" ? window.location.href : "http://localhost");
107
- return "external";
108
- } catch {
109
- return "internal";
110
- }
111
- }, [href, onClick]);
112
- const normalizedHref = React.useMemo(() => {
113
- if (!href || href.trim() === "") {
114
- return void 0;
115
- }
116
- const trimmed = href.trim();
117
- switch (linkType) {
118
- case "tel":
119
- return normalizePhoneNumber(trimmed);
120
- case "mailto":
121
- return normalizeEmail(trimmed);
122
- case "internal":
123
- return toRelativePath(trimmed);
124
- case "external":
125
- return trimmed;
126
- default:
127
- return trimmed;
128
- }
129
- }, [href, linkType]);
130
- const target = React.useMemo(() => {
131
- switch (linkType) {
132
- case "external":
133
- return "_blank";
134
- case "internal":
135
- return "_self";
136
- case "mailto":
137
- case "tel":
138
- return void 0;
139
- default:
140
- return void 0;
141
- }
142
- }, [linkType]);
143
- const rel = React.useMemo(() => {
144
- if (linkType === "external") {
145
- return "noopener noreferrer";
146
- }
147
- return void 0;
148
- }, [linkType]);
149
- const isExternal = linkType === "external";
150
- const isInternal = linkType === "internal";
151
- const shouldUseRouter = isInternal && typeof normalizedHref === "string" && normalizedHref.startsWith("/");
152
- const handleClick = React.useCallback(
153
- (event) => {
154
- if (onClick) {
155
- try {
156
- onClick(event);
157
- } catch (error) {
158
- console.error("Error in user onClick handler:", error);
159
- }
160
- }
161
- if (event.defaultPrevented) {
162
- return;
163
- }
164
- if (shouldUseRouter && normalizedHref && event.button === 0 && // left-click only
165
- !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey) {
166
- if (typeof window !== "undefined") {
167
- const handler = window.__opensiteNavigationHandler;
168
- if (typeof handler === "function") {
169
- try {
170
- const handled = handler(normalizedHref, event.nativeEvent || event);
171
- if (handled !== false) {
172
- event.preventDefault();
173
- }
174
- } catch (error) {
175
- console.error("Error in navigation handler:", error);
176
- }
177
- }
178
- }
179
- }
180
- },
181
- [onClick, shouldUseRouter, normalizedHref]
182
- );
183
- return {
184
- linkType,
185
- normalizedHref,
186
- target,
187
- rel,
188
- isExternal,
189
- isInternal,
190
- shouldUseRouter,
191
- handleClick
192
- };
193
- }
194
- var baseStyles = [
195
- // Layout
196
- "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
197
- // Typography - using CSS variables with sensible defaults
198
- "font-[var(--button-font-family,inherit)]",
199
- "font-[var(--button-font-weight,500)]",
200
- "tracking-[var(--button-letter-spacing,0)]",
201
- "leading-[var(--button-line-height,1.25)]",
202
- "[text-transform:var(--button-text-transform,none)]",
203
- "text-sm",
204
- // Border radius
205
- "rounded-[var(--button-radius,var(--radius,0.375rem))]",
206
- // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)
207
- "[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
208
- // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows
209
- "[box-shadow:var(--button-shadow,none)]",
210
- "hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]",
211
- // Disabled state
212
- "disabled:pointer-events-none disabled:opacity-50",
213
- // SVG handling
214
- "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
215
- // Focus styles
216
- "outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
217
- // Invalid state
218
- "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
219
- ].join(" ");
220
- var buttonVariants = cva(baseStyles, {
221
- variants: {
222
- variant: {
223
- // Default (Primary) variant - full customization
224
- default: [
225
- "bg-[var(--button-default-bg,hsl(var(--primary)))]",
226
- "text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
227
- "border-[length:var(--button-default-border-width,0px)]",
228
- "border-[color:var(--button-default-border,transparent)]",
229
- "[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]",
230
- "hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
231
- "hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
232
- "hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
233
- "hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
234
- ].join(" "),
235
- // Destructive variant - full customization
236
- destructive: [
237
- "bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
238
- "text-[var(--button-destructive-fg,white)]",
239
- "border-[length:var(--button-destructive-border-width,0px)]",
240
- "border-[color:var(--button-destructive-border,transparent)]",
241
- "[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]",
242
- "hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
243
- "hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
244
- "hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
245
- "hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
246
- "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
247
- "dark:bg-destructive/60"
248
- ].join(" "),
249
- // Outline variant - full customization with proper border handling
250
- outline: [
251
- "bg-[var(--button-outline-bg,hsl(var(--background)))]",
252
- "text-[var(--button-outline-fg,inherit)]",
253
- "border-[length:var(--button-outline-border-width,1px)]",
254
- "border-[color:var(--button-outline-border,hsl(var(--border)))]",
255
- "[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
256
- "hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
257
- "hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
258
- "hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
259
- "hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
260
- "dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
261
- ].join(" "),
262
- // Secondary variant - full customization
263
- secondary: [
264
- "bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
265
- "text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
266
- "border-[length:var(--button-secondary-border-width,0px)]",
267
- "border-[color:var(--button-secondary-border,transparent)]",
268
- "[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]",
269
- "hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
270
- "hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
271
- "hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
272
- "hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
273
- ].join(" "),
274
- // Ghost variant - full customization
275
- ghost: [
276
- "bg-[var(--button-ghost-bg,transparent)]",
277
- "text-[var(--button-ghost-fg,inherit)]",
278
- "border-[length:var(--button-ghost-border-width,0px)]",
279
- "border-[color:var(--button-ghost-border,transparent)]",
280
- "[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]",
281
- "hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
282
- "hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
283
- "hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
284
- "hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
285
- "dark:hover:bg-accent/50"
286
- ].join(" "),
287
- // Link variant - full customization
288
- link: [
289
- "bg-[var(--button-link-bg,transparent)]",
290
- "text-[var(--button-link-fg,hsl(var(--primary)))]",
291
- "border-[length:var(--button-link-border-width,0px)]",
292
- "border-[color:var(--button-link-border,transparent)]",
293
- "[box-shadow:var(--button-link-shadow,none)]",
294
- "hover:bg-[var(--button-link-hover-bg,transparent)]",
295
- "hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
296
- "hover:[box-shadow:var(--button-link-shadow-hover,none)]",
297
- "underline-offset-4 hover:underline"
298
- ].join(" ")
299
- },
300
- size: {
301
- default: [
302
- "h-[var(--button-height-md,2.25rem)]",
303
- "px-[var(--button-padding-x-md,1rem)]",
304
- "py-[var(--button-padding-y-md,0.5rem)]",
305
- "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
306
- ].join(" "),
307
- sm: [
308
- "h-[var(--button-height-sm,2rem)]",
309
- "px-[var(--button-padding-x-sm,0.75rem)]",
310
- "py-[var(--button-padding-y-sm,0.25rem)]",
311
- "gap-1.5",
312
- "has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
313
- ].join(" "),
314
- md: [
315
- "h-[var(--button-height-md,2.25rem)]",
316
- "px-[var(--button-padding-x-md,1rem)]",
317
- "py-[var(--button-padding-y-md,0.5rem)]",
318
- "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
319
- ].join(" "),
320
- lg: [
321
- "h-[var(--button-height-lg,2.5rem)]",
322
- "px-[var(--button-padding-x-lg,1.5rem)]",
323
- "py-[var(--button-padding-y-lg,0.5rem)]",
324
- "has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
325
- ].join(" "),
326
- icon: "size-[var(--button-height-md,2.25rem)]",
327
- "icon-sm": "size-[var(--button-height-sm,2rem)]",
328
- "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
329
- }
330
- },
331
- defaultVariants: {
332
- variant: "default",
333
- size: "default"
334
- }
335
- });
336
- var Pressable = React.forwardRef(
337
- ({
338
- children,
339
- className,
340
- href,
341
- onClick,
342
- variant,
343
- size,
344
- asButton = false,
345
- fallbackComponentType = "span",
346
- componentType,
347
- "aria-label": ariaLabel,
348
- "aria-describedby": ariaDescribedby,
349
- id,
350
- ...props
351
- }, ref) => {
352
- const navigation = useNavigation({ href, onClick });
353
- const {
354
- normalizedHref,
355
- target,
356
- rel,
357
- linkType,
358
- isInternal,
359
- handleClick
360
- } = navigation;
361
- const shouldRenderLink = normalizedHref && linkType !== "none";
362
- const shouldRenderButton = !shouldRenderLink && onClick;
363
- const effectiveComponentType = componentType || (shouldRenderLink ? "a" : shouldRenderButton ? "button" : fallbackComponentType);
364
- const finalComponentType = isInternal && shouldRenderLink ? "a" : effectiveComponentType;
365
- const shouldApplyButtonStyles = asButton || variant || size;
366
- const combinedClassName = cn(
367
- shouldApplyButtonStyles && buttonVariants({ variant, size }),
368
- className
369
- );
370
- const dataProps = Object.fromEntries(
371
- Object.entries(props).filter(([key]) => key.startsWith("data-"))
372
- );
373
- const buttonDataAttributes = shouldApplyButtonStyles ? {
374
- "data-slot": "button",
375
- "data-variant": variant ?? "default",
376
- "data-size": size ?? "default"
377
- } : {};
378
- const commonProps = {
379
- className: combinedClassName,
380
- onClick: handleClick,
381
- "aria-label": ariaLabel,
382
- "aria-describedby": ariaDescribedby,
383
- id,
384
- ...dataProps,
385
- ...buttonDataAttributes
386
- };
387
- if (finalComponentType === "a" && shouldRenderLink) {
388
- return /* @__PURE__ */ jsx(
389
- "a",
390
- {
391
- ref,
392
- href: normalizedHref,
393
- target,
394
- rel,
395
- ...commonProps,
396
- ...props,
397
- children
398
- }
399
- );
400
- }
401
- if (finalComponentType === "button") {
402
- return /* @__PURE__ */ jsx(
403
- "button",
404
- {
405
- ref,
406
- type: props.type || "button",
407
- ...commonProps,
408
- ...props,
409
- children
410
- }
411
- );
412
- }
413
- if (finalComponentType === "div") {
414
- return /* @__PURE__ */ jsx(
415
- "div",
416
- {
417
- ref,
418
- ...commonProps,
419
- children
420
- }
421
- );
422
- }
423
- return /* @__PURE__ */ jsx(
424
- "span",
425
- {
426
- ref,
427
- ...commonProps,
428
- children
429
- }
430
- );
431
- }
432
- );
433
- Pressable.displayName = "Pressable";
434
- function Card({ className, ...props }) {
435
- return /* @__PURE__ */ jsx(
436
- "div",
437
- {
438
- "data-slot": "card",
439
- className: cn(
440
- "bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
441
- className
442
- ),
443
- ...props
444
- }
445
- );
446
- }
447
- function CardContent({ className, ...props }) {
448
- return /* @__PURE__ */ jsx(
449
- "div",
450
- {
451
- "data-slot": "card-content",
452
- className: cn("px-6", className),
453
- ...props
454
- }
455
- );
16
+ var DEFAULT_ICON_API_KEY = "au382bi7fsh96w9h9xlrnat2jglx";
17
+ function DynamicIcon({ apiKey, ...props }) {
18
+ return /* @__PURE__ */ jsx(Icon, { ...props, apiKey: apiKey ?? DEFAULT_ICON_API_KEY });
456
19
  }
457
20
  var maxWidthStyles = {
458
21
  sm: "max-w-screen-sm",
@@ -829,6 +392,12 @@ var Section = React__default.forwardRef(
829
392
  }
830
393
  );
831
394
  Section.displayName = "Section";
395
+ var DEFAULT_STYLE_RULES = {
396
+ formContainer: "",
397
+ fieldsContainer: "",
398
+ fieldClassName: "",
399
+ formClassName: "space-y-4"
400
+ };
832
401
  var DEFAULT_FORM_FIELDS = [
833
402
  {
834
403
  name: "first_name",
@@ -873,207 +442,172 @@ var DEFAULT_FORM_FIELDS = [
873
442
  }
874
443
  ];
875
444
  function ContactImage({
445
+ eyebrow,
876
446
  heading,
877
447
  description,
878
- buttonText = "Submit",
448
+ buttonText = "Send Message",
879
449
  buttonIcon,
880
- actions,
881
- actionsSlot,
882
- formFields = DEFAULT_FORM_FIELDS,
883
- successMessage = "Thank you! Your message has been sent successfully.",
450
+ image,
451
+ contactOverlays,
452
+ contactOverlaysSlot,
453
+ formEngineSetup,
454
+ className,
455
+ containerClassName = "px-6 sm:px-6 md:px-8 lg:px-8",
456
+ contentClassName,
457
+ eyebrowClassName,
884
458
  headingClassName,
885
459
  descriptionClassName,
886
- cardClassName,
887
- cardContentClassName,
888
- formClassName,
889
- submitClassName,
890
- successMessageClassName,
891
- errorMessageClassName,
892
- backgroundImage,
460
+ imageClassName,
461
+ contactOverlaysClassName,
893
462
  background,
894
- optixFlowConfig,
895
- spacing = "none",
896
- className,
897
- containerClassName = "px-0 sm:px-0 lg:px-0 max-w-full relative z-10 h-screen w-screen flex justify-center items-center",
898
- contentClassName = "",
463
+ spacing = "py-16 md:py-32",
899
464
  pattern,
900
- patternOpacity = 0.1,
901
- formConfig,
902
- onSubmit,
903
- onSuccess,
904
- onError
465
+ patternOpacity,
466
+ optixFlowConfig
905
467
  }) {
906
- const {
907
- uploadTokens,
908
- uploadProgress,
909
- isUploading,
910
- uploadFiles,
911
- removeFile,
912
- resetUpload
913
- } = useFileUpload({ onError });
914
- const { form, submissionError, formMethod, resetSubmissionState } = useContactForm({
915
- formFields,
916
- formConfig,
917
- onSubmit,
918
- onSuccess: (data) => {
919
- resetUpload();
920
- onSuccess?.(data);
921
- },
922
- onError,
923
- resetOnSuccess: formConfig?.resetOnSuccess !== false,
924
- uploadTokens
925
- });
926
- const actionsContent = useMemo(() => {
927
- if (actionsSlot) return actionsSlot;
928
- if (actions && actions.length > 0) {
929
- return actions.map((action, index) => {
930
- const {
931
- label,
932
- icon,
933
- iconAfter,
934
- children,
935
- className: actionClassName,
936
- ...pressableProps
937
- } = action;
468
+ const contactOverlaysContent = React.useMemo(() => {
469
+ if (contactOverlaysSlot) return contactOverlaysSlot;
470
+ if (!contactOverlays || contactOverlays.length === 0) return null;
471
+ return /* @__PURE__ */ jsx("div", { className: cn("flex flex-col gap-3", contactOverlaysClassName), children: contactOverlays.map((item, index) => {
472
+ const content = /* @__PURE__ */ jsxs(
473
+ "div",
474
+ {
475
+ className: cn(
476
+ "rounded-2xl border border-white/10 bg-foreground/80 p-4 backdrop-blur-sm",
477
+ item.className
478
+ ),
479
+ children: [
480
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
481
+ /* @__PURE__ */ jsx("div", { className: "flex size-fit p-2 items-center justify-center rounded-full bg-primary text-primary-foreground", children: /* @__PURE__ */ jsx(DynamicIcon, { name: item.icon, size: 18 }) }),
482
+ /* @__PURE__ */ jsxs("div", { children: [
483
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-bold uppercase tracking-[0.15em] text-background/70", children: item.label }),
484
+ /* @__PURE__ */ jsx("p", { className: "font-semibold text-background", children: item.title })
485
+ ] })
486
+ ] }),
487
+ item.description && /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-background/80", children: item.description })
488
+ ]
489
+ }
490
+ );
491
+ if (item.href) {
938
492
  return /* @__PURE__ */ jsx(
939
- Pressable,
493
+ "a",
940
494
  {
941
- asButton: true,
942
- className: actionClassName,
943
- ...pressableProps,
944
- children: children ?? /* @__PURE__ */ jsxs(Fragment, { children: [
945
- icon,
946
- label,
947
- iconAfter
948
- ] })
495
+ href: item.href,
496
+ className: "block transition-transform hover:scale-[1.02]",
497
+ children: content
949
498
  },
950
499
  index
951
500
  );
952
- });
953
- }
954
- return null;
955
- }, [actionsSlot, actions]);
956
- const renderBackground = useMemo(() => {
957
- if (!backgroundImage) return null;
958
- return /* @__PURE__ */ jsxs("div", { className: "absolute inset-0", children: [
959
- /* @__PURE__ */ jsx(
960
- Img,
961
- {
962
- src: backgroundImage,
963
- alt: "Full screen background image",
964
- className: "h-full w-full object-cover",
965
- loading: "eager",
966
- optixFlowConfig
967
- }
968
- ),
969
- /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-linear-to-b from-black/80 via-black/65 to-black/20" })
970
- ] });
971
- }, [backgroundImage, optixFlowConfig]);
972
- return /* @__PURE__ */ jsxs(
501
+ }
502
+ return /* @__PURE__ */ jsx("div", { children: content }, index);
503
+ }) });
504
+ }, [contactOverlaysSlot, contactOverlays, contactOverlaysClassName]);
505
+ return /* @__PURE__ */ jsx(
973
506
  Section,
974
507
  {
975
508
  background,
976
509
  spacing,
510
+ className,
511
+ containerClassName,
977
512
  pattern,
978
513
  patternOpacity,
979
- className: cn(
980
- "relative flex h-full min-h-screen w-screen items-center justify-center overflow-hidden bg-black pb-0 pt-0 md:pt-0 px-0",
981
- className
982
- ),
983
- containerClassName,
984
- children: [
985
- renderBackground,
986
- /* @__PURE__ */ jsx(
987
- "div",
988
- {
989
- className: cn(
990
- "flex flex-col gap-4 md:gap-6 px-6 pt-28 pb-6 md:pt-0 md:pb-0",
991
- "relative z-30 m-auto max-w-full md:max-w-md flex-col items-center justify-center text-center",
992
- contentClassName
993
- ),
994
- children: /* @__PURE__ */ jsx(Card, { className: cn("mx-auto max-w-xl", cardClassName), children: /* @__PURE__ */ jsxs(CardContent, { className: cn("p-6 lg:p-8", cardContentClassName), children: [
995
- heading && (typeof heading === "string" ? /* @__PURE__ */ jsx(
996
- "h2",
997
- {
998
- className: cn(
999
- "text-5xl md:text-6xl lg:text-7xl text-card-foreground text-shadow-2xl font-semibold",
1000
- headingClassName
1001
- ),
1002
- children: heading
1003
- }
1004
- ) : heading),
1005
- description && (typeof description === "string" ? /* @__PURE__ */ jsx(
1006
- "p",
1007
- {
1008
- className: cn(
1009
- "text-center text-base text-balance text-card-foreground text-shadow-2xl",
1010
- descriptionClassName
514
+ children: /* @__PURE__ */ jsxs(
515
+ "div",
516
+ {
517
+ className: cn(
518
+ "grid grid-cols-1 items-center gap-12 lg:grid-cols-2",
519
+ contentClassName
520
+ ),
521
+ children: [
522
+ image && /* @__PURE__ */ jsx(
523
+ motion.div,
524
+ {
525
+ initial: { opacity: 0, x: -20 },
526
+ whileInView: { opacity: 1, x: 0 },
527
+ viewport: { once: true, margin: "-50px" },
528
+ transition: { duration: 0.5 },
529
+ className: "order-2 lg:order-1",
530
+ children: /* @__PURE__ */ jsxs("div", { className: "relative overflow-hidden rounded-3xl border border-white/10 shadow-2xl", children: [
531
+ /* @__PURE__ */ jsx(
532
+ Img,
533
+ {
534
+ src: image.src,
535
+ alt: image.alt,
536
+ className: cn("h-full w-full object-cover", imageClassName),
537
+ optixFlowConfig
538
+ }
1011
539
  ),
1012
- children: description
1013
- }
1014
- ) : description),
1015
- /* @__PURE__ */ jsxs(
1016
- Form,
1017
- {
1018
- form,
1019
- notificationConfig: {
1020
- submissionError,
1021
- successMessage
1022
- },
1023
- styleConfig: {
1024
- formClassName: cn("space-y-4", formClassName),
1025
- successMessageClassName,
1026
- errorMessageClassName
1027
- },
1028
- formConfig: {
1029
- endpoint: formConfig?.endpoint,
1030
- method: formMethod,
1031
- submissionConfig: formConfig?.submissionConfig
1032
- },
1033
- onNewSubmission: () => {
1034
- resetUpload();
1035
- resetSubmissionState();
1036
- },
1037
- children: [
1038
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-12 gap-6", children: formFields.map((field) => /* @__PURE__ */ jsx(
1039
- "div",
1040
- {
1041
- className: getColumnSpanClass(field.columnSpan),
1042
- children: /* @__PURE__ */ jsx(
1043
- DynamicFormField,
1044
- {
1045
- field,
1046
- uploadProgress,
1047
- onFileUpload: uploadFiles,
1048
- onFileRemove: removeFile,
1049
- isUploading
540
+ /* @__PURE__ */ jsx("div", { className: "absolute inset-0 bg-linear-to-tr from-black/70 via-transparent to-transparent" }),
541
+ contactOverlaysContent && /* @__PURE__ */ jsx("div", { className: "absolute bottom-6 left-6 right-6", children: contactOverlaysContent })
542
+ ] })
543
+ }
544
+ ),
545
+ /* @__PURE__ */ jsxs(
546
+ motion.div,
547
+ {
548
+ initial: { opacity: 0, x: 20 },
549
+ whileInView: { opacity: 1, x: 0 },
550
+ viewport: { once: true, margin: "-50px" },
551
+ transition: { duration: 0.5 },
552
+ className: "order-1 lg:order-2",
553
+ children: [
554
+ eyebrow && (typeof eyebrow === "string" ? /* @__PURE__ */ jsx(
555
+ "p",
556
+ {
557
+ className: cn(
558
+ "text-sm font-semibold uppercase tracking-[0.2em] text-muted-foreground",
559
+ eyebrowClassName
560
+ ),
561
+ children: eyebrow
562
+ }
563
+ ) : /* @__PURE__ */ jsx("div", { className: eyebrowClassName, children: eyebrow })),
564
+ heading && (typeof heading === "string" ? /* @__PURE__ */ jsx(
565
+ "h2",
566
+ {
567
+ className: cn(
568
+ "mt-2 text-3xl font-bold md:text-4xl lg:text-5xl",
569
+ headingClassName
570
+ ),
571
+ children: heading
572
+ }
573
+ ) : /* @__PURE__ */ jsx("div", { className: cn("mt-2", headingClassName), children: heading })),
574
+ description && (typeof description === "string" ? /* @__PURE__ */ jsx(
575
+ "p",
576
+ {
577
+ className: cn(
578
+ "mt-4 text-lg text-muted-foreground",
579
+ descriptionClassName
580
+ ),
581
+ children: description
582
+ }
583
+ ) : /* @__PURE__ */ jsx("div", { className: cn("mt-4", descriptionClassName), children: description })),
584
+ /* @__PURE__ */ jsx("div", { className: "mt-8", children: formEngineSetup ? /* @__PURE__ */ jsx(
585
+ FormEngine,
586
+ {
587
+ formEngineSetup: {
588
+ ...formEngineSetup,
589
+ formLayoutSettings: {
590
+ ...formEngineSetup.formLayoutSettings,
591
+ formLayout: "standard",
592
+ submitButtonSetup: {
593
+ ...formEngineSetup.formLayoutSettings?.submitButtonSetup,
594
+ submitLabel: /* @__PURE__ */ jsxs(Fragment, { children: [
595
+ buttonIcon,
596
+ buttonText
597
+ ] })
1050
598
  }
1051
- )
599
+ }
1052
600
  },
1053
- field.name
1054
- )) }),
1055
- actionsSlot || actions && actions.length > 0 ? actionsContent : /* @__PURE__ */ jsxs(
1056
- Pressable,
1057
- {
1058
- componentType: "button",
1059
- type: "submit",
1060
- className: cn("w-full", submitClassName),
1061
- size: "lg",
1062
- asButton: true,
1063
- disabled: form.isSubmitting,
1064
- children: [
1065
- buttonIcon,
1066
- buttonText
1067
- ]
1068
- }
1069
- )
1070
- ]
1071
- }
1072
- )
1073
- ] }) })
1074
- }
1075
- )
1076
- ]
601
+ defaultFields: DEFAULT_FORM_FIELDS,
602
+ defaultStyleRules: DEFAULT_STYLE_RULES
603
+ }
604
+ ) : null })
605
+ ]
606
+ }
607
+ )
608
+ ]
609
+ }
610
+ )
1077
611
  }
1078
612
  );
1079
613
  }