shared-features 0.1.8 → 0.1.9

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.
@@ -0,0 +1,25 @@
1
+ import { AdPlacement } from '../../types/campaigns';
2
+ export interface AdCarouselProps {
3
+ /** Ad placement (defaults to home_banner) */
4
+ placement?: AdPlacement;
5
+ /** Rotation interval in ms (default: 20000 = 20 seconds) */
6
+ rotationInterval?: number;
7
+ /** Maximum number of campaigns to fetch */
8
+ maxCampaigns?: number;
9
+ /** Custom CSS class */
10
+ className?: string;
11
+ /** Custom styles */
12
+ style?: React.CSSProperties;
13
+ }
14
+ /**
15
+ * AdCarousel - Slider for after hero sections
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * <AdCarousel />
20
+ * <AdCarousel placement="home_banner" rotationInterval={15000} />
21
+ * ```
22
+ */
23
+ export declare function AdCarousel({ placement, rotationInterval, maxCampaigns, className, style, }: AdCarouselProps): import("react/jsx-runtime").JSX.Element | null;
24
+ export default AdCarousel;
25
+ //# sourceMappingURL=AdCarousel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdCarousel.d.ts","sourceRoot":"","sources":["../../../src/components/ads/AdCarousel.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,uBAAuB,CAAC;AAE9E,MAAM,WAAW,eAAe;IAC9B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,EACzB,SAAyB,EACzB,gBAAwB,EACxB,YAAgB,EAChB,SAAS,EACT,KAAK,GACN,EAAE,eAAe,kDAkRjB;AAED,eAAe,UAAU,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { AdPlacement } from '../../types/campaigns';
2
+ export interface TopbarAdBannerProps {
3
+ /** Ad placement (defaults to topbar_banner) */
4
+ placement?: AdPlacement;
5
+ /** Rotation interval in ms (default: 20000 = 20 seconds) */
6
+ rotationInterval?: number;
7
+ /** Maximum number of campaigns to fetch */
8
+ maxCampaigns?: number;
9
+ /** Custom CSS class */
10
+ className?: string;
11
+ /** Custom styles */
12
+ style?: React.CSSProperties;
13
+ }
14
+ /**
15
+ * TopbarAdBanner - Compact carousel banner for top of site
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * <TopbarAdBanner />
20
+ * <TopbarAdBanner rotationInterval={15000} />
21
+ * ```
22
+ */
23
+ export declare function TopbarAdBanner({ placement, rotationInterval, maxCampaigns, className, style, }: TopbarAdBannerProps): import("react/jsx-runtime").JSX.Element | null;
24
+ export default TopbarAdBanner;
25
+ //# sourceMappingURL=TopbarAdBanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TopbarAdBanner.d.ts","sourceRoot":"","sources":["../../../src/components/ads/TopbarAdBanner.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAuB,MAAM,uBAAuB,CAAC;AAK9E,MAAM,WAAW,mBAAmB;IAClC,+CAA+C;IAC/C,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,4DAA4D;IAC5D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2CAA2C;IAC3C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oBAAoB;IACpB,KAAK,CAAC,EAAE,KAAK,CAAC,aAAa,CAAC;CAC7B;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,EAC7B,SAA2B,EAC3B,gBAAwB,EACxB,YAAgB,EAChB,SAAS,EACT,KAAK,GACN,EAAE,mBAAmB,kDA8PrB;AAED,eAAe,cAAc,CAAC"}
@@ -10,9 +10,13 @@ export { AdSlider } from './AdSlider';
10
10
  export { AdModal } from './AdModal';
11
11
  export { AdUpdateModal } from './AdUpdateModal';
12
12
  export { AdBanner } from './AdBanner';
13
+ export { TopbarAdBanner } from './TopbarAdBanner';
14
+ export { AdCarousel } from './AdCarousel';
13
15
  export type { AdSliderProps } from './AdSlider';
