@shipsite.dev/components 0.2.28 → 0.2.30

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.
Files changed (36) hide show
  1. package/dist/blog/BlogIndex.d.ts +20 -1
  2. package/dist/blog/BlogIndex.d.ts.map +1 -1
  3. package/dist/blog/BlogIndex.js +21 -3
  4. package/dist/blog/BlogIndex.js.map +1 -1
  5. package/dist/marketing/BentoGrid.d.ts +2 -1
  6. package/dist/marketing/BentoGrid.d.ts.map +1 -1
  7. package/dist/marketing/BentoGrid.js +27 -3
  8. package/dist/marketing/BentoGrid.js.map +1 -1
  9. package/dist/marketing/PricingSection.d.ts +1 -1
  10. package/dist/marketing/PricingSection.d.ts.map +1 -1
  11. package/dist/marketing/PricingSection.js +5 -18
  12. package/dist/marketing/PricingSection.js.map +1 -1
  13. package/dist/marketing/PricingSectionClient.d.ts +35 -0
  14. package/dist/marketing/PricingSectionClient.d.ts.map +1 -0
  15. package/dist/marketing/PricingSectionClient.js +21 -0
  16. package/dist/marketing/PricingSectionClient.js.map +1 -0
  17. package/dist/marketing/TabsSection.d.ts +2 -2
  18. package/dist/marketing/TabsSection.d.ts.map +1 -1
  19. package/dist/marketing/TabsSection.js +7 -14
  20. package/dist/marketing/TabsSection.js.map +1 -1
  21. package/dist/marketing/TabsSectionClient.d.ts +16 -0
  22. package/dist/marketing/TabsSectionClient.d.ts.map +1 -0
  23. package/dist/marketing/TabsSectionClient.js +14 -0
  24. package/dist/marketing/TabsSectionClient.js.map +1 -0
  25. package/dist/ui/tabs.d.ts +8 -0
  26. package/dist/ui/tabs.d.ts.map +1 -0
  27. package/dist/ui/tabs.js +16 -0
  28. package/dist/ui/tabs.js.map +1 -0
  29. package/package.json +5 -4
  30. package/src/blog/BlogIndex.tsx +104 -1
  31. package/src/marketing/BentoGrid.tsx +80 -3
  32. package/src/marketing/PricingSection.tsx +4 -94
  33. package/src/marketing/PricingSectionClient.tsx +138 -0
  34. package/src/marketing/TabsSection.tsx +7 -60
  35. package/src/marketing/TabsSectionClient.tsx +74 -0
  36. package/src/ui/tabs.tsx +55 -0
@@ -1,10 +1,29 @@
1
1
  import React from 'react';
2
+ import { type ImageSource } from '../ui/theme-image';
3
+ interface BlogArticleAuthor {
4
+ name: string;
5
+ role: string;
6
+ image: string;
7
+ }
8
+ interface BlogArticle {
9
+ slug: string;
10
+ title: string;
11
+ excerpt: string;
12
+ category?: string;
13
+ date: string;
14
+ image: ImageSource;
15
+ readingTime: number;
16
+ href: string;
17
+ featured?: boolean;
18
+ author?: BlogArticleAuthor;
19
+ }
2
20
  interface BlogIndexProps {
3
21
  id?: string;
4
22
  title?: string;
5
23
  description?: string;
24
+ articles?: BlogArticle[];
6
25
  children?: React.ReactNode;
7
26
  }
8
- export declare function BlogIndex({ id, title, description, children }: BlogIndexProps): import("react/jsx-runtime").JSX.Element;
27
+ export declare function BlogIndex({ id, title, description, articles, children }: BlogIndexProps): import("react/jsx-runtime").JSX.Element;
9
28
  export {};
10
29
  //# sourceMappingURL=BlogIndex.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"BlogIndex.d.ts","sourceRoot":"","sources":["../../src/blog/BlogIndex.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,UAAU,cAAc;IACtB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,cAAc,2CAc7E"}
