@zoyth/simple-site-framework 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +572 -0
- package/bin/create-simple-site.js +390 -0
- package/bin/simple-site.js +664 -0
- package/dist/client.js +135 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +107 -0
- package/dist/client.mjs.map +1 -0
- package/dist/components/index.d.mts +3936 -0
- package/dist/components/index.d.ts +3936 -0
- package/dist/components/index.js +38265 -0
- package/dist/components/index.js.map +1 -0
- package/dist/components/index.mjs +38173 -0
- package/dist/components/index.mjs.map +1 -0
- package/dist/config/index.d.mts +298 -0
- package/dist/config/index.d.ts +298 -0
- package/dist/config/index.js +19 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/index.mjs +1 -0
- package/dist/config/index.mjs.map +1 -0
- package/dist/index.d.mts +2184 -0
- package/dist/index.d.ts +2184 -0
- package/dist/index.js +1713 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1605 -0
- package/dist/index.mjs.map +1 -0
- package/dist/lib/i18n/index.js +665 -0
- package/dist/lib/i18n/index.js.map +1 -0
- package/dist/lib/i18n/index.mjs +621 -0
- package/dist/lib/i18n/index.mjs.map +1 -0
- package/docs/DOCUMENTATION-STRUCTURE.md +1156 -0
- package/docs/EXPORTS.md +125 -0
- package/docs/PERFORMANCE.md +757 -0
- package/docs/POLICY-PAGES.md +867 -0
- package/docs/ROADMAP.md +334 -0
- package/docs/SEO.md +455 -0
- package/docs/SITEMAP.md +708 -0
- package/docs/STRUCTURED-DATA.md +671 -0
- package/docs/accessibility/common-patterns.md +529 -0
- package/docs/accessibility/keyboard-navigation.md +263 -0
- package/docs/accessibility/overview.md +122 -0
- package/docs/accessibility/screen-readers.md +311 -0
- package/docs/accessibility/wcag-compliance.md +159 -0
- package/docs/api/README.md +164 -0
- package/docs/api/components/Accessibility.md +356 -0
- package/docs/api/components/Button.md +240 -0
- package/docs/api/components/HeroSection.md +306 -0
- package/docs/architecture/decisions.md +449 -0
- package/docs/components/AnalyticsTracker.md +58 -0
- package/docs/components/AnimatedCounter.md +48 -0
- package/docs/components/AnimatedSection.md +56 -0
- package/docs/components/BlogCard.md +42 -0
- package/docs/components/Checkbox.md +56 -0
- package/docs/components/CodeBlock.md +52 -0
- package/docs/components/ComparisonTable.md +40 -0
- package/docs/components/ComponentDemo.md +38 -0
- package/docs/components/CountdownTimer.md +51 -0
- package/docs/components/ExitIntentModal.md +56 -0
- package/docs/components/FAQAccordion.md +66 -0
- package/docs/components/FeaturesGrid.md +55 -0
- package/docs/components/FileUpload.md +54 -0
- package/docs/components/I18nMetaTags.md +55 -0
- package/docs/components/Icon.md +53 -0
- package/docs/components/LazySection.md +46 -0
- package/docs/components/LiveProof.md +53 -0
- package/docs/components/LoadingSpinner.md +46 -0
- package/docs/components/MultiStepForm.md +48 -0
- package/docs/components/PolicyLayout.md +55 -0
- package/docs/components/PricingTable.md +49 -0
- package/docs/components/Radio.md +59 -0
- package/docs/components/SEOMetaTags.md +58 -0
- package/docs/components/ScriptInjector.md +50 -0
- package/docs/components/Select.md +72 -0
- package/docs/components/Skeleton.md +47 -0
- package/docs/components/StatsSection.md +48 -0
- package/docs/components/StickyBar.md +62 -0
- package/docs/components/StructuredData.md +99 -0
- package/docs/components/StyleGuide.md +46 -0
- package/docs/components/TableOfContents.md +47 -0
- package/docs/components/TestimonialCarousel.md +42 -0
- package/docs/components/Timeline.md +51 -0
- package/docs/components/Toast.md +59 -0
- package/docs/components/TrackedLink.md +62 -0
- package/docs/components/TrustBadges.md +44 -0
- package/docs/components/conversion/MobileCTA.md +363 -0
- package/docs/components/forms/ContactForm.md +75 -0
- package/docs/components/forms/FormField.md +74 -0
- package/docs/components/layout/Footer.md +601 -0
- package/docs/components/layout/Header.md +549 -0
- package/docs/components/layout/LanguageSelector.md +54 -0
- package/docs/components/layout/LanguageSwitcher.md +24 -0
- package/docs/components/overview.md +447 -0
- package/docs/components/sections/AboutSection.md +48 -0
- package/docs/components/sections/CTASection.md +596 -0
- package/docs/components/sections/CaseStudySection.md +47 -0
- package/docs/components/sections/ContactSection.md +599 -0
- package/docs/components/sections/FeatureSection.md +44 -0
- package/docs/components/sections/HeroSection.md +404 -0
- package/docs/components/sections/LogosSection.md +47 -0
- package/docs/components/sections/PersonalTaxesSection.md +23 -0
- package/docs/components/sections/RecruitingSection.md +23 -0
- package/docs/components/sections/SecurePortalSection.md +23 -0
- package/docs/components/sections/ServicePageLayout.md +52 -0
- package/docs/components/sections/ServicesSection.md +49 -0
- package/docs/components/sections/TestimonialSection.md +44 -0
- package/docs/components/sections/WhyChooseUsSection.md +54 -0
- package/docs/components/ui/Breadcrumb.md +70 -0
- package/docs/components/ui/Button.md +514 -0
- package/docs/components/ui/Card.md +501 -0
- package/docs/components/ui/Input.md +54 -0
- package/docs/components/ui/MobileLinks.md +43 -0
- package/docs/components/ui/Modal.md +60 -0
- package/docs/components/ui/Tabs.md +62 -0
- package/docs/components/ui/Textarea.md +52 -0
- package/docs/core-concepts/configuration-driven.md +552 -0
- package/docs/core-concepts/overview.md +351 -0
- package/docs/features/accessibility/README.md +73 -0
- package/docs/features/accessibility/aria-support.md +177 -0
- package/docs/features/accessibility/color-contrast.md +155 -0
- package/docs/features/accessibility/focus-management.md +187 -0
- package/docs/features/accessibility/testing.md +196 -0
- package/docs/features/analytics/README.md +51 -0
- package/docs/features/analytics/ab-testing.md +171 -0
- package/docs/features/analytics/conversion-tracking.md +207 -0
- package/docs/features/analytics/custom-events.md +219 -0
- package/docs/features/analytics/privacy.md +198 -0
- package/docs/features/analytics/setup.md +114 -0
- package/docs/features/analytics/tracking-events.md +224 -0
- package/docs/features/i18n/README.md +51 -0
- package/docs/features/i18n/best-practices.md +273 -0
- package/docs/features/i18n/configuration.md +84 -0
- package/docs/features/i18n/formatting.md +133 -0
- package/docs/features/i18n/locale-detection.md +122 -0
- package/docs/features/i18n/routing.md +99 -0
- package/docs/features/i18n/rtl-support.md +191 -0
- package/docs/features/i18n/translations.md +129 -0
- package/docs/features/internationalization.md +595 -0
- package/docs/features/performance/README.md +77 -0
- package/docs/features/performance/bundle-size.md +134 -0
- package/docs/features/performance/caching.md +131 -0
- package/docs/features/performance/code-splitting.md +121 -0
- package/docs/features/performance/image-optimization.md +110 -0
- package/docs/features/performance/lazy-loading.md +92 -0
- package/docs/features/performance/monitoring.md +148 -0
- package/docs/features/seo/README.md +51 -0
- package/docs/features/seo/best-practices.md +184 -0
- package/docs/features/seo/canonical-urls.md +182 -0
- package/docs/features/seo/meta-tags.md +126 -0
- package/docs/features/seo/open-graph.md +166 -0
- package/docs/features/seo/robots-txt.md +146 -0
- package/docs/features/seo/sitemaps.md +162 -0
- package/docs/features/seo/structured-data.md +166 -0
- package/docs/getting-started/installation.md +292 -0
- package/docs/getting-started/introduction.md +195 -0
- package/docs/getting-started/quick-start.md +460 -0
- package/docs/guides/analytics-setup.md +616 -0
- package/docs/i18n/CONFIGURATION.md +353 -0
- package/docs/i18n/EXAMPLES.md +402 -0
- package/docs/i18n/MIGRATION.md +260 -0
- package/docs/i18n/SEO.md +392 -0
- package/docs/i18n/STATIC-GENERATION-FIX.md +71 -0
- package/docs/migration/changelog.md +136 -0
- package/docs/migration/overview.md +233 -0
- package/docs/recipes/adding-animations.md +475 -0
- package/docs/recipes/forms-with-validation.md +393 -0
- package/package.json +152 -0
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,2184 @@
|
|
|
1
|
+
import { ThemeConfig, HeroContent, LocalizedString, AboutContent, ServicesContent, HeaderConfig, FooterConfig } from './config/index.mjs';
|
|
2
|
+
export { FooterSection, LogoConfig, NavDropdown, NavDropdownItem, NavItem, NavLink, NavigationConfig, ScriptsConfig, ServiceItem, SiteContent, SiteMetadata, WhyChooseUsContent } from './config/index.mjs';
|
|
3
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
4
|
+
import { ClassValue } from 'clsx';
|
|
5
|
+
import { Metadata } from 'next';
|
|
6
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
7
|
+
import { ReactNode, ButtonHTMLAttributes, HTMLAttributes, InputHTMLAttributes, TextareaHTMLAttributes, ComponentType } from 'react';
|
|
8
|
+
import { LucideProps } from 'lucide-react';
|
|
9
|
+
import { FieldError } from 'react-hook-form';
|
|
10
|
+
|
|
11
|
+
declare function generateThemeCSS(theme: ThemeConfig): string;
|
|
12
|
+
declare function generateDesignTokens(theme: ThemeConfig): string;
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Gets the font import configuration for Next.js
|
|
16
|
+
* This prepares the data needed to dynamically import fonts
|
|
17
|
+
*/
|
|
18
|
+
declare function getFontConfig(theme: ThemeConfig): {
|
|
19
|
+
heading: {
|
|
20
|
+
family: string;
|
|
21
|
+
weights: string[];
|
|
22
|
+
variable: string;
|
|
23
|
+
};
|
|
24
|
+
body: {
|
|
25
|
+
family: string;
|
|
26
|
+
weights: string[];
|
|
27
|
+
variable: string;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Generates CSS variable names from theme fonts
|
|
32
|
+
*/
|
|
33
|
+
declare function getFontVariables(theme: ThemeConfig): string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Helper to get localized string from content
|
|
37
|
+
*/
|
|
38
|
+
declare function getLocalizedString(localizedString: {
|
|
39
|
+
[locale: string]: string;
|
|
40
|
+
}, locale: string): string;
|
|
41
|
+
|
|
42
|
+
interface PolicyMetadata {
|
|
43
|
+
/** Policy title */
|
|
44
|
+
title: string;
|
|
45
|
+
/** Last updated date (ISO string) */
|
|
46
|
+
lastUpdated: string;
|
|
47
|
+
/** Optional description for SEO */
|
|
48
|
+
description?: string;
|
|
49
|
+
/** Additional custom fields */
|
|
50
|
+
[key: string]: string | undefined;
|
|
51
|
+
}
|
|
52
|
+
interface Policy {
|
|
53
|
+
/** Compiled MDX content */
|
|
54
|
+
content: JSX.Element;
|
|
55
|
+
/** Frontmatter metadata */
|
|
56
|
+
metadata: PolicyMetadata;
|
|
57
|
+
/** Policy slug (filename without locale/extension) */
|
|
58
|
+
slug: string;
|
|
59
|
+
/** Locale */
|
|
60
|
+
locale: string;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Load a policy markdown file and compile it to React
|
|
64
|
+
*
|
|
65
|
+
* @param slug - Policy slug (e.g., 'privacy-policy', 'terms-of-service')
|
|
66
|
+
* @param locale - Locale code (e.g., 'en', 'fr')
|
|
67
|
+
* @param contentDir - Directory containing policy files @default 'src/content/policies'
|
|
68
|
+
* @returns Compiled policy with content and metadata
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```typescript
|
|
72
|
+
* const { content, metadata } = await loadPolicy('privacy-policy', 'en');
|
|
73
|
+
*
|
|
74
|
+
* return (
|
|
75
|
+
* <PolicyLayout
|
|
76
|
+
* title={metadata.title}
|
|
77
|
+
* lastUpdated={metadata.lastUpdated}
|
|
78
|
+
* >
|
|
79
|
+
* {content}
|
|
80
|
+
* </PolicyLayout>
|
|
81
|
+
* );
|
|
82
|
+
* ```
|
|
83
|
+
*/
|
|
84
|
+
declare function loadPolicy(slug: string, locale: string, contentDir?: string): Promise<Policy>;
|
|
85
|
+
/**
|
|
86
|
+
* Get all unique policy slugs (without locale suffix)
|
|
87
|
+
*
|
|
88
|
+
* @param contentDir - Directory containing policy files @default 'src/content/policies'
|
|
89
|
+
* @returns Array of policy slugs
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* const slugs = getPolicySlugs(); // ['privacy-policy', 'terms-of-service']
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
declare function getPolicySlugs(contentDir?: string): string[];
|
|
97
|
+
/**
|
|
98
|
+
* Get all policies for a specific locale with their metadata
|
|
99
|
+
*
|
|
100
|
+
* @param locale - Locale code
|
|
101
|
+
* @param contentDir - Directory containing policy files @default 'src/content/policies'
|
|
102
|
+
* @returns Array of policies with metadata
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* ```typescript
|
|
106
|
+
* const policies = await getAllPolicies('en');
|
|
107
|
+
*
|
|
108
|
+
* // Render policy list
|
|
109
|
+
* policies.map(policy => (
|
|
110
|
+
* <Link key={policy.slug} href={`/policies/${policy.slug}`}>
|
|
111
|
+
* {policy.metadata.title}
|
|
112
|
+
* </Link>
|
|
113
|
+
* ));
|
|
114
|
+
* ```
|
|
115
|
+
*/
|
|
116
|
+
declare function getAllPolicies(locale: string, contentDir?: string): Promise<Omit<Policy, 'content'>[]>;
|
|
117
|
+
/**
|
|
118
|
+
* Get available locales for a specific policy
|
|
119
|
+
*
|
|
120
|
+
* @param slug - Policy slug
|
|
121
|
+
* @param contentDir - Directory containing policy files @default 'src/content/policies'
|
|
122
|
+
* @returns Array of locale codes
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* const locales = getPolicyLocales('privacy-policy'); // ['en', 'fr']
|
|
127
|
+
* ```
|
|
128
|
+
*/
|
|
129
|
+
declare function getPolicyLocales(slug: string, contentDir?: string): string[];
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Helper to get localized string from navigation
|
|
133
|
+
*/
|
|
134
|
+
declare function getNavigationString(localizedString: {
|
|
135
|
+
[locale: string]: string;
|
|
136
|
+
}, locale: string): string;
|
|
137
|
+
/**
|
|
138
|
+
* Helper to replace placeholders in strings
|
|
139
|
+
*/
|
|
140
|
+
declare function replaceVariables(text: string, variables: Record<string, string>): string;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Locale prefix mode determines how locale appears in URLs
|
|
144
|
+
*
|
|
145
|
+
* - 'always': All routes have locale prefix (/en/about, /fr/about)
|
|
146
|
+
* - 'as-needed': Only non-default locales have prefix (/about, /fr/about)
|
|
147
|
+
* - 'never': No locale prefixes, detection via cookie/header only
|
|
148
|
+
*/
|
|
149
|
+
type LocalePrefix = 'always' | 'as-needed' | 'never';
|
|
150
|
+
/**
|
|
151
|
+
* Cookie configuration for locale persistence
|
|
152
|
+
*/
|
|
153
|
+
interface LocaleCookieConfig {
|
|
154
|
+
/** Cookie name @default 'NEXT_LOCALE' */
|
|
155
|
+
name?: string;
|
|
156
|
+
/** Cookie max age in seconds @default 31536000 (1 year) */
|
|
157
|
+
maxAge?: number;
|
|
158
|
+
/** Cookie SameSite attribute @default 'lax' */
|
|
159
|
+
sameSite?: 'lax' | 'strict' | 'none';
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Slug translations mapping for bilingual/multilingual routes
|
|
163
|
+
* Maps slugs from one locale to another
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* {
|
|
167
|
+
* fr: {
|
|
168
|
+
* '/a-propos': '/about',
|
|
169
|
+
* '/nous-contacter': '/contact'
|
|
170
|
+
* },
|
|
171
|
+
* en: {
|
|
172
|
+
* '/about': '/a-propos',
|
|
173
|
+
* '/contact': '/nous-contacter'
|
|
174
|
+
* }
|
|
175
|
+
* }
|
|
176
|
+
*/
|
|
177
|
+
interface SlugTranslations {
|
|
178
|
+
[locale: string]: {
|
|
179
|
+
[slug: string]: string;
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Complete i18n configuration for a project
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```typescript
|
|
187
|
+
* export const i18nConfig: I18nConfig = {
|
|
188
|
+
* locales: ['en', 'fr', 'es'],
|
|
189
|
+
* defaultLocale: 'en',
|
|
190
|
+
* localePrefix: 'as-needed',
|
|
191
|
+
* localeDetection: true,
|
|
192
|
+
* localeNames: {
|
|
193
|
+
* en: 'English',
|
|
194
|
+
* fr: 'Français',
|
|
195
|
+
* es: 'Español'
|
|
196
|
+
* }
|
|
197
|
+
* };
|
|
198
|
+
* ```
|
|
199
|
+
*/
|
|
200
|
+
interface I18nConfig {
|
|
201
|
+
/**
|
|
202
|
+
* Array of supported locale codes (ISO 639-1)
|
|
203
|
+
* @example ['en', 'fr', 'es', 'de', 'ja']
|
|
204
|
+
*/
|
|
205
|
+
locales: readonly string[];
|
|
206
|
+
/**
|
|
207
|
+
* Default locale to use when none is specified
|
|
208
|
+
* Must be one of the locales in the locales array
|
|
209
|
+
*/
|
|
210
|
+
defaultLocale: string;
|
|
211
|
+
/**
|
|
212
|
+
* Locale prefix mode for URL routing
|
|
213
|
+
* @default 'as-needed'
|
|
214
|
+
*/
|
|
215
|
+
localePrefix?: LocalePrefix;
|
|
216
|
+
/**
|
|
217
|
+
* Enable automatic locale detection from browser headers
|
|
218
|
+
* @default true
|
|
219
|
+
*/
|
|
220
|
+
localeDetection?: boolean;
|
|
221
|
+
/**
|
|
222
|
+
* Full locale names for display in UI
|
|
223
|
+
* @example { en: 'English', fr: 'Français' }
|
|
224
|
+
*/
|
|
225
|
+
localeNames?: Record<string, string>;
|
|
226
|
+
/**
|
|
227
|
+
* Short locale labels for compact display (e.g., language selector)
|
|
228
|
+
* @example { en: 'EN', fr: 'FR' }
|
|
229
|
+
*/
|
|
230
|
+
localeLabels?: Record<string, string>;
|
|
231
|
+
/**
|
|
232
|
+
* Locales that use right-to-left text direction
|
|
233
|
+
* @example ['ar', 'he', 'fa']
|
|
234
|
+
*/
|
|
235
|
+
rtlLocales?: readonly string[];
|
|
236
|
+
/**
|
|
237
|
+
* Cookie configuration for locale persistence
|
|
238
|
+
*/
|
|
239
|
+
localeCookie?: LocaleCookieConfig;
|
|
240
|
+
/**
|
|
241
|
+
* Custom slug translations for multilingual routes
|
|
242
|
+
* If not provided, no slug translation will occur
|
|
243
|
+
*/
|
|
244
|
+
slugTranslations?: SlugTranslations;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* Type guard to validate locale string
|
|
248
|
+
*/
|
|
249
|
+
type ValidLocale<T extends I18nConfig> = T['locales'][number];
|
|
250
|
+
/**
|
|
251
|
+
* Browser language preference with quality value (from Accept-Language header)
|
|
252
|
+
*/
|
|
253
|
+
interface LanguagePreference {
|
|
254
|
+
locale: string;
|
|
255
|
+
quality: number;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Initialize the i18n configuration
|
|
260
|
+
* Must be called before using any i18n features
|
|
261
|
+
*
|
|
262
|
+
* @param config - Complete i18n configuration
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```typescript
|
|
266
|
+
* import { setI18nConfig } from 'simple-site-framework/lib/i18n';
|
|
267
|
+
* import { i18nConfig } from './config/i18n';
|
|
268
|
+
*
|
|
269
|
+
* setI18nConfig(i18nConfig);
|
|
270
|
+
* ```
|
|
271
|
+
*/
|
|
272
|
+
declare function setI18nConfig(config: I18nConfig): void;
|
|
273
|
+
/**
|
|
274
|
+
* Get the current i18n configuration
|
|
275
|
+
* Returns legacy default config if not initialized (for build compatibility)
|
|
276
|
+
*
|
|
277
|
+
* @deprecated Calling without initialization - will throw in v1.0.0
|
|
278
|
+
*/
|
|
279
|
+
declare function getI18nConfig(): I18nConfig;
|
|
280
|
+
/**
|
|
281
|
+
* Check if i18n config has been initialized
|
|
282
|
+
*/
|
|
283
|
+
declare function isI18nConfigInitialized(): boolean;
|
|
284
|
+
/**
|
|
285
|
+
* Get supported locales array
|
|
286
|
+
*/
|
|
287
|
+
declare function getLocales(): readonly string[];
|
|
288
|
+
/**
|
|
289
|
+
* Get default locale
|
|
290
|
+
*/
|
|
291
|
+
declare function getDefaultLocale(): string;
|
|
292
|
+
/**
|
|
293
|
+
* Get locale prefix mode
|
|
294
|
+
*/
|
|
295
|
+
declare function getLocalePrefix(): LocalePrefix;
|
|
296
|
+
/**
|
|
297
|
+
* Get locale names mapping
|
|
298
|
+
*/
|
|
299
|
+
declare function getLocaleNames(): Record<string, string>;
|
|
300
|
+
/**
|
|
301
|
+
* Get locale labels mapping
|
|
302
|
+
*/
|
|
303
|
+
declare function getLocaleLabels(): Record<string, string>;
|
|
304
|
+
/**
|
|
305
|
+
* Get RTL locales array
|
|
306
|
+
*/
|
|
307
|
+
declare function getRtlLocales(): readonly string[];
|
|
308
|
+
/**
|
|
309
|
+
* Check if locale detection is enabled
|
|
310
|
+
*/
|
|
311
|
+
declare function isLocaleDetectionEnabled(): boolean;
|
|
312
|
+
/**
|
|
313
|
+
* Get locale cookie configuration
|
|
314
|
+
*/
|
|
315
|
+
declare function getLocaleCookieConfig(): {
|
|
316
|
+
name: string;
|
|
317
|
+
maxAge: number;
|
|
318
|
+
sameSite: "none" | "lax" | "strict";
|
|
319
|
+
};
|
|
320
|
+
/**
|
|
321
|
+
* Check if a locale is supported
|
|
322
|
+
*/
|
|
323
|
+
declare function isSupportedLocale(locale: string): boolean;
|
|
324
|
+
/**
|
|
325
|
+
* Get full locale name for display
|
|
326
|
+
*/
|
|
327
|
+
declare function getLocaleName(locale: string): string;
|
|
328
|
+
/**
|
|
329
|
+
* Get short locale label for compact display
|
|
330
|
+
*/
|
|
331
|
+
declare function getLocaleLabel(locale: string): string;
|
|
332
|
+
/**
|
|
333
|
+
* @deprecated Use getLocales() instead. This export will be removed in v1.0.0
|
|
334
|
+
*/
|
|
335
|
+
declare const locales: readonly ["fr", "en"];
|
|
336
|
+
/**
|
|
337
|
+
* @deprecated Import from config schemas instead
|
|
338
|
+
*/
|
|
339
|
+
type Locale = 'fr' | 'en';
|
|
340
|
+
/**
|
|
341
|
+
* @deprecated Use getDefaultLocale() instead. This export will be removed in v1.0.0
|
|
342
|
+
*/
|
|
343
|
+
declare const defaultLocale: Locale;
|
|
344
|
+
|
|
345
|
+
declare const LOCALE_COOKIE_NAME = "NEXT_LOCALE";
|
|
346
|
+
/**
|
|
347
|
+
* Get locale from cookie
|
|
348
|
+
* Returns null if cookie not found or locale not supported
|
|
349
|
+
*/
|
|
350
|
+
declare function getLocaleFromCookie(): string | null;
|
|
351
|
+
/**
|
|
352
|
+
* Set locale cookie
|
|
353
|
+
* Uses configuration from i18n config
|
|
354
|
+
*/
|
|
355
|
+
declare function setLocaleCookie(locale: string): void;
|
|
356
|
+
|
|
357
|
+
declare const defaultSlugTranslations: SlugTranslations;
|
|
358
|
+
/**
|
|
359
|
+
* Translates a path from one locale to another
|
|
360
|
+
*
|
|
361
|
+
* Merges translations in this priority order:
|
|
362
|
+
* 1. Custom translations passed as parameter (highest priority)
|
|
363
|
+
* 2. Translations from i18n config (if configured)
|
|
364
|
+
* 3. Default translations (lowest priority)
|
|
365
|
+
*
|
|
366
|
+
* @param path - The current path (without locale prefix)
|
|
367
|
+
* @param fromLocale - The current locale
|
|
368
|
+
* @param toLocale - The target locale
|
|
369
|
+
* @param customTranslations - Optional custom translations to merge with defaults
|
|
370
|
+
* @returns The translated path
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```typescript
|
|
374
|
+
* // With config translations
|
|
375
|
+
* translateSlug('/about', 'en', 'fr')
|
|
376
|
+
* // Returns: '/a-propos' (from config or defaults)
|
|
377
|
+
*
|
|
378
|
+
* // With custom override
|
|
379
|
+
* translateSlug('/about', 'en', 'fr', {
|
|
380
|
+
* en: { '/about': '/notre-equipe' }
|
|
381
|
+
* })
|
|
382
|
+
* // Returns: '/notre-equipe' (custom override)
|
|
383
|
+
*
|
|
384
|
+
* // Nested paths
|
|
385
|
+
* translateSlug('/about/team', 'en', 'fr')
|
|
386
|
+
* // Returns: '/a-propos/team' (translates base, keeps rest)
|
|
387
|
+
* ```
|
|
388
|
+
*/
|
|
389
|
+
declare function translateSlug(path: string, fromLocale: string, toLocale: string, customTranslations?: SlugTranslations): string;
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Format a date according to locale
|
|
393
|
+
*
|
|
394
|
+
* @param date - Date to format
|
|
395
|
+
* @param locale - Locale code (e.g., 'en', 'fr', 'es')
|
|
396
|
+
* @param options - Intl.DateTimeFormat options
|
|
397
|
+
*
|
|
398
|
+
* @example
|
|
399
|
+
* ```typescript
|
|
400
|
+
* formatDate(new Date('2026-02-01'), 'fr', { dateStyle: 'long' })
|
|
401
|
+
* // "1 février 2026"
|
|
402
|
+
*
|
|
403
|
+
* formatDate(new Date('2026-02-01'), 'en', { dateStyle: 'long' })
|
|
404
|
+
* // "February 1, 2026"
|
|
405
|
+
*
|
|
406
|
+
* formatDate(new Date(), 'fr', {
|
|
407
|
+
* weekday: 'long',
|
|
408
|
+
* year: 'numeric',
|
|
409
|
+
* month: 'long',
|
|
410
|
+
* day: 'numeric'
|
|
411
|
+
* })
|
|
412
|
+
* // "samedi 1 février 2026"
|
|
413
|
+
* ```
|
|
414
|
+
*/
|
|
415
|
+
declare function formatDate(date: Date, locale: string, options?: Intl.DateTimeFormatOptions): string;
|
|
416
|
+
/**
|
|
417
|
+
* Format a number according to locale
|
|
418
|
+
*
|
|
419
|
+
* @param value - Number to format
|
|
420
|
+
* @param locale - Locale code
|
|
421
|
+
* @param options - Intl.NumberFormat options
|
|
422
|
+
*
|
|
423
|
+
* @example
|
|
424
|
+
* ```typescript
|
|
425
|
+
* formatNumber(1234567.89, 'en')
|
|
426
|
+
* // "1,234,567.89"
|
|
427
|
+
*
|
|
428
|
+
* formatNumber(1234567.89, 'fr')
|
|
429
|
+
* // "1 234 567,89"
|
|
430
|
+
*
|
|
431
|
+
* formatNumber(0.42, 'en', { style: 'percent' })
|
|
432
|
+
* // "42%"
|
|
433
|
+
*
|
|
434
|
+
* formatNumber(1234, 'en', { minimumFractionDigits: 2 })
|
|
435
|
+
* // "1,234.00"
|
|
436
|
+
* ```
|
|
437
|
+
*/
|
|
438
|
+
declare function formatNumber(value: number, locale: string, options?: Intl.NumberFormatOptions): string;
|
|
439
|
+
/**
|
|
440
|
+
* Format a currency amount according to locale
|
|
441
|
+
*
|
|
442
|
+
* @param amount - Amount to format
|
|
443
|
+
* @param locale - Locale code
|
|
444
|
+
* @param currency - ISO 4217 currency code (e.g., 'USD', 'EUR', 'CAD')
|
|
445
|
+
* @param options - Additional Intl.NumberFormat options
|
|
446
|
+
*
|
|
447
|
+
* @example
|
|
448
|
+
* ```typescript
|
|
449
|
+
* formatCurrency(1299.99, 'en', 'USD')
|
|
450
|
+
* // "$1,299.99"
|
|
451
|
+
*
|
|
452
|
+
* formatCurrency(1299.99, 'fr', 'EUR')
|
|
453
|
+
* // "1 299,99 €"
|
|
454
|
+
*
|
|
455
|
+
* formatCurrency(1299.99, 'fr-CA', 'CAD')
|
|
456
|
+
* // "1 299,99 $"
|
|
457
|
+
*
|
|
458
|
+
* formatCurrency(1299.99, 'ja', 'JPY')
|
|
459
|
+
* // "¥1,300"
|
|
460
|
+
*
|
|
461
|
+
* formatCurrency(0.99, 'en', 'USD', { currencyDisplay: 'name' })
|
|
462
|
+
* // "0.99 US dollars"
|
|
463
|
+
* ```
|
|
464
|
+
*/
|
|
465
|
+
declare function formatCurrency(amount: number, locale: string, currency: string, options?: Intl.NumberFormatOptions): string;
|
|
466
|
+
/**
|
|
467
|
+
* Format relative time (e.g., "2 hours ago", "in 3 days")
|
|
468
|
+
*
|
|
469
|
+
* @param value - Numeric value (negative for past, positive for future)
|
|
470
|
+
* @param unit - Time unit
|
|
471
|
+
* @param locale - Locale code
|
|
472
|
+
* @param options - Intl.RelativeTimeFormat options
|
|
473
|
+
*
|
|
474
|
+
* @example
|
|
475
|
+
* ```typescript
|
|
476
|
+
* formatRelativeTime(-2, 'hour', 'en')
|
|
477
|
+
* // "2 hours ago"
|
|
478
|
+
*
|
|
479
|
+
* formatRelativeTime(-2, 'hour', 'fr')
|
|
480
|
+
* // "il y a 2 heures"
|
|
481
|
+
*
|
|
482
|
+
* formatRelativeTime(3, 'day', 'en')
|
|
483
|
+
* // "in 3 days"
|
|
484
|
+
*
|
|
485
|
+
* formatRelativeTime(-1, 'week', 'es')
|
|
486
|
+
* // "hace 1 semana"
|
|
487
|
+
*
|
|
488
|
+
* formatRelativeTime(-5, 'minute', 'en', { numeric: 'auto' })
|
|
489
|
+
* // "5 minutes ago"
|
|
490
|
+
* ```
|
|
491
|
+
*/
|
|
492
|
+
declare function formatRelativeTime(value: number, unit: Intl.RelativeTimeFormatUnit, locale: string, options?: Intl.RelativeTimeFormatOptions): string;
|
|
493
|
+
/**
|
|
494
|
+
* Format a date range according to locale
|
|
495
|
+
* Note: formatRange requires newer TypeScript/Node versions
|
|
496
|
+
*
|
|
497
|
+
* @param startDate - Start date
|
|
498
|
+
* @param endDate - End date
|
|
499
|
+
* @param locale - Locale code
|
|
500
|
+
* @param options - Intl.DateTimeFormat options
|
|
501
|
+
*
|
|
502
|
+
* @example
|
|
503
|
+
* ```typescript
|
|
504
|
+
* formatDateRange(
|
|
505
|
+
* new Date('2026-02-01'),
|
|
506
|
+
* new Date('2026-02-15'),
|
|
507
|
+
* 'en',
|
|
508
|
+
* { month: 'long', day: 'numeric' }
|
|
509
|
+
* )
|
|
510
|
+
* // "February 1 – 15"
|
|
511
|
+
* ```
|
|
512
|
+
*/
|
|
513
|
+
declare function formatDateRange(startDate: Date, endDate: Date, locale: string, options?: Intl.DateTimeFormatOptions): string;
|
|
514
|
+
/**
|
|
515
|
+
* Format a list of items according to locale
|
|
516
|
+
* Falls back to simple comma-separated list if Intl.ListFormat not available
|
|
517
|
+
*
|
|
518
|
+
* @param items - Array of items to format
|
|
519
|
+
* @param locale - Locale code
|
|
520
|
+
*
|
|
521
|
+
* @example
|
|
522
|
+
* ```typescript
|
|
523
|
+
* formatList(['apples', 'oranges', 'bananas'], 'en')
|
|
524
|
+
* // "apples, oranges, and bananas"
|
|
525
|
+
*
|
|
526
|
+
* formatList(['pommes', 'oranges', 'bananes'], 'fr')
|
|
527
|
+
* // "pommes, oranges et bananes"
|
|
528
|
+
* ```
|
|
529
|
+
*/
|
|
530
|
+
declare function formatList(items: string[], locale: string): string;
|
|
531
|
+
/**
|
|
532
|
+
* Format file size in bytes to human-readable format
|
|
533
|
+
* Not using Intl, but useful for internationalized apps
|
|
534
|
+
*
|
|
535
|
+
* @param bytes - File size in bytes
|
|
536
|
+
* @param locale - Locale code
|
|
537
|
+
* @param decimals - Number of decimal places @default 2
|
|
538
|
+
*
|
|
539
|
+
* @example
|
|
540
|
+
* ```typescript
|
|
541
|
+
* formatFileSize(1024, 'en')
|
|
542
|
+
* // "1.00 KB"
|
|
543
|
+
*
|
|
544
|
+
* formatFileSize(1048576, 'fr')
|
|
545
|
+
* // "1,00 Mo"
|
|
546
|
+
*
|
|
547
|
+
* formatFileSize(1073741824, 'en', 1)
|
|
548
|
+
* // "1.0 GB"
|
|
549
|
+
* ```
|
|
550
|
+
*/
|
|
551
|
+
declare function formatFileSize(bytes: number, locale: string, decimals?: number): string;
|
|
552
|
+
/**
|
|
553
|
+
* Get relative time from a date (auto-selects best unit)
|
|
554
|
+
*
|
|
555
|
+
* @param date - Date to compare against now
|
|
556
|
+
* @param locale - Locale code
|
|
557
|
+
* @param options - Intl.RelativeTimeFormat options
|
|
558
|
+
*
|
|
559
|
+
* @example
|
|
560
|
+
* ```typescript
|
|
561
|
+
* // If date is 2 hours ago
|
|
562
|
+
* getRelativeTime(pastDate, 'en')
|
|
563
|
+
* // "2 hours ago"
|
|
564
|
+
*
|
|
565
|
+
* // If date is 3 days from now
|
|
566
|
+
* getRelativeTime(futureDate, 'en')
|
|
567
|
+
* // "in 3 days"
|
|
568
|
+
*
|
|
569
|
+
* // If date is 45 seconds ago
|
|
570
|
+
* getRelativeTime(recentDate, 'fr')
|
|
571
|
+
* // "il y a 45 secondes"
|
|
572
|
+
* ```
|
|
573
|
+
*/
|
|
574
|
+
declare function getRelativeTime(date: Date, locale: string, options?: Intl.RelativeTimeFormatOptions): string;
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Create Next.js middleware for i18n routing
|
|
578
|
+
*
|
|
579
|
+
* @param config - i18n configuration
|
|
580
|
+
* @returns Middleware function
|
|
581
|
+
*
|
|
582
|
+
* @example
|
|
583
|
+
* ```typescript
|
|
584
|
+
* // middleware.ts
|
|
585
|
+
* import { createI18nMiddleware } from 'simple-site-framework/lib/i18n';
|
|
586
|
+
* import { i18nConfig } from './src/config/i18n';
|
|
587
|
+
*
|
|
588
|
+
* export default createI18nMiddleware(i18nConfig);
|
|
589
|
+
*
|
|
590
|
+
* export const config = {
|
|
591
|
+
* matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
|
|
592
|
+
* };
|
|
593
|
+
* ```
|
|
594
|
+
*/
|
|
595
|
+
declare function createI18nMiddleware(config: I18nConfig): (request: NextRequest) => NextResponse;
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Check if a locale uses right-to-left text direction
|
|
599
|
+
*
|
|
600
|
+
* @param locale - Locale code to check
|
|
601
|
+
* @returns true if locale is RTL, false otherwise
|
|
602
|
+
*
|
|
603
|
+
* @example
|
|
604
|
+
* ```typescript
|
|
605
|
+
* isRtlLocale('ar') // true (Arabic)
|
|
606
|
+
* isRtlLocale('he') // true (Hebrew)
|
|
607
|
+
* isRtlLocale('en') // false (English)
|
|
608
|
+
* isRtlLocale('fr') // false (French)
|
|
609
|
+
* ```
|
|
610
|
+
*/
|
|
611
|
+
declare function isRtlLocale(locale: string): boolean;
|
|
612
|
+
/**
|
|
613
|
+
* Get text direction for a locale
|
|
614
|
+
*
|
|
615
|
+
* @param locale - Locale code
|
|
616
|
+
* @returns 'rtl' for right-to-left locales, 'ltr' for left-to-right
|
|
617
|
+
*
|
|
618
|
+
* @example
|
|
619
|
+
* ```tsx
|
|
620
|
+
* // In layout
|
|
621
|
+
* <html lang={locale} dir={getTextDirection(locale)}>
|
|
622
|
+
*
|
|
623
|
+
* // Results:
|
|
624
|
+
* getTextDirection('ar') // 'rtl'
|
|
625
|
+
* getTextDirection('en') // 'ltr'
|
|
626
|
+
* ```
|
|
627
|
+
*/
|
|
628
|
+
declare function getTextDirection(locale: string): 'ltr' | 'rtl';
|
|
629
|
+
/**
|
|
630
|
+
* Validate if a locale is supported
|
|
631
|
+
*
|
|
632
|
+
* @param locale - Locale code to validate
|
|
633
|
+
* @returns true if locale is supported, false otherwise
|
|
634
|
+
*
|
|
635
|
+
* @example
|
|
636
|
+
* ```typescript
|
|
637
|
+
* // With config: locales: ['en', 'fr', 'es']
|
|
638
|
+
* validateLocale('en') // true
|
|
639
|
+
* validateLocale('fr') // true
|
|
640
|
+
* validateLocale('de') // false
|
|
641
|
+
* validateLocale('invalid') // false
|
|
642
|
+
* ```
|
|
643
|
+
*/
|
|
644
|
+
declare function validateLocale(locale: string): boolean;
|
|
645
|
+
/**
|
|
646
|
+
* Get all supported locales except the current one
|
|
647
|
+
*
|
|
648
|
+
* @param currentLocale - Current locale to exclude
|
|
649
|
+
* @returns Array of alternate locales
|
|
650
|
+
*
|
|
651
|
+
* @example
|
|
652
|
+
* ```typescript
|
|
653
|
+
* // With config: locales: ['en', 'fr', 'es', 'de']
|
|
654
|
+
* getAlternateLocales('en') // ['fr', 'es', 'de']
|
|
655
|
+
* getAlternateLocales('fr') // ['en', 'es', 'de']
|
|
656
|
+
* ```
|
|
657
|
+
*/
|
|
658
|
+
declare function getAlternateLocales(currentLocale: string): string[];
|
|
659
|
+
/**
|
|
660
|
+
* Normalize locale code to lowercase
|
|
661
|
+
* Handles variants like 'en-US' -> 'en', 'zh-CN' -> 'zh'
|
|
662
|
+
*
|
|
663
|
+
* @param locale - Locale code (can include region)
|
|
664
|
+
* @returns Normalized locale code
|
|
665
|
+
*
|
|
666
|
+
* @example
|
|
667
|
+
* ```typescript
|
|
668
|
+
* normalizeLocale('en-US') // 'en'
|
|
669
|
+
* normalizeLocale('zh-CN') // 'zh'
|
|
670
|
+
* normalizeLocale('FR') // 'fr'
|
|
671
|
+
* normalizeLocale('es') // 'es'
|
|
672
|
+
* ```
|
|
673
|
+
*/
|
|
674
|
+
declare function normalizeLocale(locale: string): string;
|
|
675
|
+
/**
|
|
676
|
+
* Check if a locale code matches a supported locale
|
|
677
|
+
* Handles locale variants (e.g., 'en-US' matches 'en')
|
|
678
|
+
*
|
|
679
|
+
* @param locale - Locale code to check (can include region)
|
|
680
|
+
* @returns Matching supported locale or null
|
|
681
|
+
*
|
|
682
|
+
* @example
|
|
683
|
+
* ```typescript
|
|
684
|
+
* // With config: locales: ['en', 'fr', 'es']
|
|
685
|
+
* matchLocale('en-US') // 'en'
|
|
686
|
+
* matchLocale('fr-CA') // 'fr'
|
|
687
|
+
* matchLocale('de-DE') // null
|
|
688
|
+
* ```
|
|
689
|
+
*/
|
|
690
|
+
declare function matchLocale(locale: string): string | null;
|
|
691
|
+
/**
|
|
692
|
+
* Get locale display name in its own language (autonym)
|
|
693
|
+
*
|
|
694
|
+
* @param locale - Locale code
|
|
695
|
+
* @returns Display name or locale code if not found
|
|
696
|
+
*
|
|
697
|
+
* @example
|
|
698
|
+
* ```typescript
|
|
699
|
+
* getLocaleAutonym('en') // 'English'
|
|
700
|
+
* getLocaleAutonym('fr') // 'Français'
|
|
701
|
+
* getLocaleAutonym('es') // 'Español'
|
|
702
|
+
* ```
|
|
703
|
+
*/
|
|
704
|
+
declare function getLocaleAutonym(locale: string): string;
|
|
705
|
+
/**
|
|
706
|
+
* Format locale for display (e.g., for language selector)
|
|
707
|
+
*
|
|
708
|
+
* @param locale - Locale code
|
|
709
|
+
* @param format - Display format
|
|
710
|
+
* @returns Formatted locale string
|
|
711
|
+
*
|
|
712
|
+
* @example
|
|
713
|
+
* ```typescript
|
|
714
|
+
* formatLocaleDisplay('en', 'name') // 'English'
|
|
715
|
+
* formatLocaleDisplay('fr', 'label') // 'FR'
|
|
716
|
+
* formatLocaleDisplay('es', 'code') // 'es'
|
|
717
|
+
* ```
|
|
718
|
+
*/
|
|
719
|
+
declare function formatLocaleDisplay(locale: string, format?: 'name' | 'label' | 'code'): string;
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* Combines class names using clsx and merges Tailwind classes intelligently
|
|
723
|
+
* @param inputs - Class names to combine
|
|
724
|
+
* @returns Merged class string
|
|
725
|
+
*/
|
|
726
|
+
declare function cn(...inputs: ClassValue[]): string;
|
|
727
|
+
|
|
728
|
+
type EventCategory = 'cta' | 'form' | 'navigation' | 'engagement' | 'conversion' | 'ab_test';
|
|
729
|
+
type CTAType = 'signup' | 'trial' | 'contact' | 'download' | 'other';
|
|
730
|
+
type FormAction = 'start' | 'submit' | 'error' | 'abandon';
|
|
731
|
+
type PricingAction = 'view' | 'calculate' | 'plan_select' | 'currency_change';
|
|
732
|
+
type FeatureAction = 'view' | 'video_play' | 'video_complete' | 'cta_click';
|
|
733
|
+
type VideoAction = 'play' | 'pause' | 'complete' | '25%' | '50%' | '75%';
|
|
734
|
+
type NavigationLocation = 'header' | 'footer' | 'content' | 'mobile';
|
|
735
|
+
type ConversionType = 'trial_signup' | 'contact' | 'newsletter' | 'other';
|
|
736
|
+
type ScrollDepth = 25 | 50 | 75 | 100;
|
|
737
|
+
interface AnalyticsEvent {
|
|
738
|
+
event: string;
|
|
739
|
+
event_category?: EventCategory;
|
|
740
|
+
event_label?: string;
|
|
741
|
+
value?: number;
|
|
742
|
+
[key: string]: unknown;
|
|
743
|
+
}
|
|
744
|
+
interface CTAClickEvent extends AnalyticsEvent {
|
|
745
|
+
event: 'cta_click';
|
|
746
|
+
event_category: 'cta';
|
|
747
|
+
cta_location: string;
|
|
748
|
+
cta_text: string;
|
|
749
|
+
cta_type: CTAType;
|
|
750
|
+
}
|
|
751
|
+
interface FormEvent extends AnalyticsEvent {
|
|
752
|
+
event: `form_${FormAction}`;
|
|
753
|
+
event_category: 'form';
|
|
754
|
+
form_name: string;
|
|
755
|
+
form_action: FormAction;
|
|
756
|
+
}
|
|
757
|
+
interface PricingEvent extends AnalyticsEvent {
|
|
758
|
+
event: 'pricing_interaction';
|
|
759
|
+
event_category: 'engagement';
|
|
760
|
+
pricing_action: PricingAction;
|
|
761
|
+
}
|
|
762
|
+
interface FeatureEvent extends AnalyticsEvent {
|
|
763
|
+
event: 'feature_engagement';
|
|
764
|
+
event_category: 'engagement';
|
|
765
|
+
feature_name: string;
|
|
766
|
+
feature_action: FeatureAction;
|
|
767
|
+
}
|
|
768
|
+
interface VideoEvent extends AnalyticsEvent {
|
|
769
|
+
event: 'video_interaction';
|
|
770
|
+
event_category: 'engagement';
|
|
771
|
+
video_title: string;
|
|
772
|
+
video_action: VideoAction;
|
|
773
|
+
}
|
|
774
|
+
interface NavigationEvent extends AnalyticsEvent {
|
|
775
|
+
event: 'navigation_click';
|
|
776
|
+
event_category: 'navigation';
|
|
777
|
+
link_text: string;
|
|
778
|
+
link_url: string;
|
|
779
|
+
link_location: NavigationLocation;
|
|
780
|
+
}
|
|
781
|
+
interface ConversionEvent extends AnalyticsEvent {
|
|
782
|
+
event: 'conversion';
|
|
783
|
+
event_category: 'conversion';
|
|
784
|
+
conversion_type: ConversionType;
|
|
785
|
+
}
|
|
786
|
+
interface ABTestEvent extends AnalyticsEvent {
|
|
787
|
+
event: 'ab_test_event';
|
|
788
|
+
event_category: 'ab_test';
|
|
789
|
+
test_id: string;
|
|
790
|
+
variant: 'A' | 'B';
|
|
791
|
+
test_event: string;
|
|
792
|
+
}
|
|
793
|
+
interface ScrollDepthEvent extends AnalyticsEvent {
|
|
794
|
+
event: 'scroll_depth';
|
|
795
|
+
event_category: 'engagement';
|
|
796
|
+
scroll_percentage: ScrollDepth;
|
|
797
|
+
page_path: string;
|
|
798
|
+
}
|
|
799
|
+
interface PageViewEvent extends AnalyticsEvent {
|
|
800
|
+
event: 'page_view';
|
|
801
|
+
page_path: string;
|
|
802
|
+
page_title: string;
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
declare global {
|
|
806
|
+
interface Window {
|
|
807
|
+
dataLayer: unknown[];
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Generic event tracking function
|
|
813
|
+
* Use this for custom events not covered by specific tracking functions
|
|
814
|
+
*
|
|
815
|
+
* @param eventName - Name of the event
|
|
816
|
+
* @param properties - Additional event properties
|
|
817
|
+
*
|
|
818
|
+
* @example
|
|
819
|
+
* ```typescript
|
|
820
|
+
* trackEvent('button_click', {
|
|
821
|
+
* button_text: 'Sign Up',
|
|
822
|
+
* button_location: 'hero',
|
|
823
|
+
* button_variant: 'primary'
|
|
824
|
+
* });
|
|
825
|
+
* ```
|
|
826
|
+
*/
|
|
827
|
+
declare function trackEvent(eventName: string, properties?: Record<string, unknown>): void;
|
|
828
|
+
/**
|
|
829
|
+
* Track CTA click events
|
|
830
|
+
* @param ctaLocation - Where the CTA appears (e.g., 'hero', 'pricing', 'mobile_sticky')
|
|
831
|
+
* @param ctaText - The text on the CTA button
|
|
832
|
+
* @param ctaType - Type of CTA (e.g., 'signup', 'trial', 'contact')
|
|
833
|
+
*/
|
|
834
|
+
declare function trackCTAClick(ctaLocation: string, ctaText: string, ctaType?: 'signup' | 'trial' | 'contact' | 'download' | 'other'): void;
|
|
835
|
+
/**
|
|
836
|
+
* Track form interactions
|
|
837
|
+
* @param formName - Identifier for the form
|
|
838
|
+
* @param action - Form action (start, submit, error, abandon)
|
|
839
|
+
*/
|
|
840
|
+
declare function trackFormEvent(formName: string, action: 'start' | 'submit' | 'error' | 'abandon', metadata?: Record<string, unknown>): void;
|
|
841
|
+
/**
|
|
842
|
+
* Track page view (for SPAs and custom tracking)
|
|
843
|
+
* @param pagePath - The page path
|
|
844
|
+
* @param pageTitle - The page title
|
|
845
|
+
*/
|
|
846
|
+
declare function trackPageView(pagePath: string, pageTitle: string): void;
|
|
847
|
+
/**
|
|
848
|
+
* Track pricing page interactions
|
|
849
|
+
* @param action - Type of interaction
|
|
850
|
+
*/
|
|
851
|
+
declare function trackPricingEvent(action: 'view' | 'calculate' | 'plan_select' | 'currency_change', metadata?: Record<string, unknown>): void;
|
|
852
|
+
/**
|
|
853
|
+
* Track feature page engagement
|
|
854
|
+
* @param featureName - Name of the feature being viewed
|
|
855
|
+
* @param action - Type of engagement
|
|
856
|
+
*/
|
|
857
|
+
declare function trackFeatureEngagement(featureName: string, action: 'view' | 'video_play' | 'video_complete' | 'cta_click', metadata?: Record<string, unknown>): void;
|
|
858
|
+
/**
|
|
859
|
+
* Track resource downloads
|
|
860
|
+
* @param resourceName - Name of the resource
|
|
861
|
+
* @param resourceType - Type of resource (pdf, video, etc.)
|
|
862
|
+
*/
|
|
863
|
+
declare function trackResourceDownload(resourceName: string, resourceType: string): void;
|
|
864
|
+
/**
|
|
865
|
+
* Track video interactions
|
|
866
|
+
* @param videoTitle - Title of the video
|
|
867
|
+
* @param action - Video action
|
|
868
|
+
*/
|
|
869
|
+
declare function trackVideoEvent(videoTitle: string, action: 'play' | 'pause' | 'complete' | '25%' | '50%' | '75%', metadata?: Record<string, unknown>): void;
|
|
870
|
+
/**
|
|
871
|
+
* Track navigation events
|
|
872
|
+
* @param linkText - Text of the link clicked
|
|
873
|
+
* @param linkUrl - URL of the link
|
|
874
|
+
* @param linkLocation - Where the link appears (header, footer, content)
|
|
875
|
+
*/
|
|
876
|
+
declare function trackNavigation(linkText: string, linkUrl: string, linkLocation: 'header' | 'footer' | 'content' | 'mobile'): void;
|
|
877
|
+
/**
|
|
878
|
+
* Track A/B test variant assignment and events
|
|
879
|
+
* @param testId - ID of the A/B test
|
|
880
|
+
* @param variant - Variant assigned (A or B)
|
|
881
|
+
* @param eventName - Name of the event
|
|
882
|
+
*/
|
|
883
|
+
declare function trackABTestEvent(testId: string, variant: 'A' | 'B', eventName: string, metadata?: Record<string, unknown>): void;
|
|
884
|
+
/**
|
|
885
|
+
* Track conversion events (trial signups, purchases, etc.)
|
|
886
|
+
* @param conversionType - Type of conversion
|
|
887
|
+
* @param value - Monetary value (optional)
|
|
888
|
+
*/
|
|
889
|
+
declare function trackConversion(conversionType: 'trial_signup' | 'contact' | 'newsletter' | 'other', value?: number, metadata?: Record<string, unknown>): void;
|
|
890
|
+
/**
|
|
891
|
+
* Track scroll depth
|
|
892
|
+
* @param percentage - Scroll depth percentage (25, 50, 75, 100)
|
|
893
|
+
* @param pagePath - The page path
|
|
894
|
+
*/
|
|
895
|
+
declare function trackScrollDepth(percentage: 25 | 50 | 75 | 100, pagePath: string): void;
|
|
896
|
+
/**
|
|
897
|
+
* Track search events
|
|
898
|
+
* @param searchTerm - The search term
|
|
899
|
+
* @param resultCount - Number of results (optional)
|
|
900
|
+
*/
|
|
901
|
+
declare function trackSearch(searchTerm: string, resultCount?: number): void;
|
|
902
|
+
/**
|
|
903
|
+
* Track outbound link clicks
|
|
904
|
+
* @param url - The external URL
|
|
905
|
+
* @param linkText - Text of the link
|
|
906
|
+
*/
|
|
907
|
+
declare function trackOutboundLink(url: string, linkText: string): void;
|
|
908
|
+
/**
|
|
909
|
+
* Track error events
|
|
910
|
+
* @param errorType - Type of error
|
|
911
|
+
* @param errorMessage - Error message or description
|
|
912
|
+
*/
|
|
913
|
+
declare function trackError(errorType: string, errorMessage: string): void;
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Email validation schema
|
|
917
|
+
* @example
|
|
918
|
+
* const schema = z.object({ email: emailSchema })
|
|
919
|
+
*/
|
|
920
|
+
declare const emailSchema: any;
|
|
921
|
+
/**
|
|
922
|
+
* Phone number validation (international format)
|
|
923
|
+
* Accepts formats: +1234567890, +1 (234) 567-8900, etc.
|
|
924
|
+
*/
|
|
925
|
+
declare const phoneSchema: any;
|
|
926
|
+
/**
|
|
927
|
+
* US/Canada postal code validation
|
|
928
|
+
* Accepts: 12345, 12345-6789, A1A 1A1, A1A1A1
|
|
929
|
+
*/
|
|
930
|
+
declare const postalCodeSchema: any;
|
|
931
|
+
/**
|
|
932
|
+
* Password strength validation
|
|
933
|
+
* Requires: min 8 chars, 1 uppercase, 1 lowercase, 1 number
|
|
934
|
+
*/
|
|
935
|
+
declare const passwordSchema: any;
|
|
936
|
+
/**
|
|
937
|
+
* URL validation schema
|
|
938
|
+
*/
|
|
939
|
+
declare const urlSchema: any;
|
|
940
|
+
/**
|
|
941
|
+
* Credit card number validation (Luhn algorithm)
|
|
942
|
+
*/
|
|
943
|
+
declare const creditCardSchema: any;
|
|
944
|
+
/**
|
|
945
|
+
* File upload schema with type and size validation
|
|
946
|
+
* @param acceptedTypes - MIME types (e.g., ['image/jpeg', 'image/png'])
|
|
947
|
+
* @param maxSizeMB - Maximum file size in megabytes
|
|
948
|
+
*/
|
|
949
|
+
declare function fileSchema(acceptedTypes: string[], maxSizeMB: number): any;
|
|
950
|
+
/**
|
|
951
|
+
* Image file schema (JPEG, PNG, WebP)
|
|
952
|
+
* @param maxSizeMB - Maximum file size in megabytes (default: 5)
|
|
953
|
+
*/
|
|
954
|
+
declare function imageSchema(maxSizeMB?: number): any;
|
|
955
|
+
/**
|
|
956
|
+
* Required string field (non-empty)
|
|
957
|
+
*/
|
|
958
|
+
declare const requiredString: any;
|
|
959
|
+
/**
|
|
960
|
+
* Optional string field (empty string converted to undefined)
|
|
961
|
+
*/
|
|
962
|
+
declare const optionalString: any;
|
|
963
|
+
/**
|
|
964
|
+
* Numeric string (e.g., for phone inputs)
|
|
965
|
+
*/
|
|
966
|
+
declare const numericString: any;
|
|
967
|
+
/**
|
|
968
|
+
* Date string in ISO format
|
|
969
|
+
*/
|
|
970
|
+
declare const dateString: any;
|
|
971
|
+
/**
|
|
972
|
+
* Checkbox boolean (must be true)
|
|
973
|
+
* Useful for "I agree to terms" checkboxes
|
|
974
|
+
*/
|
|
975
|
+
declare const mustBeTrue: any;
|
|
976
|
+
|
|
977
|
+
declare const formErrorMessages: {
|
|
978
|
+
readonly required: {
|
|
979
|
+
readonly en: "This field is required";
|
|
980
|
+
readonly fr: "Ce champ est obligatoire";
|
|
981
|
+
};
|
|
982
|
+
readonly email: {
|
|
983
|
+
readonly en: "Please enter a valid email address";
|
|
984
|
+
readonly fr: "Veuillez entrer une adresse e-mail valide";
|
|
985
|
+
};
|
|
986
|
+
readonly phone: {
|
|
987
|
+
readonly en: "Please enter a valid phone number";
|
|
988
|
+
readonly fr: "Veuillez entrer un numéro de téléphone valide";
|
|
989
|
+
};
|
|
990
|
+
readonly url: {
|
|
991
|
+
readonly en: "Please enter a valid URL";
|
|
992
|
+
readonly fr: "Veuillez entrer une URL valide";
|
|
993
|
+
};
|
|
994
|
+
readonly password: {
|
|
995
|
+
readonly tooShort: {
|
|
996
|
+
readonly en: "Password must be at least 8 characters";
|
|
997
|
+
readonly fr: "Le mot de passe doit contenir au moins 8 caractères";
|
|
998
|
+
};
|
|
999
|
+
readonly noUppercase: {
|
|
1000
|
+
readonly en: "Password must contain at least one uppercase letter";
|
|
1001
|
+
readonly fr: "Le mot de passe doit contenir au moins une lettre majuscule";
|
|
1002
|
+
};
|
|
1003
|
+
readonly noLowercase: {
|
|
1004
|
+
readonly en: "Password must contain at least one lowercase letter";
|
|
1005
|
+
readonly fr: "Le mot de passe doit contenir au moins une lettre minuscule";
|
|
1006
|
+
};
|
|
1007
|
+
readonly noNumber: {
|
|
1008
|
+
readonly en: "Password must contain at least one number";
|
|
1009
|
+
readonly fr: "Le mot de passe doit contenir au moins un chiffre";
|
|
1010
|
+
};
|
|
1011
|
+
};
|
|
1012
|
+
readonly postalCode: {
|
|
1013
|
+
readonly en: "Invalid postal code";
|
|
1014
|
+
readonly fr: "Code postal invalide";
|
|
1015
|
+
};
|
|
1016
|
+
readonly creditCard: {
|
|
1017
|
+
readonly en: "Invalid credit card number";
|
|
1018
|
+
readonly fr: "Numéro de carte de crédit invalide";
|
|
1019
|
+
};
|
|
1020
|
+
readonly file: {
|
|
1021
|
+
readonly tooLarge: {
|
|
1022
|
+
readonly en: (maxSize: number) => string;
|
|
1023
|
+
readonly fr: (maxSize: number) => string;
|
|
1024
|
+
};
|
|
1025
|
+
readonly invalidType: {
|
|
1026
|
+
readonly en: (types: string[]) => string;
|
|
1027
|
+
readonly fr: (types: string[]) => string;
|
|
1028
|
+
};
|
|
1029
|
+
};
|
|
1030
|
+
readonly mustAccept: {
|
|
1031
|
+
readonly en: "You must accept to continue";
|
|
1032
|
+
readonly fr: "Vous devez accepter pour continuer";
|
|
1033
|
+
};
|
|
1034
|
+
readonly minLength: {
|
|
1035
|
+
readonly en: (min: number) => string;
|
|
1036
|
+
readonly fr: (min: number) => string;
|
|
1037
|
+
};
|
|
1038
|
+
readonly maxLength: {
|
|
1039
|
+
readonly en: (max: number) => string;
|
|
1040
|
+
readonly fr: (max: number) => string;
|
|
1041
|
+
};
|
|
1042
|
+
readonly min: {
|
|
1043
|
+
readonly en: (min: number) => string;
|
|
1044
|
+
readonly fr: (min: number) => string;
|
|
1045
|
+
};
|
|
1046
|
+
readonly max: {
|
|
1047
|
+
readonly en: (max: number) => string;
|
|
1048
|
+
readonly fr: (max: number) => string;
|
|
1049
|
+
};
|
|
1050
|
+
readonly invalidFormat: {
|
|
1051
|
+
readonly en: "Invalid format";
|
|
1052
|
+
readonly fr: "Format invalide";
|
|
1053
|
+
};
|
|
1054
|
+
};
|
|
1055
|
+
/**
|
|
1056
|
+
* Get localized error message
|
|
1057
|
+
* @param key - Error message key
|
|
1058
|
+
* @param locale - Current locale
|
|
1059
|
+
* @param params - Optional parameters for dynamic messages
|
|
1060
|
+
*/
|
|
1061
|
+
declare function getErrorMessage(key: keyof typeof formErrorMessages, locale?: Locale, params?: any): string;
|
|
1062
|
+
|
|
1063
|
+
interface MetadataOptions {
|
|
1064
|
+
/** Page title */
|
|
1065
|
+
title: string;
|
|
1066
|
+
/** Page description */
|
|
1067
|
+
description: string;
|
|
1068
|
+
/** Canonical URL */
|
|
1069
|
+
url?: string;
|
|
1070
|
+
/** Open Graph image */
|
|
1071
|
+
image?: string;
|
|
1072
|
+
/** Image alt text */
|
|
1073
|
+
imageAlt?: string;
|
|
1074
|
+
/** Page type @default 'website' */
|
|
1075
|
+
type?: 'website' | 'article';
|
|
1076
|
+
/** Article specific metadata */
|
|
1077
|
+
article?: {
|
|
1078
|
+
publishedTime?: string;
|
|
1079
|
+
modifiedTime?: string;
|
|
1080
|
+
author?: string;
|
|
1081
|
+
tags?: string[];
|
|
1082
|
+
};
|
|
1083
|
+
/** Twitter card type @default 'summary_large_image' */
|
|
1084
|
+
twitterCard?: 'summary' | 'summary_large_image' | 'app' | 'player';
|
|
1085
|
+
/** Twitter handle (without @) */
|
|
1086
|
+
twitterSite?: string;
|
|
1087
|
+
/** Twitter creator handle (without @) */
|
|
1088
|
+
twitterCreator?: string;
|
|
1089
|
+
/** Locale @default 'en' */
|
|
1090
|
+
locale?: string;
|
|
1091
|
+
/** Alternate locales */
|
|
1092
|
+
alternateLocales?: string[];
|
|
1093
|
+
/** Site name */
|
|
1094
|
+
siteName?: string;
|
|
1095
|
+
/** Robots directives */
|
|
1096
|
+
robots?: {
|
|
1097
|
+
index?: boolean;
|
|
1098
|
+
follow?: boolean;
|
|
1099
|
+
googleBot?: {
|
|
1100
|
+
index?: boolean;
|
|
1101
|
+
follow?: boolean;
|
|
1102
|
+
};
|
|
1103
|
+
};
|
|
1104
|
+
/** Keywords */
|
|
1105
|
+
keywords?: string[];
|
|
1106
|
+
/** Author */
|
|
1107
|
+
author?: string;
|
|
1108
|
+
/** Additional metadata */
|
|
1109
|
+
other?: Record<string, string>;
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Generate Next.js metadata object for SEO
|
|
1113
|
+
*
|
|
1114
|
+
* Creates a complete Metadata object with Open Graph, Twitter Card,
|
|
1115
|
+
* and other SEO metadata. Compatible with Next.js 13+ App Router.
|
|
1116
|
+
*
|
|
1117
|
+
* @example
|
|
1118
|
+
* // In app/page.tsx
|
|
1119
|
+
* export const metadata = generateMetadata({
|
|
1120
|
+
* title: 'Home - My Company',
|
|
1121
|
+
* description: 'Leading provider of professional services',
|
|
1122
|
+
* url: 'https://example.com',
|
|
1123
|
+
* image: 'https://example.com/og-image.jpg',
|
|
1124
|
+
* siteName: 'My Company',
|
|
1125
|
+
* twitterSite: 'mycompany'
|
|
1126
|
+
* })
|
|
1127
|
+
*
|
|
1128
|
+
* @example
|
|
1129
|
+
* // Article page
|
|
1130
|
+
* export const metadata = generateMetadata({
|
|
1131
|
+
* title: 'Blog Post Title',
|
|
1132
|
+
* description: 'Article description...',
|
|
1133
|
+
* type: 'article',
|
|
1134
|
+
* article: {
|
|
1135
|
+
* publishedTime: '2024-01-15T00:00:00Z',
|
|
1136
|
+
* author: 'Jane Doe',
|
|
1137
|
+
* tags: ['JavaScript', 'React']
|
|
1138
|
+
* },
|
|
1139
|
+
* image: '/blog/post-image.jpg'
|
|
1140
|
+
* })
|
|
1141
|
+
*
|
|
1142
|
+
* @example
|
|
1143
|
+
* // With alternates for i18n
|
|
1144
|
+
* export const metadata = generateMetadata({
|
|
1145
|
+
* title: 'Welcome',
|
|
1146
|
+
* description: 'Description',
|
|
1147
|
+
* locale: 'en',
|
|
1148
|
+
* alternateLocales: ['fr', 'es'],
|
|
1149
|
+
* url: 'https://example.com'
|
|
1150
|
+
* })
|
|
1151
|
+
*/
|
|
1152
|
+
declare function generateMetadata({ title, description, url, image, imageAlt, type, article, twitterCard, twitterSite, twitterCreator, locale, alternateLocales, siteName, robots, keywords, author, other }: MetadataOptions): Metadata;
|
|
1153
|
+
/**
|
|
1154
|
+
* Generate metadata for a blog post or article
|
|
1155
|
+
*
|
|
1156
|
+
* Specialized helper for article pages with publication metadata.
|
|
1157
|
+
*
|
|
1158
|
+
* @example
|
|
1159
|
+
* export const metadata = generateArticleMetadata({
|
|
1160
|
+
* title: 'My Blog Post',
|
|
1161
|
+
* description: 'Post description',
|
|
1162
|
+
* image: '/blog/post.jpg',
|
|
1163
|
+
* publishedTime: '2024-01-15T00:00:00Z',
|
|
1164
|
+
* author: 'Jane Doe',
|
|
1165
|
+
* tags: ['JavaScript', 'Web Development']
|
|
1166
|
+
* })
|
|
1167
|
+
*/
|
|
1168
|
+
declare function generateArticleMetadata({ title, description, image, publishedTime, modifiedTime, author, tags, url, siteName, twitterSite }: {
|
|
1169
|
+
title: string;
|
|
1170
|
+
description: string;
|
|
1171
|
+
image?: string;
|
|
1172
|
+
publishedTime?: string;
|
|
1173
|
+
modifiedTime?: string;
|
|
1174
|
+
author?: string;
|
|
1175
|
+
tags?: string[];
|
|
1176
|
+
url?: string;
|
|
1177
|
+
siteName?: string;
|
|
1178
|
+
twitterSite?: string;
|
|
1179
|
+
}): Metadata;
|
|
1180
|
+
|
|
1181
|
+
/**
|
|
1182
|
+
* Base schema.org Thing type
|
|
1183
|
+
* @see https://schema.org/Thing
|
|
1184
|
+
*/
|
|
1185
|
+
interface Thing {
|
|
1186
|
+
'@type': string;
|
|
1187
|
+
'@id'?: string;
|
|
1188
|
+
name?: string;
|
|
1189
|
+
description?: string;
|
|
1190
|
+
image?: string | string[];
|
|
1191
|
+
url?: string;
|
|
1192
|
+
[key: string]: unknown;
|
|
1193
|
+
}
|
|
1194
|
+
/**
|
|
1195
|
+
* Organization schema
|
|
1196
|
+
* @see https://schema.org/Organization
|
|
1197
|
+
*/
|
|
1198
|
+
interface Organization extends Thing {
|
|
1199
|
+
'@type': 'Organization';
|
|
1200
|
+
name: string;
|
|
1201
|
+
url?: string;
|
|
1202
|
+
logo?: string;
|
|
1203
|
+
description?: string;
|
|
1204
|
+
email?: string;
|
|
1205
|
+
telephone?: string;
|
|
1206
|
+
address?: PostalAddress;
|
|
1207
|
+
sameAs?: string[];
|
|
1208
|
+
contactPoint?: ContactPoint[];
|
|
1209
|
+
foundingDate?: string;
|
|
1210
|
+
founders?: Person[];
|
|
1211
|
+
}
|
|
1212
|
+
/**
|
|
1213
|
+
* Postal address schema
|
|
1214
|
+
* @see https://schema.org/PostalAddress
|
|
1215
|
+
*/
|
|
1216
|
+
interface PostalAddress extends Thing {
|
|
1217
|
+
'@type': 'PostalAddress';
|
|
1218
|
+
streetAddress?: string;
|
|
1219
|
+
addressLocality?: string;
|
|
1220
|
+
addressRegion?: string;
|
|
1221
|
+
postalCode?: string;
|
|
1222
|
+
addressCountry?: string;
|
|
1223
|
+
}
|
|
1224
|
+
/**
|
|
1225
|
+
* Contact point schema
|
|
1226
|
+
* @see https://schema.org/ContactPoint
|
|
1227
|
+
*/
|
|
1228
|
+
interface ContactPoint extends Thing {
|
|
1229
|
+
'@type': 'ContactPoint';
|
|
1230
|
+
telephone?: string;
|
|
1231
|
+
email?: string;
|
|
1232
|
+
contactType?: string;
|
|
1233
|
+
availableLanguage?: string[];
|
|
1234
|
+
areaServed?: string[];
|
|
1235
|
+
}
|
|
1236
|
+
/**
|
|
1237
|
+
* Person schema
|
|
1238
|
+
* @see https://schema.org/Person
|
|
1239
|
+
*/
|
|
1240
|
+
interface Person extends Thing {
|
|
1241
|
+
'@type': 'Person';
|
|
1242
|
+
name: string;
|
|
1243
|
+
email?: string;
|
|
1244
|
+
jobTitle?: string;
|
|
1245
|
+
image?: string;
|
|
1246
|
+
sameAs?: string[];
|
|
1247
|
+
url?: string;
|
|
1248
|
+
}
|
|
1249
|
+
/**
|
|
1250
|
+
* WebSite schema with search action
|
|
1251
|
+
* @see https://schema.org/WebSite
|
|
1252
|
+
*/
|
|
1253
|
+
interface WebSite extends Thing {
|
|
1254
|
+
'@type': 'WebSite';
|
|
1255
|
+
name: string;
|
|
1256
|
+
url: string;
|
|
1257
|
+
description?: string;
|
|
1258
|
+
publisher?: Organization;
|
|
1259
|
+
potentialAction?: SearchAction;
|
|
1260
|
+
}
|
|
1261
|
+
/**
|
|
1262
|
+
* Search action for site search
|
|
1263
|
+
* @see https://schema.org/SearchAction
|
|
1264
|
+
*/
|
|
1265
|
+
interface SearchAction {
|
|
1266
|
+
'@type': 'SearchAction';
|
|
1267
|
+
target: {
|
|
1268
|
+
'@type': 'EntryPoint';
|
|
1269
|
+
urlTemplate: string;
|
|
1270
|
+
};
|
|
1271
|
+
'query-input': string;
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Product schema
|
|
1275
|
+
* @see https://schema.org/Product
|
|
1276
|
+
*/
|
|
1277
|
+
interface Product extends Thing {
|
|
1278
|
+
'@type': 'Product' | 'SoftwareApplication';
|
|
1279
|
+
name: string;
|
|
1280
|
+
description?: string;
|
|
1281
|
+
image?: string | string[];
|
|
1282
|
+
brand?: Organization | string;
|
|
1283
|
+
offers?: Offer | Offer[];
|
|
1284
|
+
aggregateRating?: AggregateRating;
|
|
1285
|
+
review?: Review[];
|
|
1286
|
+
}
|
|
1287
|
+
/**
|
|
1288
|
+
* Offer schema for products/services
|
|
1289
|
+
* @see https://schema.org/Offer
|
|
1290
|
+
*/
|
|
1291
|
+
interface Offer extends Thing {
|
|
1292
|
+
'@type': 'Offer';
|
|
1293
|
+
price: string | number;
|
|
1294
|
+
priceCurrency: string;
|
|
1295
|
+
priceValidUntil?: string;
|
|
1296
|
+
availability?: 'InStock' | 'OutOfStock' | 'PreOrder' | 'Discontinued';
|
|
1297
|
+
url?: string;
|
|
1298
|
+
seller?: Organization;
|
|
1299
|
+
}
|
|
1300
|
+
/**
|
|
1301
|
+
* Aggregate rating schema
|
|
1302
|
+
* @see https://schema.org/AggregateRating
|
|
1303
|
+
*/
|
|
1304
|
+
interface AggregateRating extends Thing {
|
|
1305
|
+
'@type': 'AggregateRating';
|
|
1306
|
+
ratingValue: number | string;
|
|
1307
|
+
reviewCount?: number;
|
|
1308
|
+
bestRating?: number | string;
|
|
1309
|
+
worstRating?: number | string;
|
|
1310
|
+
}
|
|
1311
|
+
/**
|
|
1312
|
+
* Review schema
|
|
1313
|
+
* @see https://schema.org/Review
|
|
1314
|
+
*/
|
|
1315
|
+
interface Review extends Thing {
|
|
1316
|
+
'@type': 'Review';
|
|
1317
|
+
author: Person | Organization | string;
|
|
1318
|
+
datePublished?: string;
|
|
1319
|
+
reviewBody?: string;
|
|
1320
|
+
reviewRating?: Rating;
|
|
1321
|
+
}
|
|
1322
|
+
/**
|
|
1323
|
+
* Rating schema
|
|
1324
|
+
* @see https://schema.org/Rating
|
|
1325
|
+
*/
|
|
1326
|
+
interface Rating extends Thing {
|
|
1327
|
+
'@type': 'Rating';
|
|
1328
|
+
ratingValue: number | string;
|
|
1329
|
+
bestRating?: number | string;
|
|
1330
|
+
worstRating?: number | string;
|
|
1331
|
+
}
|
|
1332
|
+
/**
|
|
1333
|
+
* FAQ Page schema
|
|
1334
|
+
* @see https://schema.org/FAQPage
|
|
1335
|
+
*/
|
|
1336
|
+
interface FAQPage extends Thing {
|
|
1337
|
+
'@type': 'FAQPage';
|
|
1338
|
+
mainEntity: Question[];
|
|
1339
|
+
}
|
|
1340
|
+
/**
|
|
1341
|
+
* Question schema for FAQ
|
|
1342
|
+
* @see https://schema.org/Question
|
|
1343
|
+
*/
|
|
1344
|
+
interface Question extends Thing {
|
|
1345
|
+
'@type': 'Question';
|
|
1346
|
+
name: string;
|
|
1347
|
+
acceptedAnswer: Answer;
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Answer schema for FAQ
|
|
1351
|
+
* @see https://schema.org/Answer
|
|
1352
|
+
*/
|
|
1353
|
+
interface Answer extends Thing {
|
|
1354
|
+
'@type': 'Answer';
|
|
1355
|
+
text: string;
|
|
1356
|
+
}
|
|
1357
|
+
/**
|
|
1358
|
+
* Article schema
|
|
1359
|
+
* @see https://schema.org/Article
|
|
1360
|
+
*/
|
|
1361
|
+
interface Article extends Thing {
|
|
1362
|
+
'@type': 'Article' | 'BlogPosting' | 'NewsArticle';
|
|
1363
|
+
headline: string;
|
|
1364
|
+
description?: string;
|
|
1365
|
+
image?: string | string[];
|
|
1366
|
+
author: Person | Organization | string;
|
|
1367
|
+
publisher: Organization;
|
|
1368
|
+
datePublished: string;
|
|
1369
|
+
dateModified?: string;
|
|
1370
|
+
mainEntityOfPage?: string;
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Breadcrumb list schema
|
|
1374
|
+
* @see https://schema.org/BreadcrumbList
|
|
1375
|
+
*/
|
|
1376
|
+
interface BreadcrumbList extends Thing {
|
|
1377
|
+
'@type': 'BreadcrumbList';
|
|
1378
|
+
itemListElement: ListItem[];
|
|
1379
|
+
}
|
|
1380
|
+
/**
|
|
1381
|
+
* List item for breadcrumbs
|
|
1382
|
+
* @see https://schema.org/ListItem
|
|
1383
|
+
*/
|
|
1384
|
+
interface ListItem extends Thing {
|
|
1385
|
+
'@type': 'ListItem';
|
|
1386
|
+
position: number;
|
|
1387
|
+
name: string;
|
|
1388
|
+
item?: string;
|
|
1389
|
+
}
|
|
1390
|
+
/**
|
|
1391
|
+
* Create Organization structured data
|
|
1392
|
+
*
|
|
1393
|
+
* @example
|
|
1394
|
+
* ```tsx
|
|
1395
|
+
* const org = createOrganization({
|
|
1396
|
+
* name: 'Acme Inc',
|
|
1397
|
+
* url: 'https://acme.com',
|
|
1398
|
+
* logo: 'https://acme.com/logo.png',
|
|
1399
|
+
* sameAs: [
|
|
1400
|
+
* 'https://twitter.com/acme',
|
|
1401
|
+
* 'https://linkedin.com/company/acme'
|
|
1402
|
+
* ]
|
|
1403
|
+
* });
|
|
1404
|
+
* ```
|
|
1405
|
+
*/
|
|
1406
|
+
declare function createOrganization(data: Omit<Organization, '@type'>): Organization;
|
|
1407
|
+
/**
|
|
1408
|
+
* Create WebSite structured data with search action
|
|
1409
|
+
*
|
|
1410
|
+
* @example
|
|
1411
|
+
* ```tsx
|
|
1412
|
+
* const website = createWebSite({
|
|
1413
|
+
* name: 'Acme',
|
|
1414
|
+
* url: 'https://acme.com',
|
|
1415
|
+
* searchUrlTemplate: 'https://acme.com/search?q={search_term_string}'
|
|
1416
|
+
* });
|
|
1417
|
+
* ```
|
|
1418
|
+
*/
|
|
1419
|
+
declare function createWebSite(data: {
|
|
1420
|
+
name: string;
|
|
1421
|
+
url: string;
|
|
1422
|
+
description?: string;
|
|
1423
|
+
publisher?: Organization;
|
|
1424
|
+
searchUrlTemplate?: string;
|
|
1425
|
+
}): WebSite;
|
|
1426
|
+
/**
|
|
1427
|
+
* Create Product structured data
|
|
1428
|
+
*
|
|
1429
|
+
* @example
|
|
1430
|
+
* ```tsx
|
|
1431
|
+
* const product = createProduct({
|
|
1432
|
+
* name: 'Premium Email Plan',
|
|
1433
|
+
* description: 'Unlimited emails, advanced features',
|
|
1434
|
+
* image: 'https://acme.com/premium.jpg',
|
|
1435
|
+
* offers: {
|
|
1436
|
+
* price: '99.00',
|
|
1437
|
+
* priceCurrency: 'USD',
|
|
1438
|
+
* availability: 'InStock'
|
|
1439
|
+
* }
|
|
1440
|
+
* });
|
|
1441
|
+
* ```
|
|
1442
|
+
*/
|
|
1443
|
+
declare function createProduct(data: Omit<Product, '@type'>): Product;
|
|
1444
|
+
/**
|
|
1445
|
+
* Create FAQ Page structured data
|
|
1446
|
+
*
|
|
1447
|
+
* @example
|
|
1448
|
+
* ```tsx
|
|
1449
|
+
* const faq = createFAQPage([
|
|
1450
|
+
* {
|
|
1451
|
+
* question: 'What is your refund policy?',
|
|
1452
|
+
* answer: 'We offer a 30-day money-back guarantee.'
|
|
1453
|
+
* },
|
|
1454
|
+
* {
|
|
1455
|
+
* question: 'Do you offer support?',
|
|
1456
|
+
* answer: 'Yes, 24/7 email and chat support is included.'
|
|
1457
|
+
* }
|
|
1458
|
+
* ]);
|
|
1459
|
+
* ```
|
|
1460
|
+
*/
|
|
1461
|
+
declare function createFAQPage(faqs: Array<{
|
|
1462
|
+
question: string;
|
|
1463
|
+
answer: string;
|
|
1464
|
+
}>): FAQPage;
|
|
1465
|
+
/**
|
|
1466
|
+
* Create Article structured data
|
|
1467
|
+
*
|
|
1468
|
+
* @example
|
|
1469
|
+
* ```tsx
|
|
1470
|
+
* const article = createArticle({
|
|
1471
|
+
* headline: '10 Email Marketing Tips',
|
|
1472
|
+
* description: 'Learn how to improve your email campaigns',
|
|
1473
|
+
* author: { name: 'Jane Doe' },
|
|
1474
|
+
* publisher: {
|
|
1475
|
+
* name: 'Acme',
|
|
1476
|
+
* logo: 'https://acme.com/logo.png'
|
|
1477
|
+
* },
|
|
1478
|
+
* datePublished: '2024-01-15T10:00:00Z',
|
|
1479
|
+
* image: 'https://acme.com/blog/tips.jpg'
|
|
1480
|
+
* });
|
|
1481
|
+
* ```
|
|
1482
|
+
*/
|
|
1483
|
+
declare function createArticle(data: {
|
|
1484
|
+
headline: string;
|
|
1485
|
+
description?: string;
|
|
1486
|
+
image?: string | string[];
|
|
1487
|
+
author: Person | string;
|
|
1488
|
+
publisher: Organization;
|
|
1489
|
+
datePublished: string;
|
|
1490
|
+
dateModified?: string;
|
|
1491
|
+
mainEntityOfPage?: string;
|
|
1492
|
+
type?: 'Article' | 'BlogPosting' | 'NewsArticle';
|
|
1493
|
+
}): Article;
|
|
1494
|
+
/**
|
|
1495
|
+
* Create Breadcrumb List structured data
|
|
1496
|
+
*
|
|
1497
|
+
* @example
|
|
1498
|
+
* ```tsx
|
|
1499
|
+
* const breadcrumbs = createBreadcrumbList([
|
|
1500
|
+
* { name: 'Home', url: 'https://acme.com' },
|
|
1501
|
+
* { name: 'Blog', url: 'https://acme.com/blog' },
|
|
1502
|
+
* { name: 'Email Tips', url: 'https://acme.com/blog/email-tips' }
|
|
1503
|
+
* ]);
|
|
1504
|
+
* ```
|
|
1505
|
+
*/
|
|
1506
|
+
declare function createBreadcrumbList(items: Array<{
|
|
1507
|
+
name: string;
|
|
1508
|
+
url?: string;
|
|
1509
|
+
}>): BreadcrumbList;
|
|
1510
|
+
/**
|
|
1511
|
+
* Serialize structured data to JSON-LD string
|
|
1512
|
+
* Safe for use in script tags
|
|
1513
|
+
*/
|
|
1514
|
+
declare function serializeStructuredData(data: Thing | Thing[]): string;
|
|
1515
|
+
|
|
1516
|
+
/**
|
|
1517
|
+
* Change frequency for sitemap entries
|
|
1518
|
+
* @see https://www.sitemaps.org/protocol.html
|
|
1519
|
+
*/
|
|
1520
|
+
type ChangeFrequency = 'always' | 'hourly' | 'daily' | 'weekly' | 'monthly' | 'yearly' | 'never';
|
|
1521
|
+
/**
|
|
1522
|
+
* A single URL entry in the sitemap
|
|
1523
|
+
*/
|
|
1524
|
+
interface SitemapEntry {
|
|
1525
|
+
/** Absolute URL of the page */
|
|
1526
|
+
url: string;
|
|
1527
|
+
/** Last modification date (ISO 8601 format) */
|
|
1528
|
+
lastModified?: string | Date;
|
|
1529
|
+
/** How frequently the page is likely to change */
|
|
1530
|
+
changeFrequency?: ChangeFrequency;
|
|
1531
|
+
/** Priority of this URL relative to other URLs (0.0 to 1.0) */
|
|
1532
|
+
priority?: number;
|
|
1533
|
+
/** Alternate language versions of this URL */
|
|
1534
|
+
alternates?: Array<{
|
|
1535
|
+
/** Language/locale code (e.g., 'en', 'fr', 'en-US') */
|
|
1536
|
+
hreflang: string;
|
|
1537
|
+
/** Absolute URL for this language version */
|
|
1538
|
+
href: string;
|
|
1539
|
+
}>;
|
|
1540
|
+
}
|
|
1541
|
+
/**
|
|
1542
|
+
* Configuration for sitemap generation
|
|
1543
|
+
*/
|
|
1544
|
+
interface SitemapConfig {
|
|
1545
|
+
/** Base URL of the website (e.g., 'https://example.com') */
|
|
1546
|
+
baseUrl: string;
|
|
1547
|
+
/** Array of sitemap entries */
|
|
1548
|
+
entries: SitemapEntry[];
|
|
1549
|
+
/** Pretty print the XML output (default: false) */
|
|
1550
|
+
prettyPrint?: boolean;
|
|
1551
|
+
}
|
|
1552
|
+
/**
|
|
1553
|
+
* Generate XML sitemap from entries
|
|
1554
|
+
*
|
|
1555
|
+
* Creates a valid sitemap.xml following the sitemaps.org protocol with
|
|
1556
|
+
* support for multi-language pages using hreflang annotations.
|
|
1557
|
+
*
|
|
1558
|
+
* @param config - Sitemap configuration
|
|
1559
|
+
* @returns XML string for sitemap.xml
|
|
1560
|
+
*
|
|
1561
|
+
* @example Basic sitemap
|
|
1562
|
+
* ```tsx
|
|
1563
|
+
* const sitemap = generateSitemap({
|
|
1564
|
+
* baseUrl: 'https://example.com',
|
|
1565
|
+
* entries: [
|
|
1566
|
+
* { url: 'https://example.com', priority: 1.0, changeFrequency: 'weekly' },
|
|
1567
|
+
* { url: 'https://example.com/about', priority: 0.8 },
|
|
1568
|
+
* { url: 'https://example.com/products', priority: 0.9, changeFrequency: 'daily' }
|
|
1569
|
+
* ]
|
|
1570
|
+
* });
|
|
1571
|
+
* ```
|
|
1572
|
+
*
|
|
1573
|
+
* @example Multi-language sitemap
|
|
1574
|
+
* ```tsx
|
|
1575
|
+
* const sitemap = generateSitemap({
|
|
1576
|
+
* baseUrl: 'https://example.com',
|
|
1577
|
+
* entries: [
|
|
1578
|
+
* {
|
|
1579
|
+
* url: 'https://example.com/en/about',
|
|
1580
|
+
* alternates: [
|
|
1581
|
+
* { hreflang: 'en', href: 'https://example.com/en/about' },
|
|
1582
|
+
* { hreflang: 'fr', href: 'https://example.com/fr/about' },
|
|
1583
|
+
* { hreflang: 'x-default', href: 'https://example.com/en/about' }
|
|
1584
|
+
* ]
|
|
1585
|
+
* }
|
|
1586
|
+
* ]
|
|
1587
|
+
* });
|
|
1588
|
+
* ```
|
|
1589
|
+
*/
|
|
1590
|
+
declare function generateSitemap(config: SitemapConfig): string;
|
|
1591
|
+
/**
|
|
1592
|
+
* Create sitemap entries for multi-language pages
|
|
1593
|
+
*
|
|
1594
|
+
* Helper to generate sitemap entries with proper hreflang annotations
|
|
1595
|
+
* for pages that exist in multiple languages.
|
|
1596
|
+
*
|
|
1597
|
+
* @param baseUrl - Base URL of the website
|
|
1598
|
+
* @param path - Page path without locale prefix (e.g., '/about')
|
|
1599
|
+
* @param locales - Array of locale codes (e.g., ['en', 'fr', 'es'])
|
|
1600
|
+
* @param defaultLocale - Default locale for x-default (optional)
|
|
1601
|
+
* @param options - Additional sitemap entry options
|
|
1602
|
+
* @returns Array of sitemap entries, one per locale
|
|
1603
|
+
*
|
|
1604
|
+
* @example
|
|
1605
|
+
* ```tsx
|
|
1606
|
+
* const entries = createMultiLanguageEntries(
|
|
1607
|
+
* 'https://example.com',
|
|
1608
|
+
* '/about',
|
|
1609
|
+
* ['en', 'fr', 'es'],
|
|
1610
|
+
* 'en',
|
|
1611
|
+
* { priority: 0.8, changeFrequency: 'monthly' }
|
|
1612
|
+
* );
|
|
1613
|
+
* // Creates entries for:
|
|
1614
|
+
* // - /en/about (with alternates to fr, es, x-default)
|
|
1615
|
+
* // - /fr/about (with alternates to en, es, x-default)
|
|
1616
|
+
* // - /es/about (with alternates to en, fr, x-default)
|
|
1617
|
+
* ```
|
|
1618
|
+
*/
|
|
1619
|
+
declare function createMultiLanguageEntries(baseUrl: string, path: string, locales: string[], defaultLocale?: string, options?: Partial<Omit<SitemapEntry, 'url' | 'alternates'>>): SitemapEntry[];
|
|
1620
|
+
/**
|
|
1621
|
+
* Create a single-language sitemap entry
|
|
1622
|
+
*
|
|
1623
|
+
* @param baseUrl - Base URL of the website
|
|
1624
|
+
* @param path - Page path (e.g., '/about')
|
|
1625
|
+
* @param options - Sitemap entry options
|
|
1626
|
+
* @returns Sitemap entry
|
|
1627
|
+
*
|
|
1628
|
+
* @example
|
|
1629
|
+
* ```tsx
|
|
1630
|
+
* const entry = createSitemapEntry(
|
|
1631
|
+
* 'https://example.com',
|
|
1632
|
+
* '/about',
|
|
1633
|
+
* { priority: 0.8, changeFrequency: 'monthly' }
|
|
1634
|
+
* );
|
|
1635
|
+
* ```
|
|
1636
|
+
*/
|
|
1637
|
+
declare function createSitemapEntry(baseUrl: string, path: string, options?: Partial<Omit<SitemapEntry, 'url'>>): SitemapEntry;
|
|
1638
|
+
/**
|
|
1639
|
+
* Validate sitemap entry
|
|
1640
|
+
*
|
|
1641
|
+
* Checks for common issues in sitemap entries.
|
|
1642
|
+
*
|
|
1643
|
+
* @param entry - Sitemap entry to validate
|
|
1644
|
+
* @returns Array of error messages (empty if valid)
|
|
1645
|
+
*/
|
|
1646
|
+
declare function validateSitemapEntry(entry: SitemapEntry): string[];
|
|
1647
|
+
/**
|
|
1648
|
+
* Validate entire sitemap configuration
|
|
1649
|
+
*
|
|
1650
|
+
* @param config - Sitemap configuration to validate
|
|
1651
|
+
* @returns Object with isValid flag and array of errors
|
|
1652
|
+
*/
|
|
1653
|
+
declare function validateSitemap(config: SitemapConfig): {
|
|
1654
|
+
isValid: boolean;
|
|
1655
|
+
errors: Array<{
|
|
1656
|
+
entry?: SitemapEntry;
|
|
1657
|
+
messages: string[];
|
|
1658
|
+
}>;
|
|
1659
|
+
};
|
|
1660
|
+
|
|
1661
|
+
interface TrackedLinkProps {
|
|
1662
|
+
href: string;
|
|
1663
|
+
children: React.ReactNode;
|
|
1664
|
+
className?: string;
|
|
1665
|
+
trackingType?: 'cta' | 'navigation' | 'outbound' | 'auto';
|
|
1666
|
+
ctaLocation?: string;
|
|
1667
|
+
ctaType?: 'signup' | 'trial' | 'contact' | 'download' | 'other';
|
|
1668
|
+
navLocation?: 'header' | 'footer' | 'content' | 'mobile';
|
|
1669
|
+
onClick?: () => void;
|
|
1670
|
+
[key: string]: unknown;
|
|
1671
|
+
}
|
|
1672
|
+
/**
|
|
1673
|
+
* Link component with built-in analytics tracking
|
|
1674
|
+
* Automatically determines tracking type if set to 'auto'
|
|
1675
|
+
*/
|
|
1676
|
+
declare function TrackedLink({ href, children, className, trackingType, ctaLocation, ctaType, navLocation, onClick, ...props }: TrackedLinkProps): react_jsx_runtime.JSX.Element;
|
|
1677
|
+
|
|
1678
|
+
interface Feature {
|
|
1679
|
+
id: string;
|
|
1680
|
+
icon?: ReactNode;
|
|
1681
|
+
name: string;
|
|
1682
|
+
description: string;
|
|
1683
|
+
benefits?: string[];
|
|
1684
|
+
useCases?: string[];
|
|
1685
|
+
learnMoreHref?: string;
|
|
1686
|
+
}
|
|
1687
|
+
interface FeatureCategory {
|
|
1688
|
+
id: string;
|
|
1689
|
+
name: string;
|
|
1690
|
+
description: string;
|
|
1691
|
+
features: Feature[];
|
|
1692
|
+
}
|
|
1693
|
+
interface FeaturesGridProps {
|
|
1694
|
+
categories: FeatureCategory[];
|
|
1695
|
+
className?: string;
|
|
1696
|
+
locale?: 'fr' | 'en';
|
|
1697
|
+
}
|
|
1698
|
+
declare function FeaturesGrid({ categories, className, locale }: FeaturesGridProps): react_jsx_runtime.JSX.Element;
|
|
1699
|
+
|
|
1700
|
+
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
|
1701
|
+
/**
|
|
1702
|
+
* Visual variant of the button
|
|
1703
|
+
* - outlined: Rounded with border (primary brand style)
|
|
1704
|
+
* - filled: Solid background with shadow
|
|
1705
|
+
* - text: Text-only button without border
|
|
1706
|
+
* - ghost: Transparent background, visible on hover
|
|
1707
|
+
* - link: Styled like a link, but button semantics
|
|
1708
|
+
* - destructive: Red variant for delete/remove actions
|
|
1709
|
+
*/
|
|
1710
|
+
variant?: 'outlined' | 'filled' | 'text' | 'ghost' | 'link' | 'destructive';
|
|
1711
|
+
/**
|
|
1712
|
+
* Size of the button
|
|
1713
|
+
*/
|
|
1714
|
+
size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
1715
|
+
/**
|
|
1716
|
+
* Full width button
|
|
1717
|
+
*/
|
|
1718
|
+
fullWidth?: boolean;
|
|
1719
|
+
/**
|
|
1720
|
+
* Loading state - shows spinner and disables button
|
|
1721
|
+
*/
|
|
1722
|
+
loading?: boolean;
|
|
1723
|
+
/**
|
|
1724
|
+
* Custom text to show during loading
|
|
1725
|
+
*/
|
|
1726
|
+
loadingText?: string;
|
|
1727
|
+
/**
|
|
1728
|
+
* Success state - shows checkmark animation
|
|
1729
|
+
*/
|
|
1730
|
+
success?: boolean;
|
|
1731
|
+
/**
|
|
1732
|
+
* Custom text to show during success
|
|
1733
|
+
*/
|
|
1734
|
+
successText?: string;
|
|
1735
|
+
/**
|
|
1736
|
+
* Duration to show success state before reverting (ms)
|
|
1737
|
+
*/
|
|
1738
|
+
successDuration?: number;
|
|
1739
|
+
/**
|
|
1740
|
+
* Icon element to display
|
|
1741
|
+
*/
|
|
1742
|
+
icon?: ReactNode;
|
|
1743
|
+
/**
|
|
1744
|
+
* Position of the icon
|
|
1745
|
+
*/
|
|
1746
|
+
iconPosition?: 'left' | 'right';
|
|
1747
|
+
/**
|
|
1748
|
+
* Icon-only button (no text)
|
|
1749
|
+
*/
|
|
1750
|
+
iconOnly?: boolean;
|
|
1751
|
+
/**
|
|
1752
|
+
* Tooltip text to show when button is disabled
|
|
1753
|
+
*/
|
|
1754
|
+
disabledTooltip?: string;
|
|
1755
|
+
/**
|
|
1756
|
+
* Enable ripple effect on click
|
|
1757
|
+
*/
|
|
1758
|
+
ripple?: boolean;
|
|
1759
|
+
/**
|
|
1760
|
+
* Analytics event name to track on click
|
|
1761
|
+
* Automatically tracks button clicks with this event name
|
|
1762
|
+
*/
|
|
1763
|
+
trackingEvent?: string;
|
|
1764
|
+
/**
|
|
1765
|
+
* Additional properties to send with tracking event
|
|
1766
|
+
* Merged with automatic button properties (variant, text, href)
|
|
1767
|
+
*/
|
|
1768
|
+
trackingProps?: Record<string, unknown>;
|
|
1769
|
+
/**
|
|
1770
|
+
* Link href (for navigation buttons)
|
|
1771
|
+
*/
|
|
1772
|
+
href?: string;
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
interface CardProps extends HTMLAttributes<HTMLDivElement> {
|
|
1776
|
+
/**
|
|
1777
|
+
* Padding variant
|
|
1778
|
+
*/
|
|
1779
|
+
padding?: 'none' | 'sm' | 'md' | 'lg';
|
|
1780
|
+
/**
|
|
1781
|
+
* Whether to show a border
|
|
1782
|
+
*/
|
|
1783
|
+
bordered?: boolean;
|
|
1784
|
+
/**
|
|
1785
|
+
* Whether to show a shadow
|
|
1786
|
+
*/
|
|
1787
|
+
shadow?: boolean;
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
|
|
1791
|
+
/**
|
|
1792
|
+
* Label text for the input
|
|
1793
|
+
*/
|
|
1794
|
+
label?: string;
|
|
1795
|
+
/**
|
|
1796
|
+
* Error message to display
|
|
1797
|
+
*/
|
|
1798
|
+
error?: string;
|
|
1799
|
+
/**
|
|
1800
|
+
* Helper text to display below input
|
|
1801
|
+
*/
|
|
1802
|
+
helperText?: string;
|
|
1803
|
+
/**
|
|
1804
|
+
* Full width input
|
|
1805
|
+
*/
|
|
1806
|
+
fullWidth?: boolean;
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
interface TextareaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
1810
|
+
/**
|
|
1811
|
+
* Label text for the textarea
|
|
1812
|
+
*/
|
|
1813
|
+
label?: string;
|
|
1814
|
+
/**
|
|
1815
|
+
* Error message to display
|
|
1816
|
+
*/
|
|
1817
|
+
error?: string;
|
|
1818
|
+
/**
|
|
1819
|
+
* Helper text to display below textarea
|
|
1820
|
+
*/
|
|
1821
|
+
helperText?: string;
|
|
1822
|
+
/**
|
|
1823
|
+
* Full width textarea
|
|
1824
|
+
*/
|
|
1825
|
+
fullWidth?: boolean;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
interface BreadcrumbItem {
|
|
1829
|
+
label: string;
|
|
1830
|
+
href?: string;
|
|
1831
|
+
}
|
|
1832
|
+
interface BreadcrumbProps {
|
|
1833
|
+
items: BreadcrumbItem[];
|
|
1834
|
+
className?: string;
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
type BadgeType = 'ssl-secure' | 'money-back-30' | 'money-back-60' | 'money-back-90' | 'bbb-accredited' | 'satisfaction-guaranteed' | 'free-shipping' | 'privacy-protected' | '24-7-support' | 'secure-payment';
|
|
1838
|
+
interface CustomBadge {
|
|
1839
|
+
type: 'custom';
|
|
1840
|
+
image: string;
|
|
1841
|
+
alt: string;
|
|
1842
|
+
tooltip?: string;
|
|
1843
|
+
}
|
|
1844
|
+
type Badge = BadgeType | CustomBadge;
|
|
1845
|
+
|
|
1846
|
+
interface HeroAnimations {
|
|
1847
|
+
/** Animation type for headline */
|
|
1848
|
+
headline?: 'fadeInUp' | 'fadeIn' | 'slideInLeft' | 'none';
|
|
1849
|
+
/** Animation type for CTA buttons */
|
|
1850
|
+
cta?: 'fadeInUp' | 'fadeIn' | 'none';
|
|
1851
|
+
/** Stagger delay between elements (seconds) */
|
|
1852
|
+
stagger?: number;
|
|
1853
|
+
/** Show scroll indicator */
|
|
1854
|
+
scrollIndicator?: boolean;
|
|
1855
|
+
}
|
|
1856
|
+
interface HeroSectionProps {
|
|
1857
|
+
locale: Locale;
|
|
1858
|
+
content: HeroContent;
|
|
1859
|
+
/** Animation configuration */
|
|
1860
|
+
animations?: HeroAnimations;
|
|
1861
|
+
/** Show sticky CTA after scrolling past hero */
|
|
1862
|
+
stickyCtaAfterScroll?: boolean;
|
|
1863
|
+
/** Trust badges to display */
|
|
1864
|
+
trustBadges?: Badge[];
|
|
1865
|
+
/** Background effect variant */
|
|
1866
|
+
backgroundEffect?: 'none' | 'particles' | 'gradient-shift' | 'mesh';
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
interface OfficeHours {
|
|
1870
|
+
monday?: {
|
|
1871
|
+
open: string;
|
|
1872
|
+
close: string;
|
|
1873
|
+
};
|
|
1874
|
+
tuesday?: {
|
|
1875
|
+
open: string;
|
|
1876
|
+
close: string;
|
|
1877
|
+
};
|
|
1878
|
+
wednesday?: {
|
|
1879
|
+
open: string;
|
|
1880
|
+
close: string;
|
|
1881
|
+
};
|
|
1882
|
+
thursday?: {
|
|
1883
|
+
open: string;
|
|
1884
|
+
close: string;
|
|
1885
|
+
};
|
|
1886
|
+
friday?: {
|
|
1887
|
+
open: string;
|
|
1888
|
+
close: string;
|
|
1889
|
+
};
|
|
1890
|
+
saturday?: {
|
|
1891
|
+
open: string;
|
|
1892
|
+
close: string;
|
|
1893
|
+
};
|
|
1894
|
+
sunday?: {
|
|
1895
|
+
open: string;
|
|
1896
|
+
close: string;
|
|
1897
|
+
};
|
|
1898
|
+
}
|
|
1899
|
+
interface Location {
|
|
1900
|
+
name: LocalizedString | string;
|
|
1901
|
+
address: string;
|
|
1902
|
+
phone?: string;
|
|
1903
|
+
email?: string;
|
|
1904
|
+
hours?: OfficeHours;
|
|
1905
|
+
coordinates?: {
|
|
1906
|
+
lat: number;
|
|
1907
|
+
lng: number;
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
interface MapConfig {
|
|
1911
|
+
provider?: 'google' | 'static' | 'link';
|
|
1912
|
+
apiKey?: string;
|
|
1913
|
+
zoom?: number;
|
|
1914
|
+
}
|
|
1915
|
+
interface SpamProtection {
|
|
1916
|
+
honeypot?: boolean;
|
|
1917
|
+
recaptcha?: string;
|
|
1918
|
+
}
|
|
1919
|
+
interface FormIntegration {
|
|
1920
|
+
type: 'sendgrid' | 'mailgun' | 'webhook' | 'custom';
|
|
1921
|
+
config: Record<string, any>;
|
|
1922
|
+
}
|
|
1923
|
+
interface ContactFormConfig {
|
|
1924
|
+
enabled?: boolean;
|
|
1925
|
+
fields?: Array<'name' | 'email' | 'phone' | 'subject' | 'message' | 'attachment'>;
|
|
1926
|
+
requiredFields?: Array<'name' | 'email' | 'phone' | 'subject' | 'message'>;
|
|
1927
|
+
onSubmit?: (data: ContactFormData) => Promise<void>;
|
|
1928
|
+
spamProtection?: SpamProtection;
|
|
1929
|
+
integration?: FormIntegration;
|
|
1930
|
+
}
|
|
1931
|
+
interface ContactSectionProps {
|
|
1932
|
+
locale: Locale;
|
|
1933
|
+
title?: LocalizedString | string;
|
|
1934
|
+
description?: LocalizedString | string;
|
|
1935
|
+
form?: ContactFormConfig;
|
|
1936
|
+
locations?: Location[];
|
|
1937
|
+
defaultLocation?: number;
|
|
1938
|
+
map?: MapConfig;
|
|
1939
|
+
showMap?: boolean;
|
|
1940
|
+
showInfo?: boolean;
|
|
1941
|
+
timezone?: string;
|
|
1942
|
+
showStatus?: boolean;
|
|
1943
|
+
className?: string;
|
|
1944
|
+
}
|
|
1945
|
+
interface ContactFormData {
|
|
1946
|
+
name: string;
|
|
1947
|
+
email: string;
|
|
1948
|
+
phone?: string;
|
|
1949
|
+
subject?: string;
|
|
1950
|
+
message: string;
|
|
1951
|
+
attachment?: File;
|
|
1952
|
+
_honeypot?: string;
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
interface AboutSectionProps {
|
|
1956
|
+
locale: Locale;
|
|
1957
|
+
content: AboutContent;
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
interface ServicesSectionProps {
|
|
1961
|
+
locale: Locale;
|
|
1962
|
+
content: ServicesContent;
|
|
1963
|
+
}
|
|
1964
|
+
|
|
1965
|
+
interface TestimonialContent {
|
|
1966
|
+
quote: {
|
|
1967
|
+
[locale: string]: string;
|
|
1968
|
+
};
|
|
1969
|
+
author?: {
|
|
1970
|
+
[locale: string]: string;
|
|
1971
|
+
};
|
|
1972
|
+
role?: {
|
|
1973
|
+
[locale: string]: string;
|
|
1974
|
+
};
|
|
1975
|
+
company?: {
|
|
1976
|
+
[locale: string]: string;
|
|
1977
|
+
};
|
|
1978
|
+
}
|
|
1979
|
+
interface TestimonialSectionProps {
|
|
1980
|
+
locale: Locale;
|
|
1981
|
+
content: TestimonialContent;
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
interface ServicePageLayoutProps {
|
|
1985
|
+
locale: Locale;
|
|
1986
|
+
title: string;
|
|
1987
|
+
description: string;
|
|
1988
|
+
children?: ReactNode;
|
|
1989
|
+
breadcrumbItems: BreadcrumbItem[];
|
|
1990
|
+
showCTA?: boolean;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
interface HeaderProps {
|
|
1994
|
+
locale: Locale;
|
|
1995
|
+
config: HeaderConfig;
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
interface FooterProps {
|
|
1999
|
+
locale: Locale;
|
|
2000
|
+
config: FooterConfig;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
interface LanguageSwitcherProps {
|
|
2004
|
+
currentLocale: string;
|
|
2005
|
+
className?: string;
|
|
2006
|
+
customSlugTranslations?: SlugTranslations;
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
interface SkipLinkProps {
|
|
2010
|
+
/** Target element ID to skip to (e.g., "main-content") */
|
|
2011
|
+
href: string;
|
|
2012
|
+
/** Link text */
|
|
2013
|
+
children: ReactNode;
|
|
2014
|
+
/** Additional CSS classes */
|
|
2015
|
+
className?: string;
|
|
2016
|
+
}
|
|
2017
|
+
|
|
2018
|
+
type AnnouncementPriority = 'polite' | 'assertive';
|
|
2019
|
+
interface A11yAnnouncerProps {
|
|
2020
|
+
/** Announcement message */
|
|
2021
|
+
message?: string;
|
|
2022
|
+
/** Priority level - 'polite' waits for pause, 'assertive' interrupts */
|
|
2023
|
+
priority?: AnnouncementPriority;
|
|
2024
|
+
/** Clear message after this many milliseconds */
|
|
2025
|
+
clearAfter?: number;
|
|
2026
|
+
}
|
|
2027
|
+
|
|
2028
|
+
type IconName = string;
|
|
2029
|
+
interface IconProps extends Omit<LucideProps, 'ref'> {
|
|
2030
|
+
/** Icon name from Lucide React */
|
|
2031
|
+
name: IconName;
|
|
2032
|
+
/** Icon size in pixels @default 24 */
|
|
2033
|
+
size?: number;
|
|
2034
|
+
/** Additional CSS classes */
|
|
2035
|
+
className?: string;
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2038
|
+
interface CodeBlockProps {
|
|
2039
|
+
/** Code to display */
|
|
2040
|
+
code: string;
|
|
2041
|
+
/** Language for syntax highlighting @default 'tsx' */
|
|
2042
|
+
language?: string;
|
|
2043
|
+
/** Show line numbers @default false */
|
|
2044
|
+
showLineNumbers?: boolean;
|
|
2045
|
+
/** Show copy button @default true */
|
|
2046
|
+
showCopy?: boolean;
|
|
2047
|
+
/** Additional CSS classes */
|
|
2048
|
+
className?: string;
|
|
2049
|
+
}
|
|
2050
|
+
|
|
2051
|
+
interface LazySectionProps {
|
|
2052
|
+
/** Component to lazy load */
|
|
2053
|
+
component: () => Promise<{
|
|
2054
|
+
default: ComponentType<any>;
|
|
2055
|
+
}>;
|
|
2056
|
+
/** Props to pass to the lazy component */
|
|
2057
|
+
componentProps?: Record<string, any>;
|
|
2058
|
+
/** Custom loading component */
|
|
2059
|
+
fallback?: ReactNode;
|
|
2060
|
+
/** Load when in viewport @default true */
|
|
2061
|
+
loadOnView?: boolean;
|
|
2062
|
+
/** Viewport intersection threshold @default 0.1 */
|
|
2063
|
+
threshold?: number;
|
|
2064
|
+
/** Additional CSS classes */
|
|
2065
|
+
className?: string;
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
interface FormFieldProps {
|
|
2069
|
+
/** Field label text */
|
|
2070
|
+
label?: string;
|
|
2071
|
+
/** Field name (matches form schema) */
|
|
2072
|
+
name: string;
|
|
2073
|
+
/** Error object from React Hook Form */
|
|
2074
|
+
error?: FieldError;
|
|
2075
|
+
/** Optional hint text shown below input */
|
|
2076
|
+
hint?: string;
|
|
2077
|
+
/** Whether field is required */
|
|
2078
|
+
required?: boolean;
|
|
2079
|
+
/** Input element to render */
|
|
2080
|
+
children: ReactNode;
|
|
2081
|
+
/** Additional CSS classes */
|
|
2082
|
+
className?: string;
|
|
2083
|
+
}
|
|
2084
|
+
|
|
2085
|
+
type ModalSize = 'sm' | 'md' | 'lg' | 'xl' | 'full';
|
|
2086
|
+
interface ModalProps {
|
|
2087
|
+
/** Whether modal is open */
|
|
2088
|
+
open: boolean;
|
|
2089
|
+
/** Callback when open state changes */
|
|
2090
|
+
onOpenChange: (open: boolean) => void;
|
|
2091
|
+
/** Modal title */
|
|
2092
|
+
title?: string;
|
|
2093
|
+
/** Modal description */
|
|
2094
|
+
description?: string;
|
|
2095
|
+
/** Modal size @default 'md' */
|
|
2096
|
+
size?: ModalSize;
|
|
2097
|
+
/** Modal content */
|
|
2098
|
+
children: ReactNode;
|
|
2099
|
+
/** Whether clicking backdrop closes modal @default true */
|
|
2100
|
+
closeOnBackdrop?: boolean;
|
|
2101
|
+
/** Whether ESC key closes modal @default true */
|
|
2102
|
+
closeOnEscape?: boolean;
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2105
|
+
interface Tab {
|
|
2106
|
+
/** Unique tab identifier */
|
|
2107
|
+
value: string;
|
|
2108
|
+
/** Tab label (bilingual) */
|
|
2109
|
+
label: LocalizedString | string;
|
|
2110
|
+
/** Tab content */
|
|
2111
|
+
content: ReactNode;
|
|
2112
|
+
/** Disabled state */
|
|
2113
|
+
disabled?: boolean;
|
|
2114
|
+
}
|
|
2115
|
+
interface TabsProps {
|
|
2116
|
+
/** Current locale */
|
|
2117
|
+
locale?: 'en' | 'fr';
|
|
2118
|
+
/** Array of tabs */
|
|
2119
|
+
tabs: Tab[];
|
|
2120
|
+
/** Default active tab value */
|
|
2121
|
+
defaultValue?: string;
|
|
2122
|
+
/** Controlled active tab value */
|
|
2123
|
+
value?: string;
|
|
2124
|
+
/** Callback when tab changes */
|
|
2125
|
+
onValueChange?: (value: string) => void;
|
|
2126
|
+
/** Sync active tab with URL query param @default false */
|
|
2127
|
+
syncWithUrl?: boolean;
|
|
2128
|
+
/** URL query param name @default 'tab' */
|
|
2129
|
+
urlParam?: string;
|
|
2130
|
+
/** Tab variant @default 'underline' */
|
|
2131
|
+
variant?: 'underline' | 'pills' | 'bordered';
|
|
2132
|
+
/** Tab orientation @default 'horizontal' */
|
|
2133
|
+
orientation?: 'horizontal' | 'vertical';
|
|
2134
|
+
/** Additional CSS classes */
|
|
2135
|
+
className?: string;
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
interface SelectOption {
|
|
2139
|
+
value: string;
|
|
2140
|
+
label: string;
|
|
2141
|
+
disabled?: boolean;
|
|
2142
|
+
}
|
|
2143
|
+
interface SelectOptionGroup {
|
|
2144
|
+
label: string;
|
|
2145
|
+
options: SelectOption[];
|
|
2146
|
+
}
|
|
2147
|
+
interface SelectProps {
|
|
2148
|
+
/** Options to display */
|
|
2149
|
+
options: SelectOption[] | SelectOptionGroup[];
|
|
2150
|
+
/** Selected value */
|
|
2151
|
+
value?: string;
|
|
2152
|
+
/** Callback when value changes */
|
|
2153
|
+
onValueChange?: (value: string) => void;
|
|
2154
|
+
/** Placeholder text */
|
|
2155
|
+
placeholder?: string;
|
|
2156
|
+
/** Whether select is disabled */
|
|
2157
|
+
disabled?: boolean;
|
|
2158
|
+
/** Additional CSS classes */
|
|
2159
|
+
className?: string;
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
interface CheckboxProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> {
|
|
2163
|
+
/** Checkbox label */
|
|
2164
|
+
label?: string;
|
|
2165
|
+
/** Description text below label */
|
|
2166
|
+
description?: string;
|
|
2167
|
+
/** Error message */
|
|
2168
|
+
error?: string;
|
|
2169
|
+
/** Size variant @default 'md' */
|
|
2170
|
+
size?: 'sm' | 'md' | 'lg';
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
interface RadioProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size'> {
|
|
2174
|
+
/** Radio label */
|
|
2175
|
+
label?: string;
|
|
2176
|
+
/** Description text below label */
|
|
2177
|
+
description?: string;
|
|
2178
|
+
/** Error message */
|
|
2179
|
+
error?: string;
|
|
2180
|
+
/** Size variant @default 'md' */
|
|
2181
|
+
size?: 'sm' | 'md' | 'lg';
|
|
2182
|
+
}
|
|
2183
|
+
|
|
2184
|
+
export { type A11yAnnouncerProps, type ABTestEvent, AboutContent, type AboutSectionProps, type AggregateRating, type AnalyticsEvent, type AnnouncementPriority, type Answer, type Article, type BreadcrumbItem, type BreadcrumbList, type BreadcrumbProps, type ButtonProps, type CTAClickEvent, type CTAType, type CardProps, type ChangeFrequency, type CheckboxProps, type CodeBlockProps, type ContactFormConfig, type ContactFormData, type ContactPoint, type ContactSectionProps, type ConversionEvent, type ConversionType, type EventCategory, type FAQPage, type Feature, type FeatureAction, type FeatureCategory, type FeatureEvent, FeaturesGrid, FooterConfig, type FooterProps, type FormAction, type FormEvent, type FormFieldProps, HeaderConfig, type HeaderProps, type HeroAnimations, HeroContent, type HeroSectionProps, type I18nConfig, type IconName, type IconProps, type InputProps, LOCALE_COOKIE_NAME, type LanguagePreference, type LanguageSwitcherProps, type LazySectionProps, type ListItem, type Locale, type LocaleCookieConfig, type LocalePrefix, LocalizedString, type Location, type MetadataOptions, type ModalProps, type NavigationEvent, type NavigationLocation, type Offer, type Organization, type PageViewEvent, type Person, type Policy, type PolicyMetadata, type PostalAddress, type PricingAction, type PricingEvent, type Product, type Question, type RadioProps, type Rating, type Review, type ScrollDepth, type ScrollDepthEvent, type SearchAction, type SelectProps, type ServicePageLayoutProps, ServicesContent, type ServicesSectionProps, type SitemapConfig, type SitemapEntry, type SkipLinkProps, type SlugTranslations, type TabsProps, type TestimonialSectionProps, type TextareaProps, ThemeConfig, type Thing, TrackedLink, type ValidLocale, type VideoAction, type VideoEvent, type WebSite, cn, createArticle, createBreadcrumbList, createFAQPage, createI18nMiddleware, createMultiLanguageEntries, createOrganization, createProduct, createSitemapEntry, createWebSite, creditCardSchema, dateString, defaultLocale, defaultSlugTranslations, emailSchema, fileSchema, formErrorMessages, formatCurrency, formatDate, formatDateRange, formatFileSize, formatList, formatLocaleDisplay, formatNumber, formatRelativeTime, generateArticleMetadata, generateDesignTokens, generateMetadata, generateSitemap, generateThemeCSS, getAllPolicies, getAlternateLocales, getDefaultLocale, getErrorMessage, getFontConfig, getFontVariables, getI18nConfig, getLocaleAutonym, getLocaleCookieConfig, getLocaleFromCookie, getLocaleLabel, getLocaleLabels, getLocaleName, getLocaleNames, getLocalePrefix, getLocales, getLocalizedString, getNavigationString, getPolicyLocales, getPolicySlugs, getRelativeTime, getRtlLocales, getTextDirection, imageSchema, isI18nConfigInitialized, isLocaleDetectionEnabled, isRtlLocale, isSupportedLocale, loadPolicy, locales, matchLocale, mustBeTrue, normalizeLocale, numericString, optionalString, passwordSchema, phoneSchema, postalCodeSchema, replaceVariables, requiredString, serializeStructuredData, setI18nConfig, setLocaleCookie, trackABTestEvent, trackCTAClick, trackConversion, trackError, trackEvent, trackFeatureEngagement, trackFormEvent, trackNavigation, trackOutboundLink, trackPageView, trackPricingEvent, trackResourceDownload, trackScrollDepth, trackSearch, trackVideoEvent, translateSlug, urlSchema, validateLocale, validateSitemap, validateSitemapEntry };
|