14
16
  export type { AdModalProps } from './AdModal';
15
17
  export type { AdUpdateModalProps } from './AdUpdateModal';
16
18
  export type { AdBannerProps } from './AdBanner';
19
+ export type { TopbarAdBannerProps } from './TopbarAdBanner';
20
+ export type { AdCarouselProps } from './AdCarousel';
17
21
  export * from './variants';
18
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/ads/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtC,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/components/ads/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAG1C,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAC9C,YAAY,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAChD,YAAY,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,YAAY,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAGpD,cAAc,YAAY,CAAC"}
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const index = require("../index-CLd2pR09.cjs");
3
+ const index = require("../index--8iNqKw6.cjs");
4
4
  exports.AdBanner = index.AdBanner;
5
+ exports.AdCarousel = index.AdCarousel;
5
6
  exports.AdModal = index.AdModal;
6
7
  exports.AdPanel = index.AdPanel;
7
8
  exports.AdSlider = index.AdSlider;
@@ -28,6 +29,7 @@ exports.SocialLinksBar = index.SocialLinksBar;
28
29
  exports.TaglineVariant = index.TaglineVariant;
29
30
  exports.TestimonialVariant = index.TestimonialVariant;
30
31
  exports.TestimonialsGrid = index.TestimonialsGrid;
32
+ exports.TopbarAdBanner = index.TopbarAdBanner;
31
33
  exports.VideoVariant = index.VideoVariant;
32
34
  exports.getLargePanelVariant = index.getLargePanelVariant;
33
35
  exports.getSmallPanelVariant = index.getSmallPanelVariant;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
@@ -1,34 +1,36 @@
1
- import { A, a, b, c, d, e, f, B, g, C, h, i, D, F, j, k, G, H, L, M, S, l, m, n, T, o, p, V, q, r } from "../index-WWndE5XT.js";
1
+ import { A, a, b, c, d, e, f, g, B, h, C, i, j, D, F, k, l, G, H, L, M, S, m, n, o, T, p, q, r, V, s, t } from "../index-gXNz1Cox.js";
2
2
  export {
3
3
  A as AdBanner,
4
- a as AdModal,
5
- b as AdPanel,
6
- c as AdSlider,
7
- d as AdUpdateModal,
8
- e as AddressCard,
9
- f as AnnouncementModal,
4
+ a as AdCarousel,
5
+ b as AdModal,
6
+ c as AdPanel,
7
+ d as AdSlider,
8
+ e as AdUpdateModal,
9
+ f as AddressCard,
10
+ g as AnnouncementModal,
10
11
  B as BroadcastBanner,
11
- g as BroadcastBanners,
12
+ h as BroadcastBanners,
12
13
  C as CardVariant,
13
- h as ComparisonVariant,
14
- i as ContactCard,
14
+ i as ComparisonVariant,
15
+ j as ContactCard,
15
16
  D as DeveloperCard,
16
17
  F as FeatureGridVariant,
17
- j as FeaturesVariant,
18
- k as FooterSection,
18
+ k as FeaturesVariant,
19
+ l as FooterSection,
19
20
  G as GradientVariant,
20
21
  H as HeroVariant,
21
22
  L as LARGE_PANEL_VARIANTS,
22
23
  M as MinimalVariant,
23
24
  S as SMALL_PANEL_VARIANTS,
24
- l as ServicesGrid,
25
- m as SkillsDisplay,
26
- n as SocialLinksBar,
25
+ m as ServicesGrid,
26
+ n as SkillsDisplay,
27
+ o as SocialLinksBar,
27
28
  T as TaglineVariant,
28
- o as TestimonialVariant,
29
- p as TestimonialsGrid,
29
+ p as TestimonialVariant,
30
+ q as TestimonialsGrid,
31
+ r as TopbarAdBanner,
30
32
  V as VideoVariant,
31
- q as getLargePanelVariant,
32
- r as getSmallPanelVariant
33
+ s as getLargePanelVariant,
34
+ t as getSmallPanelVariant
33
35
  };
