@sonordev/agency-site-kit 0.3.1 → 0.4.1
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.
- package/README.md +302 -0
- package/bin/setup.mjs +79 -0
- package/dist/{chunk-RXBK2EOE.js → chunk-4IES23GV.js} +3 -3
- package/dist/{chunk-RXBK2EOE.js.map → chunk-4IES23GV.js.map} +1 -1
- package/dist/{chunk-WNR4KNG4.cjs → chunk-5MEZU3OG.cjs} +4 -4
- package/dist/{chunk-WNR4KNG4.cjs.map → chunk-5MEZU3OG.cjs.map} +1 -1
- package/dist/chunk-7D4H36UF.js +13 -0
- package/dist/chunk-7D4H36UF.js.map +1 -0
- package/dist/chunk-7ZHRMWJU.cjs +306 -0
- package/dist/chunk-7ZHRMWJU.cjs.map +1 -0
- package/dist/chunk-CVT7I5BQ.js +71 -0
- package/dist/chunk-CVT7I5BQ.js.map +1 -0
- package/dist/chunk-HQGAX2LI.cjs +8 -0
- package/dist/chunk-HQGAX2LI.cjs.map +1 -0
- package/dist/chunk-K2BGTDSN.js +45 -0
- package/dist/chunk-K2BGTDSN.js.map +1 -0
- package/dist/chunk-KXLROAO5.js +291 -0
- package/dist/chunk-KXLROAO5.js.map +1 -0
- package/dist/chunk-L4EOQVRJ.js +6 -0
- package/dist/chunk-L4EOQVRJ.js.map +1 -0
- package/dist/chunk-NI4FVCLS.cjs +32 -0
- package/dist/chunk-NI4FVCLS.cjs.map +1 -0
- package/dist/chunk-NLGUPJZA.js +507 -0
- package/dist/chunk-NLGUPJZA.js.map +1 -0
- package/dist/chunk-QDXZ27R7.js +33 -0
- package/dist/chunk-QDXZ27R7.js.map +1 -0
- package/dist/chunk-QGSW43TM.cjs +538 -0
- package/dist/chunk-QGSW43TM.cjs.map +1 -0
- package/dist/{chunk-TVFXN6W6.js → chunk-QL6KVIFR.js} +8 -4
- package/dist/chunk-QL6KVIFR.js.map +1 -0
- package/dist/chunk-RWQ675N6.js +165 -0
- package/dist/chunk-RWQ675N6.js.map +1 -0
- package/dist/chunk-UXUU7FVT.cjs +171 -0
- package/dist/chunk-UXUU7FVT.cjs.map +1 -0
- package/dist/chunk-XJ434N7I.js +30 -0
- package/dist/chunk-XJ434N7I.js.map +1 -0
- package/dist/{chunk-EE75WRCM.cjs → chunk-XOWSXZYW.cjs} +8 -4
- package/dist/chunk-XOWSXZYW.cjs.map +1 -0
- package/dist/chunk-ZBPF7TLZ.cjs +16 -0
- package/dist/chunk-ZBPF7TLZ.cjs.map +1 -0
- package/dist/chunk-ZINKVJQU.js +67 -0
- package/dist/chunk-ZINKVJQU.js.map +1 -0
- package/dist/index.cjs +53 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -9
- package/dist/index.d.ts +17 -9
- package/dist/index.js +8 -9
- package/dist/index.js.map +1 -1
- package/dist/layout/index.cjs +3 -3
- package/dist/layout/index.d.cts +1 -1
- package/dist/layout/index.d.ts +1 -1
- package/dist/layout/index.js +2 -2
- package/dist/portfolio/client.cjs +376 -12
- package/dist/portfolio/client.cjs.map +1 -1
- package/dist/portfolio/client.d.cts +27 -85
- package/dist/portfolio/client.d.ts +27 -85
- package/dist/portfolio/client.js +182 -4
- package/dist/portfolio/client.js.map +1 -1
- package/dist/portfolio/components/PortfolioAnalytics.cjs +87 -0
- package/dist/portfolio/components/PortfolioAnalytics.cjs.map +1 -0
- package/dist/portfolio/components/PortfolioAnalytics.d.cts +5 -0
- package/dist/portfolio/components/PortfolioAnalytics.d.ts +5 -0
- package/dist/portfolio/components/PortfolioAnalytics.js +9 -0
- package/dist/portfolio/components/PortfolioAnalytics.js.map +1 -0
- package/dist/portfolio/components/PortfolioErrorFallback.cjs +65 -0
- package/dist/portfolio/components/PortfolioErrorFallback.cjs.map +1 -0
- package/dist/portfolio/components/PortfolioErrorFallback.d.cts +22 -0
- package/dist/portfolio/components/PortfolioErrorFallback.d.ts +22 -0
- package/dist/portfolio/components/PortfolioErrorFallback.js +9 -0
- package/dist/portfolio/components/PortfolioErrorFallback.js.map +1 -0
- package/dist/portfolio/components/PortfolioLeadCTA.cjs +91 -0
- package/dist/portfolio/components/PortfolioLeadCTA.cjs.map +1 -0
- package/dist/portfolio/components/PortfolioLeadCTA.d.cts +34 -0
- package/dist/portfolio/components/PortfolioLeadCTA.d.ts +34 -0
- package/dist/portfolio/components/PortfolioLeadCTA.js +9 -0
- package/dist/portfolio/components/PortfolioLeadCTA.js.map +1 -0
- package/dist/portfolio/index.cjs +43 -19
- package/dist/portfolio/index.d.cts +210 -7
- package/dist/portfolio/index.d.ts +210 -7
- package/dist/portfolio/index.js +7 -7
- package/dist/portfolio/primitives/AnimatedCounter.cjs +88 -0
- package/dist/portfolio/primitives/AnimatedCounter.cjs.map +1 -0
- package/dist/portfolio/primitives/AnimatedCounter.d.cts +22 -0
- package/dist/portfolio/primitives/AnimatedCounter.d.ts +22 -0
- package/dist/portfolio/primitives/AnimatedCounter.js +68 -0
- package/dist/portfolio/primitives/AnimatedCounter.js.map +1 -0
- package/dist/portfolio/primitives/AnnotatedImage.cjs +320 -0
- package/dist/portfolio/primitives/AnnotatedImage.cjs.map +1 -0
- package/dist/portfolio/primitives/AnnotatedImage.d.cts +18 -0
- package/dist/portfolio/primitives/AnnotatedImage.d.ts +18 -0
- package/dist/portfolio/primitives/AnnotatedImage.js +264 -0
- package/dist/portfolio/primitives/AnnotatedImage.js.map +1 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.cjs +348 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.cjs.map +1 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.d.cts +13 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.d.ts +13 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.js +292 -0
- package/dist/portfolio/primitives/BeforeAfterSlider.js.map +1 -0
- package/dist/portfolio/primitives/DeviceTrifolio.cjs +298 -0
- package/dist/portfolio/primitives/DeviceTrifolio.cjs.map +1 -0
- package/dist/portfolio/primitives/DeviceTrifolio.d.cts +14 -0
- package/dist/portfolio/primitives/DeviceTrifolio.d.ts +14 -0
- package/dist/portfolio/primitives/DeviceTrifolio.js +242 -0
- package/dist/portfolio/primitives/DeviceTrifolio.js.map +1 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.cjs +88 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.cjs.map +1 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.d.cts +10 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.d.ts +10 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.js +68 -0
- package/dist/portfolio/primitives/FrequencyBarSkeleton.js.map +1 -0
- package/dist/portfolio/primitives/GlassCard.cjs +45 -0
- package/dist/portfolio/primitives/GlassCard.cjs.map +1 -0
- package/dist/portfolio/primitives/GlassCard.d.cts +11 -0
- package/dist/portfolio/primitives/GlassCard.d.ts +11 -0
- package/dist/portfolio/primitives/GlassCard.js +43 -0
- package/dist/portfolio/primitives/GlassCard.js.map +1 -0
- package/dist/portfolio/primitives/MetricBadge.cjs +74 -0
- package/dist/portfolio/primitives/MetricBadge.cjs.map +1 -0
- package/dist/portfolio/primitives/MetricBadge.d.cts +9 -0
- package/dist/portfolio/primitives/MetricBadge.d.ts +9 -0
- package/dist/portfolio/primitives/MetricBadge.js +54 -0
- package/dist/portfolio/primitives/MetricBadge.js.map +1 -0
- package/dist/portfolio/primitives/SVGChart.cjs +316 -0
- package/dist/portfolio/primitives/SVGChart.cjs.map +1 -0
- package/dist/portfolio/primitives/SVGChart.d.cts +18 -0
- package/dist/portfolio/primitives/SVGChart.d.ts +18 -0
- package/dist/portfolio/primitives/SVGChart.js +296 -0
- package/dist/portfolio/primitives/SVGChart.js.map +1 -0
- package/dist/portfolio/primitives/ScrollReveal.cjs +97 -0
- package/dist/portfolio/primitives/ScrollReveal.cjs.map +1 -0
- package/dist/portfolio/primitives/ScrollReveal.d.cts +27 -0
- package/dist/portfolio/primitives/ScrollReveal.d.ts +27 -0
- package/dist/portfolio/primitives/ScrollReveal.js +77 -0
- package/dist/portfolio/primitives/ScrollReveal.js.map +1 -0
- package/dist/portfolio/primitives/SplitHeadline.cjs +107 -0
- package/dist/portfolio/primitives/SplitHeadline.cjs.map +1 -0
- package/dist/portfolio/primitives/SplitHeadline.d.cts +24 -0
- package/dist/portfolio/primitives/SplitHeadline.d.ts +24 -0
- package/dist/portfolio/primitives/SplitHeadline.js +87 -0
- package/dist/portfolio/primitives/SplitHeadline.js.map +1 -0
- package/dist/portfolio/sections/BeforeAfterSection.cjs +271 -0
- package/dist/portfolio/sections/BeforeAfterSection.cjs.map +1 -0
- package/dist/portfolio/sections/BeforeAfterSection.d.cts +9 -0
- package/dist/portfolio/sections/BeforeAfterSection.d.ts +9 -0
- package/dist/{BeforeAfterSection-6BHFLY4Y.js → portfolio/sections/BeforeAfterSection.js} +66 -27
- package/dist/portfolio/sections/BeforeAfterSection.js.map +1 -0
- package/dist/portfolio/sections/CTASection.cjs +123 -0
- package/dist/portfolio/sections/CTASection.cjs.map +1 -0
- package/dist/portfolio/sections/CTASection.d.cts +9 -0
- package/dist/portfolio/sections/CTASection.d.ts +9 -0
- package/dist/portfolio/sections/CTASection.js +93 -0
- package/dist/portfolio/sections/CTASection.js.map +1 -0
- package/dist/{ChallengesSection-IZ3DHECS.cjs → portfolio/sections/ChallengesSection.cjs} +13 -7
- package/dist/portfolio/sections/ChallengesSection.cjs.map +1 -0
- package/dist/portfolio/sections/ChallengesSection.d.cts +9 -0
- package/dist/portfolio/sections/ChallengesSection.d.ts +9 -0
- package/dist/{ChallengesSection-GEQGVSJN.js → portfolio/sections/ChallengesSection.js} +6 -5
- package/dist/portfolio/sections/ChallengesSection.js.map +1 -0
- package/dist/portfolio/sections/ConversionFunnelSection.cjs +263 -0
- package/dist/portfolio/sections/ConversionFunnelSection.cjs.map +1 -0
- package/dist/portfolio/sections/ConversionFunnelSection.d.cts +9 -0
- package/dist/portfolio/sections/ConversionFunnelSection.d.ts +9 -0
- package/dist/{ConversionFunnelSection-D3GE4NKE.js → portfolio/sections/ConversionFunnelSection.js} +60 -56
- package/dist/portfolio/sections/ConversionFunnelSection.js.map +1 -0
- package/dist/{DesignSystemSection-KXIQXITF.cjs → portfolio/sections/DesignSystemSection.cjs} +13 -7
- package/dist/portfolio/sections/DesignSystemSection.cjs.map +1 -0
- package/dist/portfolio/sections/DesignSystemSection.d.cts +32 -0
- package/dist/portfolio/sections/DesignSystemSection.d.ts +32 -0
- package/dist/{DesignSystemSection-2R5BRBGO.js → portfolio/sections/DesignSystemSection.js} +5 -4
- package/dist/portfolio/sections/DesignSystemSection.js.map +1 -0
- package/dist/{DetailsSection-TTUZAPZZ.cjs → portfolio/sections/DetailsSection.cjs} +12 -6
- package/dist/portfolio/sections/DetailsSection.cjs.map +1 -0
- package/dist/portfolio/sections/DetailsSection.d.cts +9 -0
- package/dist/portfolio/sections/DetailsSection.d.ts +9 -0
- package/dist/{DetailsSection-A6PZQUQL.js → portfolio/sections/DetailsSection.js} +5 -4
- package/dist/portfolio/sections/DetailsSection.js.map +1 -0
- package/dist/portfolio/sections/FeatureSpotlightSection.cjs +267 -0
- package/dist/portfolio/sections/FeatureSpotlightSection.cjs.map +1 -0
- package/dist/portfolio/sections/FeatureSpotlightSection.d.cts +9 -0
- package/dist/portfolio/sections/FeatureSpotlightSection.d.ts +9 -0
- package/dist/{FeatureSpotlightSection-B7P3JGNL.js → portfolio/sections/FeatureSpotlightSection.js} +39 -33
- package/dist/portfolio/sections/FeatureSpotlightSection.js.map +1 -0
- package/dist/portfolio/sections/GallerySection.cjs +321 -0
- package/dist/portfolio/sections/GallerySection.cjs.map +1 -0
- package/dist/portfolio/sections/GallerySection.d.cts +9 -0
- package/dist/portfolio/sections/GallerySection.d.ts +9 -0
- package/dist/{GallerySection-VMKORC47.js → portfolio/sections/GallerySection.js} +65 -18
- package/dist/portfolio/sections/GallerySection.js.map +1 -0
- package/dist/portfolio/sections/HeroSection.cjs +180 -0
- package/dist/portfolio/sections/HeroSection.cjs.map +1 -0
- package/dist/portfolio/sections/HeroSection.d.cts +15 -0
- package/dist/portfolio/sections/HeroSection.d.ts +15 -0
- package/dist/portfolio/sections/HeroSection.js +171 -0
- package/dist/portfolio/sections/HeroSection.js.map +1 -0
- package/dist/{MetricsTimelineSection-4L6DUHJ5.cjs → portfolio/sections/MetricsTimelineSection.cjs} +102 -61
- package/dist/portfolio/sections/MetricsTimelineSection.cjs.map +1 -0
- package/dist/portfolio/sections/MetricsTimelineSection.d.cts +9 -0
- package/dist/portfolio/sections/MetricsTimelineSection.d.ts +9 -0
- package/dist/{MetricsTimelineSection-6BT5GNFV.js → portfolio/sections/MetricsTimelineSection.js} +45 -29
- package/dist/portfolio/sections/MetricsTimelineSection.js.map +1 -0
- package/dist/portfolio/sections/PerformanceSection.cjs +336 -0
- package/dist/portfolio/sections/PerformanceSection.cjs.map +1 -0
- package/dist/portfolio/sections/PerformanceSection.d.cts +22 -0
- package/dist/portfolio/sections/PerformanceSection.d.ts +22 -0
- package/dist/{PerformanceSection-MGCEIXDX.js → portfolio/sections/PerformanceSection.js} +59 -104
- package/dist/portfolio/sections/PerformanceSection.js.map +1 -0
- package/dist/{ResultsSection-XLGMMQKY.cjs → portfolio/sections/ResultsSection.cjs} +23 -10
- package/dist/portfolio/sections/ResultsSection.cjs.map +1 -0
- package/dist/portfolio/sections/ResultsSection.d.cts +13 -0
- package/dist/portfolio/sections/ResultsSection.d.ts +13 -0
- package/dist/{ResultsSection-DFUJ5U6M.js → portfolio/sections/ResultsSection.js} +14 -7
- package/dist/portfolio/sections/ResultsSection.js.map +1 -0
- package/dist/portfolio/sections/ServicesSection.cjs +129 -0
- package/dist/portfolio/sections/ServicesSection.cjs.map +1 -0
- package/dist/portfolio/sections/ServicesSection.d.cts +9 -0
- package/dist/portfolio/sections/ServicesSection.d.ts +9 -0
- package/dist/portfolio/sections/ServicesSection.js +122 -0
- package/dist/portfolio/sections/ServicesSection.js.map +1 -0
- package/dist/{SiteArchitectureSection-J7BPBERH.cjs → portfolio/sections/SiteArchitectureSection.cjs} +117 -93
- package/dist/portfolio/sections/SiteArchitectureSection.cjs.map +1 -0
- package/dist/portfolio/sections/SiteArchitectureSection.d.cts +21 -0
- package/dist/portfolio/sections/SiteArchitectureSection.d.ts +21 -0
- package/dist/{SiteArchitectureSection-TE22OAWV.js → portfolio/sections/SiteArchitectureSection.js} +47 -48
- package/dist/portfolio/sections/SiteArchitectureSection.js.map +1 -0
- package/dist/{SpeedComparisonSection-EZKFQVGW.cjs → portfolio/sections/SpeedComparisonSection.cjs} +13 -7
- package/dist/portfolio/sections/SpeedComparisonSection.cjs.map +1 -0
- package/dist/portfolio/sections/SpeedComparisonSection.d.cts +20 -0
- package/dist/portfolio/sections/SpeedComparisonSection.d.ts +20 -0
- package/dist/{SpeedComparisonSection-Y3K7OFZQ.js → portfolio/sections/SpeedComparisonSection.js} +6 -5
- package/dist/portfolio/sections/SpeedComparisonSection.js.map +1 -0
- package/dist/portfolio/sections/StrategySection.cjs +233 -0
- package/dist/portfolio/sections/StrategySection.cjs.map +1 -0
- package/dist/portfolio/sections/StrategySection.d.cts +9 -0
- package/dist/portfolio/sections/StrategySection.d.ts +9 -0
- package/dist/{StrategySection-DI5RSCJU.js → portfolio/sections/StrategySection.js} +60 -26
- package/dist/portfolio/sections/StrategySection.js.map +1 -0
- package/dist/portfolio/sections/TeamSection.cjs +162 -0
- package/dist/portfolio/sections/TeamSection.cjs.map +1 -0
- package/dist/portfolio/sections/TeamSection.d.cts +9 -0
- package/dist/portfolio/sections/TeamSection.d.ts +9 -0
- package/dist/portfolio/sections/TeamSection.js +106 -0
- package/dist/portfolio/sections/TeamSection.js.map +1 -0
- package/dist/portfolio/sections/TechStackSection.cjs +106 -0
- package/dist/portfolio/sections/TechStackSection.cjs.map +1 -0
- package/dist/portfolio/sections/TechStackSection.d.cts +9 -0
- package/dist/portfolio/sections/TechStackSection.d.ts +9 -0
- package/dist/portfolio/sections/TechStackSection.js +99 -0
- package/dist/portfolio/sections/TechStackSection.js.map +1 -0
- package/dist/{TestimonialSection-XPTFUQIN.cjs → portfolio/sections/TestimonialSection.cjs} +47 -28
- package/dist/portfolio/sections/TestimonialSection.cjs.map +1 -0
- package/dist/portfolio/sections/TestimonialSection.d.cts +9 -0
- package/dist/portfolio/sections/TestimonialSection.d.ts +9 -0
- package/dist/{TestimonialSection-6RGSMXQB.js → portfolio/sections/TestimonialSection.js} +40 -27
- package/dist/portfolio/sections/TestimonialSection.js.map +1 -0
- package/dist/portfolio/sections/VideoSection.cjs +181 -0
- package/dist/portfolio/sections/VideoSection.cjs.map +1 -0
- package/dist/portfolio/sections/VideoSection.d.cts +9 -0
- package/dist/portfolio/sections/VideoSection.d.ts +9 -0
- package/dist/{VideoSection-4A2HC6K6.js → portfolio/sections/VideoSection.js} +21 -13
- package/dist/portfolio/sections/VideoSection.js.map +1 -0
- package/dist/portfolio/server.cjs +56 -144
- package/dist/portfolio/server.cjs.map +1 -1
- package/dist/portfolio/server.d.cts +157 -7
- package/dist/portfolio/server.d.ts +157 -7
- package/dist/portfolio/server.js +3 -142
- package/dist/portfolio/server.js.map +1 -1
- package/dist/preview.cjs +56 -0
- package/dist/preview.cjs.map +1 -0
- package/dist/preview.d.cts +31 -0
- package/dist/preview.d.ts +31 -0
- package/dist/preview.js +54 -0
- package/dist/preview.js.map +1 -0
- package/dist/{types-DL4t_Cfa.d.ts → types-0tVPy4pU.d.cts} +7 -1
- package/dist/{types-DL4t_Cfa.d.cts → types-0tVPy4pU.d.ts} +7 -1
- package/dist/types-D0nb6KAx.d.cts +299 -0
- package/dist/types-D0nb6KAx.d.ts +299 -0
- package/package.json +38 -14
- package/templates/api-portfolio-preview.ts +6 -0
- package/templates/api-revalidate.ts +5 -0
- package/templates/work-category-page.tsx +40 -0
- package/templates/work-error.tsx +3 -0
- package/templates/work-loading.tsx +1 -0
- package/templates/work-page.tsx +37 -0
- package/templates/work-slug-loading.tsx +1 -0
- package/templates/work-slug-page.tsx +41 -0
- package/dist/BeforeAfterSection-6BHFLY4Y.js.map +0 -1
- package/dist/BeforeAfterSection-JTORBR3A.cjs +0 -181
- package/dist/BeforeAfterSection-JTORBR3A.cjs.map +0 -1
- package/dist/CTASection-4JKLXEUF.cjs +0 -111
- package/dist/CTASection-4JKLXEUF.cjs.map +0 -1
- package/dist/CTASection-BJA72XIL.js +0 -106
- package/dist/CTASection-BJA72XIL.js.map +0 -1
- package/dist/ChallengesSection-GEQGVSJN.js.map +0 -1
- package/dist/ChallengesSection-IZ3DHECS.cjs.map +0 -1
- package/dist/ConversionFunnelSection-AUUSJ5HQ.cjs +0 -209
- package/dist/ConversionFunnelSection-AUUSJ5HQ.cjs.map +0 -1
- package/dist/ConversionFunnelSection-D3GE4NKE.js.map +0 -1
- package/dist/DesignSystemSection-2R5BRBGO.js.map +0 -1
- package/dist/DesignSystemSection-KXIQXITF.cjs.map +0 -1
- package/dist/DetailsSection-A6PZQUQL.js.map +0 -1
- package/dist/DetailsSection-TTUZAPZZ.cjs.map +0 -1
- package/dist/FeatureSpotlightSection-B7P3JGNL.js.map +0 -1
- package/dist/FeatureSpotlightSection-WRHXS7TU.cjs +0 -210
- package/dist/FeatureSpotlightSection-WRHXS7TU.cjs.map +0 -1
- package/dist/GallerySection-VMKORC47.js.map +0 -1
- package/dist/GallerySection-WJ4PQDBI.cjs +0 -219
- package/dist/GallerySection-WJ4PQDBI.cjs.map +0 -1
- package/dist/MetricsTimelineSection-4L6DUHJ5.cjs.map +0 -1
- package/dist/MetricsTimelineSection-6BT5GNFV.js.map +0 -1
- package/dist/PerformanceSection-24TVVFZA.cjs +0 -356
- package/dist/PerformanceSection-24TVVFZA.cjs.map +0 -1
- package/dist/PerformanceSection-MGCEIXDX.js.map +0 -1
- package/dist/ResultsSection-DFUJ5U6M.js.map +0 -1
- package/dist/ResultsSection-XLGMMQKY.cjs.map +0 -1
- package/dist/ServicesSection-D5V3Q4GR.js +0 -118
- package/dist/ServicesSection-D5V3Q4GR.js.map +0 -1
- package/dist/ServicesSection-WJMGK2MF.cjs +0 -120
- package/dist/ServicesSection-WJMGK2MF.cjs.map +0 -1
- package/dist/SiteArchitectureSection-J7BPBERH.cjs.map +0 -1
- package/dist/SiteArchitectureSection-TE22OAWV.js.map +0 -1
- package/dist/SpeedComparisonSection-EZKFQVGW.cjs.map +0 -1
- package/dist/SpeedComparisonSection-Y3K7OFZQ.js.map +0 -1
- package/dist/StrategySection-CJ7Y6OFQ.cjs +0 -174
- package/dist/StrategySection-CJ7Y6OFQ.cjs.map +0 -1
- package/dist/StrategySection-DI5RSCJU.js.map +0 -1
- package/dist/TeamSection-DZVSNZE6.cjs +0 -112
- package/dist/TeamSection-DZVSNZE6.cjs.map +0 -1
- package/dist/TeamSection-HGKFW6PQ.js +0 -107
- package/dist/TeamSection-HGKFW6PQ.js.map +0 -1
- package/dist/TechStackSection-2AQ7RGY3.js +0 -93
- package/dist/TechStackSection-2AQ7RGY3.js.map +0 -1
- package/dist/TechStackSection-VTNNZR5V.cjs +0 -95
- package/dist/TechStackSection-VTNNZR5V.cjs.map +0 -1
- package/dist/TestimonialSection-6RGSMXQB.js.map +0 -1
- package/dist/TestimonialSection-XPTFUQIN.cjs.map +0 -1
- package/dist/VideoSection-4A2HC6K6.js.map +0 -1
- package/dist/VideoSection-G3DFS7UH.cjs +0 -118
- package/dist/VideoSection-G3DFS7UH.cjs.map +0 -1
- package/dist/chunk-2Y4O3LWM.js +0 -53
- package/dist/chunk-2Y4O3LWM.js.map +0 -1
- package/dist/chunk-633UMQQH.cjs +0 -31
- package/dist/chunk-633UMQQH.cjs.map +0 -1
- package/dist/chunk-7CFFAKDM.js +0 -74
- package/dist/chunk-7CFFAKDM.js.map +0 -1
- package/dist/chunk-APG2QSMB.js +0 -131
- package/dist/chunk-APG2QSMB.js.map +0 -1
- package/dist/chunk-AVCH4LMG.cjs +0 -620
- package/dist/chunk-AVCH4LMG.cjs.map +0 -1
- package/dist/chunk-AYBUHRFS.js +0 -613
- package/dist/chunk-AYBUHRFS.js.map +0 -1
- package/dist/chunk-EE75WRCM.cjs.map +0 -1
- package/dist/chunk-IKBK7HYX.cjs +0 -79
- package/dist/chunk-IKBK7HYX.cjs.map +0 -1
- package/dist/chunk-KEOHORIH.cjs +0 -79
- package/dist/chunk-KEOHORIH.cjs.map +0 -1
- package/dist/chunk-OA5ZM4OA.cjs +0 -139
- package/dist/chunk-OA5ZM4OA.cjs.map +0 -1
- package/dist/chunk-TVFXN6W6.js.map +0 -1
- package/dist/chunk-VVTK4OL6.js +0 -28
- package/dist/chunk-VVTK4OL6.js.map +0 -1
- package/dist/chunk-XCKXHK44.js +0 -15
- package/dist/chunk-XCKXHK44.js.map +0 -1
- package/dist/chunk-XONXEFJY.cjs +0 -58
- package/dist/chunk-XONXEFJY.cjs.map +0 -1
- package/dist/chunk-XQNJED46.cjs +0 -19
- package/dist/chunk-XQNJED46.cjs.map +0 -1
- package/dist/chunk-YB4B3OMC.js +0 -74
- package/dist/chunk-YB4B3OMC.js.map +0 -1
- package/dist/portfolio/sections.cjs +0 -20
- package/dist/portfolio/sections.cjs.map +0 -1
- package/dist/portfolio/sections.d.cts +0 -42
- package/dist/portfolio/sections.d.ts +0 -42
- package/dist/portfolio/sections.js +0 -4
- package/dist/portfolio/sections.js.map +0 -1
|
@@ -1,12 +1,215 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { P as PortfolioItemFull } from '../types-
|
|
3
|
-
export {
|
|
4
|
-
export
|
|
5
|
-
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { P as PortfolioItemFull, M as MetricsDelta, x as PortfolioKPI, A as PortfolioResultItem } from '../types-0tVPy4pU.js';
|
|
3
|
+
export { j as MetricSource, k as MetricsSnapshot, l as PORTFOLIO_SECTION_TYPES, n as PortfolioBeforeAfterData, o as PortfolioCTAData, c as PortfolioChallengesData, r as PortfolioConversionFunnelData, d as PortfolioDetailsData, s as PortfolioFeatureSpotlightData, u as PortfolioGalleryData, e as PortfolioHeroData, w as PortfolioItem, b as PortfolioListResponse, z as PortfolioMetricsTimelineData, f as PortfolioResultsData, a as PortfolioSection, C as PortfolioSectionData, D as PortfolioSectionDataMap, E as PortfolioSectionType, F as PortfolioSeoData, g as PortfolioServicesData, H as PortfolioStrategyData, J as PortfolioTeamData, h as PortfolioTechStackData, i as PortfolioTestimonialData, N as PortfolioVideoData } from '../types-0tVPy4pU.js';
|
|
4
|
+
export * from '@sonordev/agency-site-kit/portfolio/client';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* PortfolioPage — Server component that renders a full portfolio case study.
|
|
8
|
+
*
|
|
9
|
+
* Maps each section in item.sections to the correct renderer component.
|
|
10
|
+
* All sections below the hero are loaded lazily via next/dynamic.
|
|
11
|
+
*
|
|
12
|
+
* NOTE: This file is intentionally NOT a client component — it is designed
|
|
13
|
+
* to be used as an RSC in a Next.js app directory page.
|
|
14
|
+
*
|
|
15
|
+
* Sections MUST be imported via `@sonordev/agency-site-kit/portfolio/sections/*`
|
|
16
|
+
* (self-referencing subpaths, external in tsup's server build), never via
|
|
17
|
+
* relative paths: a relative import would bundle the client sections into
|
|
18
|
+
* this server chunk, the 'use client' boundary would be lost, and the whole
|
|
19
|
+
* PortfolioItemFull would serialize into the RSC flight payload again.
|
|
20
|
+
*
|
|
21
|
+
* Whether a section is a server or client component is decided by the
|
|
22
|
+
* section file itself (its 'use client' directive; tsup routes it to the
|
|
23
|
+
* matching build). Server sections are imported statically — they ship zero
|
|
24
|
+
* client JS, so there is nothing to lazy-load. Client sections load through
|
|
25
|
+
* next/dynamic so each becomes its own browser chunk, fetched only when the
|
|
26
|
+
* case study actually renders that section type.
|
|
27
|
+
*/
|
|
6
28
|
|
|
7
29
|
interface PortfolioPageProps {
|
|
8
30
|
item: PortfolioItemFull;
|
|
9
31
|
}
|
|
10
|
-
declare function PortfolioPage({ item }: PortfolioPageProps):
|
|
32
|
+
declare function PortfolioPage({ item: rawItem }: PortfolioPageProps): React.JSX.Element;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* PortfolioSchema — JSON-LD structured data for a case study page.
|
|
36
|
+
*
|
|
37
|
+
* Server component: renders a single <script type="application/ld+json">
|
|
38
|
+
* describing the case study as a schema.org Article (there is no dedicated
|
|
39
|
+
* CaseStudy type; Article is what Google documents for editorial content).
|
|
40
|
+
* Ships zero client JS and adds rich-result eligibility + entity clarity for
|
|
41
|
+
* the agency's portfolio pages.
|
|
42
|
+
*
|
|
43
|
+
* Usage (in the case study page, next to <PortfolioPage />):
|
|
44
|
+
*
|
|
45
|
+
* ```tsx
|
|
46
|
+
* <PortfolioSchema
|
|
47
|
+
* item={item}
|
|
48
|
+
* url={`https://youragency.com/work/${item.slug}`}
|
|
49
|
+
* publisher={{ name: 'Your Agency', url: 'https://youragency.com' }}
|
|
50
|
+
* />
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* Only reads fields from the already-sanitized PortfolioItemFull (see
|
|
54
|
+
* getPortfolioItem / sanitize.ts) — never section internals.
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
interface PortfolioSchemaProps {
|
|
58
|
+
item: PortfolioItemFull;
|
|
59
|
+
/** Canonical URL of this case study page. */
|
|
60
|
+
url: string;
|
|
61
|
+
/** The agency, emitted as author + publisher Organization. */
|
|
62
|
+
publisher?: {
|
|
63
|
+
name: string;
|
|
64
|
+
url?: string;
|
|
65
|
+
/** Absolute URL to a logo image. */
|
|
66
|
+
logo?: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
declare function PortfolioSchema({ item, url, publisher }: PortfolioSchemaProps): React.JSX.Element;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* PortfolioIndex — turnkey, server-rendered /work index.
|
|
73
|
+
*
|
|
74
|
+
* Fetches items + categories and renders: category nav (real links —
|
|
75
|
+
* crawlable, no client state), the portfolio grid, pagination, and an
|
|
76
|
+
* intentional empty state. Ships zero component JS.
|
|
77
|
+
*
|
|
78
|
+
* Category filtering is route-based for SEO:
|
|
79
|
+
* /work → all items
|
|
80
|
+
* /work/category/[category] → one category (slug via slugifyCategory)
|
|
81
|
+
* Pagination via ?page= on either route.
|
|
82
|
+
*/
|
|
83
|
+
|
|
84
|
+
interface PortfolioIndexProps {
|
|
85
|
+
/** Route prefix for case studies + category links. Defaults to "/work". */
|
|
86
|
+
basePath?: string;
|
|
87
|
+
/** Active category SLUG (from the [category] route param). */
|
|
88
|
+
category?: string | null;
|
|
89
|
+
/** 1-based page number (from ?page=). */
|
|
90
|
+
page?: number;
|
|
91
|
+
/** Items per page. Defaults to 12. */
|
|
92
|
+
pageSize?: number;
|
|
93
|
+
/** Optional heading block rendered above the category nav. */
|
|
94
|
+
heading?: React.ReactNode;
|
|
95
|
+
/** Override the empty state. */
|
|
96
|
+
emptyState?: React.ReactNode;
|
|
97
|
+
/** Hide the category nav (e.g. single-category portfolios). */
|
|
98
|
+
showCategories?: boolean;
|
|
99
|
+
className?: string;
|
|
100
|
+
}
|
|
101
|
+
declare function PortfolioIndex({ basePath, category, page, pageSize, heading, emptyState, showCategories, className, }: PortfolioIndexProps): Promise<React.JSX.Element>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* MetricsProvenance — "these numbers are real" strip for case studies.
|
|
105
|
+
*
|
|
106
|
+
* Server component, zero JS. Renders the live metric deltas the platform
|
|
107
|
+
* computes from GSC/analytics baselines (MetricsDelta[]) plus the refresh
|
|
108
|
+
* timestamp — the trust signal a static agency portfolio can't fake.
|
|
109
|
+
*
|
|
110
|
+
* `direction` semantics come from the API: 'up' ALWAYS means "improved"
|
|
111
|
+
* (lower-is-better metrics like Avg Position/LCP are inverted server-side
|
|
112
|
+
* in metrics-baseline.service), so 'up' renders green. The ARROW glyph
|
|
113
|
+
* follows the raw numbers instead — an improved bounce rate shows a green ▼,
|
|
114
|
+
* never a green ▲ next to a value that visibly went down.
|
|
115
|
+
*
|
|
116
|
+
* Deltas pass through gateMetricsDeltas (curate-proof.ts) before display:
|
|
117
|
+
* raw platform deltas carry no editorial judgment, and this strip only
|
|
118
|
+
* exists to flatter — see that module for the policy.
|
|
119
|
+
*/
|
|
120
|
+
|
|
121
|
+
interface MetricsProvenanceProps {
|
|
122
|
+
deltas: MetricsDelta[];
|
|
123
|
+
/** ISO timestamp of the last live-metrics refresh. */
|
|
124
|
+
refreshedAt?: string | null;
|
|
125
|
+
}
|
|
126
|
+
declare function MetricsProvenance({ deltas, refreshedAt }: MetricsProvenanceProps): React.JSX.Element | null;
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* curate-proof — single source of truth for which proof points a public case
|
|
130
|
+
* study is allowed to display, and in what shape.
|
|
131
|
+
*
|
|
132
|
+
* Two classes of problem this guards against (both shipped to production on
|
|
133
|
+
* upforge.io before this existed):
|
|
134
|
+
*
|
|
135
|
+
* 1. Repetition: generated items arrive with four "100/100 Lighthouse X"
|
|
136
|
+
* KPIs in the hero, the same four restated as Results cards, AND a full
|
|
137
|
+
* Performance section with score gauges. One differentiator repeated
|
|
138
|
+
* twelve times per page stops differentiating. The Performance section is
|
|
139
|
+
* the one home for the Lighthouse breakdown; hero/results/cards collapse
|
|
140
|
+
* to at most one summary tile.
|
|
141
|
+
*
|
|
142
|
+
* 2. Self-harming live metrics: MetricsDelta rows are computed from raw
|
|
143
|
+
* analytics baselines with no editorial judgment — a bounce rate of
|
|
144
|
+
* "100 → 99.69" rendered as a green win. Marketing surfaces only show a
|
|
145
|
+
* delta when it's a genuinely flattering, meaningful story.
|
|
146
|
+
*
|
|
147
|
+
* Used by PortfolioPage (hero KPIs + results items), PortfolioCard (KPI
|
|
148
|
+
* preview), and MetricsProvenance (delta gate). Change display policy here,
|
|
149
|
+
* nowhere else.
|
|
150
|
+
*/
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Collapse per-category Lighthouse KPIs to at most one summary tile, business
|
|
154
|
+
* outcomes first. When every category is present at 100 the summary says so;
|
|
155
|
+
* otherwise the Performance category (the one buyers know) represents the set.
|
|
156
|
+
*/
|
|
157
|
+
declare function curateHeroKpis(kpis: PortfolioKPI[]): PortfolioKPI[];
|
|
158
|
+
/**
|
|
159
|
+
* Drop Results cards that restate Lighthouse scores when the case study also
|
|
160
|
+
* renders a Performance section (the score breakdown's single home).
|
|
161
|
+
*/
|
|
162
|
+
declare function curateResultItems(items: PortfolioResultItem[], hasPerformanceSection: boolean): PortfolioResultItem[];
|
|
163
|
+
/**
|
|
164
|
+
* Editorial gate for live metric deltas ("Verified from live analytics").
|
|
165
|
+
* `direction` comes from the platform with 'up' meaning IMPROVED (lower-is-
|
|
166
|
+
* better metrics are inverted server-side). A public case study only shows a
|
|
167
|
+
* delta when it's a win worth telling:
|
|
168
|
+
* - improved ('up'), never a regression or flat noise
|
|
169
|
+
* - moved by a double-digit relative margin (small drift isn't a story)
|
|
170
|
+
* - bounce-rate-style metrics must also be respectable in absolute terms —
|
|
171
|
+
* "100 → 99.69" is technically an improvement and still self-harm
|
|
172
|
+
*/
|
|
173
|
+
declare function gateMetricsDeltas(deltas: MetricsDelta[] | undefined | null): MetricsDelta[];
|
|
174
|
+
/**
|
|
175
|
+
* Apply the full display policy to a portfolio item before rendering.
|
|
176
|
+
* Pure — returns a new item; the fetched payload is never mutated.
|
|
177
|
+
*/
|
|
178
|
+
declare function curatePortfolioProof(item: PortfolioItemFull): PortfolioItemFull;
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Presentation formatters shared by portfolio components and consuming apps.
|
|
182
|
+
*/
|
|
183
|
+
/**
|
|
184
|
+
* Human label for a category slug. Single source of truth — HeroSection,
|
|
185
|
+
* PortfolioCard, and app-side filter chips all render categories through
|
|
186
|
+
* this. Underscores become spaces; hyphens are PRESERVED so compound slugs
|
|
187
|
+
* keep their canonical form ("e-commerce" → "E-Commerce", never "E Commerce"),
|
|
188
|
+
* and every word start (including after a hyphen) is capitalized.
|
|
189
|
+
*/
|
|
190
|
+
declare function formatCategoryLabel(slug: string | undefined | null): string;
|
|
191
|
+
/**
|
|
192
|
+
* Normalize a service tag's casing for display: the first character of each
|
|
193
|
+
* word is uppercased, the rest left alone (so "Next.js development" becomes
|
|
194
|
+
* "Next.js Development" and "Technical SEO implementation" keeps "SEO").
|
|
195
|
+
* Fixes per-item casing drift in stored tags without touching the data.
|
|
196
|
+
*/
|
|
197
|
+
declare function formatServiceTag(tag: string | undefined | null): string;
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Loading skeletons for portfolio routes — pure CSS pulse, server-safe,
|
|
201
|
+
* zero JS. Drop into `loading.tsx`:
|
|
202
|
+
*
|
|
203
|
+
* ```tsx
|
|
204
|
+
* export { PortfolioIndexSkeleton as default } from '@sonordev/agency-site-kit/portfolio';
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
|
|
208
|
+
/** Grid of pulsing card placeholders for the /work index. */
|
|
209
|
+
declare function PortfolioIndexSkeleton({ cards }: {
|
|
210
|
+
cards?: number;
|
|
211
|
+
}): React.JSX.Element;
|
|
212
|
+
/** Hero-shaped placeholder for /work/[slug] case study pages. */
|
|
213
|
+
declare function PortfolioPageSkeleton(): React.JSX.Element;
|
|
11
214
|
|
|
12
|
-
export { PortfolioItemFull, PortfolioPage };
|
|
215
|
+
export { MetricsDelta, MetricsProvenance, PortfolioIndex, type PortfolioIndexProps, PortfolioIndexSkeleton, PortfolioItemFull, PortfolioKPI, PortfolioPage, PortfolioPageSkeleton, PortfolioSchema, curateHeroKpis, curatePortfolioProof, curateResultItems, formatCategoryLabel, formatServiceTag, gateMetricsDeltas };
|
package/dist/portfolio/index.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { PORTFOLIO_SECTION_TYPES, PortfolioPage } from '../chunk-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import '../chunk-
|
|
7
|
-
import '../chunk-
|
|
1
|
+
export { PORTFOLIO_SECTION_TYPES, PortfolioIndex, PortfolioIndexSkeleton, PortfolioPage, PortfolioPageSkeleton, PortfolioSchema } from '../chunk-NLGUPJZA.js';
|
|
2
|
+
export { formatCategoryLabel, formatServiceTag } from '../chunk-7D4H36UF.js';
|
|
3
|
+
export { MetricsProvenance, curateHeroKpis, curatePortfolioProof, curateResultItems, gateMetricsDeltas } from '../chunk-RWQ675N6.js';
|
|
4
|
+
import '../chunk-KXLROAO5.js';
|
|
5
|
+
import '../chunk-N7TVKUYQ.js';
|
|
6
|
+
import '../chunk-L4EOQVRJ.js';
|
|
7
|
+
import '../chunk-QL6KVIFR.js';
|
|
8
8
|
//# sourceMappingURL=index.js.map
|
|
9
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
"use strict";
|
|
3
|
+
"use client";
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
21
|
+
|
|
22
|
+
// src/portfolio/components/primitives/AnimatedCounter.tsx
|
|
23
|
+
var AnimatedCounter_exports = {};
|
|
24
|
+
__export(AnimatedCounter_exports, {
|
|
25
|
+
default: () => AnimatedCounter
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(AnimatedCounter_exports);
|
|
28
|
+
var import_react = require("react");
|
|
29
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
30
|
+
function AnimatedCounter({
|
|
31
|
+
value,
|
|
32
|
+
suffix = "",
|
|
33
|
+
prefix = "",
|
|
34
|
+
duration = 2,
|
|
35
|
+
decimals = 0,
|
|
36
|
+
className,
|
|
37
|
+
once = true
|
|
38
|
+
}) {
|
|
39
|
+
const ref = (0, import_react.useRef)(null);
|
|
40
|
+
const format = (v) => `${prefix}${decimals > 0 ? v.toFixed(decimals) : Math.round(v).toLocaleString()}${suffix}`;
|
|
41
|
+
const [display, setDisplay] = (0, import_react.useState)(() => format(value));
|
|
42
|
+
(0, import_react.useEffect)(() => {
|
|
43
|
+
const el = ref.current;
|
|
44
|
+
if (!el) return;
|
|
45
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
46
|
+
setDisplay(format(value));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const rect = el.getBoundingClientRect();
|
|
50
|
+
if (rect.top < window.innerHeight && rect.bottom > 0) return;
|
|
51
|
+
let raf = 0;
|
|
52
|
+
let played = false;
|
|
53
|
+
const easeOut = (t) => 1 - Math.pow(1 - t, 2);
|
|
54
|
+
const run = () => {
|
|
55
|
+
const start = performance.now();
|
|
56
|
+
const ms = duration * 1e3;
|
|
57
|
+
const tick = (now) => {
|
|
58
|
+
const p = Math.min(1, (now - start) / ms);
|
|
59
|
+
setDisplay(format(value * easeOut(p)));
|
|
60
|
+
if (p < 1) raf = requestAnimationFrame(tick);
|
|
61
|
+
};
|
|
62
|
+
setDisplay(format(0));
|
|
63
|
+
raf = requestAnimationFrame(tick);
|
|
64
|
+
};
|
|
65
|
+
const io = new IntersectionObserver(
|
|
66
|
+
([entry]) => {
|
|
67
|
+
if (entry.isIntersecting) {
|
|
68
|
+
if (once && played) return;
|
|
69
|
+
played = true;
|
|
70
|
+
run();
|
|
71
|
+
if (once) io.disconnect();
|
|
72
|
+
} else if (!once && played) {
|
|
73
|
+
cancelAnimationFrame(raf);
|
|
74
|
+
setDisplay(format(0));
|
|
75
|
+
}
|
|
76
|
+
},
|
|
77
|
+
{ rootMargin: "0px 0px -15% 0px" }
|
|
78
|
+
// ≈ gsap 'top 85%'
|
|
79
|
+
);
|
|
80
|
+
io.observe(el);
|
|
81
|
+
return () => {
|
|
82
|
+
io.disconnect();
|
|
83
|
+
cancelAnimationFrame(raf);
|
|
84
|
+
};
|
|
85
|
+
}, [value, suffix, prefix, duration, decimals, once]);
|
|
86
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { ref, className, children: display });
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=AnimatedCounter.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/portfolio/components/primitives/AnimatedCounter.tsx"],"sourcesContent":["'use client';\n\nimport React, { useRef, useEffect, useState } from 'react';\n\ninterface AnimatedCounterProps {\n value: number;\n suffix?: string;\n prefix?: string;\n duration?: number;\n decimals?: number;\n className?: string;\n once?: boolean;\n}\n\n/**\n * Count-up metric — IntersectionObserver + requestAnimationFrame, no gsap.\n *\n * SSR renders the REAL final value (SEO, screen readers, and no-JS users see\n * the true number — the old gsap version served \"0\" in the HTML). The\n * count-up runs only for counters still below the viewport at hydration;\n * above-the-fold values stay put per the house LCP rule.\n */\nexport default function AnimatedCounter({\n value,\n suffix = '',\n prefix = '',\n duration = 2,\n decimals = 0,\n className,\n once = true,\n}: AnimatedCounterProps) {\n const ref = useRef<HTMLSpanElement>(null);\n const format = (v: number) =>\n `${prefix}${decimals > 0 ? v.toFixed(decimals) : Math.round(v).toLocaleString()}${suffix}`;\n const [display, setDisplay] = useState(() => format(value));\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {\n setDisplay(format(value));\n return;\n }\n\n // Already visible at hydration → keep the final value, no re-zeroing.\n const rect = el.getBoundingClientRect();\n if (rect.top < window.innerHeight && rect.bottom > 0) return;\n\n let raf = 0;\n let played = false;\n const easeOut = (t: number) => 1 - Math.pow(1 - t, 2); // ≈ power2.out\n\n const run = () => {\n const start = performance.now();\n const ms = duration * 1000;\n const tick = (now: number) => {\n const p = Math.min(1, (now - start) / ms);\n setDisplay(format(value * easeOut(p)));\n if (p < 1) raf = requestAnimationFrame(tick);\n };\n setDisplay(format(0));\n raf = requestAnimationFrame(tick);\n };\n\n const io = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n if (once && played) return;\n played = true;\n run();\n if (once) io.disconnect();\n } else if (!once && played) {\n cancelAnimationFrame(raf);\n setDisplay(format(0));\n }\n },\n { rootMargin: '0px 0px -15% 0px' }, // ≈ gsap 'top 85%'\n );\n io.observe(el);\n\n return () => {\n io.disconnect();\n cancelAnimationFrame(raf);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value, suffix, prefix, duration, decimals, once]);\n\n return (\n <span ref={ref} className={className}>\n {display}\n </span>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA,mBAAmD;AAsF/C;AAlEW,SAAR,gBAAiC;AAAA,EACtC;AAAA,EACA,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AAAA,EACX,WAAW;AAAA,EACX;AAAA,EACA,OAAO;AACT,GAAyB;AACvB,QAAM,UAAM,qBAAwB,IAAI;AACxC,QAAM,SAAS,CAAC,MACd,GAAG,MAAM,GAAG,WAAW,IAAI,EAAE,QAAQ,QAAQ,IAAI,KAAK,MAAM,CAAC,EAAE,eAAe,CAAC,GAAG,MAAM;AAC1F,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,MAAM,OAAO,KAAK,CAAC;AAE1D,8BAAU,MAAM;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,QAAI,OAAO,WAAW,kCAAkC,EAAE,SAAS;AACjE,iBAAW,OAAO,KAAK,CAAC;AACxB;AAAA,IACF;AAGA,UAAM,OAAO,GAAG,sBAAsB;AACtC,QAAI,KAAK,MAAM,OAAO,eAAe,KAAK,SAAS,EAAG;AAEtD,QAAI,MAAM;AACV,QAAI,SAAS;AACb,UAAM,UAAU,CAAC,MAAc,IAAI,KAAK,IAAI,IAAI,GAAG,CAAC;AAEpD,UAAM,MAAM,MAAM;AAChB,YAAM,QAAQ,YAAY,IAAI;AAC9B,YAAM,KAAK,WAAW;AACtB,YAAM,OAAO,CAAC,QAAgB;AAC5B,cAAM,IAAI,KAAK,IAAI,IAAI,MAAM,SAAS,EAAE;AACxC,mBAAW,OAAO,QAAQ,QAAQ,CAAC,CAAC,CAAC;AACrC,YAAI,IAAI,EAAG,OAAM,sBAAsB,IAAI;AAAA,MAC7C;AACA,iBAAW,OAAO,CAAC,CAAC;AACpB,YAAM,sBAAsB,IAAI;AAAA,IAClC;AAEA,UAAM,KAAK,IAAI;AAAA,MACb,CAAC,CAAC,KAAK,MAAM;AACX,YAAI,MAAM,gBAAgB;AACxB,cAAI,QAAQ,OAAQ;AACpB,mBAAS;AACT,cAAI;AACJ,cAAI,KAAM,IAAG,WAAW;AAAA,QAC1B,WAAW,CAAC,QAAQ,QAAQ;AAC1B,+BAAqB,GAAG;AACxB,qBAAW,OAAO,CAAC,CAAC;AAAA,QACtB;AAAA,MACF;AAAA,MACA,EAAE,YAAY,mBAAmB;AAAA;AAAA,IACnC;AACA,OAAG,QAAQ,EAAE;AAEb,WAAO,MAAM;AACX,SAAG,WAAW;AACd,2BAAqB,GAAG;AAAA,IAC1B;AAAA,EAEF,GAAG,CAAC,OAAO,QAAQ,QAAQ,UAAU,UAAU,IAAI,CAAC;AAEpD,SACE,4CAAC,UAAK,KAAU,WACb,mBACH;AAEJ;","names":[]}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface AnimatedCounterProps {
|
|
4
|
+
value: number;
|
|
5
|
+
suffix?: string;
|
|
6
|
+
prefix?: string;
|
|
7
|
+
duration?: number;
|
|
8
|
+
decimals?: number;
|
|
9
|
+
className?: string;
|
|
10
|
+
once?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Count-up metric — IntersectionObserver + requestAnimationFrame, no gsap.
|
|
14
|
+
*
|
|
15
|
+
* SSR renders the REAL final value (SEO, screen readers, and no-JS users see
|
|
16
|
+
* the true number — the old gsap version served "0" in the HTML). The
|
|
17
|
+
* count-up runs only for counters still below the viewport at hydration;
|
|
18
|
+
* above-the-fold values stay put per the house LCP rule.
|
|
19
|
+
*/
|
|
20
|
+
declare function AnimatedCounter({ value, suffix, prefix, duration, decimals, className, once, }: AnimatedCounterProps): React.JSX.Element;
|
|
21
|
+
|
|
22
|
+
export { AnimatedCounter as default };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
interface AnimatedCounterProps {
|
|
4
|
+
value: number;
|
|
5
|
+
suffix?: string;
|
|
6
|
+
prefix?: string;
|
|
7
|
+
duration?: number;
|
|
8
|
+
decimals?: number;
|
|
9
|
+
className?: string;
|
|
10
|
+
once?: boolean;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Count-up metric — IntersectionObserver + requestAnimationFrame, no gsap.
|
|
14
|
+
*
|
|
15
|
+
* SSR renders the REAL final value (SEO, screen readers, and no-JS users see
|
|
16
|
+
* the true number — the old gsap version served "0" in the HTML). The
|
|
17
|
+
* count-up runs only for counters still below the viewport at hydration;
|
|
18
|
+
* above-the-fold values stay put per the house LCP rule.
|
|
19
|
+
*/
|
|
20
|
+
declare function AnimatedCounter({ value, suffix, prefix, duration, decimals, className, once, }: AnimatedCounterProps): React.JSX.Element;
|
|
21
|
+
|
|
22
|
+
export { AnimatedCounter as default };
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
"use client";
|
|
3
|
+
|
|
4
|
+
// src/portfolio/components/primitives/AnimatedCounter.tsx
|
|
5
|
+
import { useRef, useEffect, useState } from "react";
|
|
6
|
+
import { jsx } from "react/jsx-runtime";
|
|
7
|
+
function AnimatedCounter({
|
|
8
|
+
value,
|
|
9
|
+
suffix = "",
|
|
10
|
+
prefix = "",
|
|
11
|
+
duration = 2,
|
|
12
|
+
decimals = 0,
|
|
13
|
+
className,
|
|
14
|
+
once = true
|
|
15
|
+
}) {
|
|
16
|
+
const ref = useRef(null);
|
|
17
|
+
const format = (v) => `${prefix}${decimals > 0 ? v.toFixed(decimals) : Math.round(v).toLocaleString()}${suffix}`;
|
|
18
|
+
const [display, setDisplay] = useState(() => format(value));
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const el = ref.current;
|
|
21
|
+
if (!el) return;
|
|
22
|
+
if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
|
23
|
+
setDisplay(format(value));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const rect = el.getBoundingClientRect();
|
|
27
|
+
if (rect.top < window.innerHeight && rect.bottom > 0) return;
|
|
28
|
+
let raf = 0;
|
|
29
|
+
let played = false;
|
|
30
|
+
const easeOut = (t) => 1 - Math.pow(1 - t, 2);
|
|
31
|
+
const run = () => {
|
|
32
|
+
const start = performance.now();
|
|
33
|
+
const ms = duration * 1e3;
|
|
34
|
+
const tick = (now) => {
|
|
35
|
+
const p = Math.min(1, (now - start) / ms);
|
|
36
|
+
setDisplay(format(value * easeOut(p)));
|
|
37
|
+
if (p < 1) raf = requestAnimationFrame(tick);
|
|
38
|
+
};
|
|
39
|
+
setDisplay(format(0));
|
|
40
|
+
raf = requestAnimationFrame(tick);
|
|
41
|
+
};
|
|
42
|
+
const io = new IntersectionObserver(
|
|
43
|
+
([entry]) => {
|
|
44
|
+
if (entry.isIntersecting) {
|
|
45
|
+
if (once && played) return;
|
|
46
|
+
played = true;
|
|
47
|
+
run();
|
|
48
|
+
if (once) io.disconnect();
|
|
49
|
+
} else if (!once && played) {
|
|
50
|
+
cancelAnimationFrame(raf);
|
|
51
|
+
setDisplay(format(0));
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{ rootMargin: "0px 0px -15% 0px" }
|
|
55
|
+
// ≈ gsap 'top 85%'
|
|
56
|
+
);
|
|
57
|
+
io.observe(el);
|
|
58
|
+
return () => {
|
|
59
|
+
io.disconnect();
|
|
60
|
+
cancelAnimationFrame(raf);
|
|
61
|
+
};
|
|
62
|
+
}, [value, suffix, prefix, duration, decimals, once]);
|
|
63
|
+
return /* @__PURE__ */ jsx("span", { ref, className, children: display });
|
|
64
|
+
}
|
|
65
|
+
export {
|
|
66
|
+
AnimatedCounter as default
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=AnimatedCounter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/portfolio/components/primitives/AnimatedCounter.tsx"],"sourcesContent":["'use client';\n\nimport React, { useRef, useEffect, useState } from 'react';\n\ninterface AnimatedCounterProps {\n value: number;\n suffix?: string;\n prefix?: string;\n duration?: number;\n decimals?: number;\n className?: string;\n once?: boolean;\n}\n\n/**\n * Count-up metric — IntersectionObserver + requestAnimationFrame, no gsap.\n *\n * SSR renders the REAL final value (SEO, screen readers, and no-JS users see\n * the true number — the old gsap version served \"0\" in the HTML). The\n * count-up runs only for counters still below the viewport at hydration;\n * above-the-fold values stay put per the house LCP rule.\n */\nexport default function AnimatedCounter({\n value,\n suffix = '',\n prefix = '',\n duration = 2,\n decimals = 0,\n className,\n once = true,\n}: AnimatedCounterProps) {\n const ref = useRef<HTMLSpanElement>(null);\n const format = (v: number) =>\n `${prefix}${decimals > 0 ? v.toFixed(decimals) : Math.round(v).toLocaleString()}${suffix}`;\n const [display, setDisplay] = useState(() => format(value));\n\n useEffect(() => {\n const el = ref.current;\n if (!el) return;\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {\n setDisplay(format(value));\n return;\n }\n\n // Already visible at hydration → keep the final value, no re-zeroing.\n const rect = el.getBoundingClientRect();\n if (rect.top < window.innerHeight && rect.bottom > 0) return;\n\n let raf = 0;\n let played = false;\n const easeOut = (t: number) => 1 - Math.pow(1 - t, 2); // ≈ power2.out\n\n const run = () => {\n const start = performance.now();\n const ms = duration * 1000;\n const tick = (now: number) => {\n const p = Math.min(1, (now - start) / ms);\n setDisplay(format(value * easeOut(p)));\n if (p < 1) raf = requestAnimationFrame(tick);\n };\n setDisplay(format(0));\n raf = requestAnimationFrame(tick);\n };\n\n const io = new IntersectionObserver(\n ([entry]) => {\n if (entry.isIntersecting) {\n if (once && played) return;\n played = true;\n run();\n if (once) io.disconnect();\n } else if (!once && played) {\n cancelAnimationFrame(raf);\n setDisplay(format(0));\n }\n },\n { rootMargin: '0px 0px -15% 0px' }, // ≈ gsap 'top 85%'\n );\n io.observe(el);\n\n return () => {\n io.disconnect();\n cancelAnimationFrame(raf);\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [value, suffix, prefix, duration, decimals, once]);\n\n return (\n <span ref={ref} className={className}>\n {display}\n </span>\n );\n}\n"],"mappings":";;;;AAEA,SAAgB,QAAQ,WAAW,gBAAgB;AAsF/C;AAlEW,SAAR,gBAAiC;AAAA,EACtC;AAAA,EACA,SAAS;AAAA,EACT,SAAS;AAAA,EACT,WAAW;AAAA,EACX,WAAW;AAAA,EACX;AAAA,EACA,OAAO;AACT,GAAyB;AACvB,QAAM,MAAM,OAAwB,IAAI;AACxC,QAAM,SAAS,CAAC,MACd,GAAG,MAAM,GAAG,WAAW,IAAI,EAAE,QAAQ,QAAQ,IAAI,KAAK,MAAM,CAAC,EAAE,eAAe,CAAC,GAAG,MAAM;AAC1F,QAAM,CAAC,SAAS,UAAU,IAAI,SAAS,MAAM,OAAO,KAAK,CAAC;AAE1D,YAAU,MAAM;AACd,UAAM,KAAK,IAAI;AACf,QAAI,CAAC,GAAI;AACT,QAAI,OAAO,WAAW,kCAAkC,EAAE,SAAS;AACjE,iBAAW,OAAO,KAAK,CAAC;AACxB;AAAA,IACF;AAGA,UAAM,OAAO,GAAG,sBAAsB;AACtC,QAAI,KAAK,MAAM,OAAO,eAAe,KAAK,SAAS,EAAG;AAEtD,QAAI,MAAM;AACV,QAAI,SAAS;AACb,UAAM,UAAU,CAAC,MAAc,IAAI,KAAK,IAAI,IAAI,GAAG,CAAC;AAEpD,UAAM,MAAM,MAAM;AAChB,YAAM,QAAQ,YAAY,IAAI;AAC9B,YAAM,KAAK,WAAW;AACtB,YAAM,OAAO,CAAC,QAAgB;AAC5B,cAAM,IAAI,KAAK,IAAI,IAAI,MAAM,SAAS,EAAE;AACxC,mBAAW,OAAO,QAAQ,QAAQ,CAAC,CAAC,CAAC;AACrC,YAAI,IAAI,EAAG,OAAM,sBAAsB,IAAI;AAAA,MAC7C;AACA,iBAAW,OAAO,CAAC,CAAC;AACpB,YAAM,sBAAsB,IAAI;AAAA,IAClC;AAEA,UAAM,KAAK,IAAI;AAAA,MACb,CAAC,CAAC,KAAK,MAAM;AACX,YAAI,MAAM,gBAAgB;AACxB,cAAI,QAAQ,OAAQ;AACpB,mBAAS;AACT,cAAI;AACJ,cAAI,KAAM,IAAG,WAAW;AAAA,QAC1B,WAAW,CAAC,QAAQ,QAAQ;AAC1B,+BAAqB,GAAG;AACxB,qBAAW,OAAO,CAAC,CAAC;AAAA,QACtB;AAAA,MACF;AAAA,MACA,EAAE,YAAY,mBAAmB;AAAA;AAAA,IACnC;AACA,OAAG,QAAQ,EAAE;AAEb,WAAO,MAAM;AACX,SAAG,WAAW;AACd,2BAAqB,GAAG;AAAA,IAC1B;AAAA,EAEF,GAAG,CAAC,OAAO,QAAQ,QAAQ,UAAU,UAAU,IAAI,CAAC;AAEpD,SACE,oBAAC,UAAK,KAAU,WACb,mBACH;AAEJ;","names":[]}
|