@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.
Files changed (105) hide show
  1. package/dist/components.cjs +1511 -3
  2. package/dist/components.cjs.map +1 -1
  3. package/dist/components.d.cts +13 -0
  4. package/dist/components.d.ts +13 -0
  5. package/dist/components.js +1499 -4
  6. package/dist/components.js.map +1 -1
  7. package/dist/footer-animated-social.cjs +272 -0
  8. package/dist/footer-animated-social.cjs.map +1 -0
  9. package/dist/footer-animated-social.d.cts +41 -0
  10. package/dist/footer-animated-social.d.ts +41 -0
  11. package/dist/footer-animated-social.js +250 -0
  12. package/dist/footer-animated-social.js.map +1 -0
  13. package/dist/footer-background-card.cjs +149 -0
  14. package/dist/footer-background-card.cjs.map +1 -0
  15. package/dist/footer-background-card.d.cts +74 -0
  16. package/dist/footer-background-card.d.ts +74 -0
  17. package/dist/footer-background-card.js +147 -0
  18. package/dist/footer-background-card.js.map +1 -0
  19. package/dist/footer-brand-description.cjs +244 -0
  20. package/dist/footer-brand-description.cjs.map +1 -0
  21. package/dist/footer-brand-description.d.cts +65 -0
  22. package/dist/footer-brand-description.d.ts +65 -0
  23. package/dist/footer-brand-description.js +222 -0
  24. package/dist/footer-brand-description.js.map +1 -0
  25. package/dist/footer-contact-card.cjs +238 -0
  26. package/dist/footer-contact-card.cjs.map +1 -0
  27. package/dist/footer-contact-card.d.cts +65 -0
  28. package/dist/footer-contact-card.d.ts +65 -0
  29. package/dist/footer-contact-card.js +216 -0
  30. package/dist/footer-contact-card.js.map +1 -0
  31. package/dist/footer-cta-banner.cjs +282 -0
  32. package/dist/footer-cta-banner.cjs.map +1 -0
  33. package/dist/footer-cta-banner.d.cts +77 -0
  34. package/dist/footer-cta-banner.d.ts +77 -0
  35. package/dist/footer-cta-banner.js +260 -0
  36. package/dist/footer-cta-banner.js.map +1 -0
  37. package/dist/footer-cta-social.cjs +221 -0
  38. package/dist/footer-cta-social.cjs.map +1 -0
  39. package/dist/footer-cta-social.d.cts +45 -0
  40. package/dist/footer-cta-social.d.ts +45 -0
  41. package/dist/footer-cta-social.js +199 -0
  42. package/dist/footer-cta-social.js.map +1 -0
  43. package/dist/footer-links-grid.cjs +119 -0
  44. package/dist/footer-links-grid.cjs.map +1 -0
  45. package/dist/footer-links-grid.d.cts +54 -0
  46. package/dist/footer-links-grid.d.ts +54 -0
  47. package/dist/footer-links-grid.js +117 -0
  48. package/dist/footer-links-grid.js.map +1 -0
  49. package/dist/footer-nav-social.cjs +273 -0
  50. package/dist/footer-nav-social.cjs.map +1 -0
  51. package/dist/footer-nav-social.d.cts +72 -0
  52. package/dist/footer-nav-social.d.ts +72 -0
  53. package/dist/footer-nav-social.js +251 -0
  54. package/dist/footer-nav-social.js.map +1 -0
  55. package/dist/footer-newsletter-grid.cjs +271 -0
  56. package/dist/footer-newsletter-grid.cjs.map +1 -0
  57. package/dist/footer-newsletter-grid.d.cts +74 -0
  58. package/dist/footer-newsletter-grid.d.ts +74 -0
  59. package/dist/footer-newsletter-grid.js +249 -0
  60. package/dist/footer-newsletter-grid.js.map +1 -0
  61. package/dist/footer-newsletter-minimal.cjs +271 -0
  62. package/dist/footer-newsletter-minimal.cjs.map +1 -0
  63. package/dist/footer-newsletter-minimal.d.cts +57 -0
  64. package/dist/footer-newsletter-minimal.d.ts +57 -0
  65. package/dist/footer-newsletter-minimal.js +249 -0
  66. package/dist/footer-newsletter-minimal.js.map +1 -0
  67. package/dist/footer-simple-centered.cjs +101 -0
  68. package/dist/footer-simple-centered.cjs.map +1 -0
  69. package/dist/footer-simple-centered.d.cts +52 -0
  70. package/dist/footer-simple-centered.d.ts +52 -0
  71. package/dist/footer-simple-centered.js +99 -0
  72. package/dist/footer-simple-centered.js.map +1 -0
  73. package/dist/footer-social-apps.cjs +247 -0
  74. package/dist/footer-social-apps.cjs.map +1 -0
  75. package/dist/footer-social-apps.d.cts +75 -0
  76. package/dist/footer-social-apps.d.ts +75 -0
  77. package/dist/footer-social-apps.js +225 -0
  78. package/dist/footer-social-apps.js.map +1 -0
  79. package/dist/footer-social-newsletter.cjs +267 -0
  80. package/dist/footer-social-newsletter.cjs.map +1 -0
  81. package/dist/footer-social-newsletter.d.cts +68 -0
  82. package/dist/footer-social-newsletter.d.ts +68 -0
  83. package/dist/footer-social-newsletter.js +245 -0
  84. package/dist/footer-social-newsletter.js.map +1 -0
  85. package/dist/index.cjs +1511 -3
  86. package/dist/index.cjs.map +1 -1
  87. package/dist/index.d.cts +13 -0
  88. package/dist/index.d.ts +13 -0
  89. package/dist/index.js +1499 -4
  90. package/dist/index.js.map +1 -1
  91. package/dist/pressable.cjs +10 -3
  92. package/dist/pressable.cjs.map +1 -1
  93. package/dist/pressable.js +10 -3
  94. package/dist/pressable.js.map +1 -1
  95. package/dist/registry.cjs +1971 -1
  96. package/dist/registry.cjs.map +1 -1
  97. package/dist/registry.js +1971 -1
  98. package/dist/registry.js.map +1 -1
  99. package/dist/team-media-showcase.cjs +182 -0
  100. package/dist/team-media-showcase.cjs.map +1 -0
  101. package/dist/team-media-showcase.d.cts +145 -0
  102. package/dist/team-media-showcase.d.ts +145 -0
  103. package/dist/team-media-showcase.js +160 -0
  104. package/dist/team-media-showcase.js.map +1 -0
  105. package/package.json +71 -1
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../lib/utils.ts","../components/ui/dynamic-icon.tsx","../components/blocks/footers/footer-brand-description.tsx"],"names":["iconName","jsx"],"mappings":"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","file":"footer-brand-description.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 FooterBrandDescriptionSocialLink {\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 FooterBrandDescriptionSection {\n title: string;\n links: {\n name: string;\n href: string;\n }[];\n}\n\n/**\n * Props for the FooterBrandDescription component\n */\nexport interface FooterBrandDescriptionProps {\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 /** Navigation sections */\n sections?: FooterBrandDescriptionSection[];\n /** Brand description text */\n description?: string;\n /** Social media links */\n socialLinks?: FooterBrandDescriptionSocialLink[];\n /** Copyright text */\n copyright?: string;\n /** Legal links (terms, privacy, etc.) */\n legalLinks?: {\n name: string;\n href: string;\n }[];\n /** Optional Optix Flow configuration for @page-speed/img */\n optixFlowConfig?: {\n apiKey: string;\n compression?: number;\n };\n}\n\nconst defaultSections: FooterBrandDescriptionSection[] = [\n {\n title: \"Product\",\n links: [\n { name: \"Overview\", href: \"#\" },\n { name: \"Pricing\", href: \"#\" },\n { name: \"Marketplace\", href: \"#\" },\n { name: \"Features\", 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 ],\n },\n {\n title: \"Resources\",\n links: [\n { name: \"Help\", href: \"#\" },\n { name: \"Sales\", href: \"#\" },\n { name: \"Advertise\", href: \"#\" },\n { name: \"Privacy\", href: \"#\" },\n ],\n },\n];\n\nconst defaultSocialLinks: FooterBrandDescriptionSocialLink[] = [\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\nconst defaultLegalLinks = [\n { name: \"Terms and Conditions\", href: \"#\" },\n { name: \"Privacy Policy\", href: \"#\" },\n];\n\n/**\n * FooterBrandDescription - A footer with logo, description, social icons, and navigation.\n *\n * Features a prominent brand section with logo, description, and social links on the left,\n * with multi-column navigation on the right. Ideal for brand-focused websites, startups,\n * and businesses that want to emphasize their identity and social presence in the footer.\n */\nexport function FooterBrandDescription({\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 sections = defaultSections,\n description = \"A collection of components for your startup business or side project.\",\n socialLinks = defaultSocialLinks,\n copyright = `© ${new Date().getFullYear()} Opensite AI. All rights reserved.`,\n legalLinks = defaultLegalLinks,\n optixFlowConfig,\n}: FooterBrandDescriptionProps): React.JSX.Element {\n return (\n <section className={cn(\"py-32\", className)}>\n <div className=\"container\">\n <div className=\"flex w-full flex-col justify-between gap-10 lg:flex-row lg:items-start lg:text-left\">\n <div className=\"flex w-full flex-col justify-between gap-6 lg:items-start\">\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=\"max-w-[70%] text-sm text-muted-foreground\">\n {description}\n </p>\n <ul className=\"flex items-center space-x-6 text-muted-foreground\">\n {socialLinks.map((social, idx) => (\n <li key={idx} className=\"font-medium hover:text-primary\">\n <a href={social.href} aria-label={social.label}>\n <DynamicIcon name={social.icon} size={20} />\n </a>\n </li>\n ))}\n </ul>\n </div>\n <div className=\"grid w-full gap-6 md:grid-cols-3 lg:gap-20\">\n {sections.map((section, sectionIdx) => (\n <div key={sectionIdx}>\n <h3 className=\"mb-4 font-bold\">{section.title}</h3>\n <ul className=\"space-y-3 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>\n </div>\n <div className=\"mt-8 flex flex-col justify-between gap-4 border-t py-8 text-xs font-medium text-muted-foreground md:flex-row md:items-center md:text-left\">\n <div className=\"order-2 flex flex-col gap-2 lg:order-1 lg:flex-row lg:items-center lg:gap-4\">\n <p>{copyright}</p>\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 <ul className=\"order-1 flex flex-col gap-2 md:order-2 md:flex-row\">\n {legalLinks.map((link, idx) => (\n <li key={idx} className=\"hover:text-primary\">\n <a href={link.href}>{link.name}</a>\n </li>\n ))}\n </ul>\n </div>\n </div>\n </section>\n );\n}\n"]}
@@ -0,0 +1,238 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var clsx = require('clsx');
5
+ var tailwindMerge = require('tailwind-merge');
6
+ var img = require('@page-speed/img');
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
+ // lib/utils.ts
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 defaultSocialLinks = [
140
+ { icon: "simple-icons/instagram", href: "#", label: "Instagram" },
141
+ { icon: "simple-icons/facebook", href: "#", label: "Facebook" },
142
+ { icon: "simple-icons/x", href: "#", label: "X (Twitter)" },
143
+ { icon: "simple-icons/linkedin", href: "#", label: "LinkedIn" }
144
+ ];
145
+ var defaultNavLinks = [
146
+ { name: "Home", href: "#" },
147
+ { name: "About", href: "#" },
148
+ { name: "Services", href: "#" },
149
+ { name: "Contact", href: "#" }
150
+ ];
151
+ function FooterContactCard({
152
+ logo = {
153
+ url: "https://opensite.ai",
154
+ src: "https://cdn.ing/assets/i/r/285975/eud79qeya11q5w6ueyhklueardyx/os-suircle-black-white.png",
155
+ alt: "Opensite AI",
156
+ title: "Opensite AI"
157
+ },
158
+ className,
159
+ heading = "Let's work together",
160
+ email = "hello@opensite.ai",
161
+ phone = "+1 (555) 123-4567",
162
+ address = "123 Main Street, San Francisco, CA 94102",
163
+ socialLinks = defaultSocialLinks,
164
+ navLinks = defaultNavLinks,
165
+ location = "San Francisco",
166
+ copyright = `\xA9 ${(/* @__PURE__ */ new Date()).getFullYear()} Opensite AI. All rights reserved.`,
167
+ optixFlowConfig
168
+ }) {
169
+ return /* @__PURE__ */ jsxRuntime.jsx("section", { className: cn("py-32", className), children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "container", children: /* @__PURE__ */ jsxRuntime.jsxs("footer", { children: [
170
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid gap-12 lg:grid-cols-2", children: [
171
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
172
+ /* @__PURE__ */ jsxRuntime.jsxs("a", { href: logo.url, className: "mb-8 flex items-center gap-2", children: [
173
+ /* @__PURE__ */ jsxRuntime.jsx(
174
+ img.Img,
175
+ {
176
+ src: logo.src,
177
+ alt: logo.alt,
178
+ className: "h-10",
179
+ optixFlowConfig
180
+ }
181
+ ),
182
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-xl font-semibold", children: logo.title })
183
+ ] }),
184
+ /* @__PURE__ */ jsxRuntime.jsx("h2", { className: "mb-8 text-4xl font-bold tracking-tight md:text-5xl lg:text-6xl", children: heading }),
185
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4 text-muted-foreground", children: [
186
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: `mailto:${email}`, className: "hover:text-primary", children: email }) }),
187
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: /* @__PURE__ */ jsxRuntime.jsx("a", { href: `tel:${phone}`, className: "hover:text-primary", children: phone }) }),
188
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: address })
189
+ ] })
190
+ ] }),
191
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col justify-between", children: [
192
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
193
+ /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mb-4 font-medium", children: "Follow Us" }),
194
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex items-center gap-4", children: socialLinks.map((social, idx) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
195
+ "a",
196
+ {
197
+ href: social.href,
198
+ "aria-label": social.label,
199
+ className: "flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground transition-colors hover:bg-primary hover:text-primary-foreground",
200
+ children: /* @__PURE__ */ jsxRuntime.jsx(DynamicIcon, { name: social.icon, size: 20 })
201
+ }
202
+ ) }, idx)) })
203
+ ] }),
204
+ /* @__PURE__ */ jsxRuntime.jsx("nav", { className: "mt-8", children: /* @__PURE__ */ jsxRuntime.jsx("ul", { className: "flex flex-wrap gap-6", children: navLinks.map((link, idx) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsx(
205
+ "a",
206
+ {
207
+ href: link.href,
208
+ className: "text-muted-foreground hover:text-primary",
209
+ children: link.name
210
+ }
211
+ ) }, idx)) }) })
212
+ ] })
213
+ ] }),
214
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-16 flex flex-col justify-between gap-4 border-t pt-8 text-sm text-muted-foreground md:flex-row md:items-center", children: [
215
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2 md:flex-row md:items-center md:gap-4", children: [
216
+ /* @__PURE__ */ jsxRuntime.jsx("p", { children: copyright }),
217
+ /* @__PURE__ */ jsxRuntime.jsx(
218
+ "a",
219
+ {
220
+ href: "https://opensite.ai",
221
+ className: "hover:text-primary",
222
+ target: "_blank",
223
+ rel: "noopener noreferrer",
224
+ children: "AI Website and Automation Platform by Opensite"
225
+ }
226
+ )
227
+ ] }),
228
+ /* @__PURE__ */ jsxRuntime.jsxs("p", { children: [
229
+ "Designed in ",
230
+ /* @__PURE__ */ jsxRuntime.jsx("strong", { children: location })
231
+ ] })
232
+ ] })
233
+ ] }) }) });
234
+ }
235
+
236
+ exports.FooterContactCard = FooterContactCard;
237
+ //# sourceMappingURL=footer-contact-card.cjs.map
238
+ //# sourceMappingURL=footer-contact-card.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../lib/utils.ts","../components/ui/dynamic-icon.tsx","../components/blocks/footers/footer-contact-card.tsx"],"names":["twMerge","clsx","React","iconName","jsx","jsxs","Img"],"mappings":"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","file":"footer-contact-card.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 { 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 FooterContactCardSocialLink {\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 link configuration\n */\nexport interface FooterContactCardNavLink {\n name: string;\n href: string;\n}\n\n/**\n * Props for the FooterContactCard component\n */\nexport interface FooterContactCardProps {\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 /** Main heading text */\n heading?: string;\n /** Contact email */\n email?: string;\n /** Contact phone */\n phone?: string;\n /** Contact address */\n address?: string;\n /** Social media links */\n socialLinks?: FooterContactCardSocialLink[];\n /** Navigation links */\n navLinks?: FooterContactCardNavLink[];\n /** Location text */\n location?: string;\n /** Copyright text */\n copyright?: string;\n /** Optional Optix Flow configuration for @page-speed/img */\n optixFlowConfig?: {\n apiKey: string;\n compression?: number;\n };\n}\n\nconst defaultSocialLinks: FooterContactCardSocialLink[] = [\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\nconst defaultNavLinks: FooterContactCardNavLink[] = [\n { name: \"Home\", href: \"#\" },\n { name: \"About\", href: \"#\" },\n { name: \"Services\", href: \"#\" },\n { name: \"Contact\", href: \"#\" },\n];\n\n/**\n * FooterContactCard - A footer with large heading, contact information, and social links.\n *\n * Features a prominent heading, contact details (email, phone, address), social media icons,\n * and horizontal navigation. Ideal for service businesses, agencies, and professional websites\n * that want to emphasize contact information and make it easy for visitors to get in touch.\n */\nexport function FooterContactCard({\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 heading = \"Let's work together\",\n email = \"hello@opensite.ai\",\n phone = \"+1 (555) 123-4567\",\n address = \"123 Main Street, San Francisco, CA 94102\",\n socialLinks = defaultSocialLinks,\n navLinks = defaultNavLinks,\n location = \"San Francisco\",\n copyright = `© ${new Date().getFullYear()} Opensite AI. All rights reserved.`,\n optixFlowConfig,\n}: FooterContactCardProps): React.JSX.Element {\n return (\n <section className={cn(\"py-32\", className)}>\n <div className=\"container\">\n <footer>\n <div className=\"grid gap-12 lg:grid-cols-2\">\n {/* Left Column - Heading and Contact */}\n <div>\n <a href={logo.url} className=\"mb-8 flex items-center gap-2\">\n <Img\n src={logo.src}\n alt={logo.alt}\n className=\"h-10\"\n optixFlowConfig={optixFlowConfig}\n />\n <span className=\"text-xl font-semibold\">{logo.title}</span>\n </a>\n <h2 className=\"mb-8 text-4xl font-bold tracking-tight md:text-5xl lg:text-6xl\">\n {heading}\n </h2>\n <div className=\"space-y-4 text-muted-foreground\">\n <p>\n <a href={`mailto:${email}`} className=\"hover:text-primary\">\n {email}\n </a>\n </p>\n <p>\n <a href={`tel:${phone}`} className=\"hover:text-primary\">\n {phone}\n </a>\n </p>\n <p>{address}</p>\n </div>\n </div>\n\n {/* Right Column - Social and Navigation */}\n <div className=\"flex flex-col justify-between\">\n <div>\n <p className=\"mb-4 font-medium\">Follow Us</p>\n <ul className=\"flex items-center gap-4\">\n {socialLinks.map((social, idx) => (\n <li key={idx}>\n <a\n href={social.href}\n aria-label={social.label}\n className=\"flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground transition-colors hover:bg-primary hover:text-primary-foreground\"\n >\n <DynamicIcon name={social.icon} size={20} />\n </a>\n </li>\n ))}\n </ul>\n </div>\n <nav className=\"mt-8\">\n <ul className=\"flex flex-wrap gap-6\">\n {navLinks.map((link, idx) => (\n <li key={idx}>\n <a\n href={link.href}\n className=\"text-muted-foreground hover:text-primary\"\n >\n {link.name}\n </a>\n </li>\n ))}\n </ul>\n </nav>\n </div>\n </div>\n\n {/* Bottom Section */}\n <div className=\"mt-16 flex flex-col justify-between gap-4 border-t pt-8 text-sm text-muted-foreground md:flex-row md:items-center\">\n <div className=\"flex flex-col gap-2 md:flex-row md:items-center md:gap-4\">\n <p>{copyright}</p>\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 <p>\n Designed in <strong>{location}</strong>\n </p>\n </div>\n </footer>\n </div>\n </section>\n );\n}\n"]}
@@ -0,0 +1,65 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * Social link configuration
5
+ */
6
+ interface FooterContactCardSocialLink {
7
+ /** Icon name in format: prefix/name (e.g., "simple-icons/instagram") */
8
+ icon: string;
9
+ /** Link URL */
10
+ href: string;
11
+ /** Accessible label */
12
+ label: string;
13
+ }
14
+ /**
15
+ * Navigation link configuration
16
+ */
17
+ interface FooterContactCardNavLink {
18
+ name: string;
19
+ href: string;
20
+ }
21
+ /**
22
+ * Props for the FooterContactCard component
23
+ */
24
+ interface FooterContactCardProps {
25
+ /** Logo configuration */
26
+ logo?: {
27
+ url: string;
28
+ src: string;
29
+ alt: string;
30
+ title: string;
31
+ };
32
+ /** Additional CSS classes */
33
+ className?: string;
34
+ /** Main heading text */
35
+ heading?: string;
36
+ /** Contact email */
37
+ email?: string;
38
+ /** Contact phone */
39
+ phone?: string;
40
+ /** Contact address */
41
+ address?: string;
42
+ /** Social media links */
43
+ socialLinks?: FooterContactCardSocialLink[];
44
+ /** Navigation links */
45
+ navLinks?: FooterContactCardNavLink[];
46
+ /** Location text */
47
+ location?: string;
48
+ /** Copyright text */
49
+ copyright?: string;
50
+ /** Optional Optix Flow configuration for @page-speed/img */
51
+ optixFlowConfig?: {
52
+ apiKey: string;
53
+ compression?: number;
54
+ };
55
+ }
56
+ /**
57
+ * FooterContactCard - A footer with large heading, contact information, and social links.
58
+ *
59
+ * Features a prominent heading, contact details (email, phone, address), social media icons,
60
+ * and horizontal navigation. Ideal for service businesses, agencies, and professional websites
61
+ * that want to emphasize contact information and make it easy for visitors to get in touch.
62
+ */
63
+ declare function FooterContactCard({ logo, className, heading, email, phone, address, socialLinks, navLinks, location, copyright, optixFlowConfig, }: FooterContactCardProps): React.JSX.Element;
64
+
65
+ export { FooterContactCard, type FooterContactCardNavLink, type FooterContactCardProps, type FooterContactCardSocialLink };
@@ -0,0 +1,65 @@
1
+ import * as React from 'react';
2
+
3
+ /**
4
+ * Social link configuration
5
+ */
6
+ interface FooterContactCardSocialLink {
7
+ /** Icon name in format: prefix/name (e.g., "simple-icons/instagram") */
8
+ icon: string;
9
+ /** Link URL */
10
+ href: string;
11
+ /** Accessible label */
12
+ label: string;
13
+ }
14
+ /**
15
+ * Navigation link configuration
16
+ */
17
+ interface FooterContactCardNavLink {
18
+ name: string;
19
+ href: string;
20
+ }
21
+ /**
22
+ * Props for the FooterContactCard component
23
+ */
24
+ interface FooterContactCardProps {
25
+ /** Logo configuration */
26
+ logo?: {
27
+ url: string;
28
+ src: string;
29
+ alt: string;
30
+ title: string;
31
+ };
32
+ /** Additional CSS classes */
33
+ className?: string;
34
+ /** Main heading text */
35
+ heading?: string;
36
+ /** Contact email */
37
+ email?: string;
38
+ /** Contact phone */
39
+ phone?: string;
40
+ /** Contact address */
41
+ address?: string;
42
+ /** Social media links */
43
+ socialLinks?: FooterContactCardSocialLink[];
44
+ /** Navigation links */
45
+ navLinks?: FooterContactCardNavLink[];
46
+ /** Location text */
47
+ location?: string;
48
+ /** Copyright text */
49
+ copyright?: string;
50
+ /** Optional Optix Flow configuration for @page-speed/img */
51
+ optixFlowConfig?: {
52
+ apiKey: string;
53
+ compression?: number;
54
+ };
55
+ }
56
+ /**
57
+ * FooterContactCard - A footer with large heading, contact information, and social links.
58
+ *
59
+ * Features a prominent heading, contact details (email, phone, address), social media icons,
60
+ * and horizontal navigation. Ideal for service businesses, agencies, and professional websites
61
+ * that want to emphasize contact information and make it easy for visitors to get in touch.
62
+ */
63
+ declare function FooterContactCard({ logo, className, heading, email, phone, address, socialLinks, navLinks, location, copyright, optixFlowConfig, }: FooterContactCardProps): React.JSX.Element;
64
+
65
+ export { FooterContactCard, type FooterContactCardNavLink, type FooterContactCardProps, type FooterContactCardSocialLink };