@burdenoff/website-sdk 0.0.0-dev
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/LICENSE +24 -0
- package/README.md +357 -0
- package/dist/analytics/index.d.mts +27 -0
- package/dist/analytics/index.d.ts +27 -0
- package/dist/analytics/index.js +36 -0
- package/dist/analytics/index.js.map +1 -0
- package/dist/analytics/index.mjs +34 -0
- package/dist/analytics/index.mjs.map +1 -0
- package/dist/client/index.d.mts +29 -0
- package/dist/client/index.d.ts +29 -0
- package/dist/client/index.js +261 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/index.mjs +255 -0
- package/dist/client/index.mjs.map +1 -0
- package/dist/components/contact.d.mts +144 -0
- package/dist/components/contact.d.ts +144 -0
- package/dist/components/contact.js +806 -0
- package/dist/components/contact.js.map +1 -0
- package/dist/components/contact.mjs +780 -0
- package/dist/components/contact.mjs.map +1 -0
- package/dist/components/electron-download.d.mts +32 -0
- package/dist/components/electron-download.d.ts +32 -0
- package/dist/components/electron-download.js +131 -0
- package/dist/components/electron-download.js.map +1 -0
- package/dist/components/electron-download.mjs +108 -0
- package/dist/components/electron-download.mjs.map +1 -0
- package/dist/components/launch-countdown.d.mts +15 -0
- package/dist/components/launch-countdown.d.ts +15 -0
- package/dist/components/launch-countdown.js +89 -0
- package/dist/components/launch-countdown.js.map +1 -0
- package/dist/components/launch-countdown.mjs +87 -0
- package/dist/components/launch-countdown.mjs.map +1 -0
- package/dist/components/newsletter.d.mts +155 -0
- package/dist/components/newsletter.d.ts +155 -0
- package/dist/components/newsletter.js +880 -0
- package/dist/components/newsletter.js.map +1 -0
- package/dist/components/newsletter.mjs +854 -0
- package/dist/components/newsletter.mjs.map +1 -0
- package/dist/components/partners.d.mts +88 -0
- package/dist/components/partners.d.ts +88 -0
- package/dist/components/partners.js +1169 -0
- package/dist/components/partners.js.map +1 -0
- package/dist/components/partners.mjs +1143 -0
- package/dist/components/partners.mjs.map +1 -0
- package/dist/components/pricing.d.mts +194 -0
- package/dist/components/pricing.d.ts +194 -0
- package/dist/components/pricing.js +813 -0
- package/dist/components/pricing.js.map +1 -0
- package/dist/components/pricing.mjs +791 -0
- package/dist/components/pricing.mjs.map +1 -0
- package/dist/components/problems-solved.d.mts +37 -0
- package/dist/components/problems-solved.d.ts +37 -0
- package/dist/components/problems-solved.js +157 -0
- package/dist/components/problems-solved.js.map +1 -0
- package/dist/components/problems-solved.mjs +135 -0
- package/dist/components/problems-solved.mjs.map +1 -0
- package/dist/components/resource-links.d.mts +28 -0
- package/dist/components/resource-links.d.ts +28 -0
- package/dist/components/resource-links.js +229 -0
- package/dist/components/resource-links.js.map +1 -0
- package/dist/components/resource-links.mjs +227 -0
- package/dist/components/resource-links.mjs.map +1 -0
- package/dist/components/social-links.d.mts +37 -0
- package/dist/components/social-links.d.ts +37 -0
- package/dist/components/social-links.js +268 -0
- package/dist/components/social-links.js.map +1 -0
- package/dist/components/social-links.mjs +266 -0
- package/dist/components/social-links.mjs.map +1 -0
- package/dist/components/unsubscribe.d.mts +14 -0
- package/dist/components/unsubscribe.d.ts +14 -0
- package/dist/components/unsubscribe.js +538 -0
- package/dist/components/unsubscribe.js.map +1 -0
- package/dist/components/unsubscribe.mjs +517 -0
- package/dist/components/unsubscribe.mjs.map +1 -0
- package/dist/components/waitlist.d.mts +70 -0
- package/dist/components/waitlist.d.ts +70 -0
- package/dist/components/waitlist.js +864 -0
- package/dist/components/waitlist.js.map +1 -0
- package/dist/components/waitlist.mjs +841 -0
- package/dist/components/waitlist.mjs.map +1 -0
- package/dist/components/website-switcher.d.mts +27 -0
- package/dist/components/website-switcher.d.ts +27 -0
- package/dist/components/website-switcher.js +168 -0
- package/dist/components/website-switcher.js.map +1 -0
- package/dist/components/website-switcher.mjs +146 -0
- package/dist/components/website-switcher.mjs.map +1 -0
- package/dist/content/index.d.mts +247 -0
- package/dist/content/index.d.ts +247 -0
- package/dist/content/index.js +2458 -0
- package/dist/content/index.js.map +1 -0
- package/dist/content/index.mjs +2428 -0
- package/dist/content/index.mjs.map +1 -0
- package/dist/index.d.mts +72 -0
- package/dist/index.d.ts +72 -0
- package/dist/index.js +6273 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +6204 -0
- package/dist/index.mjs.map +1 -0
- package/dist/product/index.d.mts +94 -0
- package/dist/product/index.d.ts +94 -0
- package/dist/product/index.js +282 -0
- package/dist/product/index.js.map +1 -0
- package/dist/product/index.mjs +278 -0
- package/dist/product/index.mjs.map +1 -0
- package/dist/provider-D7Rij9UM.d.mts +86 -0
- package/dist/provider-D7Rij9UM.d.ts +86 -0
- package/dist/seo/index.d.mts +64 -0
- package/dist/seo/index.d.ts +64 -0
- package/dist/seo/index.js +61 -0
- package/dist/seo/index.js.map +1 -0
- package/dist/seo/index.mjs +59 -0
- package/dist/seo/index.mjs.map +1 -0
- package/dist/utils/index.d.mts +21 -0
- package/dist/utils/index.d.ts +21 -0
- package/dist/utils/index.js +12 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/index.mjs +10 -0
- package/dist/utils/index.mjs.map +1 -0
- package/package.json +168 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react = require('react');
|
|
4
|
+
var lucideReact = require('lucide-react');
|
|
5
|
+
var clsx = require('clsx');
|
|
6
|
+
var tailwindMerge = require('tailwind-merge');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
function cn(...inputs) {
|
|
10
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
11
|
+
}
|
|
12
|
+
function splitRemaining(ms) {
|
|
13
|
+
const clamped = Math.max(0, ms);
|
|
14
|
+
const totalSeconds = Math.floor(clamped / 1e3);
|
|
15
|
+
return {
|
|
16
|
+
days: Math.floor(totalSeconds / 86400),
|
|
17
|
+
hours: Math.floor(totalSeconds % 86400 / 3600),
|
|
18
|
+
minutes: Math.floor(totalSeconds % 3600 / 60),
|
|
19
|
+
seconds: totalSeconds % 60
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function padTwo(n) {
|
|
23
|
+
return String(n).padStart(2, "0");
|
|
24
|
+
}
|
|
25
|
+
function LaunchCountdown({
|
|
26
|
+
launchDate,
|
|
27
|
+
launchLabel,
|
|
28
|
+
launchedMessage = "We have launched!",
|
|
29
|
+
className
|
|
30
|
+
}) {
|
|
31
|
+
const [now, setNow] = react.useState(() => Date.now());
|
|
32
|
+
const remainingMs = launchDate - now;
|
|
33
|
+
const hasLaunched = remainingMs <= 0;
|
|
34
|
+
react.useEffect(() => {
|
|
35
|
+
if (hasLaunched) return;
|
|
36
|
+
const interval = setInterval(() => {
|
|
37
|
+
setNow(Date.now());
|
|
38
|
+
}, 1e3);
|
|
39
|
+
return () => clearInterval(interval);
|
|
40
|
+
}, [hasLaunched]);
|
|
41
|
+
const { days, hours, minutes, seconds } = splitRemaining(remainingMs);
|
|
42
|
+
const cells = [
|
|
43
|
+
{ label: "Days", value: padTwo(Math.min(days, 99)) },
|
|
44
|
+
{ label: "Hours", value: padTwo(hours) },
|
|
45
|
+
{ label: "Minutes", value: padTwo(minutes) },
|
|
46
|
+
{ label: "Seconds", value: padTwo(seconds) }
|
|
47
|
+
];
|
|
48
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
49
|
+
"div",
|
|
50
|
+
{
|
|
51
|
+
className: cn("inline-flex flex-col gap-3", className),
|
|
52
|
+
role: "timer",
|
|
53
|
+
"aria-label": launchLabel ?? (hasLaunched ? launchedMessage : `Countdown to launch`),
|
|
54
|
+
children: [
|
|
55
|
+
!hasLaunched && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xs font-mono uppercase tracking-wider text-muted-foreground", children: "Launching in" }),
|
|
56
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 sm:gap-3", children: hasLaunched ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
57
|
+
"div",
|
|
58
|
+
{
|
|
59
|
+
className: cn(
|
|
60
|
+
"flex min-w-[15rem] items-center justify-center gap-3 rounded-lg border border-primary/30 bg-primary/10 px-6 py-4",
|
|
61
|
+
"text-center"
|
|
62
|
+
),
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Rocket, { className: "h-5 w-5 text-primary", "aria-hidden": "true" }),
|
|
65
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-lg font-semibold text-foreground", children: launchedMessage })
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
) : cells.map((cell) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
69
|
+
"div",
|
|
70
|
+
{
|
|
71
|
+
className: cn(
|
|
72
|
+
"flex min-w-[3.75rem] flex-col items-center rounded-lg border border-border/60 bg-card px-3 py-2 sm:min-w-[4.5rem] sm:px-4 sm:py-3"
|
|
73
|
+
),
|
|
74
|
+
children: [
|
|
75
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-mono text-2xl font-bold tabular-nums text-foreground sm:text-3xl", children: cell.value }),
|
|
76
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground sm:text-xs", children: cell.label })
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
cell.label
|
|
80
|
+
)) }),
|
|
81
|
+
!hasLaunched && launchLabel && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground", children: launchLabel })
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
exports.LaunchCountdown = LaunchCountdown;
|
|
88
|
+
//# sourceMappingURL=launch-countdown.js.map
|
|
89
|
+
//# sourceMappingURL=launch-countdown.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/cn.ts","../../src/components/launch-countdown.tsx"],"names":["twMerge","clsx","useState","useEffect","jsxs","jsx","Rocket"],"mappings":"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","file":"launch-countdown.js","sourcesContent":["/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx and tailwind-merge for optimal class handling\n */\n\nimport { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge multiple class names intelligently\n * - Handles conditional classes via clsx\n * - Deduplicates and resolves Tailwind conflicts via tailwind-merge\n *\n * @example\n * ```ts\n * cn('px-4 py-2', 'bg-blue-500', { 'text-white': isActive })\n * // => 'px-4 py-2 bg-blue-500 text-white' (if isActive is true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\n/**\n * LaunchCountdown Component\n *\n * Reusable countdown timer counting down to a `launchDate` (epoch ms). Renders\n * a fixed-size grid of days / hours / minutes / seconds and flips to a\n * \"Launched\" message once the moment passes. Uses semantic theme tokens so it\n * works in light, dark, and high-contrast themes.\n *\n * @example\n * ```tsx\n * import { LaunchCountdown } from '@burdenoff/website-sdk'\n *\n * <LaunchCountdown launchDate={Date.UTC(2026, 7, 15, 0, 0, 0)} />\n * ```\n */\n\nimport { useEffect, useState } from \"react\";\n\nimport { Rocket } from \"lucide-react\";\n\nimport { cn } from \"../utils/cn\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface LaunchCountdownProps {\n /** Launch moment as epoch milliseconds. */\n launchDate: number;\n /** Optional accessible label describing the launch moment. */\n launchLabel?: string;\n /** Message shown once the countdown reaches zero. */\n launchedMessage?: string;\n /** Optional additional CSS class for the outer container. */\n className?: string;\n}\n\ninterface Remaining {\n days: number;\n hours: number;\n minutes: number;\n seconds: number;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction splitRemaining(ms: number): Remaining {\n const clamped = Math.max(0, ms);\n const totalSeconds = Math.floor(clamped / 1000);\n return {\n days: Math.floor(totalSeconds / 86400),\n hours: Math.floor((totalSeconds % 86400) / 3600),\n minutes: Math.floor((totalSeconds % 3600) / 60),\n seconds: totalSeconds % 60,\n };\n}\n\nfunction padTwo(n: number): string {\n return String(n).padStart(2, \"0\");\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function LaunchCountdown({\n launchDate,\n launchLabel,\n launchedMessage = \"We have launched!\",\n className,\n}: LaunchCountdownProps) {\n const [now, setNow] = useState(() => Date.now());\n const remainingMs = launchDate - now;\n const hasLaunched = remainingMs <= 0;\n\n useEffect(() => {\n if (hasLaunched) return;\n\n const interval = setInterval(() => {\n setNow(Date.now());\n }, 1000);\n\n return () => clearInterval(interval);\n }, [hasLaunched]);\n\n const { days, hours, minutes, seconds } = splitRemaining(remainingMs);\n\n const cells: Array<{ label: string; value: string }> = [\n { label: \"Days\", value: padTwo(Math.min(days, 99)) },\n { label: \"Hours\", value: padTwo(hours) },\n { label: \"Minutes\", value: padTwo(minutes) },\n { label: \"Seconds\", value: padTwo(seconds) },\n ];\n\n return (\n <div\n className={cn(\"inline-flex flex-col gap-3\", className)}\n role=\"timer\"\n aria-label={\n launchLabel ?? (hasLaunched ? launchedMessage : `Countdown to launch`)\n }\n >\n {!hasLaunched && (\n <span className=\"text-xs font-mono uppercase tracking-wider text-muted-foreground\">\n Launching in\n </span>\n )}\n\n <div className=\"flex gap-2 sm:gap-3\">\n {hasLaunched ? (\n <div\n className={cn(\n \"flex min-w-[15rem] items-center justify-center gap-3 rounded-lg border border-primary/30 bg-primary/10 px-6 py-4\",\n \"text-center\",\n )}\n >\n <Rocket className=\"h-5 w-5 text-primary\" aria-hidden=\"true\" />\n <span className=\"text-lg font-semibold text-foreground\">\n {launchedMessage}\n </span>\n </div>\n ) : (\n cells.map((cell) => (\n <div\n key={cell.label}\n className={cn(\n \"flex min-w-[3.75rem] flex-col items-center rounded-lg border border-border/60 bg-card px-3 py-2 sm:min-w-[4.5rem] sm:px-4 sm:py-3\",\n )}\n >\n <span className=\"font-mono text-2xl font-bold tabular-nums text-foreground sm:text-3xl\">\n {cell.value}\n </span>\n <span className=\"mt-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground sm:text-xs\">\n {cell.label}\n </span>\n </div>\n ))\n )}\n </div>\n\n {!hasLaunched && launchLabel && (\n <span className=\"text-sm text-muted-foreground\">{launchLabel}</span>\n )}\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { useState, useEffect } from 'react';
|
|
2
|
+
import { Rocket } from 'lucide-react';
|
|
3
|
+
import { clsx } from 'clsx';
|
|
4
|
+
import { twMerge } from 'tailwind-merge';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function cn(...inputs) {
|
|
8
|
+
return twMerge(clsx(inputs));
|
|
9
|
+
}
|
|
10
|
+
function splitRemaining(ms) {
|
|
11
|
+
const clamped = Math.max(0, ms);
|
|
12
|
+
const totalSeconds = Math.floor(clamped / 1e3);
|
|
13
|
+
return {
|
|
14
|
+
days: Math.floor(totalSeconds / 86400),
|
|
15
|
+
hours: Math.floor(totalSeconds % 86400 / 3600),
|
|
16
|
+
minutes: Math.floor(totalSeconds % 3600 / 60),
|
|
17
|
+
seconds: totalSeconds % 60
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function padTwo(n) {
|
|
21
|
+
return String(n).padStart(2, "0");
|
|
22
|
+
}
|
|
23
|
+
function LaunchCountdown({
|
|
24
|
+
launchDate,
|
|
25
|
+
launchLabel,
|
|
26
|
+
launchedMessage = "We have launched!",
|
|
27
|
+
className
|
|
28
|
+
}) {
|
|
29
|
+
const [now, setNow] = useState(() => Date.now());
|
|
30
|
+
const remainingMs = launchDate - now;
|
|
31
|
+
const hasLaunched = remainingMs <= 0;
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (hasLaunched) return;
|
|
34
|
+
const interval = setInterval(() => {
|
|
35
|
+
setNow(Date.now());
|
|
36
|
+
}, 1e3);
|
|
37
|
+
return () => clearInterval(interval);
|
|
38
|
+
}, [hasLaunched]);
|
|
39
|
+
const { days, hours, minutes, seconds } = splitRemaining(remainingMs);
|
|
40
|
+
const cells = [
|
|
41
|
+
{ label: "Days", value: padTwo(Math.min(days, 99)) },
|
|
42
|
+
{ label: "Hours", value: padTwo(hours) },
|
|
43
|
+
{ label: "Minutes", value: padTwo(minutes) },
|
|
44
|
+
{ label: "Seconds", value: padTwo(seconds) }
|
|
45
|
+
];
|
|
46
|
+
return /* @__PURE__ */ jsxs(
|
|
47
|
+
"div",
|
|
48
|
+
{
|
|
49
|
+
className: cn("inline-flex flex-col gap-3", className),
|
|
50
|
+
role: "timer",
|
|
51
|
+
"aria-label": launchLabel ?? (hasLaunched ? launchedMessage : `Countdown to launch`),
|
|
52
|
+
children: [
|
|
53
|
+
!hasLaunched && /* @__PURE__ */ jsx("span", { className: "text-xs font-mono uppercase tracking-wider text-muted-foreground", children: "Launching in" }),
|
|
54
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-2 sm:gap-3", children: hasLaunched ? /* @__PURE__ */ jsxs(
|
|
55
|
+
"div",
|
|
56
|
+
{
|
|
57
|
+
className: cn(
|
|
58
|
+
"flex min-w-[15rem] items-center justify-center gap-3 rounded-lg border border-primary/30 bg-primary/10 px-6 py-4",
|
|
59
|
+
"text-center"
|
|
60
|
+
),
|
|
61
|
+
children: [
|
|
62
|
+
/* @__PURE__ */ jsx(Rocket, { className: "h-5 w-5 text-primary", "aria-hidden": "true" }),
|
|
63
|
+
/* @__PURE__ */ jsx("span", { className: "text-lg font-semibold text-foreground", children: launchedMessage })
|
|
64
|
+
]
|
|
65
|
+
}
|
|
66
|
+
) : cells.map((cell) => /* @__PURE__ */ jsxs(
|
|
67
|
+
"div",
|
|
68
|
+
{
|
|
69
|
+
className: cn(
|
|
70
|
+
"flex min-w-[3.75rem] flex-col items-center rounded-lg border border-border/60 bg-card px-3 py-2 sm:min-w-[4.5rem] sm:px-4 sm:py-3"
|
|
71
|
+
),
|
|
72
|
+
children: [
|
|
73
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono text-2xl font-bold tabular-nums text-foreground sm:text-3xl", children: cell.value }),
|
|
74
|
+
/* @__PURE__ */ jsx("span", { className: "mt-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground sm:text-xs", children: cell.label })
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
cell.label
|
|
78
|
+
)) }),
|
|
79
|
+
!hasLaunched && launchLabel && /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: launchLabel })
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export { LaunchCountdown };
|
|
86
|
+
//# sourceMappingURL=launch-countdown.mjs.map
|
|
87
|
+
//# sourceMappingURL=launch-countdown.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/utils/cn.ts","../../src/components/launch-countdown.tsx"],"names":[],"mappings":"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","file":"launch-countdown.mjs","sourcesContent":["/**\n * Utility function to merge Tailwind CSS classes\n * Combines clsx and tailwind-merge for optimal class handling\n */\n\nimport { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\n/**\n * Merge multiple class names intelligently\n * - Handles conditional classes via clsx\n * - Deduplicates and resolves Tailwind conflicts via tailwind-merge\n *\n * @example\n * ```ts\n * cn('px-4 py-2', 'bg-blue-500', { 'text-white': isActive })\n * // => 'px-4 py-2 bg-blue-500 text-white' (if isActive is true)\n * ```\n */\nexport function cn(...inputs: ClassValue[]): string {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\n/**\n * LaunchCountdown Component\n *\n * Reusable countdown timer counting down to a `launchDate` (epoch ms). Renders\n * a fixed-size grid of days / hours / minutes / seconds and flips to a\n * \"Launched\" message once the moment passes. Uses semantic theme tokens so it\n * works in light, dark, and high-contrast themes.\n *\n * @example\n * ```tsx\n * import { LaunchCountdown } from '@burdenoff/website-sdk'\n *\n * <LaunchCountdown launchDate={Date.UTC(2026, 7, 15, 0, 0, 0)} />\n * ```\n */\n\nimport { useEffect, useState } from \"react\";\n\nimport { Rocket } from \"lucide-react\";\n\nimport { cn } from \"../utils/cn\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface LaunchCountdownProps {\n /** Launch moment as epoch milliseconds. */\n launchDate: number;\n /** Optional accessible label describing the launch moment. */\n launchLabel?: string;\n /** Message shown once the countdown reaches zero. */\n launchedMessage?: string;\n /** Optional additional CSS class for the outer container. */\n className?: string;\n}\n\ninterface Remaining {\n days: number;\n hours: number;\n minutes: number;\n seconds: number;\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\nfunction splitRemaining(ms: number): Remaining {\n const clamped = Math.max(0, ms);\n const totalSeconds = Math.floor(clamped / 1000);\n return {\n days: Math.floor(totalSeconds / 86400),\n hours: Math.floor((totalSeconds % 86400) / 3600),\n minutes: Math.floor((totalSeconds % 3600) / 60),\n seconds: totalSeconds % 60,\n };\n}\n\nfunction padTwo(n: number): string {\n return String(n).padStart(2, \"0\");\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport function LaunchCountdown({\n launchDate,\n launchLabel,\n launchedMessage = \"We have launched!\",\n className,\n}: LaunchCountdownProps) {\n const [now, setNow] = useState(() => Date.now());\n const remainingMs = launchDate - now;\n const hasLaunched = remainingMs <= 0;\n\n useEffect(() => {\n if (hasLaunched) return;\n\n const interval = setInterval(() => {\n setNow(Date.now());\n }, 1000);\n\n return () => clearInterval(interval);\n }, [hasLaunched]);\n\n const { days, hours, minutes, seconds } = splitRemaining(remainingMs);\n\n const cells: Array<{ label: string; value: string }> = [\n { label: \"Days\", value: padTwo(Math.min(days, 99)) },\n { label: \"Hours\", value: padTwo(hours) },\n { label: \"Minutes\", value: padTwo(minutes) },\n { label: \"Seconds\", value: padTwo(seconds) },\n ];\n\n return (\n <div\n className={cn(\"inline-flex flex-col gap-3\", className)}\n role=\"timer\"\n aria-label={\n launchLabel ?? (hasLaunched ? launchedMessage : `Countdown to launch`)\n }\n >\n {!hasLaunched && (\n <span className=\"text-xs font-mono uppercase tracking-wider text-muted-foreground\">\n Launching in\n </span>\n )}\n\n <div className=\"flex gap-2 sm:gap-3\">\n {hasLaunched ? (\n <div\n className={cn(\n \"flex min-w-[15rem] items-center justify-center gap-3 rounded-lg border border-primary/30 bg-primary/10 px-6 py-4\",\n \"text-center\",\n )}\n >\n <Rocket className=\"h-5 w-5 text-primary\" aria-hidden=\"true\" />\n <span className=\"text-lg font-semibold text-foreground\">\n {launchedMessage}\n </span>\n </div>\n ) : (\n cells.map((cell) => (\n <div\n key={cell.label}\n className={cn(\n \"flex min-w-[3.75rem] flex-col items-center rounded-lg border border-border/60 bg-card px-3 py-2 sm:min-w-[4.5rem] sm:px-4 sm:py-3\",\n )}\n >\n <span className=\"font-mono text-2xl font-bold tabular-nums text-foreground sm:text-3xl\">\n {cell.value}\n </span>\n <span className=\"mt-1 text-[10px] font-medium uppercase tracking-wide text-muted-foreground sm:text-xs\">\n {cell.label}\n </span>\n </div>\n ))\n )}\n </div>\n\n {!hasLaunched && launchLabel && (\n <span className=\"text-sm text-muted-foreground\">{launchLabel}</span>\n )}\n </div>\n );\n}\n"]}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Newsletter Component
|
|
5
|
+
* Reusable newsletter subscription component with email validation and rate limiting.
|
|
6
|
+
* Uses GraphQL mutations via the WebSDKClient provider.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { NewsletterPage } from '@burdenoff/website-sdk/components/newsletter'
|
|
11
|
+
*
|
|
12
|
+
* function Newsletter() {
|
|
13
|
+
* return (
|
|
14
|
+
* <NewsletterPage
|
|
15
|
+
* productName="BigConsole"
|
|
16
|
+
* layout="centered"
|
|
17
|
+
* seoTitle="Subscribe to Newsletter"
|
|
18
|
+
* seoDescription="Stay updated with our latest news"
|
|
19
|
+
* seoKeywords="newsletter, updates, news"
|
|
20
|
+
* />
|
|
21
|
+
* )
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Newsletter page component configuration props
|
|
27
|
+
*/
|
|
28
|
+
interface NewsletterPageProps {
|
|
29
|
+
/**
|
|
30
|
+
* Product name (used in title and branding).
|
|
31
|
+
* Falls back to SDK config productId if not provided.
|
|
32
|
+
*/
|
|
33
|
+
productName?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Layout variant
|
|
36
|
+
* @default 'centered'
|
|
37
|
+
*/
|
|
38
|
+
layout?: "centered" | "split";
|
|
39
|
+
/**
|
|
40
|
+
* Rate limit attempts
|
|
41
|
+
* @default 3
|
|
42
|
+
*/
|
|
43
|
+
rateLimitAttempts?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Rate limit window in minutes
|
|
46
|
+
* @default 15
|
|
47
|
+
*/
|
|
48
|
+
rateLimitWindowMinutes?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Hero section heading
|
|
51
|
+
* @default 'Stay Updated'
|
|
52
|
+
*/
|
|
53
|
+
heroHeading?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Hero section subtitle
|
|
56
|
+
* @default 'Subscribe to our newsletter'
|
|
57
|
+
*/
|
|
58
|
+
heroSubtitle?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Hero section description
|
|
61
|
+
*/
|
|
62
|
+
heroDescription?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Features to display (for split layout)
|
|
65
|
+
*/
|
|
66
|
+
features?: Array<{
|
|
67
|
+
icon: React.ReactNode;
|
|
68
|
+
title: string;
|
|
69
|
+
description: string;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Success message after subscription
|
|
73
|
+
* @default 'Successfully subscribed to newsletter!'
|
|
74
|
+
*/
|
|
75
|
+
successMessage?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Error message on failure
|
|
78
|
+
* @default 'Failed to subscribe. Please try again.'
|
|
79
|
+
*/
|
|
80
|
+
errorMessage?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Rate limit error message
|
|
83
|
+
*/
|
|
84
|
+
rateLimitMessage?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Custom CSS class for container
|
|
87
|
+
*/
|
|
88
|
+
className?: string;
|
|
89
|
+
/**
|
|
90
|
+
* SEO: Page title (optional, for SEO meta tags)
|
|
91
|
+
*/
|
|
92
|
+
seoTitle?: string;
|
|
93
|
+
/**
|
|
94
|
+
* SEO: Page description (optional, for SEO meta tags)
|
|
95
|
+
*/
|
|
96
|
+
seoDescription?: string;
|
|
97
|
+
/**
|
|
98
|
+
* SEO: Keywords (optional, for SEO meta tags)
|
|
99
|
+
*/
|
|
100
|
+
seoKeywords?: string;
|
|
101
|
+
/**
|
|
102
|
+
* SEO: OG Image URL (optional, for SEO meta tags)
|
|
103
|
+
*/
|
|
104
|
+
seoImage?: string;
|
|
105
|
+
/**
|
|
106
|
+
* SEO: Canonical URL (optional, for SEO meta tags)
|
|
107
|
+
*/
|
|
108
|
+
seoUrl?: string;
|
|
109
|
+
/**
|
|
110
|
+
* reCAPTCHA site key (optional, falls back to SDK config)
|
|
111
|
+
*/
|
|
112
|
+
recaptchaSiteKey?: string;
|
|
113
|
+
}
|
|
114
|
+
declare function NewsletterPage({ productName, layout, rateLimitAttempts, rateLimitWindowMinutes, heroHeading, heroSubtitle, heroDescription, features, successMessage, errorMessage, rateLimitMessage, className, seoTitle, seoDescription, seoKeywords, seoImage, seoUrl, recaptchaSiteKey, }: NewsletterPageProps): react_jsx_runtime.JSX.Element;
|
|
115
|
+
interface NewsletterFormProps {
|
|
116
|
+
/**
|
|
117
|
+
* Layout variant
|
|
118
|
+
* @default 'inline'
|
|
119
|
+
*/
|
|
120
|
+
variant?: "inline" | "stacked";
|
|
121
|
+
/**
|
|
122
|
+
* Show email label
|
|
123
|
+
* @default false
|
|
124
|
+
*/
|
|
125
|
+
showLabel?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Show description text
|
|
128
|
+
* @default false
|
|
129
|
+
*/
|
|
130
|
+
showDescription?: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Description text
|
|
133
|
+
* @default 'Subscribe to our newsletter for updates'
|
|
134
|
+
*/
|
|
135
|
+
description?: string;
|
|
136
|
+
/**
|
|
137
|
+
* Button text
|
|
138
|
+
* @default 'Subscribe'
|
|
139
|
+
*/
|
|
140
|
+
buttonText?: string;
|
|
141
|
+
/**
|
|
142
|
+
* Custom CSS class
|
|
143
|
+
*/
|
|
144
|
+
className?: string;
|
|
145
|
+
/**
|
|
146
|
+
* reCAPTCHA site key (optional, falls back to SDK config)
|
|
147
|
+
*/
|
|
148
|
+
recaptchaSiteKey?: string;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Lightweight newsletter subscription form for inline use (e.g., footers)
|
|
152
|
+
*/
|
|
153
|
+
declare function NewsletterForm({ variant, showLabel, showDescription, description, buttonText, className, recaptchaSiteKey, }: NewsletterFormProps): react_jsx_runtime.JSX.Element;
|
|
154
|
+
|
|
155
|
+
export { NewsletterForm, type NewsletterFormProps, NewsletterPage, type NewsletterPageProps };
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Newsletter Component
|
|
5
|
+
* Reusable newsletter subscription component with email validation and rate limiting.
|
|
6
|
+
* Uses GraphQL mutations via the WebSDKClient provider.
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* ```tsx
|
|
10
|
+
* import { NewsletterPage } from '@burdenoff/website-sdk/components/newsletter'
|
|
11
|
+
*
|
|
12
|
+
* function Newsletter() {
|
|
13
|
+
* return (
|
|
14
|
+
* <NewsletterPage
|
|
15
|
+
* productName="BigConsole"
|
|
16
|
+
* layout="centered"
|
|
17
|
+
* seoTitle="Subscribe to Newsletter"
|
|
18
|
+
* seoDescription="Stay updated with our latest news"
|
|
19
|
+
* seoKeywords="newsletter, updates, news"
|
|
20
|
+
* />
|
|
21
|
+
* )
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Newsletter page component configuration props
|
|
27
|
+
*/
|
|
28
|
+
interface NewsletterPageProps {
|
|
29
|
+
/**
|
|
30
|
+
* Product name (used in title and branding).
|
|
31
|
+
* Falls back to SDK config productId if not provided.
|
|
32
|
+
*/
|
|
33
|
+
productName?: string;
|
|
34
|
+
/**
|
|
35
|
+
* Layout variant
|
|
36
|
+
* @default 'centered'
|
|
37
|
+
*/
|
|
38
|
+
layout?: "centered" | "split";
|
|
39
|
+
/**
|
|
40
|
+
* Rate limit attempts
|
|
41
|
+
* @default 3
|
|
42
|
+
*/
|
|
43
|
+
rateLimitAttempts?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Rate limit window in minutes
|
|
46
|
+
* @default 15
|
|
47
|
+
*/
|
|
48
|
+
rateLimitWindowMinutes?: number;
|
|
49
|
+
/**
|
|
50
|
+
* Hero section heading
|
|
51
|
+
* @default 'Stay Updated'
|
|
52
|
+
*/
|
|
53
|
+
heroHeading?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Hero section subtitle
|
|
56
|
+
* @default 'Subscribe to our newsletter'
|
|
57
|
+
*/
|
|
58
|
+
heroSubtitle?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Hero section description
|
|
61
|
+
*/
|
|
62
|
+
heroDescription?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Features to display (for split layout)
|
|
65
|
+
*/
|
|
66
|
+
features?: Array<{
|
|
67
|
+
icon: React.ReactNode;
|
|
68
|
+
title: string;
|
|
69
|
+
description: string;
|
|
70
|
+
}>;
|
|
71
|
+
/**
|
|
72
|
+
* Success message after subscription
|
|
73
|
+
* @default 'Successfully subscribed to newsletter!'
|
|
74
|
+
*/
|
|
75
|
+
successMessage?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Error message on failure
|
|
78
|
+
* @default 'Failed to subscribe. Please try again.'
|
|
79
|
+
*/
|
|
80
|
+
errorMessage?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Rate limit error message
|
|
83
|
+
*/
|
|
84
|
+
rateLimitMessage?: string;
|
|
85
|
+
/**
|
|
86
|
+
* Custom CSS class for container
|
|
87
|
+
*/
|
|
88
|
+
className?: string;
|
|
89
|
+
/**
|
|
90
|
+
* SEO: Page title (optional, for SEO meta tags)
|
|
91
|
+
*/
|
|
92
|
+
seoTitle?: string;
|
|
93
|
+
/**
|
|
94
|
+
* SEO: Page description (optional, for SEO meta tags)
|
|
95
|
+
*/
|
|
96
|
+
seoDescription?: string;
|
|
97
|
+
/**
|
|
98
|
+
* SEO: Keywords (optional, for SEO meta tags)
|
|
99
|
+
*/
|
|
100
|
+
seoKeywords?: string;
|
|
101
|
+
/**
|
|
102
|
+
* SEO: OG Image URL (optional, for SEO meta tags)
|
|
103
|
+
*/
|
|
104
|
+
seoImage?: string;
|
|
105
|
+
/**
|
|
106
|
+
* SEO: Canonical URL (optional, for SEO meta tags)
|
|
107
|
+
*/
|
|
108
|
+
seoUrl?: string;
|
|
109
|
+
/**
|
|
110
|
+
* reCAPTCHA site key (optional, falls back to SDK config)
|
|
111
|
+
*/
|
|
112
|
+
recaptchaSiteKey?: string;
|
|
113
|
+
}
|
|
114
|
+
declare function NewsletterPage({ productName, layout, rateLimitAttempts, rateLimitWindowMinutes, heroHeading, heroSubtitle, heroDescription, features, successMessage, errorMessage, rateLimitMessage, className, seoTitle, seoDescription, seoKeywords, seoImage, seoUrl, recaptchaSiteKey, }: NewsletterPageProps): react_jsx_runtime.JSX.Element;
|
|
115
|
+
interface NewsletterFormProps {
|
|
116
|
+
/**
|
|
117
|
+
* Layout variant
|
|
118
|
+
* @default 'inline'
|
|
119
|
+
*/
|
|
120
|
+
variant?: "inline" | "stacked";
|
|
121
|
+
/**
|
|
122
|
+
* Show email label
|
|
123
|
+
* @default false
|
|
124
|
+
*/
|
|
125
|
+
showLabel?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Show description text
|
|
128
|
+
* @default false
|
|
129
|
+
*/
|
|
130
|
+
showDescription?: boolean;
|
|
131
|
+
/**
|
|
132
|
+
* Description text
|
|
133
|
+
* @default 'Subscribe to our newsletter for updates'
|
|
134
|
+
*/
|
|
135
|
+
description?: string;
|
|
136
|
+
/**
|
|
137
|
+
* Button text
|
|
138
|
+
* @default 'Subscribe'
|
|
139
|
+
*/
|
|
140
|
+
buttonText?: string;
|
|
141
|
+
/**
|
|
142
|
+
* Custom CSS class
|
|
143
|
+
*/
|
|
144
|
+
className?: string;
|
|
145
|
+
/**
|
|
146
|
+
* reCAPTCHA site key (optional, falls back to SDK config)
|
|
147
|
+
*/
|
|
148
|
+
recaptchaSiteKey?: string;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Lightweight newsletter subscription form for inline use (e.g., footers)
|
|
152
|
+
*/
|
|
153
|
+
declare function NewsletterForm({ variant, showLabel, showDescription, description, buttonText, className, recaptchaSiteKey, }: NewsletterFormProps): react_jsx_runtime.JSX.Element;
|
|
154
|
+
|
|
155
|
+
export { NewsletterForm, type NewsletterFormProps, NewsletterPage, type NewsletterPageProps };
|