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.
@@ -1471,6 +1471,476 @@ function AdBanner({
1471
1471
  }
1472
1472
  );
1473
1473
  }
1474
+ const DISMISSED_TOPBAR_KEY = "sf_topbar_dismissed";
1475
+ function TopbarAdBanner({
1476
+ placement = "topbar_banner",
1477
+ rotationInterval = 2e4,
1478
+ maxCampaigns = 5,
1479
+ className,
1480
+ style
1481
+ }) {
1482
+ const [currentIndex, setCurrentIndex] = useState(0);
1483
+ const [isAnimating, setIsAnimating] = useState(false);
1484
+ const [isDismissed, setIsDismissed] = useState(false);
1485
+ const trackedImpressions = useRef(/* @__PURE__ */ new Set());
1486
+ const timerRef = useRef(null);
1487
+ useEffect(() => {
1488
+ try {
1489
+ const dismissed = sessionStorage.getItem(DISMISSED_TOPBAR_KEY);
1490
+ if (dismissed === "true") {
1491
+ setIsDismissed(true);
1492
+ }
1493
+ } catch {
1494
+ }
1495
+ }, []);
1496
+ const handleDismiss = useCallback(() => {
1497
+ setIsDismissed(true);
1498
+ try {
1499
+ sessionStorage.setItem(DISMISSED_TOPBAR_KEY, "true");
1500
+ } catch {
1501
+ }
1502
+ }, []);
1503
+ const {
1504
+ campaigns,
1505
+ loading,
1506
+ recordImpression,
1507
+ recordClick
1508
+ } = useCampaigns({ placement, maxCampaigns });
1509
+ useEffect(() => {
1510
+ if (campaigns.length === 0) return;
1511
+ const campaign2 = campaigns[currentIndex];
1512
+ if (!campaign2 || trackedImpressions.current.has(campaign2.id)) return;
1513
+ trackedImpressions.current.add(campaign2.id);
1514
+ recordImpression(campaign2);
1515
+ }, [currentIndex, campaigns, recordImpression]);
1516
+ useEffect(() => {
1517
+ if (campaigns.length <= 1) return;
1518
+ timerRef.current = setInterval(() => {
1519
+ setIsAnimating(true);
1520
+ setTimeout(() => {
1521
+ setCurrentIndex((prev) => (prev + 1) % campaigns.length);
1522
+ setTimeout(() => setIsAnimating(false), 50);
1523
+ }, 200);
1524
+ }, rotationInterval);
1525
+ return () => {
1526
+ if (timerRef.current) clearInterval(timerRef.current);
1527
+ };
1528
+ }, [campaigns.length, rotationInterval]);
1529
+ const handleClick = useCallback(
1530
+ (campaign2) => {
1531
+ recordClick(campaign2);
1532
+ const targetUrl = campaign2.customCtaUrl || campaign2.product.url;
1533
+ window.open(targetUrl, "_blank");
1534
+ },
1535
+ [recordClick]
1536
+ );
1537
+ const goToPrev = useCallback(() => {
1538
+ if (timerRef.current) clearInterval(timerRef.current);
1539
+ setIsAnimating(true);
1540
+ setTimeout(() => {
1541
+ setCurrentIndex((prev) => (prev - 1 + campaigns.length) % campaigns.length);
1542
+ setTimeout(() => setIsAnimating(false), 50);
1543
+ }, 200);
1544
+ }, [campaigns.length]);
1545
+ const goToNext = useCallback(() => {
1546
+ if (timerRef.current) clearInterval(timerRef.current);
1547
+ setIsAnimating(true);
1548
+ setTimeout(() => {
1549
+ setCurrentIndex((prev) => (prev + 1) % campaigns.length);
1550
+ setTimeout(() => setIsAnimating(false), 50);
1551
+ }, 200);
1552
+ }, [campaigns.length]);
1553
+ if (loading || campaigns.length === 0 || isDismissed) return null;
1554
+ const campaign = campaigns[currentIndex];
1555
+ if (!campaign) return null;
1556
+ const { product } = campaign;
1557
+ const displayTitle = campaign.customTitle || product.name;
1558
+ const displayTagline = campaign.customTagline || product.tagline;
1559
+ const displayCta = campaign.customCta || "Learn More";
1560
+ const displayColor = campaign.customProductColor || product.color || "#3B82F6";
1561
+ const displayIcon = campaign.customIcon || product.icon64 || "";
1562
+ return /* @__PURE__ */ jsxs(
1563
+ Box,
1564
+ {
1565
+ className,
1566
+ style: {
1567
+ background: `linear-gradient(90deg, ${displayColor}10 0%, ${displayColor}18 50%, ${displayColor}10 100%)`,
1568
+ borderBottom: `2px solid ${displayColor}40`,
1569
+ position: "relative",
1570
+ maxHeight: 100,
1571
+ overflow: "hidden",
1572
+ ...style
1573
+ },
1574
+ children: [
1575
+ /* @__PURE__ */ jsx(Box, { style: { height: 2, background: displayColor } }),
1576
+ /* @__PURE__ */ jsxs(
1577
+ Flex,
1578
+ {
1579
+ align: "center",
1580
+ justify: "between",
1581
+ gap: "3",
1582
+ px: { initial: "3", sm: "4" },
1583
+ py: "2",
1584
+ style: {
1585
+ opacity: isAnimating ? 0 : 1,
1586
+ transform: isAnimating ? "translateY(-5px)" : "translateY(0)",
1587
+ transition: "all 0.2s ease-out",
1588
+ minHeight: 50,
1589
+ maxHeight: 70
1590
+ },
1591
+ children: [
1592
+ campaigns.length > 1 && /* @__PURE__ */ jsx(Flex, { gap: "1", display: { initial: "none", sm: "flex" }, children: /* @__PURE__ */ jsx(
1593
+ IconButton,
1594
+ {
1595
+ size: "1",
1596
+ variant: "ghost",
1597
+ color: "gray",
1598
+ onClick: goToPrev,
1599
+ style: { cursor: "pointer" },
1600
+ "aria-label": "Previous ad",
1601
+ children: /* @__PURE__ */ jsx(ChevronLeft, { size: 16 })
1602
+ }
1603
+ ) }),
1604
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "3", style: { flex: 1, minWidth: 0 }, children: [
1605
+ /* @__PURE__ */ jsx(
1606
+ Box,
1607
+ {
1608
+ style: {
1609
+ width: 40,
1610
+ height: 40,
1611
+ borderRadius: 8,
1612
+ background: `${displayColor}20`,
1613
+ border: `1px solid ${displayColor}40`,
1614
+ display: "flex",
1615
+ alignItems: "center",
1616
+ justifyContent: "center",
1617
+ flexShrink: 0
1618
+ },
1619
+ dangerouslySetInnerHTML: { __html: displayIcon }
1620
+ }
1621
+ ),
1622
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "0", style: { minWidth: 0, flex: 1 }, children: [
1623
+ /* @__PURE__ */ jsx(Text, { size: "2", weight: "bold", style: { lineHeight: 1.2 }, children: displayTitle }),
1624
+ /* @__PURE__ */ jsx(
1625
+ Text,
1626
+ {
1627
+ size: "1",
1628
+ color: "gray",
1629
+ style: {
1630
+ display: "-webkit-box",
1631
+ WebkitLineClamp: 1,
1632
+ WebkitBoxOrient: "vertical",
1633
+ overflow: "hidden"
1634
+ },
1635
+ children: displayTagline
1636
+ }
1637
+ )
1638
+ ] }),
1639
+ /* @__PURE__ */ jsxs(
1640
+ Button,
1641
+ {
1642
+ size: "1",
1643
+ onClick: () => handleClick(campaign),
1644
+ style: {
1645
+ background: displayColor,
1646
+ color: "white",
1647
+ fontWeight: 600,
1648
+ fontSize: "12px",
1649
+ padding: "0 12px",
1650
+ height: 28,
1651
+ flexShrink: 0
1652
+ },
1653
+ children: [
1654
+ displayCta,
1655
+ /* @__PURE__ */ jsx(ExternalLink, { size: 12, style: { marginLeft: 4 } })
1656
+ ]
1657
+ }
1658
+ )
1659
+ ] }),
1660
+ /* @__PURE__ */ jsxs(Flex, { gap: "1", align: "center", children: [
1661
+ campaigns.length > 1 && /* @__PURE__ */ jsx(Box, { display: { initial: "none", sm: "block" }, children: /* @__PURE__ */ jsx(
1662
+ IconButton,
1663
+ {
1664
+ size: "1",
1665
+ variant: "ghost",
1666
+ color: "gray",
1667
+ onClick: goToNext,
1668
+ style: { cursor: "pointer" },
1669
+ "aria-label": "Next ad",
1670
+ children: /* @__PURE__ */ jsx(ChevronRight, { size: 16 })
1671
+ }
1672
+ ) }),
1673
+ /* @__PURE__ */ jsx(
1674
+ IconButton,
1675
+ {
1676
+ size: "1",
1677
+ variant: "ghost",
1678
+ color: "gray",
1679
+ onClick: handleDismiss,
1680
+ style: { cursor: "pointer" },
1681
+ "aria-label": "Close banner",
1682
+ children: /* @__PURE__ */ jsx(X, { size: 16 })
1683
+ }
1684
+ )
1685
+ ] })
1686
+ ]
1687
+ }
1688
+ ),
1689
+ campaigns.length > 1 && /* @__PURE__ */ jsx(Flex, { justify: "center", gap: "1", pb: "1", display: { initial: "flex", sm: "none" }, children: campaigns.map((_, i) => /* @__PURE__ */ jsx(
1690
+ Box,
1691
+ {
1692
+ style: {
1693
+ width: 6,
1694
+ height: 6,
1695
+ borderRadius: "50%",
1696
+ background: i === currentIndex ? displayColor : "var(--gray-a5)",
1697
+ transition: "background 0.2s ease"
1698
+ }
1699
+ },
1700
+ i
1701
+ )) })
1702
+ ]
1703
+ }
1704
+ );
1705
+ }
1706
+ function AdCarousel({
1707
+ placement = "home_banner",
1708
+ rotationInterval = 2e4,
1709
+ maxCampaigns = 5,
1710
+ className,
1711
+ style
1712
+ }) {
1713
+ const [currentIndex, setCurrentIndex] = useState(0);
1714
+ const [isAnimating, setIsAnimating] = useState(false);
1715
+ const [progress, setProgress] = useState(0);
1716
+ const trackedImpressions = useRef(/* @__PURE__ */ new Set());
1717
+ const timerRef = useRef(null);
1718
+ const {
1719
+ campaigns,
1720
+ loading,
1721
+ recordImpression,
1722
+ recordClick
1723
+ } = useCampaigns({ placement, maxCampaigns });
1724
+ useEffect(() => {
1725
+ if (campaigns.length === 0) return;
1726
+ const campaign2 = campaigns[currentIndex];
1727
+ if (!campaign2 || trackedImpressions.current.has(campaign2.id)) return;
1728
+ trackedImpressions.current.add(campaign2.id);
1729
+ recordImpression(campaign2);
1730
+ }, [currentIndex, campaigns, recordImpression]);
1731
+ useEffect(() => {
1732
+ if (campaigns.length <= 1) return;
1733
+ setProgress(0);
1734
+ const progressInterval = 50;
1735
+ const steps = rotationInterval / progressInterval;
1736
+ let currentStep = 0;
1737
+ timerRef.current = setInterval(() => {
1738
+ currentStep++;
1739
+ setProgress(currentStep / steps * 100);
1740
+ if (currentStep >= steps) {
1741
+ setIsAnimating(true);
1742
+ setTimeout(() => {
1743
+ setCurrentIndex((prev) => (prev + 1) % campaigns.length);
1744
+ setProgress(0);
1745
+ currentStep = 0;
1746
+ setTimeout(() => setIsAnimating(false), 50);
1747
+ }, 200);
1748
+ }
1749
+ }, progressInterval);
1750
+ return () => {
1751
+ if (timerRef.current) clearInterval(timerRef.current);
1752
+ };
1753
+ }, [campaigns.length, rotationInterval, currentIndex]);
1754
+ const handleClick = useCallback(
1755
+ (campaign2) => {
1756
+ recordClick(campaign2);
1757
+ const targetUrl = campaign2.customCtaUrl || campaign2.product.url;
1758
+ window.open(targetUrl, "_blank");
1759
+ },
1760
+ [recordClick]
1761
+ );
1762
+ const resetTimer = useCallback(() => {
1763
+ if (timerRef.current) clearInterval(timerRef.current);
1764
+ setProgress(0);
1765
+ }, []);
1766
+ const goToPrev = useCallback(() => {
1767
+ resetTimer();
1768
+ setIsAnimating(true);
1769
+ setTimeout(() => {
1770
+ setCurrentIndex((prev) => (prev - 1 + campaigns.length) % campaigns.length);
1771
+ setTimeout(() => setIsAnimating(false), 50);
1772
+ }, 200);
1773
+ }, [campaigns.length, resetTimer]);
1774
+ const goToNext = useCallback(() => {
1775
+ resetTimer();
1776
+ setIsAnimating(true);
1777
+ setTimeout(() => {
1778
+ setCurrentIndex((prev) => (prev + 1) % campaigns.length);
1779
+ setTimeout(() => setIsAnimating(false), 50);
1780
+ }, 200);
1781
+ }, [campaigns.length, resetTimer]);
1782
+ const goToSlide = useCallback((index) => {
1783
+ if (index === currentIndex) return;
1784
+ resetTimer();
1785
+ setIsAnimating(true);
1786
+ setTimeout(() => {
1787
+ setCurrentIndex(index);
1788
+ setTimeout(() => setIsAnimating(false), 50);
1789
+ }, 200);
1790
+ }, [currentIndex, resetTimer]);
1791
+ if (loading || campaigns.length === 0) return null;
1792
+ const campaign = campaigns[currentIndex];
1793
+ if (!campaign) return null;
1794
+ const { product } = campaign;
1795
+ const displayTitle = campaign.customTitle || product.name;
1796
+ const displayTagline = campaign.customTagline || product.tagline;
1797
+ const displayCta = campaign.customCta || "Learn More";
1798
+ const displayColor = campaign.customProductColor || product.color || "#3B82F6";
1799
+ const displayIcon = campaign.customIcon || product.icon64 || "";
1800
+ const displayFeatures = campaign.customFeatures || product.features || [];
1801
+ return /* @__PURE__ */ jsx(Box, { className, style: { padding: "16px 0", ...style }, children: /* @__PURE__ */ jsxs(
1802
+ Card,
1803
+ {
1804
+ style: {
1805
+ background: `linear-gradient(135deg, ${displayColor}08 0%, ${displayColor}15 100%)`,
1806
+ border: `1px solid ${displayColor}25`,
1807
+ overflow: "hidden"
1808
+ },
1809
+ children: [
1810
+ campaigns.length > 1 && /* @__PURE__ */ jsx(Box, { style: { height: 3, background: "var(--gray-a3)", position: "relative" }, children: /* @__PURE__ */ jsx(
1811
+ Box,
1812
+ {
1813
+ style: {
1814
+ position: "absolute",
1815
+ top: 0,
1816
+ left: 0,
1817
+ height: "100%",
1818
+ width: `${progress}%`,
1819
+ background: displayColor,
1820
+ transition: "width 50ms linear"
1821
+ }
1822
+ }
1823
+ ) }),
1824
+ /* @__PURE__ */ jsxs(Box, { p: { initial: "3", sm: "4" }, children: [
1825
+ /* @__PURE__ */ jsxs(
1826
+ Flex,
1827
+ {
1828
+ direction: { initial: "column", sm: "row" },
1829
+ align: "center",
1830
+ justify: "between",
1831
+ gap: "4",
1832
+ style: {
1833
+ opacity: isAnimating ? 0 : 1,
1834
+ transform: isAnimating ? "translateX(-10px)" : "translateX(0)",
1835
+ transition: "all 0.2s ease-out"
1836
+ },
1837
+ children: [
1838
+ campaigns.length > 1 && /* @__PURE__ */ jsx(Box, { display: { initial: "none", sm: "block" }, style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx(
1839
+ IconButton,
1840
+ {
1841
+ size: "2",
1842
+ variant: "ghost",
1843
+ color: "gray",
1844
+ onClick: goToPrev,
1845
+ style: { cursor: "pointer" },
1846
+ "aria-label": "Previous",
1847
+ children: /* @__PURE__ */ jsx(ChevronLeft, { size: 20 })
1848
+ }
1849
+ ) }),
1850
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "4", style: { flex: 1, minWidth: 0 }, children: [
1851
+ /* @__PURE__ */ jsx(
1852
+ Box,
1853
+ {
1854
+ style: {
1855
+ width: 64,
1856
+ height: 64,
1857
+ borderRadius: 12,
1858
+ background: `${displayColor}18`,
1859
+ border: `2px solid ${displayColor}35`,
1860
+ display: "flex",
1861
+ alignItems: "center",
1862
+ justifyContent: "center",
1863
+ flexShrink: 0
1864
+ },
1865
+ dangerouslySetInnerHTML: { __html: displayIcon }
1866
+ }
1867
+ ),
1868
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "1", style: { flex: 1, minWidth: 0 }, children: [
1869
+ /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "2", wrap: "wrap", children: [
1870
+ /* @__PURE__ */ jsx(Text, { size: "4", weight: "bold", children: displayTitle }),
1871
+ /* @__PURE__ */ jsx(
1872
+ Badge,
1873
+ {
1874
+ size: "1",
1875
+ style: {
1876
+ background: `${displayColor}20`,
1877
+ color: displayColor,
1878
+ fontWeight: 600
1879
+ },
1880
+ children: product.type === "extension" ? "Extension" : product.type === "android" ? "App" : "Web"
1881
+ }
1882
+ )
1883
+ ] }),
1884
+ /* @__PURE__ */ jsx(Text, { size: "2", color: "gray", style: { lineHeight: 1.4 }, children: displayTagline }),
1885
+ /* @__PURE__ */ jsx(Flex, { gap: "3", mt: "1", wrap: "wrap", display: { initial: "none", md: "flex" }, children: displayFeatures.slice(0, 3).map((feature, i) => /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "1", children: [
1886
+ /* @__PURE__ */ jsx(Check, { size: 14, color: displayColor, strokeWidth: 2.5 }),
1887
+ /* @__PURE__ */ jsx(Text, { size: "1", color: "gray", children: feature })
1888
+ ] }, i)) })
1889
+ ] }),
1890
+ /* @__PURE__ */ jsxs(
1891
+ Button,
1892
+ {
1893
+ size: { initial: "2", sm: "3" },
1894
+ onClick: () => handleClick(campaign),
1895
+ style: {
1896
+ background: displayColor,
1897
+ color: "white",
1898
+ fontWeight: 600,
1899
+ flexShrink: 0,
1900
+ boxShadow: `0 2px 8px ${displayColor}40`
1901
+ },
1902
+ children: [
1903
+ displayCta,
1904
+ /* @__PURE__ */ jsx(ExternalLink, { size: 16, style: { marginLeft: 6 } })
1905
+ ]
1906
+ }
1907
+ )
1908
+ ] }),
1909
+ campaigns.length > 1 && /* @__PURE__ */ jsx(Box, { display: { initial: "none", sm: "block" }, style: { flexShrink: 0 }, children: /* @__PURE__ */ jsx(
1910
+ IconButton,
1911
+ {
1912
+ size: "2",
1913
+ variant: "ghost",
1914
+ color: "gray",
1915
+ onClick: goToNext,
1916
+ style: { cursor: "pointer" },
1917
+ "aria-label": "Next",
1918
+ children: /* @__PURE__ */ jsx(ChevronRight, { size: 20 })
1919
+ }
1920
+ ) })
1921
+ ]
1922
+ }
1923
+ ),
1924
+ campaigns.length > 1 && /* @__PURE__ */ jsx(Flex, { justify: "center", gap: "2", mt: "3", children: campaigns.map((c, i) => /* @__PURE__ */ jsx(
1925
+ Box,
1926
+ {
1927
+ onClick: () => goToSlide(i),
1928
+ style: {
1929
+ width: 32,
1930
+ height: 4,
1931
+ borderRadius: 2,
1932
+ background: i === currentIndex ? c.product?.color || displayColor : "var(--gray-a4)",
1933
+ cursor: "pointer",
1934
+ transition: "background 0.2s ease"
1935
+ }
1936
+ },
1937
+ i
1938
+ )) })
1939
+ ] })
1940
+ ]
1941
+ }
1942
+ ) });
1943
+ }
1474
1944
  const TYPE_STYLES = {
1475
1945
  info: {
1476
1946
  icon: Info,
@@ -2093,23 +2563,25 @@ export {
2093
2563
  SMALL_PANEL_VARIANTS as S,
2094
2564
  TaglineVariant as T,
2095
2565
  VideoVariant as V,
2096
- AdModal as a,
2097
- AdPanel as b,
2098
- AdSlider as c,
2099
- AdUpdateModal as d,
2100
- AddressCard as e,
2101
- AnnouncementModal as f,
2102
- BroadcastBanners as g,
2103
- ComparisonVariant as h,
2104
- ContactCard as i,
2105
- FeaturesVariant as j,
2106
- FooterSection as k,
2107
- ServicesGrid as l,
2108
- SkillsDisplay as m,
2109
- SocialLinksBar as n,
2110
- TestimonialVariant as o,
2111
- TestimonialsGrid as p,
2112
- getLargePanelVariant as q,
2113
- getSmallPanelVariant as r
2566
+ AdCarousel as a,
2567
+ AdModal as b,
2568
+ AdPanel as c,
2569
+ AdSlider as d,
2570
+ AdUpdateModal as e,
2571
+ AddressCard as f,
2572
+ AnnouncementModal as g,
2573
+ BroadcastBanners as h,
2574
+ ComparisonVariant as i,
2575
+ ContactCard as j,
2576
+ FeaturesVariant as k,
2577
+ FooterSection as l,
2578
+ ServicesGrid as m,
2579
+ SkillsDisplay as n,
2580
+ SocialLinksBar as o,
2581
+ TestimonialVariant as p,
2582
+ TestimonialsGrid as q,
2583
+ TopbarAdBanner as r,
2584
+ getLargePanelVariant as s,
2585
+ getSmallPanelVariant as t
2114
2586
  };
2115
- //# sourceMappingURL=index-WWndE5XT.js.map
2587
+ //# sourceMappingURL=index-gXNz1Cox.js.map