@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.
- package/dist/blog/BlogIndex.d.ts +20 -1
- package/dist/blog/BlogIndex.d.ts.map +1 -1
- package/dist/blog/BlogIndex.js +21 -3
- package/dist/blog/BlogIndex.js.map +1 -1
- package/dist/marketing/BentoGrid.d.ts +2 -1
- package/dist/marketing/BentoGrid.d.ts.map +1 -1
- package/dist/marketing/BentoGrid.js +27 -3
- package/dist/marketing/BentoGrid.js.map +1 -1
- package/dist/marketing/PricingSection.d.ts +1 -1
- package/dist/marketing/PricingSection.d.ts.map +1 -1
- package/dist/marketing/PricingSection.js +5 -18
- package/dist/marketing/PricingSection.js.map +1 -1
- package/dist/marketing/PricingSectionClient.d.ts +35 -0
- package/dist/marketing/PricingSectionClient.d.ts.map +1 -0
- package/dist/marketing/PricingSectionClient.js +21 -0
- package/dist/marketing/PricingSectionClient.js.map +1 -0
- package/dist/marketing/TabsSection.d.ts +2 -2
- package/dist/marketing/TabsSection.d.ts.map +1 -1
- package/dist/marketing/TabsSection.js +7 -14
- package/dist/marketing/TabsSection.js.map +1 -1
- package/dist/marketing/TabsSectionClient.d.ts +16 -0
- package/dist/marketing/TabsSectionClient.d.ts.map +1 -0
- package/dist/marketing/TabsSectionClient.js +14 -0
- package/dist/marketing/TabsSectionClient.js.map +1 -0
- package/dist/ui/tabs.d.ts +8 -0
- package/dist/ui/tabs.d.ts.map +1 -0
- package/dist/ui/tabs.js +16 -0
- package/dist/ui/tabs.js.map +1 -0
- package/package.json +5 -4
- package/src/blog/BlogIndex.tsx +104 -1
- package/src/marketing/BentoGrid.tsx +80 -3
- package/src/marketing/PricingSection.tsx +4 -94
- package/src/marketing/PricingSectionClient.tsx +138 -0
- package/src/marketing/TabsSection.tsx +7 -60
- package/src/marketing/TabsSectionClient.tsx +74 -0
- package/src/ui/tabs.tsx +55 -0
package/dist/blog/BlogIndex.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/blog/BlogIndex.js
CHANGED
|
@@ -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
|
-
|
|
4
|
-
|
|
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;
|
|
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;
|
|
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
|
-
|
|
6
|
-
|
|
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-
|
|
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;
|
|
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({
|
|
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":"
|
|
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
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
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({
|
|
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
|
|
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":"AAAA,
|
|
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 {
|
|
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({
|
|
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":"
|
|
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
|
-
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
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({
|
|
11
|
-
const [activeIndex, setActiveIndex] = useState(0);
|
|
7
|
+
export function TabsSection({ children, ...rest }) {
|
|
12
8
|
const tabs = [];
|
|
13
|
-
|
|
14
|
-
if (
|
|
9
|
+
Children.forEach(children, (child) => {
|
|
10
|
+
if (isValidElement(child) && child.type === TabItem) {
|
|
15
11
|
tabs.push(child.props);
|
|
16
12
|
}
|
|
17
13
|
});
|
|
18
|
-
|
|
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,
|
|
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"}
|
package/dist/ui/tabs.js
ADDED
|
@@ -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.
|
|
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
|
-
"
|
|
51
|
+
"@types/react": "^19",
|
|
52
|
+
"tailwindcss": "^4",
|
|
53
|
+
"typescript": "^5.8.3"
|
|
53
54
|
},
|
|
54
55
|
"peerDependencies": {
|
|
55
56
|
"next": ">=15"
|
package/src/blog/BlogIndex.tsx
CHANGED
|
@@ -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
|
-
|
|
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-
|
|
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
|
-
|
|
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({
|
|
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">—</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">—</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
|
-
|
|
2
|
-
|
|
3
|
-
import
|
|
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({
|
|
28
|
-
const [activeIndex, setActiveIndex] = useState(0);
|
|
29
|
-
|
|
24
|
+
export function TabsSection({ children, ...rest }: TabsSectionProps) {
|
|
30
25
|
const tabs: TabItemProps[] = [];
|
|
31
|
-
|
|
32
|
-
if (
|
|
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
|
-
|
|
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
|
+
}
|
package/src/ui/tabs.tsx
ADDED
|
@@ -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 };
|