sdga-ui 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +4 -4
  2. package/css/dga-ui.css +571 -220
  3. package/css/dga-ui.css.map +1 -1
  4. package/demo-angular/angular.json +3 -0
  5. package/demo-angular/package-lock.json +41 -41
  6. package/demo-angular/package.json +8 -8
  7. package/demo-angular/public/404.html +35 -0
  8. package/demo-angular/public/i18n/ar.json +47 -0
  9. package/demo-angular/public/i18n/en.json +47 -0
  10. package/demo-angular/src/app/app.html +1 -1
  11. package/demo-angular/src/app/app.routes.ts +8 -4
  12. package/demo-angular/src/app/app.ts +11 -2
  13. package/demo-angular/src/app/shared/code-example/code-example.component.html +1 -1
  14. package/demo-angular/src/app/shared/code-example/code-example.component.ts +12 -1
  15. package/demo-angular/src/app/views/alerts/alerts.component.html +10 -10
  16. package/demo-angular/src/app/views/alerts/alerts.component.ts +10 -10
  17. package/demo-angular/src/app/views/buttons/buttons.component.html +118 -47
  18. package/demo-angular/src/app/views/buttons/buttons.component.scss +0 -3
  19. package/demo-angular/src/app/views/buttons/buttons.component.ts +91 -21
  20. package/demo-angular/src/app/views/cards/cards.component.html +6 -6
  21. package/demo-angular/src/app/views/cards/cards.component.ts +10 -10
  22. package/demo-angular/src/app/views/footer/footer.html +270 -0
  23. package/demo-angular/src/app/views/footer/footer.ts +276 -0
  24. package/demo-angular/src/app/views/header/header.html +1 -0
  25. package/demo-angular/src/app/views/header/header.scss +0 -0
  26. package/demo-angular/src/app/views/header/header.spec.ts +23 -0
  27. package/demo-angular/src/app/views/header/header.ts +11 -0
  28. package/demo-angular/src/app/views/home/home.component.html +0 -5
  29. package/demo-angular/src/app/views/links/links.component.html +2 -2
  30. package/demo-angular/src/app/views/links/links.component.ts +1 -1
  31. package/demo-angular/src/app/views/toasts/toasts.component.html +7 -7
  32. package/demo-angular/src/app/views/toasts/toasts.component.ts +7 -7
  33. package/demo-angular/src/index.html +15 -1
  34. package/demo-angular/tsconfig.app.json +2 -1
  35. package/package.json +2 -2
  36. package/sdga-ui/README.md +45 -0
  37. package/sdga-ui/content/docs/components/alerts.mdx +475 -0
  38. package/sdga-ui/content/docs/index.mdx +239 -0
  39. package/sdga-ui/next.config.mjs +10 -0
  40. package/sdga-ui/package-lock.json +5851 -0
  41. package/sdga-ui/package.json +32 -0
  42. package/sdga-ui/postcss.config.mjs +5 -0
  43. package/sdga-ui/source.config.ts +27 -0
  44. package/sdga-ui/src/app/(home)/layout.tsx +6 -0
  45. package/sdga-ui/src/app/(home)/page.tsx +202 -0
  46. package/sdga-ui/src/app/api/search/route.ts +7 -0
  47. package/sdga-ui/src/app/docs/[[...slug]]/page.tsx +54 -0
  48. package/sdga-ui/src/app/docs/layout.tsx +11 -0
  49. package/sdga-ui/src/app/global.css +3 -0
  50. package/sdga-ui/src/app/layout.tsx +25 -0
  51. package/sdga-ui/src/app/llms-full.txt/route.ts +10 -0
  52. package/sdga-ui/src/app/og/docs/[...slug]/route.tsx +34 -0
  53. package/sdga-ui/src/app/sdga-scoped.css +7 -0
  54. package/sdga-ui/src/components/sdga-preview.tsx +105 -0
  55. package/sdga-ui/src/lib/layout.shared.tsx +9 -0
  56. package/sdga-ui/src/lib/source.ts +27 -0
  57. package/sdga-ui/src/mdx-components.tsx +9 -0
  58. package/sdga-ui/tsconfig.json +46 -0
  59. package/theme/_variables.scss +6 -5
  60. package/theme/components/_buttons.scss +1 -197
  61. package/theme/config/_base.scss +11 -1
  62. package/theme/customizations/_alerts.scss +115 -126
  63. package/theme/customizations/_badges.scss +15 -0
  64. package/theme/customizations/_buttons.scss +349 -146
  65. package/theme/customizations/_cards.scss +52 -0
  66. package/theme/customizations/_footer.scss +160 -0
  67. package/theme/customizations/_global.scss +20 -2
  68. package/theme/customizations/_links.scss +62 -45
  69. package/theme/customizations/_toasts.scss +92 -114
  70. package/theme/dga-ui.scss +2 -1
  71. package/demo-angular/src/app/views/bootstrap/bootstrap.component.html +0 -14
  72. package/demo-angular/src/app/views/bootstrap/bootstrap.component.scss +0 -91
  73. package/demo-angular/src/app/views/bootstrap/bootstrap.component.ts +0 -23
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "sdga-ui",
3
+ "version": "0.0.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "build": "next build",
7
+ "dev": "next dev",
8
+ "start": "next start",
9
+ "types:check": "fumadocs-mdx && tsc --noEmit",
10
+ "postinstall": "fumadocs-mdx"
11
+ },
12
+ "dependencies": {
13
+ "fumadocs-core": "16.2.2",
14
+ "fumadocs-mdx": "14.0.4",
15
+ "fumadocs-ui": "16.2.2",
16
+ "lucide-react": "^0.552.0",
17
+ "next": "16.0.1",
18
+ "react": "^19.2.0",
19
+ "react-dom": "^19.2.0",
20
+ "sdga-ui": "^1.0.6"
21
+ },
22
+ "devDependencies": {
23
+ "@tailwindcss/postcss": "^4.1.16",
24
+ "@types/mdx": "^2.0.13",
25
+ "@types/node": "^24.10.0",
26
+ "@types/react": "^19.2.2",
27
+ "@types/react-dom": "^19.2.2",
28
+ "postcss": "^8.5.6",
29
+ "tailwindcss": "^4.1.16",
30
+ "typescript": "^5.9.3"
31
+ }
32
+ }
@@ -0,0 +1,5 @@
1
+ export default {
2
+ plugins: {
3
+ '@tailwindcss/postcss': {},
4
+ },
5
+ };
@@ -0,0 +1,27 @@
1
+ import {
2
+ defineConfig,
3
+ defineDocs,
4
+ frontmatterSchema,
5
+ metaSchema,
6
+ } from 'fumadocs-mdx/config';
7
+
8
+ // You can customise Zod schemas for frontmatter and `meta.json` here
9
+ // see https://fumadocs.dev/docs/mdx/collections
10
+ export const docs = defineDocs({
11
+ dir: 'content/docs',
12
+ docs: {
13
+ schema: frontmatterSchema,
14
+ postprocess: {
15
+ includeProcessedMarkdown: true,
16
+ },
17
+ },
18
+ meta: {
19
+ schema: metaSchema,
20
+ },
21
+ });
22
+
23
+ export default defineConfig({
24
+ mdxOptions: {
25
+ // MDX options
26
+ },
27
+ });
@@ -0,0 +1,6 @@
1
+ import { HomeLayout } from 'fumadocs-ui/layouts/home';
2
+ import { baseOptions } from '@/lib/layout.shared';
3
+
4
+ export default function Layout({ children }: LayoutProps<'/'>) {
5
+ return <HomeLayout {...baseOptions()}>{children}</HomeLayout>;
6
+ }
@@ -0,0 +1,202 @@
1
+ import Link from 'next/link';
2
+ import { ArrowRight, Code2, Palette, Zap, Globe, Sparkles, BookOpen } from 'lucide-react';
3
+
4
+ export default function HomePage() {
5
+ return (
6
+ <div className="flex flex-col">
7
+ {/* Hero Section */}
8
+ <section className="relative overflow-hidden">
9
+ <div className="absolute inset-0 bg-linear-to-br from-blue-50 via-indigo-50 to-purple-50 dark:from-blue-950/20 dark:via-indigo-950/20 dark:to-purple-950/20" />
10
+ <div className="absolute inset-0 opacity-[0.02]" style={{ backgroundImage: 'radial-gradient(circle at 1px 1px, rgb(0 0 0 / 0.5) 1px, transparent 0)', backgroundSize: '40px 40px' }} />
11
+
12
+ <div className="relative container mx-auto px-4 py-24 md:py-32">
13
+ <div className="flex flex-col items-center text-center max-w-4xl mx-auto">
14
+ <div className="inline-flex items-center gap-2 px-4 py-2 rounded-full bg-white/80 dark:bg-neutral-900/80 backdrop-blur-sm border border-neutral-200 dark:border-neutral-800 mb-6 shadow-sm">
15
+ <Sparkles className="w-4 h-4 text-indigo-600 dark:text-indigo-400" />
16
+ <span className="text-sm font-medium">Saudi Digital Government Authority</span>
17
+ </div>
18
+
19
+ <h1 className="text-5xl md:text-7xl font-bold mb-6 bg-clip-text text-transparent bg-linear-to-r from-indigo-600 via-purple-600 to-pink-600 dark:from-indigo-400 dark:via-purple-400 dark:to-pink-400">
20
+ SDGA UI
21
+ </h1>
22
+
23
+ <p className="text-xl md:text-2xl text-neutral-600 dark:text-neutral-400 mb-8 max-w-2xl">
24
+ A beautiful, accessible, and customizable component library built for modern web applications
25
+ </p>
26
+
27
+ <div className="flex flex-wrap items-center justify-center gap-4 mb-12">
28
+ <Link
29
+ href="/docs"
30
+ className="inline-flex items-center gap-2 px-8 py-4 bg-indigo-600 hover:bg-indigo-700 text-white rounded-lg font-semibold transition-all hover:scale-105 hover:shadow-xl shadow-lg"
31
+ >
32
+ Get Started
33
+ <ArrowRight className="w-5 h-5" />
34
+ </Link>
35
+
36
+ <Link
37
+ href="/docs"
38
+ className="inline-flex items-center gap-2 px-8 py-4 bg-white dark:bg-neutral-900 hover:bg-neutral-50 dark:hover:bg-neutral-800 text-neutral-900 dark:text-white rounded-lg font-semibold border border-neutral-200 dark:border-neutral-800 transition-all hover:shadow-lg"
39
+ >
40
+ <BookOpen className="w-5 h-5" />
41
+ View Documentation
42
+ </Link>
43
+ </div>
44
+
45
+ {/* Stats */}
46
+ <div className="grid grid-cols-3 gap-8 md:gap-12 pt-8 border-t border-neutral-200 dark:border-neutral-800 w-full max-w-2xl">
47
+ <div>
48
+ <div className="text-3xl md:text-4xl font-bold text-indigo-600 dark:text-indigo-400 mb-1">50+</div>
49
+ <div className="text-sm text-neutral-600 dark:text-neutral-400">Components</div>
50
+ </div>
51
+ <div>
52
+ <div className="text-3xl md:text-4xl font-bold text-purple-600 dark:text-purple-400 mb-1">100%</div>
53
+ <div className="text-sm text-neutral-600 dark:text-neutral-400">Accessible</div>
54
+ </div>
55
+ <div>
56
+ <div className="text-3xl md:text-4xl font-bold text-pink-600 dark:text-pink-400 mb-1">RTL</div>
57
+ <div className="text-sm text-neutral-600 dark:text-neutral-400">Support</div>
58
+ </div>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ </section>
63
+
64
+ {/* Features Section */}
65
+ <section className="py-24 px-4">
66
+ <div className="container mx-auto max-w-6xl">
67
+ <div className="text-center mb-16">
68
+ <h2 className="text-4xl md:text-5xl font-bold mb-4">Why Choose SDGA UI?</h2>
69
+ <p className="text-lg text-neutral-600 dark:text-neutral-400 max-w-2xl mx-auto">
70
+ Built with modern technologies and best practices to help you create stunning interfaces faster
71
+ </p>
72
+ </div>
73
+
74
+ <div className="grid md:grid-cols-2 lg:grid-cols-3 gap-8">
75
+ <FeatureCard
76
+ icon={<Zap className="w-8 h-8" />}
77
+ title="Lightning Fast"
78
+ description="Optimized for performance with minimal bundle size and zero runtime overhead"
79
+ gradient="from-yellow-500 to-orange-500"
80
+ />
81
+
82
+ <FeatureCard
83
+ icon={<Palette className="w-8 h-8" />}
84
+ title="Fully Customizable"
85
+ description="Extensive theming system with CSS variables and SCSS support for complete control"
86
+ gradient="from-pink-500 to-rose-500"
87
+ />
88
+
89
+ <FeatureCard
90
+ icon={<Code2 className="w-8 h-8" />}
91
+ title="Developer Friendly"
92
+ description="Clean API, TypeScript support, and comprehensive documentation for seamless integration"
93
+ gradient="from-blue-500 to-cyan-500"
94
+ />
95
+
96
+ <FeatureCard
97
+ icon={<Globe className="w-8 h-8" />}
98
+ title="RTL Support"
99
+ description="Built-in right-to-left language support for Arabic and other RTL languages"
100
+ gradient="from-green-500 to-emerald-500"
101
+ />
102
+
103
+ <FeatureCard
104
+ icon={<Sparkles className="w-8 h-8" />}
105
+ title="Modern Design"
106
+ description="Contemporary UI patterns and components that follow the latest design trends"
107
+ gradient="from-purple-500 to-indigo-500"
108
+ />
109
+
110
+ <FeatureCard
111
+ icon={<BookOpen className="w-8 h-8" />}
112
+ title="Rich Documentation"
113
+ description="Detailed guides, examples, and interactive demos for every component"
114
+ gradient="from-orange-500 to-red-500"
115
+ />
116
+ </div>
117
+ </div>
118
+ </section>
119
+
120
+ {/* Code Preview Section */}
121
+ <section className="py-24 px-4 bg-neutral-50 dark:bg-neutral-900/50">
122
+ <div className="container mx-auto max-w-5xl">
123
+ <div className="text-center mb-12">
124
+ <h2 className="text-4xl font-bold mb-4">Simple to Use</h2>
125
+ <p className="text-lg text-neutral-600 dark:text-neutral-400">
126
+ Get started in minutes with our intuitive component API
127
+ </p>
128
+ </div>
129
+
130
+ <div className="bg-white dark:bg-neutral-900 rounded-2xl shadow-2xl border border-neutral-200 dark:border-neutral-800 overflow-hidden">
131
+ <div className="bg-neutral-100 dark:bg-neutral-800 px-6 py-4 border-b border-neutral-200 dark:border-neutral-700 flex items-center gap-2">
132
+ <div className="w-3 h-3 rounded-full bg-red-500" />
133
+ <div className="w-3 h-3 rounded-full bg-yellow-500" />
134
+ <div className="w-3 h-3 rounded-full bg-green-500" />
135
+ <span className="ml-4 text-sm text-neutral-600 dark:text-neutral-400 font-mono">example.html</span>
136
+ </div>
137
+ <pre className="p-6 overflow-x-auto">
138
+ <code className="text-sm font-mono text-neutral-800 dark:text-neutral-200">
139
+ {`<!-- Import SDGA UI -->
140
+ <link rel="stylesheet" href="sdga-ui.css">
141
+
142
+ <!-- Use components -->
143
+ <button class="btn btn-primary">
144
+ Click me
145
+ </button>
146
+
147
+ <div class="alert alert-success">
148
+ Success! Your changes have been saved.
149
+ </div>`}
150
+ </code>
151
+ </pre>
152
+ </div>
153
+ </div>
154
+ </section>
155
+
156
+ {/* CTA Section */}
157
+ <section className="py-24 px-4">
158
+ <div className="container mx-auto max-w-4xl text-center">
159
+ <div className="bg-linear-to-r from-indigo-600 to-purple-600 rounded-3xl p-12 md:p-16 shadow-2xl">
160
+ <h2 className="text-4xl md:text-5xl font-bold text-white mb-6">
161
+ Ready to Build Something Amazing?
162
+ </h2>
163
+ <p className="text-xl text-indigo-100 mb-8 max-w-2xl mx-auto">
164
+ Start creating beautiful, accessible interfaces with SDGA UI today
165
+ </p>
166
+ <Link
167
+ href="/docs"
168
+ className="inline-flex items-center gap-2 px-8 py-4 bg-white text-indigo-600 rounded-lg font-semibold hover:bg-indigo-50 transition-all hover:scale-105 shadow-lg"
169
+ >
170
+ Explore Documentation
171
+ <ArrowRight className="w-5 h-5" />
172
+ </Link>
173
+ </div>
174
+ </div>
175
+ </section>
176
+ </div>
177
+ );
178
+ }
179
+
180
+ function FeatureCard({
181
+ icon,
182
+ title,
183
+ description,
184
+ gradient
185
+ }: {
186
+ icon: React.ReactNode;
187
+ title: string;
188
+ description: string;
189
+ gradient: string;
190
+ }) {
191
+ return (
192
+ <div className="group relative p-8 rounded-2xl bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-800 hover:border-neutral-300 dark:hover:border-neutral-700 transition-all hover:shadow-xl">
193
+ <div className={`inline-flex p-3 rounded-xl bg-linear-to-br ${gradient} text-white mb-4 group-hover:scale-110 transition-transform`}>
194
+ {icon}
195
+ </div>
196
+ <h3 className="text-xl font-bold mb-2">{title}</h3>
197
+ <p className="text-neutral-600 dark:text-neutral-400 leading-relaxed">
198
+ {description}
199
+ </p>
200
+ </div>
201
+ );
202
+ }
@@ -0,0 +1,7 @@
1
+ import { source } from '@/lib/source';
2
+ import { createFromSource } from 'fumadocs-core/search/server';
3
+
4
+ export const { GET } = createFromSource(source, {
5
+ // https://docs.orama.com/docs/orama-js/supported-languages
6
+ language: 'english',
7
+ });
@@ -0,0 +1,54 @@
1
+ import { getPageImage, source } from '@/lib/source';
2
+ import {
3
+ DocsBody,
4
+ DocsDescription,
5
+ DocsPage,
6
+ DocsTitle,
7
+ } from 'fumadocs-ui/layouts/docs/page';
8
+ import { notFound } from 'next/navigation';
9
+ import { getMDXComponents } from '@/mdx-components';
10
+ import type { Metadata } from 'next';
11
+ import { createRelativeLink } from 'fumadocs-ui/mdx';
12
+
13
+ export default async function Page(props: PageProps<'/docs/[[...slug]]'>) {
14
+ const params = await props.params;
15
+ const page = source.getPage(params.slug);
16
+ if (!page) notFound();
17
+
18
+ const MDX = page.data.body;
19
+
20
+ return (
21
+ <DocsPage toc={page.data.toc} full={page.data.full}>
22
+ <DocsTitle>{page.data.title}</DocsTitle>
23
+ <DocsDescription>{page.data.description}</DocsDescription>
24
+ <DocsBody>
25
+ <MDX
26
+ components={getMDXComponents({
27
+ // this allows you to link to other pages with relative file paths
28
+ a: createRelativeLink(source, page),
29
+ })}
30
+ />
31
+ </DocsBody>
32
+ </DocsPage>
33
+ );
34
+ }
35
+
36
+ export async function generateStaticParams() {
37
+ return source.generateParams();
38
+ }
39
+
40
+ export async function generateMetadata(
41
+ props: PageProps<'/docs/[[...slug]]'>,
42
+ ): Promise<Metadata> {
43
+ const params = await props.params;
44
+ const page = source.getPage(params.slug);
45
+ if (!page) notFound();
46
+
47
+ return {
48
+ title: page.data.title,
49
+ description: page.data.description,
50
+ openGraph: {
51
+ images: getPageImage(page).url,
52
+ },
53
+ };
54
+ }
@@ -0,0 +1,11 @@
1
+ import { source } from '@/lib/source';
2
+ import { DocsLayout } from 'fumadocs-ui/layouts/docs';
3
+ import { baseOptions } from '@/lib/layout.shared';
4
+
5
+ export default function Layout({ children }: LayoutProps<'/docs'>) {
6
+ return (
7
+ <DocsLayout tree={source.pageTree} {...baseOptions()}>
8
+ {children}
9
+ </DocsLayout>
10
+ );
11
+ }
@@ -0,0 +1,3 @@
1
+ @import 'tailwindcss';
2
+ @import 'fumadocs-ui/css/neutral.css';
3
+ @import 'fumadocs-ui/css/preset.css';
@@ -0,0 +1,25 @@
1
+ import { RootProvider } from 'fumadocs-ui/provider/next';
2
+ import './global.css';
3
+ import { Inter } from 'next/font/google';
4
+ import Script from 'next/script';
5
+
6
+ const inter = Inter({
7
+ subsets: ['latin'],
8
+ });
9
+
10
+ export default function Layout({ children }: LayoutProps<'/'>) {
11
+ return (
12
+ <html lang="en" className={inter.className} suppressHydrationWarning>
13
+ <body className="flex flex-col min-h-screen">
14
+ <RootProvider>{children}</RootProvider>
15
+ {/* Bootstrap JS for interactive components */}
16
+ <Script
17
+ src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js"
18
+ integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz"
19
+ crossOrigin="anonymous"
20
+ strategy="afterInteractive"
21
+ />
22
+ </body>
23
+ </html>
24
+ );
25
+ }
@@ -0,0 +1,10 @@
1
+ import { getLLMText, source } from '@/lib/source';
2
+
3
+ export const revalidate = false;
4
+
5
+ export async function GET() {
6
+ const scan = source.getPages().map(getLLMText);
7
+ const scanned = await Promise.all(scan);
8
+
9
+ return new Response(scanned.join('\n\n'));
10
+ }
@@ -0,0 +1,34 @@
1
+ import { getPageImage, source } from '@/lib/source';
2
+ import { notFound } from 'next/navigation';
3
+ import { ImageResponse } from 'next/og';
4
+ import { generate as DefaultImage } from 'fumadocs-ui/og';
5
+
6
+ export const revalidate = false;
7
+
8
+ export async function GET(
9
+ _req: Request,
10
+ { params }: RouteContext<'/og/docs/[...slug]'>,
11
+ ) {
12
+ const { slug } = await params;
13
+ const page = source.getPage(slug.slice(0, -1));
14
+ if (!page) notFound();
15
+
16
+ return new ImageResponse(
17
+ <DefaultImage
18
+ title={page.data.title}
19
+ description={page.data.description}
20
+ site="My App"
21
+ />,
22
+ {
23
+ width: 1200,
24
+ height: 630,
25
+ },
26
+ );
27
+ }
28
+
29
+ export function generateStaticParams() {
30
+ return source.getPages().map((page) => ({
31
+ lang: page.locale,
32
+ slug: getPageImage(page).segments,
33
+ }));
34
+ }
@@ -0,0 +1,7 @@
1
+ /* Isolate SDGA UI styles to .sdga-preview container */
2
+ @import 'sdga-ui/css/dga-ui.css';
3
+
4
+ /* Ensure proper isolation */
5
+ .sdga-preview {
6
+ isolation: isolate;
7
+ }
@@ -0,0 +1,105 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef, useState } from 'react';
4
+
5
+ interface SdgaPreviewProps {
6
+ html: string;
7
+ minHeight?: string;
8
+ }
9
+
10
+ // Preload resources once for all previews
11
+ if (typeof window !== 'undefined') {
12
+ const preloadCSS = document.createElement('link');
13
+ preloadCSS.rel = 'preload';
14
+ preloadCSS.as = 'style';
15
+ preloadCSS.href = 'https://cdn.jsdelivr.net/npm/sdga-ui@latest/css/dga-ui.css';
16
+
17
+ const preloadJS = document.createElement('link');
18
+ preloadJS.rel = 'preload';
19
+ preloadJS.as = 'script';
20
+ preloadJS.href = 'https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js';
21
+
22
+ if (!document.querySelector(`link[href="${preloadCSS.href}"]`)) {
23
+ document.head.appendChild(preloadCSS);
24
+ document.head.appendChild(preloadJS);
25
+ }
26
+ }
27
+
28
+ export function SdgaPreview({ html, minHeight = '150px' }: SdgaPreviewProps) {
29
+ const iframeRef = useRef<HTMLIFrameElement>(null);
30
+ const containerRef = useRef<HTMLDivElement>(null);
31
+ const [isVisible, setIsVisible] = useState(false);
32
+
33
+ // Lazy load iframe when it enters viewport
34
+ useEffect(() => {
35
+ const container = containerRef.current;
36
+ if (!container) return;
37
+
38
+ const observer = new IntersectionObserver(
39
+ (entries) => {
40
+ entries.forEach((entry) => {
41
+ if (entry.isIntersecting) {
42
+ setIsVisible(true);
43
+ observer.disconnect();
44
+ }
45
+ });
46
+ },
47
+ { rootMargin: '200px' } // Load 200px before entering viewport
48
+ );
49
+
50
+ observer.observe(container);
51
+ return () => observer.disconnect();
52
+ }, []);
53
+
54
+ useEffect(() => {
55
+ if (!isVisible) return;
56
+
57
+ const iframe = iframeRef.current;
58
+ if (!iframe) return;
59
+
60
+ const content = `<!DOCTYPE html>
61
+ <html lang="en">
62
+ <head>
63
+ <meta charset="UTF-8">
64
+ <meta name="viewport" content="width=device-width,initial-scale=1.0">
65
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sdga-ui@latest/css/dga-ui.css">
66
+ <style>body{margin:0;padding:1.5rem;background:transparent;overflow-x:hidden}</style>
67
+ </head>
68
+ <body>${html}<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/js/bootstrap.bundle.min.js" integrity="sha384-FKyoEForCGlyvwx9Hj09JcYn3nv7wiPVlz7YYwJrWVcXK/BmnVDxM+D2scQbITxI" crossorigin="anonymous"><\/script><script>let t;function r(){clearTimeout(t);t=setTimeout(()=>window.parent.postMessage({type:'resize',height:document.body.scrollHeight},'*'),150)}window.addEventListener('load',()=>setTimeout(r,100));if(!window.__RO_DISABLED__){const o=new MutationObserver(()=>requestAnimationFrame(r));o.observe(document.body,{childList:true,subtree:true,attributes:true,attributeFilter:['class','style']})}<\/script></body></html>`;
69
+
70
+ iframe.srcdoc = content;
71
+ }, [html, isVisible]);
72
+
73
+ useEffect(() => {
74
+ const handleMessage = (event: MessageEvent) => {
75
+ if (event.data.type === 'resize' && iframeRef.current) {
76
+ iframeRef.current.style.height = `${event.data.height + 10}px`;
77
+ }
78
+ };
79
+
80
+ window.addEventListener('message', handleMessage);
81
+ return () => window.removeEventListener('message', handleMessage);
82
+ }, []);
83
+
84
+ return (
85
+ <div ref={containerRef} style={{ minHeight }}>
86
+ {isVisible ? (
87
+ <iframe
88
+ ref={iframeRef}
89
+ className="w-full border border-neutral-200 dark:border-neutral-800 rounded-lg"
90
+ style={{ minHeight, border: '1px solid rgb(229 229 229)', borderRadius: '0.5rem' }}
91
+ sandbox="allow-scripts allow-same-origin"
92
+ loading="lazy"
93
+ title="SDGA UI Preview"
94
+ />
95
+ ) : (
96
+ <div
97
+ className="w-full border border-neutral-200 dark:border-neutral-800 rounded-lg flex items-center justify-center"
98
+ style={{ minHeight, background: '#f9fafb' }}
99
+ >
100
+ <span className="text-neutral-400">Loading preview...</span>
101
+ </div>
102
+ )}
103
+ </div>
104
+ );
105
+ }
@@ -0,0 +1,9 @@
1
+ import type { BaseLayoutProps } from 'fumadocs-ui/layouts/shared';
2
+
3
+ export function baseOptions(): BaseLayoutProps {
4
+ return {
5
+ nav: {
6
+ title: 'My App',
7
+ },
8
+ };
9
+ }
@@ -0,0 +1,27 @@
1
+ import { docs } from 'fumadocs-mdx:collections/server';
2
+ import { type InferPageType, loader } from 'fumadocs-core/source';
3
+ import { lucideIconsPlugin } from 'fumadocs-core/source/lucide-icons';
4
+
5
+ // See https://fumadocs.dev/docs/headless/source-api for more info
6
+ export const source = loader({
7
+ baseUrl: '/docs',
8
+ source: docs.toFumadocsSource(),
9
+ plugins: [lucideIconsPlugin()],
10
+ });
11
+
12
+ export function getPageImage(page: InferPageType<typeof source>) {
13
+ const segments = [...page.slugs, 'image.png'];
14
+
15
+ return {
16
+ segments,
17
+ url: `/og/docs/${segments.join('/')}`,
18
+ };
19
+ }
20
+
21
+ export async function getLLMText(page: InferPageType<typeof source>) {
22
+ const processed = await page.data.getText('processed');
23
+
24
+ return `# ${page.data.title}
25
+
26
+ ${processed}`;
27
+ }
@@ -0,0 +1,9 @@
1
+ import defaultMdxComponents from 'fumadocs-ui/mdx';
2
+ import type { MDXComponents } from 'mdx/types';
3
+
4
+ export function getMDXComponents(components?: MDXComponents): MDXComponents {
5
+ return {
6
+ ...defaultMdxComponents,
7
+ ...components,
8
+ };
9
+ }
@@ -0,0 +1,46 @@
1
+ {
2
+ "compilerOptions": {
3
+ "baseUrl": ".",
4
+ "target": "ESNext",
5
+ "lib": [
6
+ "dom",
7
+ "dom.iterable",
8
+ "esnext"
9
+ ],
10
+ "allowJs": true,
11
+ "skipLibCheck": true,
12
+ "strict": true,
13
+ "forceConsistentCasingInFileNames": true,
14
+ "noEmit": true,
15
+ "esModuleInterop": true,
16
+ "module": "esnext",
17
+ "moduleResolution": "bundler",
18
+ "resolveJsonModule": true,
19
+ "isolatedModules": true,
20
+ "jsx": "react-jsx",
21
+ "incremental": true,
22
+ "paths": {
23
+ "@/*": [
24
+ "./src/*"
25
+ ],
26
+ "fumadocs-mdx:collections/*": [
27
+ ".source/*"
28
+ ]
29
+ },
30
+ "plugins": [
31
+ {
32
+ "name": "next"
33
+ }
34
+ ]
35
+ },
36
+ "include": [
37
+ "next-env.d.ts",
38
+ "**/*.ts",
39
+ "**/*.tsx",
40
+ ".next/types/**/*.ts",
41
+ ".next/dev/types/**/*.ts"
42
+ ],
43
+ "exclude": [
44
+ "node_modules"
45
+ ]
46
+ }
@@ -31,17 +31,18 @@
31
31
  @import 'components/buttons';
32
32
  @import 'components/alerts';
33
33
  @import 'components/toasts';
34
+ @import 'components/forms';
35
+ @import 'components/dropdowns';
36
+ @import 'components/cards';
37
+ @import 'components/tables';
38
+
39
+ @import 'components/navbar';
34
40
  @import 'components/modals';
35
41
  @import 'components/tooltips';
36
42
  @import 'components/popovers';
37
43
  @import 'components/offcanvas';
38
- @import 'components/forms';
39
- @import 'components/navbar';
40
- @import 'components/dropdowns';
41
44
  @import 'components/pagination';
42
45
  @import 'components/breadcrumb';
43
- @import 'components/cards';
44
- @import 'components/tables';
45
46
  @import 'components/list-group';
46
47
  @import 'components/accordion';
47
48
  @import 'components/badges';