@opensite/ui 0.7.7 → 0.7.8

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,12 +1,13 @@
1
1
  "use client";
2
2
  'use strict';
3
3
 
4
- var React3 = require('react');
4
+ var React6 = require('react');
5
5
  var framerMotion = require('framer-motion');
6
6
  var clsx = require('clsx');
7
7
  var tailwindMerge = require('tailwind-merge');
8
- var img = require('@page-speed/img');
8
+ var classVarianceAuthority = require('class-variance-authority');
9
9
  var jsxRuntime = require('react/jsx-runtime');
10
+ var img = require('@page-speed/img');
10
11
 
11
12
  function _interopNamespace(e) {
12
13
  if (e && e.__esModule) return e;
@@ -26,12 +27,535 @@ function _interopNamespace(e) {
26
27
  return Object.freeze(n);
27
28
  }
28
29
 
29
- var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
30
+ var React6__namespace = /*#__PURE__*/_interopNamespace(React6);
30
31
 
31
32
  // components/blocks/carousel/carousel-progress-slider.tsx
32
33
  function cn(...inputs) {
33
34
  return tailwindMerge.twMerge(clsx.clsx(inputs));
34
35
  }
36
+ function normalizePhoneNumber(input) {
37
+ const trimmed = input.trim();
38
+ if (trimmed.toLowerCase().startsWith("tel:")) {
39
+ return trimmed;
40
+ }
41
+ const match = trimmed.match(/^[\s\+\-\(\)]*(\d[\d\s\-\(\)\.]*\d)[\s\-]*(x|ext\.?|extension)?[\s\-]*(\d+)?$/i);
42
+ if (match) {
43
+ const mainNumber = match[1].replace(/[\s\-\(\)\.]/g, "");
44
+ const extension = match[3];
45
+ const normalized = mainNumber.length >= 10 && !trimmed.startsWith("+") ? `+${mainNumber}` : mainNumber;
46
+ const withExtension = extension ? `${normalized};ext=${extension}` : normalized;
47
+ return `tel:${withExtension}`;
48
+ }
49
+ const cleaned = trimmed.replace(/[\s\-\(\)\.]/g, "");
50
+ return `tel:${cleaned}`;
51
+ }
52
+ function normalizeEmail(input) {
53
+ const trimmed = input.trim();
54
+ if (trimmed.toLowerCase().startsWith("mailto:")) {
55
+ return trimmed;
56
+ }
57
+ return `mailto:${trimmed}`;
58
+ }
59
+ function isEmail(input) {
60
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
61
+ return emailRegex.test(input.trim());
62
+ }
63
+ function isPhoneNumber(input) {
64
+ const trimmed = input.trim();
65
+ if (trimmed.toLowerCase().startsWith("tel:")) {
66
+ return true;
67
+ }
68
+ const phoneRegex = /^[\s\+\-\(\)]*\d[\d\s\-\(\)\.]*\d[\s\-]*(x|ext\.?|extension)?[\s\-]*\d*$/i;
69
+ return phoneRegex.test(trimmed);
70
+ }
71
+ function isInternalUrl(href) {
72
+ if (typeof window === "undefined") {
73
+ return href.startsWith("/") && !href.startsWith("//");
74
+ }
75
+ const trimmed = href.trim();
76
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
77
+ return true;
78
+ }
79
+ try {
80
+ const url = new URL(trimmed, window.location.href);
81
+ const currentOrigin = window.location.origin;
82
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
83
+ return normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin);
84
+ } catch {
85
+ return false;
86
+ }
87
+ }
88
+ function toRelativePath(href) {
89
+ if (typeof window === "undefined") {
90
+ return href;
91
+ }
92
+ const trimmed = href.trim();
93
+ if (trimmed.startsWith("/") && !trimmed.startsWith("//")) {
94
+ return trimmed;
95
+ }
96
+ try {
97
+ const url = new URL(trimmed, window.location.href);
98
+ const currentOrigin = window.location.origin;
99
+ const normalizeOrigin = (origin) => origin.replace(/^(https?:\/\/)(www\.)?/, "$1");
100
+ if (normalizeOrigin(url.origin) === normalizeOrigin(currentOrigin)) {
101
+ return url.pathname + url.search + url.hash;
102
+ }
103
+ } catch {
104
+ }
105
+ return trimmed;
106
+ }
107
+ function useNavigation({
108
+ href,
109
+ onClick
110
+ } = {}) {
111
+ const linkType = React6__namespace.useMemo(() => {
112
+ if (!href || href.trim() === "") {
113
+ return onClick ? "none" : "none";
114
+ }
115
+ const trimmed = href.trim();
116
+ if (trimmed.toLowerCase().startsWith("mailto:") || isEmail(trimmed)) {
117
+ return "mailto";
118
+ }
119
+ if (trimmed.toLowerCase().startsWith("tel:") || isPhoneNumber(trimmed)) {
120
+ return "tel";
121
+ }
122
+ if (isInternalUrl(trimmed)) {
123
+ return "internal";
124
+ }
125
+ try {
126
+ new URL(trimmed, typeof window !== "undefined" ? window.location.href : "http://localhost");
127
+ return "external";
128
+ } catch {
129
+ return "internal";
130
+ }
131
+ }, [href, onClick]);
132
+ const normalizedHref = React6__namespace.useMemo(() => {
133
+ if (!href || href.trim() === "") {
134
+ return void 0;
135
+ }
136
+ const trimmed = href.trim();
137
+ switch (linkType) {
138
+ case "tel":
139
+ return normalizePhoneNumber(trimmed);
140
+ case "mailto":
141
+ return normalizeEmail(trimmed);
142
+ case "internal":
143
+ return toRelativePath(trimmed);
144
+ case "external":
145
+ return trimmed;
146
+ default:
147
+ return trimmed;
148
+ }
149
+ }, [href, linkType]);
150
+ const target = React6__namespace.useMemo(() => {
151
+ switch (linkType) {
152
+ case "external":
153
+ return "_blank";
154
+ case "internal":
155
+ return "_self";
156
+ case "mailto":
157
+ case "tel":
158
+ return void 0;
159
+ default:
160
+ return void 0;
161
+ }
162
+ }, [linkType]);
163
+ const rel = React6__namespace.useMemo(() => {
164
+ if (linkType === "external") {
165
+ return "noopener noreferrer";
166
+ }
167
+ return void 0;
168
+ }, [linkType]);
169
+ const isExternal = linkType === "external";
170
+ const isInternal = linkType === "internal";
171
+ const shouldUseRouter = isInternal && typeof normalizedHref === "string" && normalizedHref.startsWith("/");
172
+ const handleClick = React6__namespace.useCallback(
173
+ (event) => {
174
+ if (onClick) {
175
+ try {
176
+ onClick(event);
177
+ } catch (error) {
178
+ console.error("Error in user onClick handler:", error);
179
+ }
180
+ }
181
+ if (event.defaultPrevented) {
182
+ return;
183
+ }
184
+ if (shouldUseRouter && normalizedHref && event.button === 0 && // left-click only
185
+ !event.metaKey && !event.altKey && !event.ctrlKey && !event.shiftKey) {
186
+ if (typeof window !== "undefined") {
187
+ const handler = window.__opensiteNavigationHandler;
188
+ if (typeof handler === "function") {
189
+ try {
190
+ const handled = handler(normalizedHref, event.nativeEvent || event);
191
+ if (handled !== false) {
192
+ event.preventDefault();
193
+ }
194
+ } catch (error) {
195
+ console.error("Error in navigation handler:", error);
196
+ }
197
+ }
198
+ }
199
+ }
200
+ },
201
+ [onClick, shouldUseRouter, normalizedHref]
202
+ );
203
+ return {
204
+ linkType,
205
+ normalizedHref,
206
+ target,
207
+ rel,
208
+ isExternal,
209
+ isInternal,
210
+ shouldUseRouter,
211
+ handleClick
212
+ };
213
+ }
214
+ var baseStyles = [
215
+ // Layout
216
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap shrink-0",
217
+ // Typography - using CSS variables with sensible defaults
218
+ "font-[var(--button-font-family,inherit)]",
219
+ "font-[var(--button-font-weight,500)]",
220
+ "tracking-[var(--button-letter-spacing,0)]",
221
+ "leading-[var(--button-line-height,1.25)]",
222
+ "[text-transform:var(--button-text-transform,none)]",
223
+ "text-sm",
224
+ // Border radius
225
+ "rounded-[var(--button-radius,var(--radius,0.375rem))]",
226
+ // Smooth transition - using [transition:...] to set full shorthand property (not just transition-property)
227
+ "[transition:var(--button-transition,all_250ms_cubic-bezier(0.4,0,0.2,1))]",
228
+ // Box shadow (master level) - using [box-shadow:...] for complex multi-value shadows
229
+ "[box-shadow:var(--button-shadow,none)]",
230
+ "hover:[box-shadow:var(--button-shadow-hover,var(--button-shadow,none))]",
231
+ // Disabled state
232
+ "disabled:pointer-events-none disabled:opacity-50",
233
+ // SVG handling
234
+ "[&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 [&_svg]:shrink-0",
235
+ // Focus styles
236
+ "outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
237
+ // Invalid state
238
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive"
239
+ ].join(" ");
240
+ var buttonVariants = classVarianceAuthority.cva(baseStyles, {
241
+ variants: {
242
+ variant: {
243
+ // Default (Primary) variant - full customization
244
+ default: [
245
+ "bg-[var(--button-default-bg,hsl(var(--primary)))]",
246
+ "text-[var(--button-default-fg,hsl(var(--primary-foreground)))]",
247
+ "border-[length:var(--button-default-border-width,0px)]",
248
+ "border-[color:var(--button-default-border,transparent)]",
249
+ "[box-shadow:var(--button-default-shadow,var(--button-shadow,none))]",
250
+ "hover:bg-[var(--button-default-hover-bg,hsl(var(--primary)/0.9))]",
251
+ "hover:text-[var(--button-default-hover-fg,var(--button-default-fg,hsl(var(--primary-foreground))))]",
252
+ "hover:border-[color:var(--button-default-hover-border,var(--button-default-border,transparent))]",
253
+ "hover:[box-shadow:var(--button-default-shadow-hover,var(--button-shadow-hover,var(--button-default-shadow,var(--button-shadow,none))))]"
254
+ ].join(" "),
255
+ // Destructive variant - full customization
256
+ destructive: [
257
+ "bg-[var(--button-destructive-bg,hsl(var(--destructive)))]",
258
+ "text-[var(--button-destructive-fg,white)]",
259
+ "border-[length:var(--button-destructive-border-width,0px)]",
260
+ "border-[color:var(--button-destructive-border,transparent)]",
261
+ "[box-shadow:var(--button-destructive-shadow,var(--button-shadow,none))]",
262
+ "hover:bg-[var(--button-destructive-hover-bg,hsl(var(--destructive)/0.9))]",
263
+ "hover:text-[var(--button-destructive-hover-fg,var(--button-destructive-fg,white))]",
264
+ "hover:border-[color:var(--button-destructive-hover-border,var(--button-destructive-border,transparent))]",
265
+ "hover:[box-shadow:var(--button-destructive-shadow-hover,var(--button-shadow-hover,var(--button-destructive-shadow,var(--button-shadow,none))))]",
266
+ "focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40",
267
+ "dark:bg-destructive/60"
268
+ ].join(" "),
269
+ // Outline variant - full customization with proper border handling
270
+ outline: [
271
+ "bg-[var(--button-outline-bg,hsl(var(--background)))]",
272
+ "text-[var(--button-outline-fg,inherit)]",
273
+ "border-[length:var(--button-outline-border-width,1px)]",
274
+ "border-[color:var(--button-outline-border,hsl(var(--border)))]",
275
+ "[box-shadow:var(--button-outline-shadow,var(--button-shadow,0_1px_2px_0_rgb(0_0_0/0.05)))]",
276
+ "hover:bg-[var(--button-outline-hover-bg,hsl(var(--accent)))]",
277
+ "hover:text-[var(--button-outline-hover-fg,hsl(var(--accent-foreground)))]",
278
+ "hover:border-[color:var(--button-outline-hover-border,var(--button-outline-border,hsl(var(--border))))]",
279
+ "hover:[box-shadow:var(--button-outline-shadow-hover,var(--button-shadow-hover,var(--button-outline-shadow,var(--button-shadow,none))))]",
280
+ "dark:bg-input/30 dark:border-input dark:hover:bg-input/50"
281
+ ].join(" "),
282
+ // Secondary variant - full customization
283
+ secondary: [
284
+ "bg-[var(--button-secondary-bg,hsl(var(--secondary)))]",
285
+ "text-[var(--button-secondary-fg,hsl(var(--secondary-foreground)))]",
286
+ "border-[length:var(--button-secondary-border-width,0px)]",
287
+ "border-[color:var(--button-secondary-border,transparent)]",
288
+ "[box-shadow:var(--button-secondary-shadow,var(--button-shadow,none))]",
289
+ "hover:bg-[var(--button-secondary-hover-bg,hsl(var(--secondary)/0.8))]",
290
+ "hover:text-[var(--button-secondary-hover-fg,var(--button-secondary-fg,hsl(var(--secondary-foreground))))]",
291
+ "hover:border-[color:var(--button-secondary-hover-border,var(--button-secondary-border,transparent))]",
292
+ "hover:[box-shadow:var(--button-secondary-shadow-hover,var(--button-shadow-hover,var(--button-secondary-shadow,var(--button-shadow,none))))]"
293
+ ].join(" "),
294
+ // Ghost variant - full customization
295
+ ghost: [
296
+ "bg-[var(--button-ghost-bg,transparent)]",
297
+ "text-[var(--button-ghost-fg,inherit)]",
298
+ "border-[length:var(--button-ghost-border-width,0px)]",
299
+ "border-[color:var(--button-ghost-border,transparent)]",
300
+ "[box-shadow:var(--button-ghost-shadow,var(--button-shadow,none))]",
301
+ "hover:bg-[var(--button-ghost-hover-bg,hsl(var(--accent)))]",
302
+ "hover:text-[var(--button-ghost-hover-fg,hsl(var(--accent-foreground)))]",
303
+ "hover:border-[color:var(--button-ghost-hover-border,var(--button-ghost-border,transparent))]",
304
+ "hover:[box-shadow:var(--button-ghost-shadow-hover,var(--button-shadow-hover,var(--button-ghost-shadow,var(--button-shadow,none))))]",
305
+ "dark:hover:bg-accent/50"
306
+ ].join(" "),
307
+ // Link variant - full customization
308
+ link: [
309
+ "bg-[var(--button-link-bg,transparent)]",
310
+ "text-[var(--button-link-fg,hsl(var(--primary)))]",
311
+ "border-[length:var(--button-link-border-width,0px)]",
312
+ "border-[color:var(--button-link-border,transparent)]",
313
+ "[box-shadow:var(--button-link-shadow,none)]",
314
+ "hover:bg-[var(--button-link-hover-bg,transparent)]",
315
+ "hover:text-[var(--button-link-hover-fg,var(--button-link-fg,hsl(var(--primary))))]",
316
+ "hover:[box-shadow:var(--button-link-shadow-hover,none)]",
317
+ "underline-offset-4 hover:underline"
318
+ ].join(" ")
319
+ },
320
+ size: {
321
+ default: [
322
+ "h-[var(--button-height-md,2.25rem)]",
323
+ "px-[var(--button-padding-x-md,1rem)]",
324
+ "py-[var(--button-padding-y-md,0.5rem)]",
325
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
326
+ ].join(" "),
327
+ sm: [
328
+ "h-[var(--button-height-sm,2rem)]",
329
+ "px-[var(--button-padding-x-sm,0.75rem)]",
330
+ "py-[var(--button-padding-y-sm,0.25rem)]",
331
+ "gap-1.5",
332
+ "has-[>svg]:px-[calc(var(--button-padding-x-sm,0.75rem)*0.83)]"
333
+ ].join(" "),
334
+ md: [
335
+ "h-[var(--button-height-md,2.25rem)]",
336
+ "px-[var(--button-padding-x-md,1rem)]",
337
+ "py-[var(--button-padding-y-md,0.5rem)]",
338
+ "has-[>svg]:px-[calc(var(--button-padding-x-md,1rem)*0.75)]"
339
+ ].join(" "),
340
+ lg: [
341
+ "h-[var(--button-height-lg,2.5rem)]",
342
+ "px-[var(--button-padding-x-lg,1.5rem)]",
343
+ "py-[var(--button-padding-y-lg,0.5rem)]",
344
+ "has-[>svg]:px-[calc(var(--button-padding-x-lg,1.5rem)*0.67)]"
345
+ ].join(" "),
346
+ icon: "size-[var(--button-height-md,2.25rem)]",
347
+ "icon-sm": "size-[var(--button-height-sm,2rem)]",
348
+ "icon-lg": "size-[var(--button-height-lg,2.5rem)]"
349
+ }
350
+ },
351
+ defaultVariants: {
352
+ variant: "default",
353
+ size: "default"
354
+ }
355
+ });
356
+ var Pressable = React6__namespace.forwardRef(
357
+ ({
358
+ children,
359
+ className,
360
+ href,
361
+ onClick,
362
+ variant,
363
+ size,
364
+ asButton = false,
365
+ fallbackComponentType = "span",
366
+ componentType,
367
+ "aria-label": ariaLabel,
368
+ "aria-describedby": ariaDescribedby,
369
+ id,
370
+ ...props
371
+ }, ref) => {
372
+ const navigation = useNavigation({ href, onClick });
373
+ const {
374
+ normalizedHref,
375
+ target,
376
+ rel,
377
+ linkType,
378
+ isInternal,
379
+ handleClick
380
+ } = navigation;
381
+ const shouldRenderLink = normalizedHref && linkType !== "none";
382
+ const shouldRenderButton = !shouldRenderLink && onClick;
383
+ const effectiveComponentType = componentType || (shouldRenderLink ? "a" : shouldRenderButton ? "button" : fallbackComponentType);
384
+ const finalComponentType = isInternal && shouldRenderLink ? "a" : effectiveComponentType;
385
+ const shouldApplyButtonStyles = asButton || variant || size;
386
+ const combinedClassName = cn(
387
+ shouldApplyButtonStyles && buttonVariants({ variant, size }),
388
+ className
389
+ );
390
+ const dataProps = Object.fromEntries(
391
+ Object.entries(props).filter(([key]) => key.startsWith("data-"))
392
+ );
393
+ const buttonDataAttributes = shouldApplyButtonStyles ? {
394
+ "data-slot": "button",
395
+ "data-variant": variant ?? "default",
396
+ "data-size": size ?? "default"
397
+ } : {};
398
+ const commonProps = {
399
+ className: combinedClassName,
400
+ onClick: handleClick,
401
+ "aria-label": ariaLabel,
402
+ "aria-describedby": ariaDescribedby,
403
+ id,
404
+ ...dataProps,
405
+ ...buttonDataAttributes
406
+ };
407
+ if (finalComponentType === "a" && shouldRenderLink) {
408
+ return /* @__PURE__ */ jsxRuntime.jsx(
409
+ "a",
410
+ {
411
+ ref,
412
+ href: normalizedHref,
413
+ target,
414
+ rel,
415
+ ...commonProps,
416
+ ...props,
417
+ children
418
+ }
419
+ );
420
+ }
421
+ if (finalComponentType === "button") {
422
+ return /* @__PURE__ */ jsxRuntime.jsx(
423
+ "button",
424
+ {
425
+ ref,
426
+ type: props.type || "button",
427
+ ...commonProps,
428
+ ...props,
429
+ children
430
+ }
431
+ );
432
+ }
433
+ if (finalComponentType === "div") {
434
+ return /* @__PURE__ */ jsxRuntime.jsx(
435
+ "div",
436
+ {
437
+ ref,
438
+ ...commonProps,
439
+ children
440
+ }
441
+ );
442
+ }
443
+ return /* @__PURE__ */ jsxRuntime.jsx(
444
+ "span",
445
+ {
446
+ ref,
447
+ ...commonProps,
448
+ children
449
+ }
450
+ );
451
+ }
452
+ );
453
+ Pressable.displayName = "Pressable";
454
+ var svgCache = /* @__PURE__ */ new Map();
455
+ function DynamicIcon({
456
+ name,
457
+ size = 28,
458
+ color,
459
+ className,
460
+ alt
461
+ }) {
462
+ const [svgContent, setSvgContent] = React6__namespace.useState(null);
463
+ const [isLoading, setIsLoading] = React6__namespace.useState(true);
464
+ const [error, setError] = React6__namespace.useState(null);
465
+ const { url, iconName } = React6__namespace.useMemo(() => {
466
+ const separator = name.includes("/") ? "/" : ":";
467
+ const [prefix, iconName2] = name.split(separator);
468
+ const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName2}?format=svg&width=${size}&height=${size}`;
469
+ return {
470
+ url: baseUrl,
471
+ iconName: iconName2
472
+ };
473
+ }, [name, size]);
474
+ React6__namespace.useEffect(() => {
475
+ let isMounted = true;
476
+ const fetchSvg = async () => {
477
+ const cached = svgCache.get(url);
478
+ if (cached) {
479
+ if (isMounted) {
480
+ setSvgContent(cached);
481
+ setIsLoading(false);
482
+ }
483
+ return;
484
+ }
485
+ try {
486
+ setIsLoading(true);
487
+ setError(null);
488
+ const response = await fetch(url);
489
+ if (!response.ok) {
490
+ throw new Error(`Failed to fetch icon: ${response.status}`);
491
+ }
492
+ let svg = await response.text();
493
+ svg = processSvgForCurrentColor(svg);
494
+ svgCache.set(url, svg);
495
+ if (isMounted) {
496
+ setSvgContent(svg);
497
+ setIsLoading(false);
498
+ }
499
+ } catch (err) {
500
+ if (isMounted) {
501
+ setError(err instanceof Error ? err.message : "Failed to load icon");
502
+ setIsLoading(false);
503
+ }
504
+ }
505
+ };
506
+ fetchSvg();
507
+ return () => {
508
+ isMounted = false;
509
+ };
510
+ }, [url]);
511
+ if (isLoading) {
512
+ return /* @__PURE__ */ jsxRuntime.jsx(
513
+ "span",
514
+ {
515
+ className: cn("inline-block", className),
516
+ style: { width: size, height: size },
517
+ "aria-hidden": "true"
518
+ }
519
+ );
520
+ }
521
+ if (error || !svgContent) {
522
+ return /* @__PURE__ */ jsxRuntime.jsx(
523
+ "span",
524
+ {
525
+ className: cn("inline-block", className),
526
+ style: { width: size, height: size },
527
+ role: "img",
528
+ "aria-label": alt || iconName
529
+ }
530
+ );
531
+ }
532
+ return /* @__PURE__ */ jsxRuntime.jsx(
533
+ "span",
534
+ {
535
+ className: cn("inline-flex items-center justify-center", className),
536
+ style: {
537
+ width: size,
538
+ height: size,
539
+ color: color || "inherit"
540
+ },
541
+ role: "img",
542
+ "aria-label": alt || iconName,
543
+ dangerouslySetInnerHTML: { __html: svgContent }
544
+ }
545
+ );
546
+ }
547
+ function processSvgForCurrentColor(svg) {
548
+ let processed = svg;
549
+ processed = processed.replace(
550
+ /stroke=["'](#000000|#000|black)["']/gi,
551
+ 'stroke="currentColor"'
552
+ );
553
+ processed = processed.replace(
554
+ /fill=["'](#000000|#000|black)["']/gi,
555
+ 'fill="currentColor"'
556
+ );
557
+ return processed;
558
+ }
35
559
  var maxWidthStyles = {
36
560
  sm: "max-w-screen-sm",
37
561
  md: "max-w-screen-md",
@@ -41,7 +565,7 @@ var maxWidthStyles = {
41
565
  "4xl": "max-w-[1536px]",
42
566
  full: "max-w-full"
43
567
  };
44
- var Container = React3__namespace.default.forwardRef(
568
+ var Container = React6__namespace.default.forwardRef(
45
569
  ({ children, maxWidth = "xl", className, as = "div", ...props }, ref) => {
46
570
  const Component = as;
47
571
  return /* @__PURE__ */ jsxRuntime.jsx(
@@ -346,7 +870,7 @@ var spacingStyles = {
346
870
  };
347
871
  var predefinedSpacings = ["none", "sm", "md", "lg", "xl"];
348
872
  var isPredefinedSpacing = (spacing) => predefinedSpacings.includes(spacing);
349
- var Section = React3__namespace.default.forwardRef(
873
+ var Section = React6__namespace.default.forwardRef(
350
874
  ({
351
875
  id,
352
876
  title,
@@ -407,9 +931,9 @@ var Section = React3__namespace.default.forwardRef(
407
931
  }
408
932
  );
409
933
  Section.displayName = "Section";
410
- var ProgressSliderContext = React3__namespace.createContext(void 0);
934
+ var ProgressSliderContext = React6__namespace.createContext(void 0);
411
935
  function useProgressSliderContext() {
412
- const context = React3__namespace.useContext(ProgressSliderContext);
936
+ const context = React6__namespace.useContext(ProgressSliderContext);
413
937
  if (!context) {
414
938
  throw new Error(
415
939
  "useProgressSliderContext must be used within a ProgressSlider"
@@ -438,13 +962,13 @@ function SliderBtn({
438
962
  /* @__PURE__ */ jsxRuntime.jsx(
439
963
  "div",
440
964
  {
441
- className: "absolute inset-0 -z-10 max-h-full max-w-full overflow-hidden",
965
+ className: "absolute inset-0 -z-10 max-h-full max-w-full overflow-hidden rounded-lg",
442
966
  role: "progressbar",
443
967
  "aria-valuenow": active === value ? progress : 0,
444
968
  children: /* @__PURE__ */ jsxRuntime.jsx(
445
969
  "span",
446
970
  {
447
- className: cn("absolute left-0", progressBarClass),
971
+ className: cn("absolute left-0 rounded-b-lg", progressBarClass),
448
972
  style: {
449
973
  [vertical ? "height" : "width"]: active === value ? `${progress}%` : "0%"
450
974
  }
@@ -475,7 +999,7 @@ function CarouselProgressSlider({
475
999
  subheading,
476
1000
  slides,
477
1001
  slidesSlot,
478
- duration = 5e3,
1002
+ duration = 8e3,
479
1003
  fastDuration = 400,
480
1004
  vertical = false,
481
1005
  className,
@@ -491,25 +1015,38 @@ function CarouselProgressSlider({
491
1015
  pattern,
492
1016
  patternOpacity
493
1017
  }) {
494
- const [active, setActive] = React3__namespace.useState(slides?.[0]?.id ?? "");
495
- const [progress, setProgress] = React3__namespace.useState(0);
496
- const [isFastForward, setIsFastForward] = React3__namespace.useState(false);
497
- const frame = React3__namespace.useRef(0);
498
- const firstFrameTime = React3__namespace.useRef(performance.now());
499
- const targetValue = React3__namespace.useRef(null);
500
- const sliderValues = React3__namespace.useMemo(
1018
+ const [active, setActive] = React6__namespace.useState(slides?.[0]?.id ?? "");
1019
+ const [progress, setProgress] = React6__namespace.useState(0);
1020
+ const [isFastForward, setIsFastForward] = React6__namespace.useState(false);
1021
+ const [isPaused, setIsPaused] = React6__namespace.useState(false);
1022
+ const frame = React6__namespace.useRef(0);
1023
+ const firstFrameTime = React6__namespace.useRef(performance.now());
1024
+ const targetValue = React6__namespace.useRef(null);
1025
+ const pausedProgress = React6__namespace.useRef(0);
1026
+ const sliderValues = React6__namespace.useMemo(
501
1027
  () => slides?.map((slide) => slide.id),
502
1028
  [slides]
503
1029
  );
504
- React3__namespace.useEffect(() => {
505
- if ((sliderValues?.length ?? 0) > 0) {
1030
+ React6__namespace.useEffect(() => {
1031
+ if ((sliderValues?.length ?? 0) > 0 && !isPaused) {
506
1032
  firstFrameTime.current = performance.now();
1033
+ if (pausedProgress.current > 0) {
1034
+ setProgress(pausedProgress.current);
1035
+ pausedProgress.current = 0;
1036
+ }
507
1037
  frame.current = requestAnimationFrame(animate);
508
1038
  }
509
1039
  return () => {
510
1040
  cancelAnimationFrame(frame.current);
511
1041
  };
512
- }, [sliderValues, active, isFastForward]);
1042
+ }, [sliderValues, active, isFastForward, isPaused]);
1043
+ const togglePause = () => {
1044
+ if (!isPaused) {
1045
+ pausedProgress.current = progress;
1046
+ cancelAnimationFrame(frame.current);
1047
+ }
1048
+ setIsPaused(!isPaused);
1049
+ };
513
1050
  const animate = (now) => {
514
1051
  const currentDuration = isFastForward ? fastDuration : duration;
515
1052
  const elapsedTime = now - firstFrameTime.current;
@@ -561,26 +1098,40 @@ function CarouselProgressSlider({
561
1098
  pattern,
562
1099
  patternOpacity,
563
1100
  children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative", containerClassName), children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("grid gap-8 lg:grid-cols-2", contentClassName), children: [
564
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("relative min-h-[300px]", imageClassName), children: slidesSlot ? slidesSlot : slides?.map((slide) => /* @__PURE__ */ jsxRuntime.jsx(
565
- SliderWrapper,
566
- {
567
- value: slide.id,
568
- className: cn("absolute inset-0", slide.className),
569
- children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-video overflow-hidden rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
570
- img.Img,
571
- {
572
- src: slide.image,
573
- alt: typeof slide.title === "string" ? slide.title : `Slide ${slide.id}`,
574
- className: cn(
575
- "h-full w-full object-cover",
576
- slide.imageClassName
577
- ),
578
- optixFlowConfig
579
- }
580
- ) })
581
- },
582
- slide.id
583
- )) }),
1101
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("relative", imageClassName), children: [
1102
+ slidesSlot ? slidesSlot : slides?.map((slide) => /* @__PURE__ */ jsxRuntime.jsx(
1103
+ SliderWrapper,
1104
+ {
1105
+ value: slide.id,
1106
+ className: cn("", slide.className),
1107
+ children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "aspect-video overflow-hidden rounded-lg", children: /* @__PURE__ */ jsxRuntime.jsx(
1108
+ img.Img,
1109
+ {
1110
+ src: slide.image,
1111
+ alt: typeof slide.title === "string" ? slide.title : `Slide ${slide.id}`,
1112
+ className: cn(
1113
+ "h-full w-full object-cover",
1114
+ slide.imageClassName
1115
+ ),
1116
+ optixFlowConfig
1117
+ }
1118
+ ) })
1119
+ },
1120
+ slide.id
1121
+ )),
1122
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-4 flex justify-center lg:justify-start", children: /* @__PURE__ */ jsxRuntime.jsx(
1123
+ Pressable,
1124
+ {
1125
+ onClick: togglePause,
1126
+ asButton: true,
1127
+ variant: "outline",
1128
+ size: "icon",
1129
+ className: "flex h-10 w-10 items-center justify-center rounded-full",
1130
+ "aria-label": isPaused ? "Play" : "Pause",
1131
+ children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: isPaused ? "lucide/play" : "lucide/pause", size: 18 })
1132
+ }
1133
+ ) })
1134
+ ] }),
584
1135
  /* @__PURE__ */ jsxRuntime.jsx(
585
1136
  "div",
586
1137
  {