@opensite/ui 0.1.2 → 0.1.4
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/components.cjs +1511 -3
- package/dist/components.cjs.map +1 -1
- package/dist/components.d.cts +13 -0
- package/dist/components.d.ts +13 -0
- package/dist/components.js +1499 -4
- package/dist/components.js.map +1 -1
- package/dist/footer-animated-social.cjs +272 -0
- package/dist/footer-animated-social.cjs.map +1 -0
- package/dist/footer-animated-social.d.cts +41 -0
- package/dist/footer-animated-social.d.ts +41 -0
- package/dist/footer-animated-social.js +250 -0
- package/dist/footer-animated-social.js.map +1 -0
- package/dist/footer-background-card.cjs +149 -0
- package/dist/footer-background-card.cjs.map +1 -0
- package/dist/footer-background-card.d.cts +74 -0
- package/dist/footer-background-card.d.ts +74 -0
- package/dist/footer-background-card.js +147 -0
- package/dist/footer-background-card.js.map +1 -0
- package/dist/footer-brand-description.cjs +244 -0
- package/dist/footer-brand-description.cjs.map +1 -0
- package/dist/footer-brand-description.d.cts +65 -0
- package/dist/footer-brand-description.d.ts +65 -0
- package/dist/footer-brand-description.js +222 -0
- package/dist/footer-brand-description.js.map +1 -0
- package/dist/footer-contact-card.cjs +238 -0
- package/dist/footer-contact-card.cjs.map +1 -0
- package/dist/footer-contact-card.d.cts +65 -0
- package/dist/footer-contact-card.d.ts +65 -0
- package/dist/footer-contact-card.js +216 -0
- package/dist/footer-contact-card.js.map +1 -0
- package/dist/footer-cta-banner.cjs +282 -0
- package/dist/footer-cta-banner.cjs.map +1 -0
- package/dist/footer-cta-banner.d.cts +77 -0
- package/dist/footer-cta-banner.d.ts +77 -0
- package/dist/footer-cta-banner.js +260 -0
- package/dist/footer-cta-banner.js.map +1 -0
- package/dist/footer-cta-social.cjs +221 -0
- package/dist/footer-cta-social.cjs.map +1 -0
- package/dist/footer-cta-social.d.cts +45 -0
- package/dist/footer-cta-social.d.ts +45 -0
- package/dist/footer-cta-social.js +199 -0
- package/dist/footer-cta-social.js.map +1 -0
- package/dist/footer-links-grid.cjs +119 -0
- package/dist/footer-links-grid.cjs.map +1 -0
- package/dist/footer-links-grid.d.cts +54 -0
- package/dist/footer-links-grid.d.ts +54 -0
- package/dist/footer-links-grid.js +117 -0
- package/dist/footer-links-grid.js.map +1 -0
- package/dist/footer-nav-social.cjs +273 -0
- package/dist/footer-nav-social.cjs.map +1 -0
- package/dist/footer-nav-social.d.cts +72 -0
- package/dist/footer-nav-social.d.ts +72 -0
- package/dist/footer-nav-social.js +251 -0
- package/dist/footer-nav-social.js.map +1 -0
- package/dist/footer-newsletter-grid.cjs +271 -0
- package/dist/footer-newsletter-grid.cjs.map +1 -0
- package/dist/footer-newsletter-grid.d.cts +74 -0
- package/dist/footer-newsletter-grid.d.ts +74 -0
- package/dist/footer-newsletter-grid.js +249 -0
- package/dist/footer-newsletter-grid.js.map +1 -0
- package/dist/footer-newsletter-minimal.cjs +271 -0
- package/dist/footer-newsletter-minimal.cjs.map +1 -0
- package/dist/footer-newsletter-minimal.d.cts +57 -0
- package/dist/footer-newsletter-minimal.d.ts +57 -0
- package/dist/footer-newsletter-minimal.js +249 -0
- package/dist/footer-newsletter-minimal.js.map +1 -0
- package/dist/footer-simple-centered.cjs +101 -0
- package/dist/footer-simple-centered.cjs.map +1 -0
- package/dist/footer-simple-centered.d.cts +52 -0
- package/dist/footer-simple-centered.d.ts +52 -0
- package/dist/footer-simple-centered.js +99 -0
- package/dist/footer-simple-centered.js.map +1 -0
- package/dist/footer-social-apps.cjs +247 -0
- package/dist/footer-social-apps.cjs.map +1 -0
- package/dist/footer-social-apps.d.cts +75 -0
- package/dist/footer-social-apps.d.ts +75 -0
- package/dist/footer-social-apps.js +225 -0
- package/dist/footer-social-apps.js.map +1 -0
- package/dist/footer-social-newsletter.cjs +267 -0
- package/dist/footer-social-newsletter.cjs.map +1 -0
- package/dist/footer-social-newsletter.d.cts +68 -0
- package/dist/footer-social-newsletter.d.ts +68 -0
- package/dist/footer-social-newsletter.js +245 -0
- package/dist/footer-social-newsletter.js.map +1 -0
- package/dist/index.cjs +1511 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +1499 -4
- package/dist/index.js.map +1 -1
- package/dist/pressable.cjs +10 -3
- package/dist/pressable.cjs.map +1 -1
- package/dist/pressable.js +10 -3
- package/dist/pressable.js.map +1 -1
- package/dist/registry.cjs +1971 -1
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +1971 -1
- package/dist/registry.js.map +1 -1
- package/dist/team-media-showcase.cjs +182 -0
- package/dist/team-media-showcase.cjs.map +1 -0
- package/dist/team-media-showcase.d.cts +145 -0
- package/dist/team-media-showcase.d.ts +145 -0
- package/dist/team-media-showcase.js +160 -0
- package/dist/team-media-showcase.js.map +1 -0
- package/package.json +71 -1
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { clsx } from 'clsx';
|
|
3
|
+
import { twMerge } from 'tailwind-merge';
|
|
4
|
+
import { Img } from '@page-speed/img';
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
// lib/utils.ts
|
|
9
|
+
function cn(...inputs) {
|
|
10
|
+
return twMerge(clsx(inputs));
|
|
11
|
+
}
|
|
12
|
+
var svgCache = /* @__PURE__ */ new Map();
|
|
13
|
+
function DynamicIcon({
|
|
14
|
+
name,
|
|
15
|
+
size = 28,
|
|
16
|
+
color,
|
|
17
|
+
className,
|
|
18
|
+
alt
|
|
19
|
+
}) {
|
|
20
|
+
const [svgContent, setSvgContent] = React.useState(null);
|
|
21
|
+
const [isLoading, setIsLoading] = React.useState(true);
|
|
22
|
+
const [error, setError] = React.useState(null);
|
|
23
|
+
const { url, iconName } = React.useMemo(() => {
|
|
24
|
+
const separator = name.includes("/") ? "/" : ":";
|
|
25
|
+
const [prefix, iconName2] = name.split(separator);
|
|
26
|
+
const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName2}?format=svg&width=${size}&height=${size}`;
|
|
27
|
+
return {
|
|
28
|
+
url: baseUrl,
|
|
29
|
+
iconName: iconName2
|
|
30
|
+
};
|
|
31
|
+
}, [name, size]);
|
|
32
|
+
React.useEffect(() => {
|
|
33
|
+
let isMounted = true;
|
|
34
|
+
const fetchSvg = async () => {
|
|
35
|
+
const cached = svgCache.get(url);
|
|
36
|
+
if (cached) {
|
|
37
|
+
if (isMounted) {
|
|
38
|
+
setSvgContent(cached);
|
|
39
|
+
setIsLoading(false);
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
setIsLoading(true);
|
|
45
|
+
setError(null);
|
|
46
|
+
const response = await fetch(url);
|
|
47
|
+
if (!response.ok) {
|
|
48
|
+
throw new Error(`Failed to fetch icon: ${response.status}`);
|
|
49
|
+
}
|
|
50
|
+
let svg = await response.text();
|
|
51
|
+
svg = processSvgForCurrentColor(svg);
|
|
52
|
+
svgCache.set(url, svg);
|
|
53
|
+
if (isMounted) {
|
|
54
|
+
setSvgContent(svg);
|
|
55
|
+
setIsLoading(false);
|
|
56
|
+
}
|
|
57
|
+
} catch (err) {
|
|
58
|
+
if (isMounted) {
|
|
59
|
+
setError(err instanceof Error ? err.message : "Failed to load icon");
|
|
60
|
+
setIsLoading(false);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
fetchSvg();
|
|
65
|
+
return () => {
|
|
66
|
+
isMounted = false;
|
|
67
|
+
};
|
|
68
|
+
}, [url]);
|
|
69
|
+
if (isLoading) {
|
|
70
|
+
return /* @__PURE__ */ jsx(
|
|
71
|
+
"span",
|
|
72
|
+
{
|
|
73
|
+
className: cn("inline-block", className),
|
|
74
|
+
style: { width: size, height: size },
|
|
75
|
+
"aria-hidden": "true"
|
|
76
|
+
}
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
if (error || !svgContent) {
|
|
80
|
+
return /* @__PURE__ */ jsx(
|
|
81
|
+
"span",
|
|
82
|
+
{
|
|
83
|
+
className: cn("inline-block", className),
|
|
84
|
+
style: { width: size, height: size },
|
|
85
|
+
role: "img",
|
|
86
|
+
"aria-label": alt || iconName
|
|
87
|
+
}
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return /* @__PURE__ */ jsx(
|
|
91
|
+
"span",
|
|
92
|
+
{
|
|
93
|
+
className: cn("inline-flex items-center justify-center", className),
|
|
94
|
+
style: {
|
|
95
|
+
width: size,
|
|
96
|
+
height: size,
|
|
97
|
+
color: color || "inherit"
|
|
98
|
+
},
|
|
99
|
+
role: "img",
|
|
100
|
+
"aria-label": alt || iconName,
|
|
101
|
+
dangerouslySetInnerHTML: { __html: svgContent }
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
function processSvgForCurrentColor(svg) {
|
|
106
|
+
let processed = svg;
|
|
107
|
+
processed = processed.replace(
|
|
108
|
+
/stroke=["'](#000000|#000|black)["']/gi,
|
|
109
|
+
'stroke="currentColor"'
|
|
110
|
+
);
|
|
111
|
+
processed = processed.replace(
|
|
112
|
+
/fill=["'](#000000|#000|black)["']/gi,
|
|
113
|
+
'fill="currentColor"'
|
|
114
|
+
);
|
|
115
|
+
return processed;
|
|
116
|
+
}
|
|
117
|
+
var defaultSections = [
|
|
118
|
+
{
|
|
119
|
+
title: "Product",
|
|
120
|
+
links: [
|
|
121
|
+
{ name: "Overview", href: "#" },
|
|
122
|
+
{ name: "Pricing", href: "#" },
|
|
123
|
+
{ name: "Marketplace", href: "#" },
|
|
124
|
+
{ name: "Features", href: "#" },
|
|
125
|
+
{ name: "Integrations", href: "#" },
|
|
126
|
+
{ name: "Marketing", href: "#" }
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
title: "Company",
|
|
131
|
+
links: [
|
|
132
|
+
{ name: "About", href: "#" },
|
|
133
|
+
{ name: "Team", href: "#" },
|
|
134
|
+
{ name: "Blog", href: "#" },
|
|
135
|
+
{ name: "Careers", href: "#" },
|
|
136
|
+
{ name: "Contact", href: "#" }
|
|
137
|
+
]
|
|
138
|
+
}
|
|
139
|
+
];
|
|
140
|
+
var defaultSocialLinks = [
|
|
141
|
+
{ icon: "simple-icons/instagram", href: "#", label: "Instagram" },
|
|
142
|
+
{ icon: "simple-icons/facebook", href: "#", label: "Facebook" },
|
|
143
|
+
{ icon: "simple-icons/x", href: "#", label: "X (Twitter)" },
|
|
144
|
+
{ icon: "simple-icons/linkedin", href: "#", label: "LinkedIn" }
|
|
145
|
+
];
|
|
146
|
+
function FooterNewsletterGrid({
|
|
147
|
+
logo = {
|
|
148
|
+
url: "https://opensite.ai",
|
|
149
|
+
src: "https://cdn.ing/assets/i/r/285975/eud79qeya11q5w6ueyhklueardyx/os-suircle-black-white.png",
|
|
150
|
+
alt: "Opensite AI",
|
|
151
|
+
title: "Opensite AI"
|
|
152
|
+
},
|
|
153
|
+
className,
|
|
154
|
+
description = "A collection of 100+ responsive HTML templates for your startup business or side project.",
|
|
155
|
+
sections = defaultSections,
|
|
156
|
+
socialLinks = defaultSocialLinks,
|
|
157
|
+
newsletterTitle = "Newsletter",
|
|
158
|
+
newsletterPlaceholder = "Email",
|
|
159
|
+
newsletterButtonText = "Subscribe",
|
|
160
|
+
privacyText = "By submitting, you agree to our",
|
|
161
|
+
privacyLinkText = "Privacy Policy",
|
|
162
|
+
privacyLinkUrl = "#",
|
|
163
|
+
copyright = `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} Opensite AI. All rights reserved.`,
|
|
164
|
+
optixFlowConfig
|
|
165
|
+
}) {
|
|
166
|
+
return /* @__PURE__ */ jsx("section", { className: cn("py-32", className), children: /* @__PURE__ */ jsx("div", { className: "container", children: /* @__PURE__ */ jsxs("footer", { children: [
|
|
167
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-4 justify-between gap-10 lg:grid-cols-6 lg:text-left", children: [
|
|
168
|
+
/* @__PURE__ */ jsxs("div", { className: "col-span-4 flex w-full flex-col gap-6 lg:col-span-2", children: [
|
|
169
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 lg:justify-start", children: [
|
|
170
|
+
/* @__PURE__ */ jsx("a", { href: logo.url, children: /* @__PURE__ */ jsx(
|
|
171
|
+
Img,
|
|
172
|
+
{
|
|
173
|
+
src: logo.src,
|
|
174
|
+
alt: logo.alt,
|
|
175
|
+
className: "h-8",
|
|
176
|
+
optixFlowConfig
|
|
177
|
+
}
|
|
178
|
+
) }),
|
|
179
|
+
/* @__PURE__ */ jsx("h2", { className: "text-xl font-semibold", children: logo.title })
|
|
180
|
+
] }),
|
|
181
|
+
/* @__PURE__ */ jsx("p", { className: "text-muted-foreground", children: description }),
|
|
182
|
+
/* @__PURE__ */ jsx("ul", { className: "flex items-center space-x-6", children: socialLinks.map((social, idx) => /* @__PURE__ */ jsx(
|
|
183
|
+
"li",
|
|
184
|
+
{
|
|
185
|
+
className: "font-medium duration-200 hover:scale-110 hover:text-muted-foreground",
|
|
186
|
+
children: /* @__PURE__ */ jsx("a", { href: social.href, "aria-label": social.label, children: /* @__PURE__ */ jsx(DynamicIcon, { name: social.icon, size: 24 }) })
|
|
187
|
+
},
|
|
188
|
+
idx
|
|
189
|
+
)) })
|
|
190
|
+
] }),
|
|
191
|
+
sections.map((section, sectionIdx) => /* @__PURE__ */ jsxs("div", { className: "col-span-2 md:col-span-1", children: [
|
|
192
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-5 font-medium", children: section.title }),
|
|
193
|
+
/* @__PURE__ */ jsx("ul", { className: "space-y-4 text-sm text-muted-foreground", children: section.links.map((link, linkIdx) => /* @__PURE__ */ jsx(
|
|
194
|
+
"li",
|
|
195
|
+
{
|
|
196
|
+
className: "font-medium hover:text-primary",
|
|
197
|
+
children: /* @__PURE__ */ jsx("a", { href: link.href, children: link.name })
|
|
198
|
+
},
|
|
199
|
+
linkIdx
|
|
200
|
+
)) })
|
|
201
|
+
] }, sectionIdx)),
|
|
202
|
+
/* @__PURE__ */ jsxs("div", { className: "col-span-4 md:col-span-2", children: [
|
|
203
|
+
/* @__PURE__ */ jsx("h3", { className: "mb-5 font-medium", children: newsletterTitle }),
|
|
204
|
+
/* @__PURE__ */ jsx("div", { className: "grid gap-1.5", children: /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center space-x-2", children: [
|
|
205
|
+
/* @__PURE__ */ jsx(
|
|
206
|
+
"input",
|
|
207
|
+
{
|
|
208
|
+
type: "email",
|
|
209
|
+
placeholder: newsletterPlaceholder,
|
|
210
|
+
className: "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
211
|
+
}
|
|
212
|
+
),
|
|
213
|
+
/* @__PURE__ */ jsx(
|
|
214
|
+
"button",
|
|
215
|
+
{
|
|
216
|
+
type: "submit",
|
|
217
|
+
className: "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2",
|
|
218
|
+
children: newsletterButtonText
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
] }) }),
|
|
222
|
+
/* @__PURE__ */ jsxs("p", { className: "mt-1 text-xs font-medium text-muted-foreground", children: [
|
|
223
|
+
privacyText,
|
|
224
|
+
/* @__PURE__ */ jsx("a", { href: privacyLinkUrl, className: "ml-1 text-primary hover:underline", children: privacyLinkText })
|
|
225
|
+
] })
|
|
226
|
+
] })
|
|
227
|
+
] }),
|
|
228
|
+
/* @__PURE__ */ jsxs("div", { className: "mt-20 flex flex-col justify-between gap-4 border-t pt-8 text-sm font-medium text-muted-foreground lg:flex-row lg:items-center lg:text-left", children: [
|
|
229
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 lg:flex-row lg:items-center lg:gap-4", children: /* @__PURE__ */ jsxs("p", { children: [
|
|
230
|
+
/* @__PURE__ */ jsx("span", { className: "mr-1 font-bold text-primary", children: "Opensite AI" }),
|
|
231
|
+
copyright
|
|
232
|
+
] }) }),
|
|
233
|
+
/* @__PURE__ */ jsx(
|
|
234
|
+
"a",
|
|
235
|
+
{
|
|
236
|
+
href: "https://opensite.ai",
|
|
237
|
+
className: "hover:text-primary",
|
|
238
|
+
target: "_blank",
|
|
239
|
+
rel: "noopener noreferrer",
|
|
240
|
+
children: "AI Website and Automation Platform by Opensite"
|
|
241
|
+
}
|
|
242
|
+
)
|
|
243
|
+
] })
|
|
244
|
+
] }) }) });
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export { FooterNewsletterGrid };
|
|
248
|
+
//# sourceMappingURL=footer-newsletter-grid.js.map
|
|
249
|
+
//# sourceMappingURL=footer-newsletter-grid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../lib/utils.ts","../components/ui/dynamic-icon.tsx","../components/blocks/footers/footer-newsletter-grid.tsx"],"names":["iconName","jsx"],"mappings":"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","file":"footer-newsletter-grid.js","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\ninterface DynamicIconProps {\n /**\n * Icon name in format: prefix/name or prefix:name\n * Examples: \"lucide/home\", \"mdi:account\", \"heroicons/check\"\n */\n name: string;\n /**\n * Icon size in pixels\n * @default 28\n */\n size?: number;\n /**\n * Icon color - accepts any valid CSS color\n * Note: When not specified, the icon inherits color from parent via CSS currentColor\n */\n color?: string;\n /**\n * Additional CSS classes\n */\n className?: string;\n /**\n * Alt text for accessibility\n */\n alt?: string;\n}\n\n// Simple in-memory cache for fetched SVGs\nconst svgCache = new Map<string, string>();\n\n/**\n * Lightweight icon component that dynamically loads SVG icons from icons.opensite.ai API.\n *\n * Features:\n * - Pulls SVGs from https://icons.opensite.ai API and inlines them for CSS color inheritance\n * - Supports currentColor - icons inherit color from parent element\n * - Accepts prefix/name or prefix:name format\n * - Customizable size and explicit color via props\n * - In-memory caching to prevent duplicate fetches\n *\n * @example\n * ```tsx\n * // Icon inherits color from parent (recommended for hover states, etc.)\n * <span className=\"text-white hover:text-red-500\">\n * <DynamicIcon name=\"lucide/home\" size={24} />\n * </span>\n *\n * // Icon with explicit color\n * <DynamicIcon name=\"mdi:account\" size={32} color=\"#ff0000\" />\n * ```\n */\nexport function DynamicIcon({\n name,\n size = 28,\n color,\n className,\n alt,\n}: DynamicIconProps) {\n const [svgContent, setSvgContent] = React.useState<string | null>(null);\n const [isLoading, setIsLoading] = React.useState(true);\n const [error, setError] = React.useState<string | null>(null);\n\n const { url, iconName } = React.useMemo(() => {\n const separator = name.includes(\"/\") ? \"/\" : \":\";\n const [prefix, iconName] = name.split(separator);\n // Don't pass color to API - we'll handle it via CSS\n const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName}?format=svg&width=${size}&height=${size}`;\n\n return {\n url: baseUrl,\n iconName,\n };\n }, [name, size]);\n\n React.useEffect(() => {\n let isMounted = true;\n\n const fetchSvg = async () => {\n // Check cache first\n const cached = svgCache.get(url);\n if (cached) {\n if (isMounted) {\n setSvgContent(cached);\n setIsLoading(false);\n }\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n let svg = await response.text();\n\n // Process SVG to ensure currentColor works:\n // 1. Replace any hardcoded colors with currentColor\n // 2. Ensure stroke/fill use currentColor where appropriate\n svg = processSvgForCurrentColor(svg);\n\n // Cache the processed SVG\n svgCache.set(url, svg);\n\n if (isMounted) {\n setSvgContent(svg);\n setIsLoading(false);\n }\n } catch (err) {\n if (isMounted) {\n setError(err instanceof Error ? err.message : \"Failed to load icon\");\n setIsLoading(false);\n }\n }\n };\n\n fetchSvg();\n\n return () => {\n isMounted = false;\n };\n }, [url]);\n\n // Loading state - show placeholder with same dimensions\n if (isLoading) {\n return (\n <span\n className={cn(\"inline-block\", className)}\n style={{ width: size, height: size }}\n aria-hidden=\"true\"\n />\n );\n }\n\n // Error state - show nothing or fallback\n if (error || !svgContent) {\n return (\n <span\n className={cn(\"inline-block\", className)}\n style={{ width: size, height: size }}\n role=\"img\"\n aria-label={alt || iconName}\n />\n );\n }\n\n // Render inline SVG\n // The color prop applies an explicit color, otherwise inherits from parent via currentColor\n return (\n <span\n className={cn(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\",\n }}\n role=\"img\"\n aria-label={alt || iconName}\n dangerouslySetInnerHTML={{ __html: svgContent }}\n />\n );\n}\n\n/**\n * Process SVG to ensure it uses currentColor for proper CSS inheritance.\n * This handles various icon libraries that may use different color approaches.\n */\nfunction processSvgForCurrentColor(svg: string): string {\n // Replace stroke=\"currentColor\" is already correct, but ensure fill also works\n // Some icons use fill=\"none\" with stroke, others use fill with no stroke\n\n // Ensure the SVG doesn't have hardcoded colors that should be currentColor\n // Common patterns to replace:\n // - stroke=\"#000\" or stroke=\"#000000\" or stroke=\"black\" -> stroke=\"currentColor\"\n // - fill=\"#000\" or fill=\"#000000\" or fill=\"black\" -> fill=\"currentColor\"\n\n let processed = svg;\n\n // Replace common black color values with currentColor for stroke\n processed = processed.replace(\n /stroke=[\"'](#000000|#000|black)[\"']/gi,\n 'stroke=\"currentColor\"'\n );\n\n // Replace common black color values with currentColor for fill\n // But be careful not to replace fill=\"none\"\n processed = processed.replace(\n /fill=[\"'](#000000|#000|black)[\"']/gi,\n 'fill=\"currentColor\"'\n );\n\n return processed;\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { cn } from \"../../../lib/utils\";\nimport { Img } from \"@page-speed/img\";\nimport { DynamicIcon } from \"../../ui/dynamic-icon\";\n\n/**\n * Social link configuration\n */\nexport interface FooterNewsletterGridSocialLink {\n /** Icon name in format: prefix/name (e.g., \"simple-icons/instagram\") */\n icon: string;\n /** Link URL */\n href: string;\n /** Accessible label */\n label: string;\n}\n\n/**\n * Navigation section configuration\n */\nexport interface FooterNewsletterGridSection {\n title: string;\n links: {\n name: string;\n href: string;\n }[];\n}\n\n/**\n * Props for the FooterNewsletterGrid component\n */\nexport interface FooterNewsletterGridProps {\n /** Logo configuration */\n logo?: {\n url: string;\n src: string;\n alt: string;\n title: string;\n };\n /** Additional CSS classes */\n className?: string;\n /** Brand description text */\n description?: string;\n /** Navigation sections */\n sections?: FooterNewsletterGridSection[];\n /** Social media links */\n socialLinks?: FooterNewsletterGridSocialLink[];\n /** Newsletter section title */\n newsletterTitle?: string;\n /** Newsletter placeholder text */\n newsletterPlaceholder?: string;\n /** Newsletter button text */\n newsletterButtonText?: string;\n /** Privacy policy text */\n privacyText?: string;\n /** Privacy policy link text */\n privacyLinkText?: string;\n /** Privacy policy URL */\n privacyLinkUrl?: string;\n /** Copyright text */\n copyright?: string;\n /** Attribution text */\n attribution?: string;\n /** Optional Optix Flow configuration for @page-speed/img */\n optixFlowConfig?: {\n apiKey: string;\n compression?: number;\n };\n}\n\nconst defaultSections: FooterNewsletterGridSection[] = [\n {\n title: \"Product\",\n links: [\n { name: \"Overview\", href: \"#\" },\n { name: \"Pricing\", href: \"#\" },\n { name: \"Marketplace\", href: \"#\" },\n { name: \"Features\", href: \"#\" },\n { name: \"Integrations\", href: \"#\" },\n { name: \"Marketing\", href: \"#\" },\n ],\n },\n {\n title: \"Company\",\n links: [\n { name: \"About\", href: \"#\" },\n { name: \"Team\", href: \"#\" },\n { name: \"Blog\", href: \"#\" },\n { name: \"Careers\", href: \"#\" },\n { name: \"Contact\", href: \"#\" },\n ],\n },\n];\n\nconst defaultSocialLinks: FooterNewsletterGridSocialLink[] = [\n { icon: \"simple-icons/instagram\", href: \"#\", label: \"Instagram\" },\n { icon: \"simple-icons/facebook\", href: \"#\", label: \"Facebook\" },\n { icon: \"simple-icons/x\", href: \"#\", label: \"X (Twitter)\" },\n { icon: \"simple-icons/linkedin\", href: \"#\", label: \"LinkedIn\" },\n];\n\n/**\n * FooterNewsletterGrid - A comprehensive footer with logo, social icons, navigation, and newsletter.\n *\n * Features a full-width grid layout with brand section (logo, description, social icons),\n * multi-column navigation, and a prominent newsletter signup form. Ideal for content-heavy\n * websites, SaaS products, and businesses that prioritize email marketing and social engagement.\n */\nexport function FooterNewsletterGrid({\n logo = {\n url: \"https://opensite.ai\",\n src: \"https://cdn.ing/assets/i/r/285975/eud79qeya11q5w6ueyhklueardyx/os-suircle-black-white.png\",\n alt: \"Opensite AI\",\n title: \"Opensite AI\",\n },\n className,\n description = \"A collection of 100+ responsive HTML templates for your startup business or side project.\",\n sections = defaultSections,\n socialLinks = defaultSocialLinks,\n newsletterTitle = \"Newsletter\",\n newsletterPlaceholder = \"Email\",\n newsletterButtonText = \"Subscribe\",\n privacyText = \"By submitting, you agree to our\",\n privacyLinkText = \"Privacy Policy\",\n privacyLinkUrl = \"#\",\n copyright = `© ${new Date().getFullYear()} Opensite AI. All rights reserved.`,\n optixFlowConfig,\n}: FooterNewsletterGridProps): React.JSX.Element {\n return (\n <section className={cn(\"py-32\", className)}>\n <div className=\"container\">\n <footer>\n <div className=\"grid grid-cols-4 justify-between gap-10 lg:grid-cols-6 lg:text-left\">\n <div className=\"col-span-4 flex w-full flex-col gap-6 lg:col-span-2\">\n <div className=\"flex items-center gap-2 lg:justify-start\">\n <a href={logo.url}>\n <Img\n src={logo.src}\n alt={logo.alt}\n className=\"h-8\"\n optixFlowConfig={optixFlowConfig}\n />\n </a>\n <h2 className=\"text-xl font-semibold\">{logo.title}</h2>\n </div>\n <p className=\"text-muted-foreground\">{description}</p>\n <ul className=\"flex items-center space-x-6\">\n {socialLinks.map((social, idx) => (\n <li\n key={idx}\n className=\"font-medium duration-200 hover:scale-110 hover:text-muted-foreground\"\n >\n <a href={social.href} aria-label={social.label}>\n <DynamicIcon name={social.icon} size={24} />\n </a>\n </li>\n ))}\n </ul>\n </div>\n {sections.map((section, sectionIdx) => (\n <div key={sectionIdx} className=\"col-span-2 md:col-span-1\">\n <h3 className=\"mb-5 font-medium\">{section.title}</h3>\n <ul className=\"space-y-4 text-sm text-muted-foreground\">\n {section.links.map((link, linkIdx) => (\n <li\n key={linkIdx}\n className=\"font-medium hover:text-primary\"\n >\n <a href={link.href}>{link.name}</a>\n </li>\n ))}\n </ul>\n </div>\n ))}\n <div className=\"col-span-4 md:col-span-2\">\n <h3 className=\"mb-5 font-medium\">{newsletterTitle}</h3>\n <div className=\"grid gap-1.5\">\n <div className=\"flex w-full items-center space-x-2\">\n <input\n type=\"email\"\n placeholder={newsletterPlaceholder}\n className=\"flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50\"\n />\n <button\n type=\"submit\"\n className=\"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 bg-primary text-primary-foreground hover:bg-primary/90 h-10 px-4 py-2\"\n >\n {newsletterButtonText}\n </button>\n </div>\n </div>\n <p className=\"mt-1 text-xs font-medium text-muted-foreground\">\n {privacyText}\n <a href={privacyLinkUrl} className=\"ml-1 text-primary hover:underline\">\n {privacyLinkText}\n </a>\n </p>\n </div>\n </div>\n <div className=\"mt-20 flex flex-col justify-between gap-4 border-t pt-8 text-sm font-medium text-muted-foreground lg:flex-row lg:items-center lg:text-left\">\n <div className=\"flex flex-col gap-2 lg:flex-row lg:items-center lg:gap-4\">\n <p>\n <span className=\"mr-1 font-bold text-primary\">Opensite AI</span>\n {copyright}\n </p>\n </div>\n <a\n href=\"https://opensite.ai\"\n className=\"hover:text-primary\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n AI Website and Automation Platform by Opensite\n </a>\n </div>\n </footer>\n </div>\n </section>\n );\n}\n"]}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
'use strict';
|
|
3
|
+
|
|
4
|
+
var framerMotion = require('framer-motion');
|
|
5
|
+
var clsx = require('clsx');
|
|
6
|
+
var tailwindMerge = require('tailwind-merge');
|
|
7
|
+
var React = require('react');
|
|
8
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
9
|
+
|
|
10
|
+
function _interopNamespace(e) {
|
|
11
|
+
if (e && e.__esModule) return e;
|
|
12
|
+
var n = Object.create(null);
|
|
13
|
+
if (e) {
|
|
14
|
+
Object.keys(e).forEach(function (k) {
|
|
15
|
+
if (k !== 'default') {
|
|
16
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
17
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: function () { return e[k]; }
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
n.default = e;
|
|
25
|
+
return Object.freeze(n);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
var React__namespace = /*#__PURE__*/_interopNamespace(React);
|
|
29
|
+
|
|
30
|
+
// components/blocks/footers/footer-newsletter-minimal.tsx
|
|
31
|
+
function cn(...inputs) {
|
|
32
|
+
return tailwindMerge.twMerge(clsx.clsx(inputs));
|
|
33
|
+
}
|
|
34
|
+
var svgCache = /* @__PURE__ */ new Map();
|
|
35
|
+
function DynamicIcon({
|
|
36
|
+
name,
|
|
37
|
+
size = 28,
|
|
38
|
+
color,
|
|
39
|
+
className,
|
|
40
|
+
alt
|
|
41
|
+
}) {
|
|
42
|
+
const [svgContent, setSvgContent] = React__namespace.useState(null);
|
|
43
|
+
const [isLoading, setIsLoading] = React__namespace.useState(true);
|
|
44
|
+
const [error, setError] = React__namespace.useState(null);
|
|
45
|
+
const { url, iconName } = React__namespace.useMemo(() => {
|
|
46
|
+
const separator = name.includes("/") ? "/" : ":";
|
|
47
|
+
const [prefix, iconName2] = name.split(separator);
|
|
48
|
+
const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName2}?format=svg&width=${size}&height=${size}`;
|
|
49
|
+
return {
|
|
50
|
+
url: baseUrl,
|
|
51
|
+
iconName: iconName2
|
|
52
|
+
};
|
|
53
|
+
}, [name, size]);
|
|
54
|
+
React__namespace.useEffect(() => {
|
|
55
|
+
let isMounted = true;
|
|
56
|
+
const fetchSvg = async () => {
|
|
57
|
+
const cached = svgCache.get(url);
|
|
58
|
+
if (cached) {
|
|
59
|
+
if (isMounted) {
|
|
60
|
+
setSvgContent(cached);
|
|
61
|
+
setIsLoading(false);
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
try {
|
|
66
|
+
setIsLoading(true);
|
|
67
|
+
setError(null);
|
|
68
|
+
const response = await fetch(url);
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
throw new Error(`Failed to fetch icon: ${response.status}`);
|
|
71
|
+
}
|
|
72
|
+
let svg = await response.text();
|
|
73
|
+
svg = processSvgForCurrentColor(svg);
|
|
74
|
+
svgCache.set(url, svg);
|
|
75
|
+
if (isMounted) {
|
|
76
|
+
setSvgContent(svg);
|
|
77
|
+
setIsLoading(false);
|
|
78
|
+
}
|
|
79
|
+
} catch (err) {
|
|
80
|
+
if (isMounted) {
|
|
81
|
+
setError(err instanceof Error ? err.message : "Failed to load icon");
|
|
82
|
+
setIsLoading(false);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
fetchSvg();
|
|
87
|
+
return () => {
|
|
88
|
+
isMounted = false;
|
|
89
|
+
};
|
|
90
|
+
}, [url]);
|
|
91
|
+
if (isLoading) {
|
|
92
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
93
|
+
"span",
|
|
94
|
+
{
|
|
95
|
+
className: cn("inline-block", className),
|
|
96
|
+
style: { width: size, height: size },
|
|
97
|
+
"aria-hidden": "true"
|
|
98
|
+
}
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
if (error || !svgContent) {
|
|
102
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
103
|
+
"span",
|
|
104
|
+
{
|
|
105
|
+
className: cn("inline-block", className),
|
|
106
|
+
style: { width: size, height: size },
|
|
107
|
+
role: "img",
|
|
108
|
+
"aria-label": alt || iconName
|
|
109
|
+
}
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
113
|
+
"span",
|
|
114
|
+
{
|
|
115
|
+
className: cn("inline-flex items-center justify-center", className),
|
|
116
|
+
style: {
|
|
117
|
+
width: size,
|
|
118
|
+
height: size,
|
|
119
|
+
color: color || "inherit"
|
|
120
|
+
},
|
|
121
|
+
role: "img",
|
|
122
|
+
"aria-label": alt || iconName,
|
|
123
|
+
dangerouslySetInnerHTML: { __html: svgContent }
|
|
124
|
+
}
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
function processSvgForCurrentColor(svg) {
|
|
128
|
+
let processed = svg;
|
|
129
|
+
processed = processed.replace(
|
|
130
|
+
/stroke=["'](#000000|#000|black)["']/gi,
|
|
131
|
+
'stroke="currentColor"'
|
|
132
|
+
);
|
|
133
|
+
processed = processed.replace(
|
|
134
|
+
/fill=["'](#000000|#000|black)["']/gi,
|
|
135
|
+
'fill="currentColor"'
|
|
136
|
+
);
|
|
137
|
+
return processed;
|
|
138
|
+
}
|
|
139
|
+
var defaultNavLinks = [
|
|
140
|
+
{ label: "Home", href: "#" },
|
|
141
|
+
{ label: "Collection", href: "#" },
|
|
142
|
+
{ label: "Projects", href: "#" },
|
|
143
|
+
{ label: "Pricing", href: "#" },
|
|
144
|
+
{ label: "Login", href: "#" }
|
|
145
|
+
];
|
|
146
|
+
var defaultSocialLinks = [
|
|
147
|
+
{ label: "Linkedin", href: "#" },
|
|
148
|
+
{ label: "Twitter", href: "#" },
|
|
149
|
+
{ label: "Facebook", href: "#" }
|
|
150
|
+
];
|
|
151
|
+
var defaultFooterLinks = [
|
|
152
|
+
{ label: "Privacy Policy", href: "#" },
|
|
153
|
+
{ label: "Terms & Conditions", href: "#" }
|
|
154
|
+
];
|
|
155
|
+
function FooterNewsletterMinimal({
|
|
156
|
+
className,
|
|
157
|
+
heading = "Unlock 800+ blocks now",
|
|
158
|
+
supportEmail = "hi@opensite.ai",
|
|
159
|
+
navLinks = defaultNavLinks,
|
|
160
|
+
socialLinks = defaultSocialLinks,
|
|
161
|
+
footerLinks = defaultFooterLinks,
|
|
162
|
+
newsletterLabel = "Sign up for newsletter :",
|
|
163
|
+
newsletterPlaceholder = "Name*",
|
|
164
|
+
location = "San Francisco, CA"
|
|
165
|
+
}) {
|
|
166
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
167
|
+
"section",
|
|
168
|
+
{
|
|
169
|
+
className: cn("dark bg-background py-32 text-foreground", className),
|
|
170
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "container", children: [
|
|
171
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col justify-between gap-15 lg:flex-row", children: [
|
|
172
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-10", children: [
|
|
173
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "relative text-4xl font-medium tracking-tight lg:text-5xl", children: heading }),
|
|
174
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 text-sm font-light tracking-tight lg:text-base", children: [
|
|
175
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: "Get Support : " }),
|
|
176
|
+
/* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${supportEmail}`, children: supportEmail })
|
|
177
|
+
] })
|
|
178
|
+
] }),
|
|
179
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid w-full max-w-xs grid-cols-2 gap-10 text-sm font-light lg:text-base", children: [
|
|
180
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1", children: navLinks.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
181
|
+
"a",
|
|
182
|
+
{
|
|
183
|
+
href: item.href,
|
|
184
|
+
className: "tracking-tight text-foreground hover:text-foreground/30",
|
|
185
|
+
children: item.label
|
|
186
|
+
}
|
|
187
|
+
) }, item.label)) }),
|
|
188
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1", children: socialLinks.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
189
|
+
"a",
|
|
190
|
+
{
|
|
191
|
+
href: item.href,
|
|
192
|
+
className: "group flex items-center gap-1 tracking-tight text-foreground hover:text-foreground/30",
|
|
193
|
+
children: [
|
|
194
|
+
item.label,
|
|
195
|
+
" ",
|
|
196
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
197
|
+
DynamicIcon,
|
|
198
|
+
{
|
|
199
|
+
name: "lucide/arrow-up-right",
|
|
200
|
+
size: 14,
|
|
201
|
+
className: "text-foreground group-hover:text-muted-foreground/50"
|
|
202
|
+
}
|
|
203
|
+
)
|
|
204
|
+
]
|
|
205
|
+
}
|
|
206
|
+
) }, item.label)) })
|
|
207
|
+
] })
|
|
208
|
+
] }),
|
|
209
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-20 flex flex-col justify-between gap-15 lg:flex-row", children: [
|
|
210
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full max-w-md flex-col gap-10", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-1 text-sm font-light tracking-tight lg:text-base", children: [
|
|
211
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { children: newsletterLabel }),
|
|
212
|
+
/* @__PURE__ */ jsxRuntime.jsxs("form", { className: "flex w-full items-end border-b border-b-foreground/10", children: [
|
|
213
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
214
|
+
"input",
|
|
215
|
+
{
|
|
216
|
+
type: "text",
|
|
217
|
+
placeholder: newsletterPlaceholder,
|
|
218
|
+
className: "mt-10 w-full rounded-none border-0 bg-transparent p-0 uppercase shadow-none placeholder:text-foreground/20 focus:outline-none focus:ring-0 lg:text-base"
|
|
219
|
+
}
|
|
220
|
+
),
|
|
221
|
+
/* @__PURE__ */ jsxRuntime.jsx("button", { type: "submit", className: "p-2 hover:bg-muted/20", children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: "lucide/arrow-right", size: 20 }) })
|
|
222
|
+
] })
|
|
223
|
+
] }) }),
|
|
224
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid w-full max-w-xs grid-cols-2 gap-10 text-sm font-light lg:text-base", children: [
|
|
225
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-32", children: location }),
|
|
226
|
+
/* @__PURE__ */ jsxRuntime.jsx("ul", { className: "space-y-1", children: footerLinks.map((item) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
227
|
+
"a",
|
|
228
|
+
{
|
|
229
|
+
href: item.href,
|
|
230
|
+
className: "group flex items-center gap-1 tracking-tight text-foreground hover:text-foreground/30",
|
|
231
|
+
children: item.label
|
|
232
|
+
}
|
|
233
|
+
) }, item.label)) })
|
|
234
|
+
] })
|
|
235
|
+
] }),
|
|
236
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-20 w-full lg:mt-32", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
237
|
+
framerMotion.motion.div,
|
|
238
|
+
{
|
|
239
|
+
initial: { opacity: 0, y: 20 },
|
|
240
|
+
whileInView: { opacity: 1, y: 0 },
|
|
241
|
+
viewport: { once: true },
|
|
242
|
+
transition: { duration: 0.6 },
|
|
243
|
+
className: "text-center",
|
|
244
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-6xl font-bold tracking-tighter md:text-8xl lg:text-9xl", children: "OPENSITE" })
|
|
245
|
+
}
|
|
246
|
+
) }),
|
|
247
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-8 text-center text-sm text-muted-foreground", children: [
|
|
248
|
+
/* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
|
|
249
|
+
"\xA9 ",
|
|
250
|
+
(/* @__PURE__ */ new Date()).getFullYear(),
|
|
251
|
+
" Opensite AI. All rights reserved."
|
|
252
|
+
] }),
|
|
253
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
254
|
+
"a",
|
|
255
|
+
{
|
|
256
|
+
href: "https://opensite.ai",
|
|
257
|
+
className: "mt-2 inline-block hover:text-foreground",
|
|
258
|
+
target: "_blank",
|
|
259
|
+
rel: "noopener noreferrer",
|
|
260
|
+
children: "AI Website and Automation Platform by Opensite"
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
] })
|
|
264
|
+
] })
|
|
265
|
+
}
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
exports.FooterNewsletterMinimal = FooterNewsletterMinimal;
|
|
270
|
+
//# sourceMappingURL=footer-newsletter-minimal.cjs.map
|
|
271
|
+
//# sourceMappingURL=footer-newsletter-minimal.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../lib/utils.ts","../components/ui/dynamic-icon.tsx","../components/blocks/footers/footer-newsletter-minimal.tsx"],"names":["twMerge","clsx","React","iconName","jsx","jsxs","motion"],"mappings":"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","file":"footer-newsletter-minimal.cjs","sourcesContent":["import { clsx, type ClassValue } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n","\"use client\";\n\nimport * as React from \"react\";\n\nimport { cn } from \"../../lib/utils\";\n\ninterface DynamicIconProps {\n /**\n * Icon name in format: prefix/name or prefix:name\n * Examples: \"lucide/home\", \"mdi:account\", \"heroicons/check\"\n */\n name: string;\n /**\n * Icon size in pixels\n * @default 28\n */\n size?: number;\n /**\n * Icon color - accepts any valid CSS color\n * Note: When not specified, the icon inherits color from parent via CSS currentColor\n */\n color?: string;\n /**\n * Additional CSS classes\n */\n className?: string;\n /**\n * Alt text for accessibility\n */\n alt?: string;\n}\n\n// Simple in-memory cache for fetched SVGs\nconst svgCache = new Map<string, string>();\n\n/**\n * Lightweight icon component that dynamically loads SVG icons from icons.opensite.ai API.\n *\n * Features:\n * - Pulls SVGs from https://icons.opensite.ai API and inlines them for CSS color inheritance\n * - Supports currentColor - icons inherit color from parent element\n * - Accepts prefix/name or prefix:name format\n * - Customizable size and explicit color via props\n * - In-memory caching to prevent duplicate fetches\n *\n * @example\n * ```tsx\n * // Icon inherits color from parent (recommended for hover states, etc.)\n * <span className=\"text-white hover:text-red-500\">\n * <DynamicIcon name=\"lucide/home\" size={24} />\n * </span>\n *\n * // Icon with explicit color\n * <DynamicIcon name=\"mdi:account\" size={32} color=\"#ff0000\" />\n * ```\n */\nexport function DynamicIcon({\n name,\n size = 28,\n color,\n className,\n alt,\n}: DynamicIconProps) {\n const [svgContent, setSvgContent] = React.useState<string | null>(null);\n const [isLoading, setIsLoading] = React.useState(true);\n const [error, setError] = React.useState<string | null>(null);\n\n const { url, iconName } = React.useMemo(() => {\n const separator = name.includes(\"/\") ? \"/\" : \":\";\n const [prefix, iconName] = name.split(separator);\n // Don't pass color to API - we'll handle it via CSS\n const baseUrl = `https://icons.opensite.ai/api/icon/${prefix}/${iconName}?format=svg&width=${size}&height=${size}`;\n\n return {\n url: baseUrl,\n iconName,\n };\n }, [name, size]);\n\n React.useEffect(() => {\n let isMounted = true;\n\n const fetchSvg = async () => {\n // Check cache first\n const cached = svgCache.get(url);\n if (cached) {\n if (isMounted) {\n setSvgContent(cached);\n setIsLoading(false);\n }\n return;\n }\n\n try {\n setIsLoading(true);\n setError(null);\n\n const response = await fetch(url);\n if (!response.ok) {\n throw new Error(`Failed to fetch icon: ${response.status}`);\n }\n\n let svg = await response.text();\n\n // Process SVG to ensure currentColor works:\n // 1. Replace any hardcoded colors with currentColor\n // 2. Ensure stroke/fill use currentColor where appropriate\n svg = processSvgForCurrentColor(svg);\n\n // Cache the processed SVG\n svgCache.set(url, svg);\n\n if (isMounted) {\n setSvgContent(svg);\n setIsLoading(false);\n }\n } catch (err) {\n if (isMounted) {\n setError(err instanceof Error ? err.message : \"Failed to load icon\");\n setIsLoading(false);\n }\n }\n };\n\n fetchSvg();\n\n return () => {\n isMounted = false;\n };\n }, [url]);\n\n // Loading state - show placeholder with same dimensions\n if (isLoading) {\n return (\n <span\n className={cn(\"inline-block\", className)}\n style={{ width: size, height: size }}\n aria-hidden=\"true\"\n />\n );\n }\n\n // Error state - show nothing or fallback\n if (error || !svgContent) {\n return (\n <span\n className={cn(\"inline-block\", className)}\n style={{ width: size, height: size }}\n role=\"img\"\n aria-label={alt || iconName}\n />\n );\n }\n\n // Render inline SVG\n // The color prop applies an explicit color, otherwise inherits from parent via currentColor\n return (\n <span\n className={cn(\"inline-flex items-center justify-center\", className)}\n style={{\n width: size,\n height: size,\n color: color || \"inherit\",\n }}\n role=\"img\"\n aria-label={alt || iconName}\n dangerouslySetInnerHTML={{ __html: svgContent }}\n />\n );\n}\n\n/**\n * Process SVG to ensure it uses currentColor for proper CSS inheritance.\n * This handles various icon libraries that may use different color approaches.\n */\nfunction processSvgForCurrentColor(svg: string): string {\n // Replace stroke=\"currentColor\" is already correct, but ensure fill also works\n // Some icons use fill=\"none\" with stroke, others use fill with no stroke\n\n // Ensure the SVG doesn't have hardcoded colors that should be currentColor\n // Common patterns to replace:\n // - stroke=\"#000\" or stroke=\"#000000\" or stroke=\"black\" -> stroke=\"currentColor\"\n // - fill=\"#000\" or fill=\"#000000\" or fill=\"black\" -> fill=\"currentColor\"\n\n let processed = svg;\n\n // Replace common black color values with currentColor for stroke\n processed = processed.replace(\n /stroke=[\"'](#000000|#000|black)[\"']/gi,\n 'stroke=\"currentColor\"'\n );\n\n // Replace common black color values with currentColor for fill\n // But be careful not to replace fill=\"none\"\n processed = processed.replace(\n /fill=[\"'](#000000|#000|black)[\"']/gi,\n 'fill=\"currentColor\"'\n );\n\n return processed;\n}\n","\"use client\";\n\nimport * as React from \"react\";\nimport { motion } from \"framer-motion\";\nimport { cn } from \"../../../lib/utils\";\nimport { DynamicIcon } from \"../../ui/dynamic-icon\";\n\n/**\n * Navigation link configuration\n */\nexport interface FooterNewsletterMinimalNavLink {\n label: string;\n href: string;\n}\n\n/**\n * Social link configuration\n */\nexport interface FooterNewsletterMinimalSocialLink {\n label: string;\n href: string;\n}\n\n/**\n * Footer link configuration\n */\nexport interface FooterNewsletterMinimalFooterLink {\n label: string;\n href: string;\n}\n\n/**\n * Props for the FooterNewsletterMinimal component\n */\nexport interface FooterNewsletterMinimalProps {\n /** Additional CSS classes */\n className?: string;\n /** Main heading text */\n heading?: string;\n /** Support email */\n supportEmail?: string;\n /** Navigation links */\n navLinks?: FooterNewsletterMinimalNavLink[];\n /** Social links */\n socialLinks?: FooterNewsletterMinimalSocialLink[];\n /** Footer links (privacy, terms) */\n footerLinks?: FooterNewsletterMinimalFooterLink[];\n /** Newsletter label */\n newsletterLabel?: string;\n /** Newsletter placeholder */\n newsletterPlaceholder?: string;\n /** Location text */\n location?: string;\n}\n\nconst defaultNavLinks: FooterNewsletterMinimalNavLink[] = [\n { label: \"Home\", href: \"#\" },\n { label: \"Collection\", href: \"#\" },\n { label: \"Projects\", href: \"#\" },\n { label: \"Pricing\", href: \"#\" },\n { label: \"Login\", href: \"#\" },\n];\n\nconst defaultSocialLinks: FooterNewsletterMinimalSocialLink[] = [\n { label: \"Linkedin\", href: \"#\" },\n { label: \"Twitter\", href: \"#\" },\n { label: \"Facebook\", href: \"#\" },\n];\n\nconst defaultFooterLinks: FooterNewsletterMinimalFooterLink[] = [\n { label: \"Privacy Policy\", href: \"#\" },\n { label: \"Terms & Conditions\", href: \"#\" },\n];\n\n/**\n * FooterNewsletterMinimal - A dark-themed minimal footer with newsletter and animated logo.\n *\n * Features a clean layout with main heading, support email, navigation columns,\n * newsletter signup form, and a large animated brand logo. Ideal for modern SaaS products,\n * creative agencies, and businesses that want a sophisticated, dark-themed footer\n * with strong visual branding.\n */\nexport function FooterNewsletterMinimal({\n className,\n heading = \"Unlock 800+ blocks now\",\n supportEmail = \"hi@opensite.ai\",\n navLinks = defaultNavLinks,\n socialLinks = defaultSocialLinks,\n footerLinks = defaultFooterLinks,\n newsletterLabel = \"Sign up for newsletter :\",\n newsletterPlaceholder = \"Name*\",\n location = \"San Francisco, CA\",\n}: FooterNewsletterMinimalProps): React.JSX.Element {\n return (\n <section\n className={cn(\"dark bg-background py-32 text-foreground\", className)}\n >\n <div className=\"container\">\n <div className=\"flex flex-col justify-between gap-15 lg:flex-row\">\n <div className=\"flex flex-col gap-10\">\n <p className=\"relative text-4xl font-medium tracking-tight lg:text-5xl\">\n {heading}\n </p>\n <div className=\"space-y-1 text-sm font-light tracking-tight lg:text-base\">\n <p>Get Support : </p>\n <a href={`mailto:${supportEmail}`}>{supportEmail}</a>\n </div>\n </div>\n <div className=\"grid w-full max-w-xs grid-cols-2 gap-10 text-sm font-light lg:text-base\">\n <ul className=\"space-y-1\">\n {navLinks.map((item) => (\n <li key={item.label}>\n <a\n href={item.href}\n className=\"tracking-tight text-foreground hover:text-foreground/30\"\n >\n {item.label}\n </a>\n </li>\n ))}\n </ul>\n <ul className=\"space-y-1\">\n {socialLinks.map((item) => (\n <li key={item.label}>\n <a\n href={item.href}\n className=\"group flex items-center gap-1 tracking-tight text-foreground hover:text-foreground/30\"\n >\n {item.label}{\" \"}\n <DynamicIcon\n name=\"lucide/arrow-up-right\"\n size={14}\n className=\"text-foreground group-hover:text-muted-foreground/50\"\n />\n </a>\n </li>\n ))}\n </ul>\n </div>\n </div>\n <div className=\"mt-20 flex flex-col justify-between gap-15 lg:flex-row\">\n <div className=\"flex w-full max-w-md flex-col gap-10\">\n <div className=\"space-y-1 text-sm font-light tracking-tight lg:text-base\">\n <p>{newsletterLabel}</p>\n <form className=\"flex w-full items-end border-b border-b-foreground/10\">\n <input\n type=\"text\"\n placeholder={newsletterPlaceholder}\n className=\"mt-10 w-full rounded-none border-0 bg-transparent p-0 uppercase shadow-none placeholder:text-foreground/20 focus:outline-none focus:ring-0 lg:text-base\"\n />\n <button type=\"submit\" className=\"p-2 hover:bg-muted/20\">\n <DynamicIcon name=\"lucide/arrow-right\" size={20} />\n </button>\n </form>\n </div>\n </div>\n <div className=\"grid w-full max-w-xs grid-cols-2 gap-10 text-sm font-light lg:text-base\">\n <div className=\"w-32\">{location}</div>\n <ul className=\"space-y-1\">\n {footerLinks.map((item) => (\n <li key={item.label}>\n <a\n href={item.href}\n className=\"group flex items-center gap-1 tracking-tight text-foreground hover:text-foreground/30\"\n >\n {item.label}\n </a>\n </li>\n ))}\n </ul>\n </div>\n </div>\n <div className=\"mt-20 w-full lg:mt-32\">\n <motion.div\n initial={{ opacity: 0, y: 20 }}\n whileInView={{ opacity: 1, y: 0 }}\n viewport={{ once: true }}\n transition={{ duration: 0.6 }}\n className=\"text-center\"\n >\n <span className=\"text-6xl font-bold tracking-tighter md:text-8xl lg:text-9xl\">\n OPENSITE\n </span>\n </motion.div>\n </div>\n <div className=\"mt-8 text-center text-sm text-muted-foreground\">\n <p>© {new Date().getFullYear()} Opensite AI. All rights reserved.</p>\n <a\n href=\"https://opensite.ai\"\n className=\"mt-2 inline-block hover:text-foreground\"\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n >\n AI Website and Automation Platform by Opensite\n </a>\n </div>\n </div>\n </section>\n );\n}\n"]}
|