1
+ {"version":3,"file":"BlogIndex.d.ts","sourceRoot":"","sources":["../../src/blog/BlogIndex.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEjE,UAAU,iBAAiB;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,WAAW,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B;AAED,UAAU,cAAc;IACtB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,WAAW,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AA+DD,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,cAAc,2CAmCvF"}
@@ -1,6 +1,24 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Section } from '../ui/section';
3
- export function BlogIndex({ id, title, description, children }) {
4
- return (_jsx(Section, { id: id, children: _jsxs("div", { className: "container-main", children: [(title || description) && (_jsxs("div", { className: "text-center mb-12", children: [title && _jsx("h2", { className: "text-3xl md:text-4xl font-bold text-foreground mb-4", children: title }), description && _jsx("p", { className: "text-lg text-muted-foreground", children: description })] })), children] }) }));
3
+ import { ThemeImage } from '../ui/theme-image';
4
+ function formatDate(dateStr) {
5
+ try {
6
+ return new Date(dateStr).toLocaleDateString(undefined, {
7
+ year: 'numeric',
8
+ month: 'short',
9
+ day: 'numeric',
10
+ });
11
+ }
12
+ catch {
13
+ return dateStr;
14
+ }
15
+ }
16
+ function ArticleCard({ article }) {
17
+ return (_jsxs("a", { href: article.href, className: "group block rounded-3xl p-2 -m-2 hover:bg-muted/50 hover:scale-[1.015] transition-all duration-300 ease-out focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring", children: [_jsx("div", { className: "aspect-[3/2] overflow-hidden rounded-2xl", children: _jsx(ThemeImage, { src: article.image, alt: article.title, className: "w-full h-full object-cover transition-transform duration-300 group-hover:scale-105" }) }), _jsxs("div", { className: "pt-4", children: [_jsxs("div", { className: "flex items-center justify-between text-sm text-muted-foreground mb-2", children: [article.category && (_jsx("span", { className: "font-medium text-primary", children: article.category })), article.date && _jsx("time", { dateTime: article.date, children: formatDate(article.date) })] }), _jsx("h3", { className: "text-lg font-semibold leading-snug text-foreground mb-2 group-hover:text-primary transition-colors", children: article.title }), _jsx("p", { className: "text-base text-muted-foreground line-clamp-2 mb-4", children: article.excerpt }), article.author && (_jsxs("div", { className: "flex items-center gap-3", children: [article.author.image && (_jsx("img", { src: article.author.image, alt: article.author.name, className: "w-10 h-10 rounded-full object-cover" })), _jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium text-foreground", children: article.author.name }), article.author.role && (_jsx("div", { className: "text-xs text-muted-foreground", children: article.author.role }))] })] }))] })] }, article.slug));
18
+ }
19
+ export function BlogIndex({ id, title, description, articles, children }) {
20
+ const featured = articles?.filter((a) => a.featured) ?? [];
21
+ const all = articles ?? [];
22
+ return (_jsx(Section, { id: id, children: _jsxs("div", { className: "container-main", children: [(title || description) && (_jsxs("div", { className: "text-center mb-12", children: [title && _jsx("h2", { className: "text-3xl md:text-4xl font-bold text-foreground mb-4", children: title }), description && _jsx("p", { className: "text-lg text-muted-foreground", children: description })] })), featured.length > 0 && (_jsxs(_Fragment, { children: [_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-8 mb-12", children: featured.map((article) => (_jsx(ArticleCard, { article: article }, article.slug))) }), _jsx("hr", { className: "border-border mb-12" }), _jsx("h3", { className: "text-xl font-semibold text-foreground mb-8", children: "All Articles" })] })), all.length > 0 && (_jsx("div", { className: "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8", children: all.map((article) => (_jsx(ArticleCard, { article: article }, article.slug))) })), children] }) }));
5
23
  }
6
24
  //# sourceMappingURL=BlogIndex.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BlogIndex.js","sourceRoot":"","sources":["../../src/blog/BlogIndex.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AASxC,MAAM,UAAU,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAkB;IAC5E,OAAO,CACL,KAAC,OAAO,IAAC,EAAE,EAAE,EAAE,YACb,eAAK,SAAS,EAAC,gBAAgB,aAC5B,CAAC,KAAK,IAAI,WAAW,CAAC,IAAI,CACzB,eAAK,SAAS,EAAC,mBAAmB,aAC/B,KAAK,IAAI,aAAI,SAAS,EAAC,qDAAqD,YAAE,KAAK,GAAM,EACzF,WAAW,IAAI,YAAG,SAAS,EAAC,+BAA+B,YAAE,WAAW,GAAK,IAC1E,CACP,EACA,QAAQ,IACL,GACE,CACX,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"BlogIndex.js","sourceRoot":"","sources":["../../src/blog/BlogIndex.tsx"],"names":[],"mappings":"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"}
@@ -4,10 +4,11 @@ interface BentoItemProps {
4
4
  title: string;
5
5
  description?: string;
6
6
  image?: ImageSource;
7
+ visual?: string;
7
8
  span?: 1 | 2;
8
9
  children?: React.ReactNode;
9
10
  }
10
- export declare function BentoItem({ title, description, image, span, children }: BentoItemProps): import("react/jsx-runtime").JSX.Element;
11
+ export declare function BentoItem({ title, description, image, visual, span, children }: BentoItemProps): import("react/jsx-runtime").JSX.Element;
11
12
  interface BentoGridProps {
12
13
  id?: string;
13
14
  title?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"BentoGrid.d.ts","sourceRoot":"","sources":["../../src/marketing/BentoGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEjE,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,IAAQ,EAAE,QAAQ,EAAE,EAAE,cAAc,2CAgB1F;AAED,UAAU,cAAc;IACtB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,cAAc,2CAc7E"}
1
+ {"version":3,"file":"BentoGrid.d.ts","sourceRoot":"","sources":["../../src/marketing/BentoGrid.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAkEjE,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;IACb,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,wBAAgB,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,IAAQ,EAAE,QAAQ,EAAE,EAAE,cAAc,2CAwBlG;AAMD,UAAU,cAAc;IACtB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,cAAc,2CAc7E"}
@@ -2,10 +2,34 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Section } from '../ui/section';
3
3
  import { cn } from '../lib/utils';
4
4
  import { ThemeImage } from '../ui/theme-image';
5
- export function BentoItem({ title, description, image, span = 1, children }) {
6
- return (_jsxs("div", { className: cn('glass-1 hover:glass-2 rounded-2xl p-6 md:p-8 transition-all overflow-hidden flex flex-col', span === 2 && 'md:col-span-2'), children: [_jsx("h3", { className: "text-lg font-semibold text-foreground mb-2", children: title }), description && _jsx("p", { className: "text-sm text-muted-foreground mb-4 leading-relaxed", children: description }), image && (_jsx("div", { className: "mt-auto -mx-6 -mb-6 md:-mx-8 md:-mb-8", children: _jsx(ThemeImage, { src: image, alt: title, className: "w-full" }) })), children] }));
5
+ /* ---------------------------------------------------------------------------
6
+ * Abstract visual decorations
7
+ * Pure CSS, theme-aware (uses `primary`), product-agnostic.
8
+ * -------------------------------------------------------------------------*/
9
+ function VisualAurora() {
10
+ return (_jsxs("div", { className: "relative h-32 overflow-hidden rounded-xl", children: [_jsx("div", { className: "absolute -top-10 -left-10 h-36 w-36 rounded-full bg-primary/20 blur-3xl" }), _jsx("div", { className: "absolute -bottom-8 right-4 h-32 w-32 rounded-full bg-primary/30 blur-2xl" }), _jsx("div", { className: "absolute top-2 left-1/3 h-24 w-48 rounded-full bg-primary/10 blur-2xl" })] }));
11
+ }
12
+ function VisualOrbs() {
13
+ return (_jsxs("div", { className: "relative h-32 overflow-hidden", children: [_jsx("div", { className: "absolute top-1 right-6 h-24 w-24 rounded-full border border-primary/20 bg-primary/5" }), _jsx("div", { className: "absolute -bottom-2 left-10 h-20 w-20 rounded-full border border-primary/15 bg-primary/10" }), _jsx("div", { className: "absolute top-6 left-1/4 h-28 w-28 rounded-full border border-primary/10 bg-primary/5" })] }));
14
+ }
15
+ function VisualRings() {
16
+ return (_jsxs("div", { className: "relative h-32 flex items-center justify-center overflow-hidden", children: [_jsx("div", { className: "absolute h-44 w-44 rounded-full border border-primary/5" }), _jsx("div", { className: "absolute h-32 w-32 rounded-full border border-primary/10" }), _jsx("div", { className: "absolute h-20 w-20 rounded-full border border-primary/15 bg-primary/5" }), _jsx("div", { className: "absolute h-8 w-8 rounded-full bg-primary/20" })] }));
17
+ }
18
+ function VisualDots() {
19
+ const opacities = [10, 20, 5, 15, 25, 15, 5, 20, 10, 25, 10, 15, 20, 5, 20];
20
+ return (_jsx("div", { className: "grid grid-cols-5 gap-3 px-2 py-4", children: opacities.map((op, i) => (_jsx("div", { className: "aspect-square rounded-full bg-primary", style: { opacity: op / 100 } }, i))) }));
21
+ }
22
+ const bentoVisuals = {
23
+ aurora: VisualAurora,
24
+ orbs: VisualOrbs,
25
+ rings: VisualRings,
26
+ dots: VisualDots,
27
+ };
28
+ export function BentoItem({ title, description, image, visual, span = 1, children }) {
29
+ const Visual = visual ? bentoVisuals[visual] : null;
30
+ return (_jsxs("div", { className: cn('glass-1 hover:glass-2 rounded-2xl p-6 md:p-8 transition-all overflow-hidden flex flex-col', 'hover:-translate-y-0.5 hover:shadow-lg', span === 2 && 'md:col-span-2'), children: [_jsx("h3", { className: "text-lg md:text-xl font-semibold text-foreground mb-2", children: title }), description && _jsx("p", { className: "text-sm text-muted-foreground mb-4 leading-relaxed", children: description }), Visual && (_jsx("div", { className: "mt-auto pt-2", children: _jsx(Visual, {}) })), image && (_jsx("div", { className: "mt-auto -mx-6 -mb-6 md:-mx-8 md:-mb-8", children: _jsx(ThemeImage, { src: image, alt: title, className: "w-full" }) })), children] }));
7
31
  }
8
32
  export function BentoGrid({ id, title, description, children }) {
9
- return (_jsx(Section, { id: id, children: _jsxs("div", { className: "container-main", children: [(title || description) && (_jsxs("div", { className: "text-center mb-12", children: [title && _jsx("h2", { className: "text-3xl md:text-4xl font-bold text-foreground mb-4", children: title }), description && _jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto", children: description })] })), _jsx("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-6 auto-rows-fr", children: children })] }) }));
33
+ return (_jsx(Section, { id: id, children: _jsxs("div", { className: "container-main", children: [(title || description) && (_jsxs("div", { className: "text-center mb-12", children: [title && _jsx("h2", { className: "text-3xl md:text-4xl font-bold text-foreground mb-4", children: title }), description && _jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto", children: description })] })), _jsx("div", { className: "grid grid-cols-1 md:grid-cols-3 gap-4", children: children })] }) }));
10
34
  }
11
35
  //# sourceMappingURL=BentoGrid.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BentoGrid.js","sourceRoot":"","sources":["../../src/marketing/BentoGrid.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AAUjE,MAAM,UAAU,SAAS,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI,GAAG,CAAC,EAAE,QAAQ,EAAkB;IACzF,OAAO,CACL,eAAK,SAAS,EAAE,EAAE,CAChB,2FAA2F,EAC3F,IAAI,KAAK,CAAC,IAAI,eAAe,CAC9B,aACC,aAAI,SAAS,EAAC,4CAA4C,YAAE,KAAK,GAAM,EACtE,WAAW,IAAI,YAAG,SAAS,EAAC,oDAAoD,YAAE,WAAW,GAAK,EAClG,KAAK,IAAI,CACR,cAAK,SAAS,EAAC,uCAAuC,YACpD,KAAC,UAAU,IAAC,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE,KAAK,EAAE,SAAS,EAAC,QAAQ,GAAG,GACrD,CACP,EACA,QAAQ,IACL,CACP,CAAC;AACJ,CAAC;AASD,MAAM,UAAU,SAAS,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAkB;IAC5E,OAAO,CACL,KAAC,OAAO,IAAC,EAAE,EAAE,EAAE,YACb,eAAK,SAAS,EAAC,gBAAgB,aAC5B,CAAC,KAAK,IAAI,WAAW,CAAC,IAAI,CACzB,eAAK,SAAS,EAAC,mBAAmB,aAC/B,KAAK,IAAI,aAAI,SAAS,EAAC,qDAAqD,YAAE,KAAK,GAAM,EACzF,WAAW,IAAI,YAAG,SAAS,EAAC,iDAAiD,YAAE,WAAW,GAAK,IAC5F,CACP,EACD,cAAK,SAAS,EAAC,oDAAoD,YAAE,QAAQ,GAAO,IAChF,GACE,CACX,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"BentoGrid.js","sourceRoot":"","sources":["../../src/marketing/BentoGrid.tsx"],"names":[],"mappings":"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"}
@@ -30,6 +30,6 @@ interface PricingSectionProps {
30
30
  mostPopularLabel?: string;
31
31
  children: React.ReactNode;
32
32
  }
33
- export declare function PricingSection({ id, title, description, monthlyLabel, yearlyLabel, mostPopularLabel, children }: PricingSectionProps): import("react/jsx-runtime").JSX.Element;
33
+ export declare function PricingSection({ children, ...rest }: PricingSectionProps): import("react/jsx-runtime").JSX.Element;
34
34
  export {};
35
35
  //# sourceMappingURL=PricingSection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PricingSection.d.ts","sourceRoot":"","sources":["../../src/marketing/PricingSection.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA6C,MAAM,OAAO,CAAC;AAMlE,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,gBAAgB,QAEnD;AAED,UAAU,kBAAkB;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,CAAC,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CAC9B;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,QAEvD;AAED,UAAU,uBAAuB;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,uBAAuB,QAEjE;AAED,UAAU,mBAAmB;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,YAAwB,EAAE,WAAsB,EAAE,gBAAiC,EAAE,QAAQ,EAAE,EAAE,mBAAmB,2CAiG5K"}
1
+ {"version":3,"file":"PricingSection.d.ts","sourceRoot":"","sources":["../../src/marketing/PricingSection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmC,MAAM,OAAO,CAAC;AAGxD,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,WAAW,CAAC,MAAM,EAAE,gBAAgB,QAEnD;AAED,UAAU,kBAAkB;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,CAAC,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CAC9B;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,kBAAkB,QAEvD;AAED,UAAU,uBAAuB;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,uBAAuB,QAEjE;AAED,UAAU,mBAAmB;IAC3B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,mBAAmB,2CAYxE"}
@@ -1,10 +1,6 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useState, Children, isValidElement } from 'react';
4
- import { Check } from 'lucide-react';
5
- import { cn } from '../lib/utils';
6
- import { Section } from '../ui/section';
7
- import { Button } from '../ui/button';
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Children, isValidElement } from 'react';
3
+ import { PricingSectionClient } from './PricingSectionClient';
8
4
  export function PricingPlan(_props) {
9
5
  return null;
10
6
  }
@@ -14,8 +10,7 @@ export function ComparisonRow(_props) {
14
10
  export function ComparisonCategory(_props) {
15
11
  return null;
16
12
  }
17
- export function PricingSection({ id, title, description, monthlyLabel = 'Monthly', yearlyLabel = 'Yearly', mostPopularLabel = 'Most Popular', children }) {
18
- const [isYearly, setIsYearly] = useState(false);
13
+ export function PricingSection({ children, ...rest }) {
19
14
  const plans = [];
20
15
  const rows = [];
21
16
  Children.forEach(children, (child) => {
@@ -28,14 +23,6 @@ export function PricingSection({ id, title, description, monthlyLabel = 'Monthly
28
23
  else if (child.type === ComparisonCategory)
29
24
  rows.push({ type: 'category', props: child.props });
30
25
  });
31
- return (_jsx(Section, { id: id, children: _jsxs("div", { className: "container-main", children: [(title || description) && (_jsxs("div", { className: "text-center mb-12", children: [title && _jsx("h2", { className: "text-3xl md:text-4xl font-bold text-foreground mb-4", children: title }), description && _jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto", children: description })] })), plans.some((p) => p.yearlyPrice) && (_jsxs("div", { className: "flex items-center justify-center gap-3 mb-12", children: [_jsx("span", { className: cn('text-sm font-medium', !isYearly ? 'text-foreground' : 'text-muted-foreground'), children: monthlyLabel }), _jsx("button", { onClick: () => setIsYearly(!isYearly), className: cn('relative w-12 h-6 rounded-full transition-colors', isYearly ? 'bg-primary' : 'bg-muted'), children: _jsx("span", { className: cn('absolute top-0.5 w-5 h-5 bg-background rounded-full shadow transition-transform', isYearly ? 'translate-x-6' : 'translate-x-0.5') }) }), _jsx("span", { className: cn('text-sm font-medium', isYearly ? 'text-foreground' : 'text-muted-foreground'), children: yearlyLabel })] })), _jsx("div", { className: cn('grid grid-cols-1 gap-6 mb-16', plans.length === 2 && 'md:grid-cols-2', plans.length >= 3 && 'md:grid-cols-3'), children: plans.map((plan) => (_jsxs("div", { className: cn('relative rounded-2xl p-8', plan.popular
32
- ? 'glass-4 ring-2 ring-primary shadow-xl'
33
- : 'glass-1'), children: [plan.popular && (_jsx("div", { className: "absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-1 bg-primary text-primary-foreground text-xs font-medium rounded-full", children: mostPopularLabel })), _jsx("h3", { className: "text-xl font-bold mb-2 text-foreground", children: plan.name }), plan.description && _jsx("p", { className: "text-sm mb-4 text-muted-foreground", children: plan.description }), _jsx("div", { className: "mb-6", children: _jsx("span", { className: "text-4xl font-bold text-foreground", children: isYearly && plan.yearlyPrice ? plan.yearlyPrice : plan.price }) }), _jsx(Button, { asChild: true, className: "w-full", variant: plan.popular ? 'default' : 'glow', children: _jsx("a", { href: plan.cta.href, children: plan.cta.label }) }), _jsx("ul", { className: "mt-6 space-y-3", children: plan.features.map((feature) => (_jsxs("li", { className: "flex items-start gap-2 text-sm text-muted-foreground", children: [_jsx(Check, { className: "w-4 h-4 mt-0.5 shrink-0 text-primary" }), feature] }, feature))) })] }, plan.name))) }), rows.length > 0 && (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b border-border", children: [_jsx("th", { className: "text-left py-4 pr-4 font-medium text-foreground", children: "Feature" }), plans.map((plan) => _jsx("th", { className: "text-center py-4 px-4 font-medium text-foreground", children: plan.name }, plan.name))] }) }), _jsx("tbody", { children: rows.map((row, i) => {
34
- if (row.type === 'category') {
35
- return _jsx("tr", { className: "bg-muted", children: _jsx("td", { colSpan: plans.length + 1, className: "py-3 px-4 font-semibold text-foreground", children: row.props.title }) }, i);
36
- }
37
- const r = row.props;
38
- return (_jsxs("tr", { className: "border-b border-border", children: [_jsx("td", { className: "py-3 pr-4 text-muted-foreground", children: r.feature }), r.values.map((val, j) => (_jsx("td", { className: "text-center py-3 px-4", children: typeof val === 'boolean' ? (val ? _jsx(Check, { className: "w-5 h-5 mx-auto text-primary" }) : _jsx("span", { className: "text-muted-foreground/30", children: "\u2014" })) : _jsx("span", { className: "text-muted-foreground", children: val }) }, j)))] }, i));
39
- }) })] }) }))] }) }));
26
+ return _jsx(PricingSectionClient, { ...rest, plans: plans, rows: rows });
40
27
  }
41
28
  //# sourceMappingURL=PricingSection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"PricingSection.js","sourceRoot":"","sources":["../../src/marketing/PricingSection.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"PricingSection.js","sourceRoot":"","sources":["../../src/marketing/PricingSection.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAY9D,MAAM,UAAU,WAAW,CAAC,MAAwB;IAClD,OAAO,IAAI,CAAC;AACd,CAAC;AAOD,MAAM,UAAU,aAAa,CAAC,MAA0B;IACtD,OAAO,IAAI,CAAC;AACd,CAAC;AAMD,MAAM,UAAU,kBAAkB,CAAC,MAA+B;IAChE,OAAO,IAAI,CAAC;AACd,CAAC;AAYD,MAAM,UAAU,cAAc,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAuB;IACvE,MAAM,KAAK,GAAuB,EAAE,CAAC;IACrC,MAAM,IAAI,GAAwF,EAAE,CAAC;IAErG,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;QACnC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;YAAE,OAAO;QACnC,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;YAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAyB,CAAC,CAAC;aACvE,IAAI,KAAK,CAAC,IAAI,KAAK,aAAa;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,CAAC,KAA2B,EAAE,CAAC,CAAC;aACvG,IAAI,KAAK,CAAC,IAAI,KAAK,kBAAkB;YAAE,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,CAAC,KAAgC,EAAE,CAAC,CAAC;IAC7H,CAAC,CAAC,CAAC;IAEH,OAAO,KAAC,oBAAoB,OAAK,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,GAAI,CAAC;AACtE,CAAC"}
@@ -0,0 +1,35 @@
1
+ interface PricingPlanProps {
2
+ name: string;
3
+ price: string;
4
+ yearlyPrice?: string;
5
+ description?: string;
6
+ features: string[];
7
+ cta: {
8
+ label: string;
9
+ href: string;
10
+ };
11
+ popular?: boolean;
12
+ }
13
+ interface ComparisonRowProps {
14
+ feature: string;
15
+ values: (string | boolean)[];
16
+ }
17
+ interface ComparisonCategoryProps {
18
+ title: string;
19
+ }
20
+ interface PricingSectionClientProps {
21
+ id?: string;
22
+ title?: string;
23
+ description?: string;
24
+ monthlyLabel?: string;
25
+ yearlyLabel?: string;
26
+ mostPopularLabel?: string;
27
+ plans: PricingPlanProps[];
28
+ rows: {
29
+ type: 'category' | 'row';
30
+ props: ComparisonRowProps | ComparisonCategoryProps;
31
+ }[];
32
+ }
33
+ export declare function PricingSectionClient({ id, title, description, monthlyLabel, yearlyLabel, mostPopularLabel, plans, rows }: PricingSectionClientProps): import("react/jsx-runtime").JSX.Element;
34
+ export {};
35
+ //# sourceMappingURL=PricingSectionClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PricingSectionClient.d.ts","sourceRoot":"","sources":["../../src/marketing/PricingSectionClient.tsx"],"names":[],"mappings":"AASA,UAAU,gBAAgB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACrC,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,UAAU,kBAAkB;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,CAAC,MAAM,GAAG,OAAO,CAAC,EAAE,CAAC;CAC9B;AAED,UAAU,uBAAuB;IAC/B,KAAK,EAAE,MAAM,CAAC;CACf;AAED,UAAU,yBAAyB;IACjC,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,IAAI,EAAE;QAAE,IAAI,EAAE,UAAU,GAAG,KAAK,CAAC;QAAC,KAAK,EAAE,kBAAkB,GAAG,uBAAuB,CAAA;KAAE,EAAE,CAAC;CAC3F;AAED,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,YAAwB,EAAE,WAAsB,EAAE,gBAAiC,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,yBAAyB,2CAkG3L"}
@@ -0,0 +1,21 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { Check } from 'lucide-react';
5
+ import { cn } from '../lib/utils';
6
+ import { Section } from '../ui/section';
7
+ import { Button } from '../ui/button';
8
+ import * as TabsPrimitive from '@radix-ui/react-tabs';
9
+ export function PricingSectionClient({ id, title, description, monthlyLabel = 'Monthly', yearlyLabel = 'Yearly', mostPopularLabel = 'Most Popular', plans, rows }) {
10
+ const [isYearly, setIsYearly] = useState(false);
11
+ return (_jsx(Section, { id: id, children: _jsxs("div", { className: "container-main", children: [(title || description) && (_jsxs("div", { className: "text-center mb-12", children: [title && _jsx("h2", { className: "text-3xl md:text-4xl font-bold text-foreground mb-4", children: title }), description && _jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto", children: description })] })), plans.some((p) => p.yearlyPrice) && (_jsx("div", { className: "flex justify-center mb-12", children: _jsx(TabsPrimitive.Root, { defaultValue: "monthly", onValueChange: (v) => setIsYearly(v === 'yearly'), children: _jsxs(TabsPrimitive.List, { className: "inline-flex rounded-full glass-1 p-1 gap-1", children: [_jsx(TabsPrimitive.Trigger, { value: "monthly", className: "rounded-full px-5 py-2 text-sm font-medium text-muted-foreground transition-all hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[state=active]:bg-primary data-[state=active]:text-primary-foreground", children: monthlyLabel }), _jsx(TabsPrimitive.Trigger, { value: "yearly", className: "rounded-full px-5 py-2 text-sm font-medium text-muted-foreground transition-all hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[state=active]:bg-primary data-[state=active]:text-primary-foreground", children: yearlyLabel })] }) }) })), _jsx("div", { className: cn('grid grid-cols-1 gap-6 mb-16', plans.length === 2 && 'md:grid-cols-2', plans.length >= 3 && 'md:grid-cols-3'), children: plans.map((plan) => (_jsxs("div", { className: cn('relative rounded-2xl p-8', plan.popular
12
+ ? 'glass-4 ring-2 ring-primary shadow-xl'
13
+ : 'glass-1'), children: [plan.popular && (_jsx("div", { className: "absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-1 bg-primary text-primary-foreground text-xs font-medium rounded-full", children: mostPopularLabel })), _jsx("h3", { className: "text-xl font-bold mb-2 text-foreground", children: plan.name }), plan.description && _jsx("p", { className: "text-sm mb-4 text-muted-foreground", children: plan.description }), _jsx("div", { className: "mb-6", children: _jsx("span", { className: "text-4xl font-bold text-foreground", children: isYearly && plan.yearlyPrice ? plan.yearlyPrice : plan.price }) }), _jsx(Button, { asChild: true, className: "w-full", variant: plan.popular ? 'default' : 'glow', children: _jsx("a", { href: plan.cta.href, children: plan.cta.label }) }), _jsx("ul", { className: "mt-6 space-y-3", children: plan.features.map((feature) => (_jsxs("li", { className: "flex items-start gap-2 text-sm text-muted-foreground", children: [_jsx(Check, { className: "w-4 h-4 mt-0.5 shrink-0 text-primary" }), feature] }, feature))) })] }, plan.name))) }), rows.length > 0 && (_jsx("div", { className: "overflow-x-auto", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b border-border", children: [_jsx("th", { className: "text-left py-4 pr-4 font-medium text-foreground", children: "Feature" }), plans.map((plan) => _jsx("th", { className: "text-center py-4 px-4 font-medium text-foreground", children: plan.name }, plan.name))] }) }), _jsx("tbody", { children: rows.map((row, i) => {
14
+ if (row.type === 'category') {
15
+ return _jsx("tr", { className: "bg-muted", children: _jsx("td", { colSpan: plans.length + 1, className: "py-3 px-4 font-semibold text-foreground", children: row.props.title }) }, i);
16
+ }
17
+ const r = row.props;
18
+ return (_jsxs("tr", { className: "border-b border-border", children: [_jsx("td", { className: "py-3 pr-4 text-muted-foreground", children: r.feature }), r.values.map((val, j) => (_jsx("td", { className: "text-center py-3 px-4", children: typeof val === 'boolean' ? (val ? _jsx(Check, { className: "w-5 h-5 mx-auto text-primary" }) : _jsx("span", { className: "text-muted-foreground/30", children: "\u2014" })) : _jsx("span", { className: "text-muted-foreground", children: val }) }, j)))] }, i));
19
+ }) })] }) }))] }) }));
20
+ }
21
+ //# sourceMappingURL=PricingSectionClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PricingSectionClient.js","sourceRoot":"","sources":["../../src/marketing/PricingSectionClient.tsx"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import React from 'react';
2
- import { type ImageSource } from '../ui/theme-image';
2
+ import type { ImageSource } from '../ui/theme-image';
3
3
  interface TabItemProps {
4
4
  label: string;
5
5
  title?: string;
@@ -14,6 +14,6 @@ interface TabsSectionProps {
14
14
  description?: string;
15
15
  children: React.ReactNode;
16
16
  }
17
- export declare function TabsSection({ id, title, description, children }: TabsSectionProps): import("react/jsx-runtime").JSX.Element;
17
+ export declare function TabsSection({ children, ...rest }: TabsSectionProps): import("react/jsx-runtime").JSX.Element;
18
18
  export {};
19
19
  //# sourceMappingURL=TabsSection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TabsSection.d.ts","sourceRoot":"","sources":["../../src/marketing/TabsSection.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAmB,MAAM,OAAO,CAAC;AAGxC,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEjE,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,YAAY,QAE3C;AAED,UAAU,gBAAgB;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,WAAW,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,gBAAgB,2CA2DjF"}
1
+ {"version":3,"file":"TabsSection.d.ts","sourceRoot":"","sources":["../../src/marketing/TabsSection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmC,MAAM,OAAO,CAAC;AAExD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAED,wBAAgB,OAAO,CAAC,MAAM,EAAE,YAAY,QAE3C;AAED,UAAU,gBAAgB;IACxB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAE,EAAE,gBAAgB,2CASlE"}
@@ -1,23 +1,16 @@
1
- 'use client';
2
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import React, { useState } from 'react';
4
- import { Section } from '../ui/section';
5
- import { cn } from '../lib/utils';
6
- import { ThemeImage } from '../ui/theme-image';
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Children, isValidElement } from 'react';
3
+ import { TabsSectionClient } from './TabsSectionClient';
7
4
  export function TabItem(_props) {
8
5
  return null;
9
6
  }
10
- export function TabsSection({ id, title, description, children }) {
11
- const [activeIndex, setActiveIndex] = useState(0);
7
+ export function TabsSection({ children, ...rest }) {
12
8
  const tabs = [];
13
- React.Children.forEach(children, (child) => {
14
- if (React.isValidElement(child) && child.type === TabItem) {
9
+ Children.forEach(children, (child) => {
10
+ if (isValidElement(child) && child.type === TabItem) {
15
11
  tabs.push(child.props);
16
12
  }
17
13
  });
18
- const activeTab = tabs[activeIndex];
19
- return (_jsx(Section, { id: id, children: _jsxs("div", { className: "container-main", children: [(title || description) && (_jsxs("div", { className: "text-center mb-12", children: [title && _jsx("h2", { className: "text-3xl md:text-4xl font-bold text-foreground mb-4", children: title }), description && _jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto", children: description })] })), _jsx("div", { className: "flex justify-center mb-8", children: _jsx("div", { className: "inline-flex rounded-full glass-1 p-1 gap-1", children: tabs.map((tab, i) => (_jsx("button", { onClick: () => setActiveIndex(i), className: cn('px-4 py-2 rounded-full text-sm font-medium transition-all', i === activeIndex
20
- ? 'bg-primary text-primary-foreground'
21
- : 'text-muted-foreground hover:text-foreground'), children: tab.label }, i))) }) }), activeTab && (_jsx("div", { className: "glass-1 rounded-2xl p-8 md:p-12", children: _jsxs("div", { className: cn('grid gap-8', activeTab.image && 'md:grid-cols-2 items-center'), children: [_jsxs("div", { children: [activeTab.title && (_jsx("h3", { className: "text-2xl font-bold text-foreground mb-4", children: activeTab.title })), activeTab.description && (_jsx("p", { className: "text-muted-foreground leading-relaxed", children: activeTab.description }))] }), activeTab.image && (_jsx(ThemeImage, { src: activeTab.image, alt: activeTab.title || activeTab.label, className: "w-full rounded-xl" }))] }) }))] }) }));
14
+ return _jsx(TabsSectionClient, { ...rest, tabs: tabs });
22
15
  }
23
16
  //# sourceMappingURL=TabsSection.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"TabsSection.js","sourceRoot":"","sources":["../../src/marketing/TabsSection.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AAUjE,MAAM,UAAU,OAAO,CAAC,MAAoB;IAC1C,OAAO,IAAI,CAAC;AACd,CAAC;AASD,MAAM,UAAU,WAAW,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAoB;IAChF,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAElD,MAAM,IAAI,GAAmB,EAAE,CAAC;IAChC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;QACzC,IAAI,KAAK,CAAC,cAAc,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1D,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAqB,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;IAEpC,OAAO,CACL,KAAC,OAAO,IAAC,EAAE,EAAE,EAAE,YACb,eAAK,SAAS,EAAC,gBAAgB,aAC5B,CAAC,KAAK,IAAI,WAAW,CAAC,IAAI,CACzB,eAAK,SAAS,EAAC,mBAAmB,aAC/B,KAAK,IAAI,aAAI,SAAS,EAAC,qDAAqD,YAAE,KAAK,GAAM,EACzF,WAAW,IAAI,YAAG,SAAS,EAAC,iDAAiD,YAAE,WAAW,GAAK,IAC5F,CACP,EACD,cAAK,SAAS,EAAC,0BAA0B,YACvC,cAAK,SAAS,EAAC,4CAA4C,YACxD,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CACpB,iBAEE,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,EAChC,SAAS,EAAE,EAAE,CACX,2DAA2D,EAC3D,CAAC,KAAK,WAAW;gCACf,CAAC,CAAC,oCAAoC;gCACtC,CAAC,CAAC,6CAA6C,CAClD,YAEA,GAAG,CAAC,KAAK,IATL,CAAC,CAUC,CACV,CAAC,GACE,GACF,EACL,SAAS,IAAI,CACZ,cAAK,SAAS,EAAC,iCAAiC,YAC9C,eAAK,SAAS,EAAE,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC,KAAK,IAAI,6BAA6B,CAAC,aAChF,0BACG,SAAS,CAAC,KAAK,IAAI,CAClB,aAAI,SAAS,EAAC,yCAAyC,YAAE,SAAS,CAAC,KAAK,GAAM,CAC/E,EACA,SAAS,CAAC,WAAW,IAAI,CACxB,YAAG,SAAS,EAAC,uCAAuC,YAAE,SAAS,CAAC,WAAW,GAAK,CACjF,IACG,EACL,SAAS,CAAC,KAAK,IAAI,CAClB,KAAC,UAAU,IAAC,GAAG,EAAE,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,CAAC,KAAK,IAAI,SAAS,CAAC,KAAK,EAAE,SAAS,EAAC,mBAAmB,GAAG,CAC5G,IACG,GACF,CACP,IACG,GACE,CACX,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"TabsSection.js","sourceRoot":"","sources":["../../src/marketing/TabsSection.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAWxD,MAAM,UAAU,OAAO,CAAC,MAAoB;IAC1C,OAAO,IAAI,CAAC;AACd,CAAC;AASD,MAAM,UAAU,WAAW,CAAC,EAAE,QAAQ,EAAE,GAAG,IAAI,EAAoB;IACjE,MAAM,IAAI,GAAmB,EAAE,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE;QACnC,IAAI,cAAc,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YACpD,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,KAAqB,CAAC,CAAC;QACzC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,OAAO,KAAC,iBAAiB,OAAK,IAAI,EAAE,IAAI,EAAE,IAAI,GAAI,CAAC;AACrD,CAAC"}
@@ -0,0 +1,16 @@
1
+ import { type ImageSource } from '../ui/theme-image';
2
+ interface TabItemProps {
3
+ label: string;
4
+ title?: string;
5
+ description?: string;
6
+ image?: ImageSource;
7
+ }
8
+ interface TabsSectionClientProps {
9
+ id?: string;
10
+ title?: string;
11
+ description?: string;
12
+ tabs: TabItemProps[];
13
+ }
14
+ export declare function TabsSectionClient({ id, title, description, tabs }: TabsSectionClientProps): import("react/jsx-runtime").JSX.Element;
15
+ export {};
16
+ //# sourceMappingURL=TabsSectionClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TabsSectionClient.d.ts","sourceRoot":"","sources":["../../src/marketing/TabsSectionClient.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEjE,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB;AAED,UAAU,sBAAsB;IAC9B,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,YAAY,EAAE,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,EAAE,sBAAsB,2CAoDzF"}
@@ -0,0 +1,14 @@
1
+ 'use client';
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from 'react';
4
+ import { Section } from '../ui/section';
5
+ import { cn } from '../lib/utils';
6
+ import { ThemeImage } from '../ui/theme-image';
7
+ export function TabsSectionClient({ id, title, description, tabs }) {
8
+ const [activeIndex, setActiveIndex] = useState(0);
9
+ const activeTab = tabs[activeIndex];
10
+ return (_jsx(Section, { id: id, children: _jsxs("div", { className: "container-main", children: [(title || description) && (_jsxs("div", { className: "text-center mb-12", children: [title && _jsx("h2", { className: "text-3xl md:text-4xl font-bold text-foreground mb-4", children: title }), description && _jsx("p", { className: "text-lg text-muted-foreground max-w-2xl mx-auto", children: description })] })), _jsx("div", { className: "flex justify-center mb-8", children: _jsx("div", { className: "inline-flex rounded-full glass-1 p-1 gap-1", children: tabs.map((tab, i) => (_jsx("button", { onClick: () => setActiveIndex(i), className: cn('px-4 py-2 rounded-full text-sm font-medium transition-all', i === activeIndex
11
+ ? 'bg-primary text-primary-foreground'
12
+ : 'text-muted-foreground hover:text-foreground'), children: tab.label }, i))) }) }), activeTab && (_jsx("div", { className: "glass-1 rounded-2xl p-8 md:p-12", children: _jsxs("div", { className: cn('grid gap-8', activeTab.image && 'md:grid-cols-2 items-center'), children: [_jsxs("div", { children: [activeTab.title && (_jsx("h3", { className: "text-2xl font-bold text-foreground mb-4", children: activeTab.title })), activeTab.description && (_jsx("p", { className: "text-muted-foreground leading-relaxed", children: activeTab.description }))] }), activeTab.image && (_jsx(ThemeImage, { src: activeTab.image, alt: activeTab.title || activeTab.label, className: "w-full rounded-xl" }))] }) }))] }) }));
13
+ }
14
+ //# sourceMappingURL=TabsSectionClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TabsSectionClient.js","sourceRoot":"","sources":["../../src/marketing/TabsSectionClient.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AACxC,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAClC,OAAO,EAAE,UAAU,EAAoB,MAAM,mBAAmB,CAAC;AAgBjE,MAAM,UAAU,iBAAiB,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAA0B;IACxF,MAAM,CAAC,WAAW,EAAE,cAAc,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAElD,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,CAAC,CAAC;IAEpC,OAAO,CACL,KAAC,OAAO,IAAC,EAAE,EAAE,EAAE,YACb,eAAK,SAAS,EAAC,gBAAgB,aAC5B,CAAC,KAAK,IAAI,WAAW,CAAC,IAAI,CACzB,eAAK,SAAS,EAAC,mBAAmB,aAC/B,KAAK,IAAI,aAAI,SAAS,EAAC,qDAAqD,YAAE,KAAK,GAAM,EACzF,WAAW,IAAI,YAAG,SAAS,EAAC,iDAAiD,YAAE,WAAW,GAAK,IAC5F,CACP,EACD,cAAK,SAAS,EAAC,0BAA0B,YACvC,cAAK,SAAS,EAAC,4CAA4C,YACxD,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CACpB,iBAEE,OAAO,EAAE,GAAG,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,EAChC,SAAS,EAAE,EAAE,CACX,2DAA2D,EAC3D,CAAC,KAAK,WAAW;gCACf,CAAC,CAAC,oCAAoC;gCACtC,CAAC,CAAC,6CAA6C,CAClD,YAEA,GAAG,CAAC,KAAK,IATL,CAAC,CAUC,CACV,CAAC,GACE,GACF,EACL,SAAS,IAAI,CACZ,cAAK,SAAS,EAAC,iCAAiC,YAC9C,eAAK,SAAS,EAAE,EAAE,CAAC,YAAY,EAAE,SAAS,CAAC,KAAK,IAAI,6BAA6B,CAAC,aAChF,0BACG,SAAS,CAAC,KAAK,IAAI,CAClB,aAAI,SAAS,EAAC,yCAAyC,YAAE,SAAS,CAAC,KAAK,GAAM,CAC/E,EACA,SAAS,CAAC,WAAW,IAAI,CACxB,YAAG,SAAS,EAAC,uCAAuC,YAAE,SAAS,CAAC,WAAW,GAAK,CACjF,IACG,EACL,SAAS,CAAC,KAAK,IAAI,CAClB,KAAC,UAAU,IAAC,GAAG,EAAE,SAAS,CAAC,KAAK,EAAE,GAAG,EAAE,SAAS,CAAC,KAAK,IAAI,SAAS,CAAC,KAAK,EAAE,SAAS,EAAC,mBAAmB,GAAG,CAC5G,IACG,GACF,CACP,IACG,GACE,CACX,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
2
+ import * as React from "react";
3
+ declare const Tabs: React.ForwardRefExoticComponent<TabsPrimitive.TabsProps & React.RefAttributes<HTMLDivElement>>;
4
+ declare function TabsList({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.List>): import("react/jsx-runtime").JSX.Element;
5
+ declare function TabsTrigger({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Trigger>): import("react/jsx-runtime").JSX.Element;
6
+ declare function TabsContent({ className, ...props }: React.ComponentProps<typeof TabsPrimitive.Content>): import("react/jsx-runtime").JSX.Element;
7
+ export { Tabs, TabsContent, TabsList, TabsTrigger };
8
+ //# sourceMappingURL=tabs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.d.ts","sourceRoot":"","sources":["../../src/ui/tabs.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAC;AACtD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAI/B,QAAA,MAAM,IAAI,gGAAqB,CAAC;AAEhC,iBAAS,QAAQ,CAAC,EAChB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,aAAa,CAAC,IAAI,CAAC,2CAQjD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,aAAa,CAAC,OAAO,CAAC,2CAWpD;AAED,iBAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EACT,EAAE,KAAK,CAAC,cAAc,CAAC,OAAO,aAAa,CAAC,OAAO,CAAC,2CAWpD;AAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
4
+ import { cn } from "../lib/utils";
5
+ const Tabs = TabsPrimitive.Root;
6
+ function TabsList({ className, ...props }) {
7
+ return (_jsx(TabsPrimitive.List, { "data-slot": "tabs-list", className: cn("text-muted-foreground flex items-center", className), ...props }));
8
+ }
9
+ function TabsTrigger({ className, ...props }) {
10
+ return (_jsx(TabsPrimitive.Trigger, { "data-slot": "tabs-trigger", className: cn("data-[state=active]:glass-4 ring-offset-background hover:bg-accent/50 hover:text-accent-foreground focus-visible:ring-ring data-[state=active]:text-foreground flex flex-col gap-3 rounded-md border border-transparent px-5 pt-4 pb-6 text-left text-sm font-medium transition-all focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-xl dark:border-b-0", className), ...props }));
11
+ }
12
+ function TabsContent({ className, ...props }) {
13
+ return (_jsx(TabsPrimitive.Content, { "data-slot": "tabs-content", className: cn("border-border dark:border-border/20 bg-input/30 ring-offset-background focus-visible:ring-ring dark:bg-card relative overflow-hidden rounded-lg border focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden", className), ...props }));
14
+ }
15
+ export { Tabs, TabsContent, TabsList, TabsTrigger };
16
+ //# sourceMappingURL=tabs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tabs.js","sourceRoot":"","sources":["../../src/ui/tabs.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,KAAK,aAAa,MAAM,sBAAsB,CAAC;AAGtD,OAAO,EAAE,EAAE,EAAE,MAAM,cAAc,CAAC;AAElC,MAAM,IAAI,GAAG,aAAa,CAAC,IAAI,CAAC;AAEhC,SAAS,QAAQ,CAAC,EAChB,SAAS,EACT,GAAG,KAAK,EACwC;IAChD,OAAO,CACL,KAAC,aAAa,CAAC,IAAI,iBACP,WAAW,EACrB,SAAS,EAAE,EAAE,CAAC,yCAAyC,EAAE,SAAS,CAAC,KAC/D,KAAK,GACT,CACH,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EAC2C;IACnD,OAAO,CACL,KAAC,aAAa,CAAC,OAAO,iBACV,cAAc,EACxB,SAAS,EAAE,EAAE,CACX,kcAAkc,EAClc,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAC;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,EACnB,SAAS,EACT,GAAG,KAAK,EAC2C;IACnD,OAAO,CACL,KAAC,aAAa,CAAC,OAAO,iBACV,cAAc,EACxB,SAAS,EAAE,EAAE,CACX,sOAAsO,EACtO,SAAS,CACV,KACG,KAAK,GACT,CACH,CAAC;AACJ,CAAC;AAED,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shipsite.dev/components",
3
- "version": "0.2.28",
3
+ "version": "0.2.30",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/shipsite/shipsite",
@@ -37,6 +37,7 @@
37
37
  "@radix-ui/react-accordion": "^1.2.3",
38
38
  "@radix-ui/react-dialog": "^1.1.6",
39
39
  "@radix-ui/react-slot": "^1.2.0",
40
+ "@radix-ui/react-tabs": "^1.1.13",
40
41
  "class-variance-authority": "^0.7.1",
41
42
  "clsx": "^2.1.1",
42
43
  "lucide-react": "^0.475.0",
@@ -46,10 +47,10 @@
46
47
  "tailwind-merge": "^3.0.2"
47
48
  },
48
49
  "devDependencies": {
49
- "typescript": "^5.8.3",
50
- "@types/react": "^19",
51
50
  "@types/node": "^20",
52
- "tailwindcss": "^4"
51
+ "@types/react": "^19",
52
+ "tailwindcss": "^4",
53
+ "typescript": "^5.8.3"
53
54
  },
54
55
  "peerDependencies": {
55
56
  "next": ">=15"
@@ -1,14 +1,99 @@
1
1
  import React from 'react';
2
2
  import { Section } from '../ui/section';
3
+ import { ThemeImage, type ImageSource } from '../ui/theme-image';
4
+
5
+ interface BlogArticleAuthor {
6
+ name: string;
7
+ role: string;
8
+ image: string;
9
+ }
10
+
11
+ interface BlogArticle {
12
+ slug: string;
13
+ title: string;
14
+ excerpt: string;
15
+ category?: string;
16
+ date: string;
17
+ image: ImageSource;
18
+ readingTime: number;
19
+ href: string;
20
+ featured?: boolean;
21
+ author?: BlogArticleAuthor;
22
+ }
3
23
 
4
24
  interface BlogIndexProps {
5
25
  id?: string;
6
26
  title?: string;
7
27
  description?: string;
28
+ articles?: BlogArticle[];
8
29
  children?: React.ReactNode;
9
30
  }
10
31
 
11
- export function BlogIndex({ id, title, description, children }: BlogIndexProps) {
32
+ function formatDate(dateStr: string): string {
33
+ try {
34
+ return new Date(dateStr).toLocaleDateString(undefined, {
35
+ year: 'numeric',
36
+ month: 'short',
37
+ day: 'numeric',
38
+ });
39
+ } catch {
40
+ return dateStr;
41
+ }
42
+ }
43
+
44
+ function ArticleCard({ article }: { article: BlogArticle }) {
45
+ return (
46
+ <a
47
+ key={article.slug}
48
+ href={article.href}
49
+ className="group block rounded-3xl p-2 -m-2 hover:bg-muted/50 hover:scale-[1.015] transition-all duration-300 ease-out focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring"
50
+ >
51
+ <div className="aspect-[3/2] overflow-hidden rounded-2xl">
52
+ <ThemeImage
53
+ src={article.image}
54
+ alt={article.title}
55
+ className="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105"
56
+ />
57
+ </div>
58
+ <div className="pt-4">
59
+ <div className="flex items-center justify-between text-sm text-muted-foreground mb-2">
60
+ {article.category && (
61
+ <span className="font-medium text-primary">{article.category}</span>
62
+ )}
63
+ {article.date && <time dateTime={article.date}>{formatDate(article.date)}</time>}
64
+ </div>
65
+ <h3 className="text-lg font-semibold leading-snug text-foreground mb-2 group-hover:text-primary transition-colors">
66
+ {article.title}
67
+ </h3>
68
+ <p className="text-base text-muted-foreground line-clamp-2 mb-4">
69
+ {article.excerpt}
70
+ </p>
71
+ {article.author && (
72
+ <div className="flex items-center gap-3">
73
+ {article.author.image && (
74
+ <img
75
+ src={article.author.image}
76
+ alt={article.author.name}
77
+ className="w-10 h-10 rounded-full object-cover"
78
+ />
79
+ )}
80
+ <div>
81
+ <div className="text-sm font-medium text-foreground">{article.author.name}</div>
82
+ {article.author.role && (
83
+ <div className="text-xs text-muted-foreground">{article.author.role}</div>
84
+ )}
85
+ </div>
86
+ </div>
87
+ )}
88
+ </div>
89
+ </a>
90
+ );
91
+ }
92
+
93
+ export function BlogIndex({ id, title, description, articles, children }: BlogIndexProps) {
94
+ const featured = articles?.filter((a) => a.featured) ?? [];
95
+ const all = articles ?? [];
96
+
12
97
  return (
13
98
  <Section id={id}>
14
99
  <div className="container-main">
@@ -18,6 +103,24 @@ export function BlogIndex({ id, title, description, children }: BlogIndexProps)
18
103
  {description && <p className="text-lg text-muted-foreground">{description}</p>}
19
104
  </div>
20
105
  )}
106
+ {featured.length > 0 && (
107
+ <>
108
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
109
+ {featured.map((article) => (
110
+ <ArticleCard key={article.slug} article={article} />
111
+ ))}
112
+ </div>
113
+ <hr className="border-border mb-12" />
114
+ <h3 className="text-xl font-semibold text-foreground mb-8">All Articles</h3>
115
+ </>
116
+ )}
117
+ {all.length > 0 && (
118
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
119
+ {all.map((article) => (
120
+ <ArticleCard key={article.slug} article={article} />
121
+ ))}
122
+ </div>
123
+ )}
21
124
  {children}
22
125
  </div>
23
126
  </Section>
@@ -3,22 +3,95 @@ import { Section } from '../ui/section';
3
3
  import { cn } from '../lib/utils';
4
4
  import { ThemeImage, type ImageSource } from '../ui/theme-image';
5
5
 
6
+ /* ---------------------------------------------------------------------------
7
+ * Abstract visual decorations
8
+ * Pure CSS, theme-aware (uses `primary`), product-agnostic.
9
+ * -------------------------------------------------------------------------*/
10
+
11
+ function VisualAurora() {
12
+ return (
13
+ <div className="relative h-32 overflow-hidden rounded-xl">
14
+ <div className="absolute -top-10 -left-10 h-36 w-36 rounded-full bg-primary/20 blur-3xl" />
15
+ <div className="absolute -bottom-8 right-4 h-32 w-32 rounded-full bg-primary/30 blur-2xl" />
16
+ <div className="absolute top-2 left-1/3 h-24 w-48 rounded-full bg-primary/10 blur-2xl" />
17
+ </div>
18
+ );
19
+ }
20
+
21
+ function VisualOrbs() {
22
+ return (
23
+ <div className="relative h-32 overflow-hidden">
24
+ <div className="absolute top-1 right-6 h-24 w-24 rounded-full border border-primary/20 bg-primary/5" />
25
+ <div className="absolute -bottom-2 left-10 h-20 w-20 rounded-full border border-primary/15 bg-primary/10" />
26
+ <div className="absolute top-6 left-1/4 h-28 w-28 rounded-full border border-primary/10 bg-primary/5" />
27
+ </div>
28
+ );
29
+ }
30
+
31
+ function VisualRings() {
32
+ return (
33
+ <div className="relative h-32 flex items-center justify-center overflow-hidden">
34
+ <div className="absolute h-44 w-44 rounded-full border border-primary/5" />
35
+ <div className="absolute h-32 w-32 rounded-full border border-primary/10" />
36
+ <div className="absolute h-20 w-20 rounded-full border border-primary/15 bg-primary/5" />
37
+ <div className="absolute h-8 w-8 rounded-full bg-primary/20" />
38
+ </div>
39
+ );
40
+ }
41
+
42
+ function VisualDots() {
43
+ const opacities = [10, 20, 5, 15, 25, 15, 5, 20, 10, 25, 10, 15, 20, 5, 20];
44
+ return (
45
+ <div className="grid grid-cols-5 gap-3 px-2 py-4">
46
+ {opacities.map((op, i) => (
47
+ <div
48
+ key={i}
49
+ className="aspect-square rounded-full bg-primary"
50
+ style={{ opacity: op / 100 }}
51
+ />
52
+ ))}
53
+ </div>
54
+ );
55
+ }
56
+
57
+ type BentoVisual = 'aurora' | 'orbs' | 'rings' | 'dots';
58
+
59
+ const bentoVisuals: Record<BentoVisual, React.FC> = {
60
+ aurora: VisualAurora,
61
+ orbs: VisualOrbs,
62
+ rings: VisualRings,
63
+ dots: VisualDots,
64
+ };
65
+
66
+ /* ---------------------------------------------------------------------------
67
+ * BentoItem
68
+ * -------------------------------------------------------------------------*/
69
+
6
70
  interface BentoItemProps {
7
71
  title: string;
8
72
  description?: string;
9
73
  image?: ImageSource;
74
+ visual?: string;
10
75
  span?: 1 | 2;
11
76
  children?: React.ReactNode;
12
77
  }
13
78
 
14
- export function BentoItem({ title, description, image, span = 1, children }: BentoItemProps) {
79
+ export function BentoItem({ title, description, image, visual, span = 1, children }: BentoItemProps) {
80
+ const Visual = visual ? bentoVisuals[visual as BentoVisual] : null;
81
+
15
82
  return (
16
83
  <div className={cn(
17
84
  'glass-1 hover:glass-2 rounded-2xl p-6 md:p-8 transition-all overflow-hidden flex flex-col',
85
+ 'hover:-translate-y-0.5 hover:shadow-lg',
18
86
  span === 2 && 'md:col-span-2',
19
87
  )}>
20
- <h3 className="text-lg font-semibold text-foreground mb-2">{title}</h3>
88
+ <h3 className="text-lg md:text-xl font-semibold text-foreground mb-2">{title}</h3>
21
89
  {description && <p className="text-sm text-muted-foreground mb-4 leading-relaxed">{description}</p>}
90
+ {Visual && (
91
+ <div className="mt-auto pt-2">
92
+ <Visual />
93
+ </div>
94
+ )}
22
95
  {image && (
23
96
  <div className="mt-auto -mx-6 -mb-6 md:-mx-8 md:-mb-8">
24
97
  <ThemeImage src={image} alt={title} className="w-full" />
@@ -29,6 +102,10 @@ export function BentoItem({ title, description, image, span = 1, children }: Ben
29
102
  );
30
103
  }
31
104
 
105
+ /* ---------------------------------------------------------------------------
106
+ * BentoGrid
107
+ * -------------------------------------------------------------------------*/
108
+
32
109
  interface BentoGridProps {
33
110
  id?: string;
34
111
  title?: string;
@@ -46,7 +123,7 @@ export function BentoGrid({ id, title, description, children }: BentoGridProps)
46
123
  {description && <p className="text-lg text-muted-foreground max-w-2xl mx-auto">{description}</p>}
47
124
  </div>
48
125
  )}
49
- <div className="grid grid-cols-1 md:grid-cols-3 gap-6 auto-rows-fr">{children}</div>
126
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-4">{children}</div>
50
127
  </div>
51
128
  </Section>
52
129
  );
@@ -1,10 +1,5 @@
1
- 'use client';
2
-
3
- import React, { useState, Children, isValidElement } from 'react';
4
- import { Check } from 'lucide-react';
5
- import { cn } from '../lib/utils';
6
- import { Section } from '../ui/section';
7
- import { Button } from '../ui/button';
1
+ import React, { Children, isValidElement } from 'react';
2
+ import { PricingSectionClient } from './PricingSectionClient';
8
3
 
9
4
  interface PricingPlanProps {
10
5
  name: string;
@@ -47,9 +42,7 @@ interface PricingSectionProps {
47
42
  children: React.ReactNode;
48
43
  }
49
44
 
50
- export function PricingSection({ id, title, description, monthlyLabel = 'Monthly', yearlyLabel = 'Yearly', mostPopularLabel = 'Most Popular', children }: PricingSectionProps) {
51
- const [isYearly, setIsYearly] = useState(false);
52
-
45
+ export function PricingSection({ children, ...rest }: PricingSectionProps) {
53
46
  const plans: PricingPlanProps[] = [];
54
47
  const rows: { type: 'category' | 'row'; props: ComparisonRowProps | ComparisonCategoryProps }[] = [];
55
48
 
@@ -60,88 +53,5 @@ export function PricingSection({ id, title, description, monthlyLabel = 'Monthly
60
53
  else if (child.type === ComparisonCategory) rows.push({ type: 'category', props: child.props as ComparisonCategoryProps });
61
54
  });
62
55
 
63
- return (
64
- <Section id={id}>
65
- <div className="container-main">
66
- {(title || description) && (
67
- <div className="text-center mb-12">
68
- {title && <h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4">{title}</h2>}
69
- {description && <p className="text-lg text-muted-foreground max-w-2xl mx-auto">{description}</p>}
70
- </div>
71
- )}
72
-
73
- {plans.some((p) => p.yearlyPrice) && (
74
- <div className="flex items-center justify-center gap-3 mb-12">
75
- <span className={cn('text-sm font-medium', !isYearly ? 'text-foreground' : 'text-muted-foreground')}>{monthlyLabel}</span>
76
- <button onClick={() => setIsYearly(!isYearly)} className={cn('relative w-12 h-6 rounded-full transition-colors', isYearly ? 'bg-primary' : 'bg-muted')}>
77
- <span className={cn('absolute top-0.5 w-5 h-5 bg-background rounded-full shadow transition-transform', isYearly ? 'translate-x-6' : 'translate-x-0.5')} />
78
- </button>
79
- <span className={cn('text-sm font-medium', isYearly ? 'text-foreground' : 'text-muted-foreground')}>{yearlyLabel}</span>
80
- </div>
81
- )}
82
-
83
- <div className={cn('grid grid-cols-1 gap-6 mb-16', plans.length === 2 && 'md:grid-cols-2', plans.length >= 3 && 'md:grid-cols-3')}>
84
- {plans.map((plan) => (
85
- <div key={plan.name} className={cn(
86
- 'relative rounded-2xl p-8',
87
- plan.popular
88
- ? 'glass-4 ring-2 ring-primary shadow-xl'
89
- : 'glass-1'
90
- )}>
91
- {plan.popular && (
92
- <div className="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-1 bg-primary text-primary-foreground text-xs font-medium rounded-full">{mostPopularLabel}</div>
93
- )}
94
- <h3 className="text-xl font-bold mb-2 text-foreground">{plan.name}</h3>
95
- {plan.description && <p className="text-sm mb-4 text-muted-foreground">{plan.description}</p>}
96
- <div className="mb-6">
97
- <span className="text-4xl font-bold text-foreground">{isYearly && plan.yearlyPrice ? plan.yearlyPrice : plan.price}</span>
98
- </div>
99
- <Button asChild className="w-full" variant={plan.popular ? 'default' : 'glow'}>
100
- <a href={plan.cta.href}>{plan.cta.label}</a>
101
- </Button>
102
- <ul className="mt-6 space-y-3">
103
- {plan.features.map((feature) => (
104
- <li key={feature} className="flex items-start gap-2 text-sm text-muted-foreground">
105
- <Check className="w-4 h-4 mt-0.5 shrink-0 text-primary" />
106
- {feature}
107
- </li>
108
- ))}
109
- </ul>
110
- </div>
111
- ))}
112
- </div>
113
-
114
- {rows.length > 0 && (
115
- <div className="overflow-x-auto">
116
- <table className="w-full text-sm">
117
- <thead>
118
- <tr className="border-b border-border">
119
- <th className="text-left py-4 pr-4 font-medium text-foreground">Feature</th>
120
- {plans.map((plan) => <th key={plan.name} className="text-center py-4 px-4 font-medium text-foreground">{plan.name}</th>)}
121
- </tr>
122
- </thead>
123
- <tbody>
124
- {rows.map((row, i) => {
125
- if (row.type === 'category') {
126
- return <tr key={i} className="bg-muted"><td colSpan={plans.length + 1} className="py-3 px-4 font-semibold text-foreground">{(row.props as ComparisonCategoryProps).title}</td></tr>;
127
- }
128
- const r = row.props as ComparisonRowProps;
129
- return (
130
- <tr key={i} className="border-b border-border">
131
- <td className="py-3 pr-4 text-muted-foreground">{r.feature}</td>
132
- {r.values.map((val, j) => (
133
- <td key={j} className="text-center py-3 px-4">
134
- {typeof val === 'boolean' ? (val ? <Check className="w-5 h-5 mx-auto text-primary" /> : <span className="text-muted-foreground/30">&mdash;</span>) : <span className="text-muted-foreground">{val}</span>}
135
- </td>
136
- ))}
137
- </tr>
138
- );
139
- })}
140
- </tbody>
141
- </table>
142
- </div>
143
- )}
144
- </div>
145
- </Section>
146
- );
56
+ return <PricingSectionClient {...rest} plans={plans} rows={rows} />;
147
57
  }
@@ -0,0 +1,138 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+ import { Check } from 'lucide-react';
5
+ import { cn } from '../lib/utils';
6
+ import { Section } from '../ui/section';
7
+ import { Button } from '../ui/button';
8
+ import * as TabsPrimitive from '@radix-ui/react-tabs';
9
+
10
+ interface PricingPlanProps {
11
+ name: string;
12
+ price: string;
13
+ yearlyPrice?: string;
14
+ description?: string;
15
+ features: string[];
16
+ cta: { label: string; href: string };
17
+ popular?: boolean;
18
+ }
19
+
20
+ interface ComparisonRowProps {
21
+ feature: string;
22
+ values: (string | boolean)[];
23
+ }
24
+
25
+ interface ComparisonCategoryProps {
26
+ title: string;
27
+ }
28
+
29
+ interface PricingSectionClientProps {
30
+ id?: string;
31
+ title?: string;
32
+ description?: string;
33
+ monthlyLabel?: string;
34
+ yearlyLabel?: string;
35
+ mostPopularLabel?: string;
36
+ plans: PricingPlanProps[];
37
+ rows: { type: 'category' | 'row'; props: ComparisonRowProps | ComparisonCategoryProps }[];
38
+ }
39
+
40
+ export function PricingSectionClient({ id, title, description, monthlyLabel = 'Monthly', yearlyLabel = 'Yearly', mostPopularLabel = 'Most Popular', plans, rows }: PricingSectionClientProps) {
41
+ const [isYearly, setIsYearly] = useState(false);
42
+
43
+ return (
44
+ <Section id={id}>
45
+ <div className="container-main">
46
+ {(title || description) && (
47
+ <div className="text-center mb-12">
48
+ {title && <h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4">{title}</h2>}
49
+ {description && <p className="text-lg text-muted-foreground max-w-2xl mx-auto">{description}</p>}
50
+ </div>
51
+ )}
52
+
53
+ {plans.some((p) => p.yearlyPrice) && (
54
+ <div className="flex justify-center mb-12">
55
+ <TabsPrimitive.Root defaultValue="monthly" onValueChange={(v) => setIsYearly(v === 'yearly')}>
56
+ <TabsPrimitive.List className="inline-flex rounded-full glass-1 p-1 gap-1">
57
+ <TabsPrimitive.Trigger
58
+ value="monthly"
59
+ className="rounded-full px-5 py-2 text-sm font-medium text-muted-foreground transition-all hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[state=active]:bg-primary data-[state=active]:text-primary-foreground"
60
+ >
61
+ {monthlyLabel}
62
+ </TabsPrimitive.Trigger>
63
+ <TabsPrimitive.Trigger
64
+ value="yearly"
65
+ className="rounded-full px-5 py-2 text-sm font-medium text-muted-foreground transition-all hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring data-[state=active]:bg-primary data-[state=active]:text-primary-foreground"
66
+ >
67
+ {yearlyLabel}
68
+ </TabsPrimitive.Trigger>
69
+ </TabsPrimitive.List>
70
+ </TabsPrimitive.Root>
71
+ </div>
72
+ )}
73
+
74
+ <div className={cn('grid grid-cols-1 gap-6 mb-16', plans.length === 2 && 'md:grid-cols-2', plans.length >= 3 && 'md:grid-cols-3')}>
75
+ {plans.map((plan) => (
76
+ <div key={plan.name} className={cn(
77
+ 'relative rounded-2xl p-8',
78
+ plan.popular
79
+ ? 'glass-4 ring-2 ring-primary shadow-xl'
80
+ : 'glass-1'
81
+ )}>
82
+ {plan.popular && (
83
+ <div className="absolute -top-3 left-1/2 -translate-x-1/2 px-3 py-1 bg-primary text-primary-foreground text-xs font-medium rounded-full">{mostPopularLabel}</div>
84
+ )}
85
+ <h3 className="text-xl font-bold mb-2 text-foreground">{plan.name}</h3>
86
+ {plan.description && <p className="text-sm mb-4 text-muted-foreground">{plan.description}</p>}
87
+ <div className="mb-6">
88
+ <span className="text-4xl font-bold text-foreground">{isYearly && plan.yearlyPrice ? plan.yearlyPrice : plan.price}</span>
89
+ </div>
90
+ <Button asChild className="w-full" variant={plan.popular ? 'default' : 'glow'}>
91
+ <a href={plan.cta.href}>{plan.cta.label}</a>
92
+ </Button>
93
+ <ul className="mt-6 space-y-3">
94
+ {plan.features.map((feature) => (
95
+ <li key={feature} className="flex items-start gap-2 text-sm text-muted-foreground">
96
+ <Check className="w-4 h-4 mt-0.5 shrink-0 text-primary" />
97
+ {feature}
98
+ </li>
99
+ ))}
100
+ </ul>
101
+ </div>
102
+ ))}
103
+ </div>
104
+
105
+ {rows.length > 0 && (
106
+ <div className="overflow-x-auto">
107
+ <table className="w-full text-sm">
108
+ <thead>
109
+ <tr className="border-b border-border">
110
+ <th className="text-left py-4 pr-4 font-medium text-foreground">Feature</th>
111
+ {plans.map((plan) => <th key={plan.name} className="text-center py-4 px-4 font-medium text-foreground">{plan.name}</th>)}
112
+ </tr>
113
+ </thead>
114
+ <tbody>
115
+ {rows.map((row, i) => {
116
+ if (row.type === 'category') {
117
+ return <tr key={i} className="bg-muted"><td colSpan={plans.length + 1} className="py-3 px-4 font-semibold text-foreground">{(row.props as ComparisonCategoryProps).title}</td></tr>;
118
+ }
119
+ const r = row.props as ComparisonRowProps;
120
+ return (
121
+ <tr key={i} className="border-b border-border">
122
+ <td className="py-3 pr-4 text-muted-foreground">{r.feature}</td>
123
+ {r.values.map((val, j) => (
124
+ <td key={j} className="text-center py-3 px-4">
125
+ {typeof val === 'boolean' ? (val ? <Check className="w-5 h-5 mx-auto text-primary" /> : <span className="text-muted-foreground/30">&mdash;</span>) : <span className="text-muted-foreground">{val}</span>}
126
+ </td>
127
+ ))}
128
+ </tr>
129
+ );
130
+ })}
131
+ </tbody>
132
+ </table>
133
+ </div>
134
+ )}
135
+ </div>
136
+ </Section>
137
+ );
138
+ }
@@ -1,9 +1,6 @@
1
- 'use client';
2
-
3
- import React, { useState } from 'react';
4
- import { Section } from '../ui/section';
5
- import { cn } from '../lib/utils';
6
- import { ThemeImage, type ImageSource } from '../ui/theme-image';
1
+ import React, { Children, isValidElement } from 'react';
2
+ import { TabsSectionClient } from './TabsSectionClient';
3
+ import type { ImageSource } from '../ui/theme-image';
7
4
 
8
5
  interface TabItemProps {
9
6
  label: string;
@@ -24,63 +21,13 @@ interface TabsSectionProps {
24
21
  children: React.ReactNode;
25
22
  }
26
23
 
27
- export function TabsSection({ id, title, description, children }: TabsSectionProps) {
28
- const [activeIndex, setActiveIndex] = useState(0);
29
-
24
+ export function TabsSection({ children, ...rest }: TabsSectionProps) {
30
25
  const tabs: TabItemProps[] = [];
31
- React.Children.forEach(children, (child) => {
32
- if (React.isValidElement(child) && child.type === TabItem) {
26
+ Children.forEach(children, (child) => {
27
+ if (isValidElement(child) && child.type === TabItem) {
33
28
  tabs.push(child.props as TabItemProps);
34
29
  }
35
30
  });
36
31
 
37
- const activeTab = tabs[activeIndex];
38
-
39
- return (
40
- <Section id={id}>
41
- <div className="container-main">
42
- {(title || description) && (
43
- <div className="text-center mb-12">
44
- {title && <h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4">{title}</h2>}
45
- {description && <p className="text-lg text-muted-foreground max-w-2xl mx-auto">{description}</p>}
46
- </div>
47
- )}
48
- <div className="flex justify-center mb-8">
49
- <div className="inline-flex rounded-full glass-1 p-1 gap-1">
50
- {tabs.map((tab, i) => (
51
- <button
52
- key={i}
53
- onClick={() => setActiveIndex(i)}
54
- className={cn(
55
- 'px-4 py-2 rounded-full text-sm font-medium transition-all',
56
- i === activeIndex
57
- ? 'bg-primary text-primary-foreground'
58
- : 'text-muted-foreground hover:text-foreground',
59
- )}
60
- >
61
- {tab.label}
62
- </button>
63
- ))}
64
- </div>
65
- </div>
66
- {activeTab && (
67
- <div className="glass-1 rounded-2xl p-8 md:p-12">
68
- <div className={cn('grid gap-8', activeTab.image && 'md:grid-cols-2 items-center')}>
69
- <div>
70
- {activeTab.title && (
71
- <h3 className="text-2xl font-bold text-foreground mb-4">{activeTab.title}</h3>
72
- )}
73
- {activeTab.description && (
74
- <p className="text-muted-foreground leading-relaxed">{activeTab.description}</p>
75
- )}
76
- </div>
77
- {activeTab.image && (
78
- <ThemeImage src={activeTab.image} alt={activeTab.title || activeTab.label} className="w-full rounded-xl" />
79
- )}
80
- </div>
81
- </div>
82
- )}
83
- </div>
84
- </Section>
85
- );
32
+ return <TabsSectionClient {...rest} tabs={tabs} />;
86
33
  }
@@ -0,0 +1,74 @@
1
+ 'use client';
2
+
3
+ import React, { useState } from 'react';
4
+ import { Section } from '../ui/section';
5
+ import { cn } from '../lib/utils';
6
+ import { ThemeImage, type ImageSource } from '../ui/theme-image';
7
+
8
+ interface TabItemProps {
9
+ label: string;
10
+ title?: string;
11
+ description?: string;
12
+ image?: ImageSource;
13
+ }
14
+
15
+ interface TabsSectionClientProps {
16
+ id?: string;
17
+ title?: string;
18
+ description?: string;
19
+ tabs: TabItemProps[];
20
+ }
21
+
22
+ export function TabsSectionClient({ id, title, description, tabs }: TabsSectionClientProps) {
23
+ const [activeIndex, setActiveIndex] = useState(0);
24
+
25
+ const activeTab = tabs[activeIndex];
26
+
27
+ return (
28
+ <Section id={id}>
29
+ <div className="container-main">
30
+ {(title || description) && (
31
+ <div className="text-center mb-12">
32
+ {title && <h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4">{title}</h2>}
33
+ {description && <p className="text-lg text-muted-foreground max-w-2xl mx-auto">{description}</p>}
34
+ </div>
35
+ )}
36
+ <div className="flex justify-center mb-8">
37
+ <div className="inline-flex rounded-full glass-1 p-1 gap-1">
38
+ {tabs.map((tab, i) => (
39
+ <button
40
+ key={i}
41
+ onClick={() => setActiveIndex(i)}
42
+ className={cn(
43
+ 'px-4 py-2 rounded-full text-sm font-medium transition-all',
44
+ i === activeIndex
45
+ ? 'bg-primary text-primary-foreground'
46
+ : 'text-muted-foreground hover:text-foreground',
47
+ )}
48
+ >
49
+ {tab.label}
50
+ </button>
51
+ ))}
52
+ </div>
53
+ </div>
54
+ {activeTab && (
55
+ <div className="glass-1 rounded-2xl p-8 md:p-12">
56
+ <div className={cn('grid gap-8', activeTab.image && 'md:grid-cols-2 items-center')}>
57
+ <div>
58
+ {activeTab.title && (
59
+ <h3 className="text-2xl font-bold text-foreground mb-4">{activeTab.title}</h3>
60
+ )}
61
+ {activeTab.description && (
62
+ <p className="text-muted-foreground leading-relaxed">{activeTab.description}</p>
63
+ )}
64
+ </div>
65
+ {activeTab.image && (
66
+ <ThemeImage src={activeTab.image} alt={activeTab.title || activeTab.label} className="w-full rounded-xl" />
67
+ )}
68
+ </div>
69
+ </div>
70
+ )}
71
+ </div>
72
+ </Section>
73
+ );
74
+ }
@@ -0,0 +1,55 @@
1
+ "use client";
2
+
3
+ import * as TabsPrimitive from "@radix-ui/react-tabs";
4
+ import * as React from "react";
5
+
6
+ import { cn } from "../lib/utils";
7
+
8
+ const Tabs = TabsPrimitive.Root;
9
+
10
+ function TabsList({
11
+ className,
12
+ ...props
13
+ }: React.ComponentProps<typeof TabsPrimitive.List>) {
14
+ return (
15
+ <TabsPrimitive.List
16
+ data-slot="tabs-list"
17
+ className={cn("text-muted-foreground flex items-center", className)}
18
+ {...props}
19
+ />
20
+ );
21
+ }
22
+
23
+ function TabsTrigger({
24
+ className,
25
+ ...props
26
+ }: React.ComponentProps<typeof TabsPrimitive.Trigger>) {
27
+ return (
28
+ <TabsPrimitive.Trigger
29
+ data-slot="tabs-trigger"
30
+ className={cn(
31
+ "data-[state=active]:glass-4 ring-offset-background hover:bg-accent/50 hover:text-accent-foreground focus-visible:ring-ring data-[state=active]:text-foreground flex flex-col gap-3 rounded-md border border-transparent px-5 pt-4 pb-6 text-left text-sm font-medium transition-all focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-xl dark:border-b-0",
32
+ className,
33
+ )}
34
+ {...props}
35
+ />
36
+ );
37
+ }
38
+
39
+ function TabsContent({
40
+ className,
41
+ ...props
42
+ }: React.ComponentProps<typeof TabsPrimitive.Content>) {
43
+ return (
44
+ <TabsPrimitive.Content
45
+ data-slot="tabs-content"
46
+ className={cn(
47
+ "border-border dark:border-border/20 bg-input/30 ring-offset-background focus-visible:ring-ring dark:bg-card relative overflow-hidden rounded-lg border focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-hidden",
48
+ className,
49
+ )}
50
+ {...props}
51
+ />
52
+ );
53
+ }
54
+
55
+ export { Tabs, TabsContent, TabsList, TabsTrigger };