34
36
  //# sourceMappingURL=index.js.map
@@ -1472,6 +1472,476 @@ function AdBanner({
1472
1472
  }
1473
1473
  );
1474
1474
  }
1475
+ const DISMISSED_TOPBAR_KEY = "sf_topbar_dismissed";
1476
+ function TopbarAdBanner({
1477
+ placement = "topbar_banner",
1478
+ rotationInterval = 2e4,
1479
+ maxCampaigns = 5,
1480
+ className,
1481
+ style
1482
+ }) {
1483
+ const [currentIndex, setCurrentIndex] = react.useState(0);
1484
+ const [isAnimating, setIsAnimating] = react.useState(false);
1485
+ const [isDismissed, setIsDismissed] = react.useState(false);
1486
+ const trackedImpressions = react.useRef(/* @__PURE__ */ new Set());
1487
+ const timerRef = react.useRef(null);
1488
+ react.useEffect(() => {
1489
+ try {
1490
+ const dismissed = sessionStorage.getItem(DISMISSED_TOPBAR_KEY);
1491
+ if (dismissed === "true") {
1492
+ setIsDismissed(true);
1493
+ }
1494
+ } catch {
1495
+ }
1496
+ }, []);
1497
+ const handleDismiss = react.useCallback(() => {
1498
+ setIsDismissed(true);
1499
+ try {
1500
+ sessionStorage.setItem(DISMISSED_TOPBAR_KEY, "true");
1501
+ } catch {
1502
+ }
1503
+ }, []);
1504
+ const {
1505
+ campaigns,
1506
+ loading,
1507
+ recordImpression,
1508
+ recordClick
1509
+ } = useCommonFeatures.useCampaigns({ placement, maxCampaigns });
1510
+ react.useEffect(() => {
1511
+ if (campaigns.length === 0) return;
1512
+ const campaign2 = campaigns[currentIndex];
1513
+ if (!campaign2 || trackedImpressions.current.has(campaign2.id)) return;
1514
+ trackedImpressions.current.add(campaign2.id);
1515
+ recordImpression(campaign2);
1516
+ }, [currentIndex, campaigns, recordImpression]);
1517
+ react.useEffect(() => {
1518
+ if (campaigns.length <= 1) return;
1519
+ timerRef.current = setInterval(() => {
1520
+ setIsAnimating(true);
1521
+ setTimeout(() => {
1522
+ setCurrentIndex((prev) => (prev + 1) % campaigns.length);
1523
+ setTimeout(() => setIsAnimating(false), 50);
1524
+ }, 200);
1525
+ }, rotationInterval);
1526
+ return () => {
1527
+ if (timerRef.current) clearInterval(timerRef.current);
1528
+ };
1529
+ }, [campaigns.length, rotationInterval]);
1530
+ const handleClick = react.useCallback(
1531
+ (campaign2) => {
1532
+ recordClick(campaign2);
1533
+ const targetUrl = campaign2.customCtaUrl || campaign2.product.url;
1534
+ window.open(targetUrl, "_blank");
1535
+ },
1536
+ [recordClick]
1537
+ );
1538
+ const goToPrev = react.useCallback(() => {
1539
+ if (timerRef.current) clearInterval(timerRef.current);
1540
+ setIsAnimating(true);
1541
+ setTimeout(() => {
1542
+ setCurrentIndex((prev) => (prev - 1 + campaigns.length) % campaigns.length);
1543
+ setTimeout(() => setIsAnimating(false), 50);
1544
+ }, 200);
1545
+ }, [campaigns.length]);
1546
+ const goToNext = react.useCallback(() => {
1547
+ if (timerRef.current) clearInterval(timerRef.current);
1548
+ setIsAnimating(true);
1549
+ setTimeout(() => {
1550
+ setCurrentIndex((prev) => (prev + 1) % campaigns.length);
1551
+ setTimeout(() => setIsAnimating(false), 50);
1552
+ }, 200);
1553
+ }, [campaigns.length]);
1554
+ if (loading || campaigns.length === 0 || isDismissed) return null;
1555
+ const campaign = campaigns[currentIndex];
1556
+ if (!campaign) return null;
1557
+ const { product } = campaign;
1558
+ const displayTitle = campaign.customTitle || product.name;
1559
+ const displayTagline = campaign.customTagline || product.tagline;
1560
+ const displayCta = campaign.customCta || "Learn More";
1561
+ const displayColor = campaign.customProductColor || product.color || "#3B82F6";
1562
+ const displayIcon = campaign.customIcon || product.icon64 || "";
1563
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1564
+ themes.Box,
1565
+ {
1566
+ className,
1567
+ style: {
1568
+ background: `linear-gradient(90deg, ${displayColor}10 0%, ${displayColor}18 50%, ${displayColor}10 100%)`,
1569
+ borderBottom: `2px solid ${displayColor}40`,
1570
+ position: "relative",
1571
+ maxHeight: 100,
1572
+ overflow: "hidden",
1573
+ ...style
1574
+ },
1575
+ children: [
1576
+ /* @__PURE__ */ jsxRuntime.jsx(themes.Box, { style: { height: 2, background: displayColor } }),
1577
+ /* @__PURE__ */ jsxRuntime.jsxs(
1578
+ themes.Flex,
1579
+ {
1580
+ align: "center",
1581
+ justify: "between",
1582
+ gap: "3",
1583
+ px: { initial: "3", sm: "4" },
1584
+ py: "2",
1585
+ style: {
1586
+ opacity: isAnimating ? 0 : 1,
1587
+ transform: isAnimating ? "translateY(-5px)" : "translateY(0)",
1588
+ transition: "all 0.2s ease-out",
1589
+ minHeight: 50,
1590
+ maxHeight: 70
1591
+ },
1592
+ children: [
1593
+ campaigns.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(themes.Flex, { gap: "1", display: { initial: "none", sm: "flex" }, children: /* @__PURE__ */ jsxRuntime.jsx(
1594
+ themes.IconButton,
1595
+ {
1596
+ size: "1",
1597
+ variant: "ghost",
1598
+ color: "gray",
1599
+ onClick: goToPrev,
1600
+ style: { cursor: "pointer" },
1601
+ "aria-label": "Previous ad",
1602
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 16 })
1603
+ }
1604
+ ) }),
1605
+ /* @__PURE__ */ jsxRuntime.jsxs(themes.Flex, { align: "center", gap: "3", style: { flex: 1, minWidth: 0 }, children: [
1606
+ /* @__PURE__ */ jsxRuntime.jsx(
1607
+ themes.Box,
1608
+ {
1609
+ style: {
1610
+ width: 40,
1611
+ height: 40,
1612
+ borderRadius: 8,
1613
+ background: `${displayColor}20`,
1614
+ border: `1px solid ${displayColor}40`,
1615
+ display: "flex",
1616
+ alignItems: "center",
1617
+ justifyContent: "center",
1618
+ flexShrink: 0
1619
+ },
1620
+ dangerouslySetInnerHTML: { __html: displayIcon }
1621
+ }
1622
+ ),
1623
+ /* @__PURE__ */ jsxRuntime.jsxs(themes.Flex, { direction: "column", gap: "0", style: { minWidth: 0, flex: 1 }, children: [
1624
+ /* @__PURE__ */ jsxRuntime.jsx(themes.Text, { size: "2", weight: "bold", style: { lineHeight: 1.2 }, children: displayTitle }),
1625
+ /* @__PURE__ */ jsxRuntime.jsx(
1626
+ themes.Text,
1627
+ {
1628
+ size: "1",
1629
+ color: "gray",
1630
+ style: {
1631
+ display: "-webkit-box",
1632
+ WebkitLineClamp: 1,
1633
+ WebkitBoxOrient: "vertical",
1634
+ overflow: "hidden"
1635
+ },
1636
+ children: displayTagline
1637
+ }
1638
+ )
1639
+ ] }),
1640
+ /* @__PURE__ */ jsxRuntime.jsxs(
1641
+ themes.Button,
1642
+ {
1643
+ size: "1",
1644
+ onClick: () => handleClick(campaign),
1645
+ style: {
1646
+ background: displayColor,
1647
+ color: "white",
1648
+ fontWeight: 600,
1649
+ fontSize: "12px",
1650
+ padding: "0 12px",
1651
+ height: 28,
1652
+ flexShrink: 0
1653
+ },
1654
+ children: [
1655
+ displayCta,
1656
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 12, style: { marginLeft: 4 } })
1657
+ ]
1658
+ }
1659
+ )
1660
+ ] }),
1661
+ /* @__PURE__ */ jsxRuntime.jsxs(themes.Flex, { gap: "1", align: "center", children: [
1662
+ campaigns.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(themes.Box, { display: { initial: "none", sm: "block" }, children: /* @__PURE__ */ jsxRuntime.jsx(
1663
+ themes.IconButton,
1664
+ {
1665
+ size: "1",
1666
+ variant: "ghost",
1667
+ color: "gray",
1668
+ onClick: goToNext,
1669
+ style: { cursor: "pointer" },
1670
+ "aria-label": "Next ad",
1671
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 16 })
1672
+ }
1673
+ ) }),
1674
+ /* @__PURE__ */ jsxRuntime.jsx(
1675
+ themes.IconButton,
1676
+ {
1677
+ size: "1",
1678
+ variant: "ghost",
1679
+ color: "gray",
1680
+ onClick: handleDismiss,
1681
+ style: { cursor: "pointer" },
1682
+ "aria-label": "Close banner",
1683
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { size: 16 })
1684
+ }
1685
+ )
1686
+ ] })
1687
+ ]
1688
+ }
1689
+ ),
1690
+ campaigns.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(themes.Flex, { justify: "center", gap: "1", pb: "1", display: { initial: "flex", sm: "none" }, children: campaigns.map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(
1691
+ themes.Box,
1692
+ {
1693
+ style: {
1694
+ width: 6,
1695
+ height: 6,
1696
+ borderRadius: "50%",
1697
+ background: i === currentIndex ? displayColor : "var(--gray-a5)",
1698
+ transition: "background 0.2s ease"
1699
+ }
1700
+ },
1701
+ i
1702
+ )) })
1703
+ ]
1704
+ }
1705
+ );
1706
+ }
1707
+ function AdCarousel({
1708
+ placement = "home_banner",
1709
+ rotationInterval = 2e4,
1710
+ maxCampaigns = 5,
1711
+ className,
1712
+ style
1713
+ }) {
1714
+ const [currentIndex, setCurrentIndex] = react.useState(0);
1715
+ const [isAnimating, setIsAnimating] = react.useState(false);
1716
+ const [progress, setProgress] = react.useState(0);
1717
+ const trackedImpressions = react.useRef(/* @__PURE__ */ new Set());
1718
+ const timerRef = react.useRef(null);
1719
+ const {
1720
+ campaigns,
1721
+ loading,
1722
+ recordImpression,
1723
+ recordClick
1724
+ } = useCommonFeatures.useCampaigns({ placement, maxCampaigns });
1725
+ react.useEffect(() => {
1726
+ if (campaigns.length === 0) return;
1727
+ const campaign2 = campaigns[currentIndex];
1728
+ if (!campaign2 || trackedImpressions.current.has(campaign2.id)) return;
1729
+ trackedImpressions.current.add(campaign2.id);
1730
+ recordImpression(campaign2);
1731
+ }, [currentIndex, campaigns, recordImpression]);
1732
+ react.useEffect(() => {
1733
+ if (campaigns.length <= 1) return;
1734
+ setProgress(0);
1735
+ const progressInterval = 50;
1736
+ const steps = rotationInterval / progressInterval;
1737
+ let currentStep = 0;
1738
+ timerRef.current = setInterval(() => {
1739
+ currentStep++;
1740
+ setProgress(currentStep / steps * 100);
1741
+ if (currentStep >= steps) {
1742
+ setIsAnimating(true);
1743
+ setTimeout(() => {
1744
+ setCurrentIndex((prev) => (prev + 1) % campaigns.length);
1745
+ setProgress(0);
1746
+ currentStep = 0;
1747
+ setTimeout(() => setIsAnimating(false), 50);
1748
+ }, 200);
1749
+ }
1750
+ }, progressInterval);
1751
+ return () => {
1752
+ if (timerRef.current) clearInterval(timerRef.current);
1753
+ };
1754
+ }, [campaigns.length, rotationInterval, currentIndex]);
1755
+ const handleClick = react.useCallback(
1756
+ (campaign2) => {
1757
+ recordClick(campaign2);
1758
+ const targetUrl = campaign2.customCtaUrl || campaign2.product.url;
1759
+ window.open(targetUrl, "_blank");
1760
+ },
1761
+ [recordClick]
1762
+ );
1763
+ const resetTimer = react.useCallback(() => {
1764
+ if (timerRef.current) clearInterval(timerRef.current);
1765
+ setProgress(0);
1766
+ }, []);
1767
+ const goToPrev = react.useCallback(() => {
1768
+ resetTimer();
1769
+ setIsAnimating(true);
1770
+ setTimeout(() => {
1771
+ setCurrentIndex((prev) => (prev - 1 + campaigns.length) % campaigns.length);
1772
+ setTimeout(() => setIsAnimating(false), 50);
1773
+ }, 200);
1774
+ }, [campaigns.length, resetTimer]);
1775
+ const goToNext = react.useCallback(() => {
1776
+ resetTimer();
1777
+ setIsAnimating(true);
1778
+ setTimeout(() => {
1779
+ setCurrentIndex((prev) => (prev + 1) % campaigns.length);
1780
+ setTimeout(() => setIsAnimating(false), 50);
1781
+ }, 200);
1782
+ }, [campaigns.length, resetTimer]);
1783
+ const goToSlide = react.useCallback((index) => {
1784
+ if (index === currentIndex) return;
1785
+ resetTimer();
1786
+ setIsAnimating(true);
1787
+ setTimeout(() => {
1788
+ setCurrentIndex(index);
1789
+ setTimeout(() => setIsAnimating(false), 50);
1790
+ }, 200);
1791
+ }, [currentIndex, resetTimer]);
1792
+ if (loading || campaigns.length === 0) return null;
1793
+ const campaign = campaigns[currentIndex];
1794
+ if (!campaign) return null;
1795
+ const { product } = campaign;
1796
+ const displayTitle = campaign.customTitle || product.name;
1797
+ const displayTagline = campaign.customTagline || product.tagline;
1798
+ const displayCta = campaign.customCta || "Learn More";
1799
+ const displayColor = campaign.customProductColor || product.color || "#3B82F6";
1800
+ const displayIcon = campaign.customIcon || product.icon64 || "";
1801
+ const displayFeatures = campaign.customFeatures || product.features || [];
1802
+ return /* @__PURE__ */ jsxRuntime.jsx(themes.Box, { className, style: { padding: "16px 0", ...style }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1803
+ themes.Card,
1804
+ {
1805
+ style: {
1806
+ background: `linear-gradient(135deg, ${displayColor}08 0%, ${displayColor}15 100%)`,
1807
+ border: `1px solid ${displayColor}25`,
1808
+ overflow: "hidden"
1809
+ },
1810
+ children: [
1811
+ campaigns.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(themes.Box, { style: { height: 3, background: "var(--gray-a3)", position: "relative" }, children: /* @__PURE__ */ jsxRuntime.jsx(
1812
+ themes.Box,
1813
+ {
1814
+ style: {
1815
+ position: "absolute",
1816
+ top: 0,
1817
+ left: 0,
1818
+ height: "100%",
1819
+ width: `${progress}%`,
1820
+ background: displayColor,
1821
+ transition: "width 50ms linear"
1822
+ }
1823
+ }
1824
+ ) }),
1825
+ /* @__PURE__ */ jsxRuntime.jsxs(themes.Box, { p: { initial: "3", sm: "4" }, children: [
1826
+ /* @__PURE__ */ jsxRuntime.jsxs(
1827
+ themes.Flex,
1828
+ {
1829
+ direction: { initial: "column", sm: "row" },
1830
+ align: "center",
1831
+ justify: "between",
1832
+ gap: "4",
1833
+ style: {
1834
+ opacity: isAnimating ? 0 : 1,
1835
+ transform: isAnimating ? "translateX(-10px)" : "translateX(0)",
1836
+ transition: "all 0.2s ease-out"
1837
+ },
1838
+ children: [
1839
+ campaigns.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(themes.Box, { display: { initial: "none", sm: "block" }, style: { flexShrink: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1840
+ themes.IconButton,
1841
+ {
1842
+ size: "2",
1843
+ variant: "ghost",
1844
+ color: "gray",
1845
+ onClick: goToPrev,
1846
+ style: { cursor: "pointer" },
1847
+ "aria-label": "Previous",
1848
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { size: 20 })
1849
+ }
1850
+ ) }),
1851
+ /* @__PURE__ */ jsxRuntime.jsxs(themes.Flex, { align: "center", gap: "4", style: { flex: 1, minWidth: 0 }, children: [
1852
+ /* @__PURE__ */ jsxRuntime.jsx(
1853
+ themes.Box,
1854
+ {
1855
+ style: {
1856
+ width: 64,
1857
+ height: 64,
1858
+ borderRadius: 12,
1859
+ background: `${displayColor}18`,
1860
+ border: `2px solid ${displayColor}35`,
1861
+ display: "flex",
1862
+ alignItems: "center",
1863
+ justifyContent: "center",
1864
+ flexShrink: 0
1865
+ },
1866
+ dangerouslySetInnerHTML: { __html: displayIcon }
1867
+ }
1868
+ ),
1869
+ /* @__PURE__ */ jsxRuntime.jsxs(themes.Flex, { direction: "column", gap: "1", style: { flex: 1, minWidth: 0 }, children: [
1870
+ /* @__PURE__ */ jsxRuntime.jsxs(themes.Flex, { align: "center", gap: "2", wrap: "wrap", children: [
1871
+ /* @__PURE__ */ jsxRuntime.jsx(themes.Text, { size: "4", weight: "bold", children: displayTitle }),
1872
+ /* @__PURE__ */ jsxRuntime.jsx(
1873
+ themes.Badge,
1874
+ {
1875
+ size: "1",
1876
+ style: {
1877
+ background: `${displayColor}20`,
1878
+ color: displayColor,
1879
+ fontWeight: 600
1880
+ },
1881
+ children: product.type === "extension" ? "Extension" : product.type === "android" ? "App" : "Web"
1882
+ }
1883
+ )
1884
+ ] }),
1885
+ /* @__PURE__ */ jsxRuntime.jsx(themes.Text, { size: "2", color: "gray", style: { lineHeight: 1.4 }, children: displayTagline }),
1886
+ /* @__PURE__ */ jsxRuntime.jsx(themes.Flex, { gap: "3", mt: "1", wrap: "wrap", display: { initial: "none", md: "flex" }, children: displayFeatures.slice(0, 3).map((feature, i) => /* @__PURE__ */ jsxRuntime.jsxs(themes.Flex, { align: "center", gap: "1", children: [
1887
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Check, { size: 14, color: displayColor, strokeWidth: 2.5 }),
1888
+ /* @__PURE__ */ jsxRuntime.jsx(themes.Text, { size: "1", color: "gray", children: feature })
1889
+ ] }, i)) })
1890
+ ] }),
1891
+ /* @__PURE__ */ jsxRuntime.jsxs(
1892
+ themes.Button,
1893
+ {
1894
+ size: { initial: "2", sm: "3" },
1895
+ onClick: () => handleClick(campaign),
1896
+ style: {
1897
+ background: displayColor,
1898
+ color: "white",
1899
+ fontWeight: 600,
1900
+ flexShrink: 0,
1901
+ boxShadow: `0 2px 8px ${displayColor}40`
1902
+ },
1903
+ children: [
1904
+ displayCta,
1905
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ExternalLink, { size: 16, style: { marginLeft: 6 } })
1906
+ ]
1907
+ }
1908
+ )
1909
+ ] }),
1910
+ campaigns.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(themes.Box, { display: { initial: "none", sm: "block" }, style: { flexShrink: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1911
+ themes.IconButton,
1912
+ {
1913
+ size: "2",
1914
+ variant: "ghost",
1915
+ color: "gray",
1916
+ onClick: goToNext,
1917
+ style: { cursor: "pointer" },
1918
+ "aria-label": "Next",
1919
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { size: 20 })
1920
+ }
1921
+ ) })
1922
+ ]
1923
+ }
1924
+ ),
1925
+ campaigns.length > 1 && /* @__PURE__ */ jsxRuntime.jsx(themes.Flex, { justify: "center", gap: "2", mt: "3", children: campaigns.map((c, i) => /* @__PURE__ */ jsxRuntime.jsx(
1926
+ themes.Box,
1927
+ {
1928
+ onClick: () => goToSlide(i),
1929
+ style: {
1930
+ width: 32,
1931
+ height: 4,
1932
+ borderRadius: 2,
1933
+ background: i === currentIndex ? c.product?.color || displayColor : "var(--gray-a4)",
1934
+ cursor: "pointer",
1935
+ transition: "background 0.2s ease"
1936
+ }
1937
+ },
1938
+ i
1939
+ )) })
1940
+ ] })
1941
+ ]
1942
+ }
1943
+ ) });
1944
+ }
1475
1945
  const TYPE_STYLES = {
1476
1946
  info: {
1477
1947
  icon: lucideReact.Info,
@@ -2082,6 +2552,7 @@ function FooterSection({ showContact = true, showSocialLinks = true, showAddress
2082
2552
  ] });
2083
2553
  }
2084
2554
  exports.AdBanner = AdBanner;
2555
+ exports.AdCarousel = AdCarousel;
2085
2556
  exports.AdModal = AdModal;
2086
2557
  exports.AdPanel = AdPanel;
2087
2558
  exports.AdSlider = AdSlider;
@@ -2108,7 +2579,8 @@ exports.SocialLinksBar = SocialLinksBar;
2108
2579
  exports.TaglineVariant = TaglineVariant;
2109
2580
  exports.TestimonialVariant = TestimonialVariant;
2110
2581
  exports.TestimonialsGrid = TestimonialsGrid;
2582
+ exports.TopbarAdBanner = TopbarAdBanner;
2111
2583
  exports.VideoVariant = VideoVariant;
2112
2584
  exports.getLargePanelVariant = getLargePanelVariant;
2113
2585
  exports.getSmallPanelVariant = getSmallPanelVariant;
2114
- //# sourceMappingURL=index-CLd2pR09.cjs.map
2586
+ //# sourceMappingURL=index--8iNqKw6.cjs.map