@djangocfg/layouts 1.2.16 → 1.2.18
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/package.json +5 -5
- package/src/layouts/AppLayout/components/PackageVersions/packageVersions.config.ts +8 -8
- package/src/layouts/UILayout/SUMMARY.md +298 -0
- package/src/layouts/UILayout/components/index.ts +15 -0
- package/src/layouts/UILayout/components/layout/Header/CopyAIButton.tsx +58 -0
- package/src/layouts/UILayout/components/layout/Header/Header.tsx +60 -0
- package/src/layouts/UILayout/components/layout/Header/HeaderDesktop.tsx +44 -0
- package/src/layouts/UILayout/components/layout/Header/HeaderMobile.tsx +71 -0
- package/src/layouts/UILayout/components/layout/Header/index.ts +9 -0
- package/src/layouts/UILayout/components/layout/MobileOverlay/MobileOverlay.tsx +46 -0
- package/src/layouts/UILayout/components/layout/MobileOverlay/index.ts +6 -0
- package/src/layouts/UILayout/components/layout/Sidebar/Sidebar.tsx +94 -0
- package/src/layouts/UILayout/components/layout/Sidebar/SidebarCategory.tsx +54 -0
- package/src/layouts/UILayout/components/layout/Sidebar/SidebarContent.tsx +86 -0
- package/src/layouts/UILayout/components/layout/Sidebar/SidebarFooter.tsx +49 -0
- package/src/layouts/UILayout/components/layout/Sidebar/index.ts +9 -0
- package/src/layouts/UILayout/components/layout/index.ts +8 -0
- package/src/layouts/UILayout/components/shared/Badge/CountBadge.tsx +38 -0
- package/src/layouts/UILayout/components/shared/Badge/index.ts +5 -0
- package/src/layouts/UILayout/components/shared/CodeBlock/CodeBlock.tsx +48 -0
- package/src/layouts/UILayout/components/shared/CodeBlock/CopyButton.tsx +49 -0
- package/src/layouts/UILayout/components/shared/CodeBlock/index.ts +6 -0
- package/src/layouts/UILayout/components/shared/Section/Section.tsx +63 -0
- package/src/layouts/UILayout/components/shared/Section/index.ts +5 -0
- package/src/layouts/UILayout/components/shared/index.ts +8 -0
- package/src/layouts/UILayout/config/components/navigation.config.tsx +29 -0
- package/src/layouts/UILayout/{UIGuideLanding.tsx → core/UIGuideLanding.tsx} +1 -1
- package/src/layouts/UILayout/{UIGuideView.tsx → core/UIGuideView.tsx} +4 -4
- package/src/layouts/UILayout/{UILayout.tsx → core/UILayout.tsx} +8 -25
- package/src/layouts/UILayout/core/index.ts +9 -0
- package/src/layouts/UILayout/hooks/index.ts +9 -0
- package/src/layouts/UILayout/hooks/useAIExport.ts +78 -0
- package/src/layouts/UILayout/hooks/useCategoryNavigation.ts +92 -0
- package/src/layouts/UILayout/hooks/useComponentSearch.ts +81 -0
- package/src/layouts/UILayout/hooks/useSidebarState.ts +36 -0
- package/src/layouts/UILayout/index.ts +121 -22
- package/src/layouts/UILayout/types/component.ts +45 -0
- package/src/layouts/UILayout/types/index.ts +23 -0
- package/src/layouts/UILayout/types/layout.ts +59 -0
- package/src/layouts/UILayout/types/navigation.ts +33 -0
- package/src/layouts/UILayout/utils/ai-export/formatters.ts +71 -0
- package/src/layouts/UILayout/utils/ai-export/generators.ts +130 -0
- package/src/layouts/UILayout/utils/ai-export/index.ts +6 -0
- package/src/layouts/UILayout/utils/component-helpers/filter.ts +109 -0
- package/src/layouts/UILayout/utils/component-helpers/index.ts +6 -0
- package/src/layouts/UILayout/utils/component-helpers/search.ts +95 -0
- package/src/layouts/UILayout/utils/index.ts +6 -0
- package/src/layouts/UILayout/REFACTORING.md +0 -331
- package/src/layouts/UILayout/components/Header.tsx +0 -114
- package/src/layouts/UILayout/components/MobileOverlay.tsx +0 -33
- package/src/layouts/UILayout/components/Sidebar.tsx +0 -188
- package/src/layouts/UILayout/types.ts +0 -13
- /package/src/layouts/UILayout/{UIGuideApp.tsx → core/UIGuideApp.tsx} +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar Component
|
|
3
|
+
* Navigation sidebar for component categories
|
|
4
|
+
* Adaptive: desktop (static) or mobile (portal)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use client';
|
|
8
|
+
|
|
9
|
+
import React from 'react';
|
|
10
|
+
import { createPortal } from 'react-dom';
|
|
11
|
+
import { cn } from '@djangocfg/ui/lib';
|
|
12
|
+
import { useIsMobile } from '@djangocfg/ui';
|
|
13
|
+
import type { ComponentCategory } from '../../../types';
|
|
14
|
+
import { SidebarContent } from './SidebarContent';
|
|
15
|
+
|
|
16
|
+
export interface SidebarProps {
|
|
17
|
+
/** Available categories */
|
|
18
|
+
categories: ComponentCategory[];
|
|
19
|
+
/** Current selected category */
|
|
20
|
+
currentCategory?: string;
|
|
21
|
+
/** Category change callback */
|
|
22
|
+
onCategoryChange?: (categoryId: string) => void;
|
|
23
|
+
/** Is sidebar open (mobile only) */
|
|
24
|
+
isOpen?: boolean;
|
|
25
|
+
/** Project name */
|
|
26
|
+
projectName?: string;
|
|
27
|
+
/** Logo component */
|
|
28
|
+
logo?: React.ReactNode;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Sidebar Component
|
|
33
|
+
* Desktop: Always visible static sidebar
|
|
34
|
+
* Mobile: Portal-based sidebar with slide animation
|
|
35
|
+
*/
|
|
36
|
+
export function Sidebar({
|
|
37
|
+
categories,
|
|
38
|
+
currentCategory,
|
|
39
|
+
onCategoryChange,
|
|
40
|
+
isOpen = false,
|
|
41
|
+
projectName = 'Django CFG',
|
|
42
|
+
logo,
|
|
43
|
+
}: SidebarProps) {
|
|
44
|
+
const isMobile = useIsMobile();
|
|
45
|
+
const [mounted, setMounted] = React.useState(false);
|
|
46
|
+
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
setMounted(true);
|
|
49
|
+
}, []);
|
|
50
|
+
|
|
51
|
+
// Desktop sidebar - always visible, no portal
|
|
52
|
+
if (!isMobile) {
|
|
53
|
+
return (
|
|
54
|
+
<aside className="w-64 border-r bg-background flex-shrink-0">
|
|
55
|
+
<SidebarContent
|
|
56
|
+
categories={categories}
|
|
57
|
+
currentCategory={currentCategory}
|
|
58
|
+
onCategoryChange={onCategoryChange}
|
|
59
|
+
projectName={projectName}
|
|
60
|
+
logo={logo}
|
|
61
|
+
isMobile={false}
|
|
62
|
+
/>
|
|
63
|
+
</aside>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Mobile sidebar - use portal when open
|
|
68
|
+
if (!isOpen || !mounted) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (typeof window === 'undefined') {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return createPortal(
|
|
77
|
+
<aside
|
|
78
|
+
className={cn(
|
|
79
|
+
'fixed inset-y-0 left-0 w-64 z-[200] bg-background border-r shadow-lg transition-transform duration-300',
|
|
80
|
+
isOpen ? 'translate-x-0' : '-translate-x-full'
|
|
81
|
+
)}
|
|
82
|
+
>
|
|
83
|
+
<SidebarContent
|
|
84
|
+
categories={categories}
|
|
85
|
+
currentCategory={currentCategory}
|
|
86
|
+
onCategoryChange={onCategoryChange}
|
|
87
|
+
projectName={projectName}
|
|
88
|
+
logo={logo}
|
|
89
|
+
isMobile={true}
|
|
90
|
+
/>
|
|
91
|
+
</aside>,
|
|
92
|
+
document.body
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SidebarCategory Component
|
|
3
|
+
* Single category item in sidebar navigation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { cn } from '@djangocfg/ui/lib';
|
|
10
|
+
import type { ComponentCategory } from '../../../types';
|
|
11
|
+
import { CountBadge } from '../../shared/Badge';
|
|
12
|
+
|
|
13
|
+
interface SidebarCategoryProps {
|
|
14
|
+
/** Category data */
|
|
15
|
+
category: ComponentCategory;
|
|
16
|
+
/** Is this category active */
|
|
17
|
+
active?: boolean;
|
|
18
|
+
/** Click handler */
|
|
19
|
+
onClick?: () => void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Sidebar Category Item
|
|
24
|
+
* Displays a category with icon, label, and optional count badge
|
|
25
|
+
*/
|
|
26
|
+
export function SidebarCategory({
|
|
27
|
+
category,
|
|
28
|
+
active = false,
|
|
29
|
+
onClick,
|
|
30
|
+
}: SidebarCategoryProps) {
|
|
31
|
+
return (
|
|
32
|
+
<button
|
|
33
|
+
onClick={onClick}
|
|
34
|
+
className={cn(
|
|
35
|
+
'w-full flex items-center gap-3 px-3 py-2 rounded-md text-sm font-medium transition-colors',
|
|
36
|
+
active
|
|
37
|
+
? 'bg-primary text-primary-foreground'
|
|
38
|
+
: 'text-muted-foreground hover:bg-muted hover:text-foreground'
|
|
39
|
+
)}
|
|
40
|
+
title={category.description}
|
|
41
|
+
>
|
|
42
|
+
{/* Icon */}
|
|
43
|
+
<span className="flex-shrink-0">{category.icon}</span>
|
|
44
|
+
|
|
45
|
+
{/* Label */}
|
|
46
|
+
<span className="flex-1 text-left break-words">{category.label}</span>
|
|
47
|
+
|
|
48
|
+
{/* Count Badge */}
|
|
49
|
+
{category.count !== undefined && (
|
|
50
|
+
<CountBadge count={category.count} active={active} />
|
|
51
|
+
)}
|
|
52
|
+
</button>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SidebarContent Component
|
|
3
|
+
* Main content of the sidebar (logo, navigation, footer)
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { cn } from '@djangocfg/ui/lib';
|
|
10
|
+
import type { ComponentCategory } from '../../../types';
|
|
11
|
+
import { SidebarCategory } from './SidebarCategory';
|
|
12
|
+
import { SidebarFooter } from './SidebarFooter';
|
|
13
|
+
|
|
14
|
+
interface SidebarContentProps {
|
|
15
|
+
/** Available categories */
|
|
16
|
+
categories: ComponentCategory[];
|
|
17
|
+
/** Current selected category */
|
|
18
|
+
currentCategory?: string;
|
|
19
|
+
/** Category change callback */
|
|
20
|
+
onCategoryChange?: (categoryId: string) => void;
|
|
21
|
+
/** Project name */
|
|
22
|
+
projectName?: string;
|
|
23
|
+
/** Logo component */
|
|
24
|
+
logo?: React.ReactNode;
|
|
25
|
+
/** Is mobile view */
|
|
26
|
+
isMobile: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Sidebar Content
|
|
31
|
+
* Contains logo (desktop only), navigation, and footer
|
|
32
|
+
*/
|
|
33
|
+
export function SidebarContent({
|
|
34
|
+
categories,
|
|
35
|
+
currentCategory,
|
|
36
|
+
onCategoryChange,
|
|
37
|
+
projectName,
|
|
38
|
+
logo,
|
|
39
|
+
isMobile,
|
|
40
|
+
}: SidebarContentProps) {
|
|
41
|
+
return (
|
|
42
|
+
<div
|
|
43
|
+
className={cn(
|
|
44
|
+
'flex flex-col overflow-hidden',
|
|
45
|
+
isMobile ? 'h-full' : 'h-screen'
|
|
46
|
+
)}
|
|
47
|
+
>
|
|
48
|
+
{/* Logo - Desktop only */}
|
|
49
|
+
{!isMobile && (
|
|
50
|
+
<div className="flex h-14 items-center border-b px-6 gap-2 flex-shrink-0">
|
|
51
|
+
{logo}
|
|
52
|
+
<span className="font-semibold text-sm">{projectName}</span>
|
|
53
|
+
</div>
|
|
54
|
+
)}
|
|
55
|
+
|
|
56
|
+
{/* Navigation */}
|
|
57
|
+
<div className="flex-1 overflow-y-auto scrollbar-thin">
|
|
58
|
+
<div className="p-4">
|
|
59
|
+
<nav>
|
|
60
|
+
{/* Section Header */}
|
|
61
|
+
<div className="mb-4">
|
|
62
|
+
<h3 className="mb-2 px-2 text-xs font-semibold text-muted-foreground uppercase tracking-wider">
|
|
63
|
+
Component Categories
|
|
64
|
+
</h3>
|
|
65
|
+
</div>
|
|
66
|
+
|
|
67
|
+
{/* Categories List */}
|
|
68
|
+
<div className="space-y-1">
|
|
69
|
+
{categories.map((category) => (
|
|
70
|
+
<SidebarCategory
|
|
71
|
+
key={category.id}
|
|
72
|
+
category={category}
|
|
73
|
+
active={currentCategory === category.id}
|
|
74
|
+
onClick={() => onCategoryChange?.(category.id)}
|
|
75
|
+
/>
|
|
76
|
+
))}
|
|
77
|
+
</div>
|
|
78
|
+
</nav>
|
|
79
|
+
</div>
|
|
80
|
+
</div>
|
|
81
|
+
|
|
82
|
+
{/* Footer */}
|
|
83
|
+
<SidebarFooter />
|
|
84
|
+
</div>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SidebarFooter Component
|
|
3
|
+
* Footer section with Tailwind CSS v4 information
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Sidebar Footer
|
|
12
|
+
* Displays Tailwind CSS v4 features and benefits
|
|
13
|
+
*/
|
|
14
|
+
export function SidebarFooter() {
|
|
15
|
+
return (
|
|
16
|
+
<div className="border-t p-4 bg-muted/30">
|
|
17
|
+
{/* Header */}
|
|
18
|
+
<div className="mb-3">
|
|
19
|
+
<h4 className="text-xs font-semibold text-foreground uppercase tracking-wider mb-2">
|
|
20
|
+
Tailwind CSS v4
|
|
21
|
+
</h4>
|
|
22
|
+
<p className="text-xs text-muted-foreground leading-relaxed mb-3">
|
|
23
|
+
This UI library uses Tailwind CSS v4 with CSS-first configuration
|
|
24
|
+
</p>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
{/* Features List */}
|
|
28
|
+
<div className="space-y-2 text-xs">
|
|
29
|
+
<FeatureItem text="CSS-first @theme configuration" />
|
|
30
|
+
<FeatureItem text="10x faster build times" />
|
|
31
|
+
<FeatureItem text="Modern CSS features (color-mix, @property)" />
|
|
32
|
+
<FeatureItem text="Responsive: px-4 sm:px-6 lg:px-8" />
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Feature Item
|
|
40
|
+
* Single feature in the footer list
|
|
41
|
+
*/
|
|
42
|
+
function FeatureItem({ text }: { text: string }) {
|
|
43
|
+
return (
|
|
44
|
+
<div className="flex items-start gap-2">
|
|
45
|
+
<span className="text-primary mt-0.5">✓</span>
|
|
46
|
+
<span className="text-muted-foreground">{text}</span>
|
|
47
|
+
</div>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sidebar Components
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { Sidebar } from './Sidebar';
|
|
6
|
+
export type { SidebarProps } from './Sidebar';
|
|
7
|
+
export { SidebarContent } from './SidebarContent';
|
|
8
|
+
export { SidebarCategory } from './SidebarCategory';
|
|
9
|
+
export { SidebarFooter } from './SidebarFooter';
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CountBadge Component
|
|
3
|
+
* Badge for displaying count numbers
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { cn } from '@djangocfg/ui/lib';
|
|
10
|
+
|
|
11
|
+
interface CountBadgeProps {
|
|
12
|
+
/** Count to display */
|
|
13
|
+
count: number;
|
|
14
|
+
/** Is active/selected */
|
|
15
|
+
active?: boolean;
|
|
16
|
+
/** Custom class name */
|
|
17
|
+
className?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Count Badge
|
|
22
|
+
* Displays a count number with optional active state
|
|
23
|
+
*/
|
|
24
|
+
export function CountBadge({ count, active = false, className }: CountBadgeProps) {
|
|
25
|
+
return (
|
|
26
|
+
<span
|
|
27
|
+
className={cn(
|
|
28
|
+
'text-xs px-2 py-0.5 rounded-full flex-shrink-0',
|
|
29
|
+
active
|
|
30
|
+
? 'bg-primary-foreground/20 text-primary-foreground'
|
|
31
|
+
: 'bg-muted text-muted-foreground',
|
|
32
|
+
className
|
|
33
|
+
)}
|
|
34
|
+
>
|
|
35
|
+
{count}
|
|
36
|
+
</span>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CodeBlock Component
|
|
3
|
+
* Displays code with syntax highlighting and copy button
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { cn } from '@djangocfg/ui/lib';
|
|
10
|
+
import { CopyButton } from './CopyButton';
|
|
11
|
+
|
|
12
|
+
interface CodeBlockProps {
|
|
13
|
+
/** Code to display */
|
|
14
|
+
code: string;
|
|
15
|
+
/** Programming language */
|
|
16
|
+
language?: string;
|
|
17
|
+
/** Show copy button */
|
|
18
|
+
showCopy?: boolean;
|
|
19
|
+
/** Custom class name */
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Code Block
|
|
25
|
+
* Displays formatted code with optional copy functionality
|
|
26
|
+
*/
|
|
27
|
+
export function CodeBlock({
|
|
28
|
+
code,
|
|
29
|
+
language = 'tsx',
|
|
30
|
+
showCopy = true,
|
|
31
|
+
className,
|
|
32
|
+
}: CodeBlockProps) {
|
|
33
|
+
return (
|
|
34
|
+
<div className={cn('relative group', className)}>
|
|
35
|
+
{/* Copy Button */}
|
|
36
|
+
{showCopy && (
|
|
37
|
+
<div className="absolute right-2 top-2 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
38
|
+
<CopyButton code={code} />
|
|
39
|
+
</div>
|
|
40
|
+
)}
|
|
41
|
+
|
|
42
|
+
{/* Code Display */}
|
|
43
|
+
<pre className="bg-muted/50 rounded-lg p-4 overflow-x-auto">
|
|
44
|
+
<code className={`language-${language} text-sm`}>{code}</code>
|
|
45
|
+
</pre>
|
|
46
|
+
</div>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CopyButton Component
|
|
3
|
+
* Button for copying code to clipboard
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
import React, { useState } from 'react';
|
|
9
|
+
import { Button } from '@djangocfg/ui';
|
|
10
|
+
import { Copy, Check } from 'lucide-react';
|
|
11
|
+
import { useCopy } from '@djangocfg/ui';
|
|
12
|
+
|
|
13
|
+
interface CopyButtonProps {
|
|
14
|
+
/** Code to copy */
|
|
15
|
+
code: string;
|
|
16
|
+
/** Button size */
|
|
17
|
+
size?: 'sm' | 'default' | 'lg';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Copy Button
|
|
22
|
+
* Shows check icon after successful copy
|
|
23
|
+
*/
|
|
24
|
+
export function CopyButton({ code, size = 'sm' }: CopyButtonProps) {
|
|
25
|
+
const [copied, setCopied] = useState(false);
|
|
26
|
+
const { copyToClipboard } = useCopy();
|
|
27
|
+
|
|
28
|
+
const handleCopy = async () => {
|
|
29
|
+
await copyToClipboard(code);
|
|
30
|
+
setCopied(true);
|
|
31
|
+
setTimeout(() => setCopied(false), 2000);
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Button
|
|
36
|
+
variant="ghost"
|
|
37
|
+
size={size}
|
|
38
|
+
onClick={handleCopy}
|
|
39
|
+
className="h-8 w-8 p-0"
|
|
40
|
+
aria-label="Copy code"
|
|
41
|
+
>
|
|
42
|
+
{copied ? (
|
|
43
|
+
<Check className="h-4 w-4 text-green-500" />
|
|
44
|
+
) : (
|
|
45
|
+
<Copy className="h-4 w-4" />
|
|
46
|
+
)}
|
|
47
|
+
</Button>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Section Component
|
|
3
|
+
* Reusable section with title and optional description
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
'use client';
|
|
7
|
+
|
|
8
|
+
import React, { ReactNode } from 'react';
|
|
9
|
+
import { cn } from '@djangocfg/ui/lib';
|
|
10
|
+
|
|
11
|
+
interface SectionProps {
|
|
12
|
+
/** Section title */
|
|
13
|
+
title?: string;
|
|
14
|
+
/** Section description */
|
|
15
|
+
description?: string;
|
|
16
|
+
/** Section content */
|
|
17
|
+
children: ReactNode;
|
|
18
|
+
/** Custom class name */
|
|
19
|
+
className?: string;
|
|
20
|
+
/** Title class name */
|
|
21
|
+
titleClassName?: string;
|
|
22
|
+
/** Content class name */
|
|
23
|
+
contentClassName?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Section Component
|
|
28
|
+
* Displays a section with optional title and description
|
|
29
|
+
*/
|
|
30
|
+
export function Section({
|
|
31
|
+
title,
|
|
32
|
+
description,
|
|
33
|
+
children,
|
|
34
|
+
className,
|
|
35
|
+
titleClassName,
|
|
36
|
+
contentClassName,
|
|
37
|
+
}: SectionProps) {
|
|
38
|
+
return (
|
|
39
|
+
<section className={cn('space-y-4', className)}>
|
|
40
|
+
{/* Header */}
|
|
41
|
+
{(title || description) && (
|
|
42
|
+
<div className="space-y-1">
|
|
43
|
+
{title && (
|
|
44
|
+
<h2
|
|
45
|
+
className={cn(
|
|
46
|
+
'text-2xl font-bold tracking-tight',
|
|
47
|
+
titleClassName
|
|
48
|
+
)}
|
|
49
|
+
>
|
|
50
|
+
{title}
|
|
51
|
+
</h2>
|
|
52
|
+
)}
|
|
53
|
+
{description && (
|
|
54
|
+
<p className="text-sm text-muted-foreground">{description}</p>
|
|
55
|
+
)}
|
|
56
|
+
</div>
|
|
57
|
+
)}
|
|
58
|
+
|
|
59
|
+
{/* Content */}
|
|
60
|
+
<div className={contentClassName}>{children}</div>
|
|
61
|
+
</section>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
PaginationNext,
|
|
30
30
|
PaginationPrevious,
|
|
31
31
|
SSRPagination,
|
|
32
|
+
StaticPagination,
|
|
32
33
|
} from '@djangocfg/ui';
|
|
33
34
|
import type { ComponentConfig } from './types';
|
|
34
35
|
|
|
@@ -291,4 +292,32 @@ export const NAVIGATION_COMPONENTS: ComponentConfig[] = [
|
|
|
291
292
|
/>
|
|
292
293
|
),
|
|
293
294
|
},
|
|
295
|
+
{
|
|
296
|
+
name: 'StaticPagination',
|
|
297
|
+
category: 'navigation',
|
|
298
|
+
description: 'Client-side pagination component for static builds with callback support',
|
|
299
|
+
importPath: `import { StaticPagination } from '@djangocfg/ui';`,
|
|
300
|
+
example: `const [currentPage, setCurrentPage] = useState(1);
|
|
301
|
+
|
|
302
|
+
<StaticPagination
|
|
303
|
+
currentPage={currentPage}
|
|
304
|
+
totalPages={10}
|
|
305
|
+
totalItems={100}
|
|
306
|
+
itemsPerPage={10}
|
|
307
|
+
hasNextPage={currentPage < 10}
|
|
308
|
+
hasPreviousPage={currentPage > 1}
|
|
309
|
+
onPageChange={(page) => setCurrentPage(page)}
|
|
310
|
+
/>`,
|
|
311
|
+
preview: (
|
|
312
|
+
<StaticPagination
|
|
313
|
+
currentPage={2}
|
|
314
|
+
totalPages={10}
|
|
315
|
+
totalItems={100}
|
|
316
|
+
itemsPerPage={10}
|
|
317
|
+
hasNextPage={true}
|
|
318
|
+
hasPreviousPage={true}
|
|
319
|
+
onPageChange={(page) => console.log('Page changed to:', page)}
|
|
320
|
+
/>
|
|
321
|
+
),
|
|
322
|
+
},
|
|
294
323
|
];
|
|
@@ -8,11 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
import React from 'react';
|
|
10
10
|
import { UILayout } from './UILayout';
|
|
11
|
-
import { CATEGORIES } from '
|
|
12
|
-
import { ShowcaseProvider, useShowcase } from '
|
|
11
|
+
import { CATEGORIES } from '../config';
|
|
12
|
+
import { ShowcaseProvider, useShowcase } from '../context';
|
|
13
13
|
import { UIGuideLanding } from './UIGuideLanding';
|
|
14
|
-
import { CategoryRenderer } from '
|
|
15
|
-
import { TailwindGuideRenderer } from '
|
|
14
|
+
import { CategoryRenderer } from '../components/CategoryRenderer';
|
|
15
|
+
import { TailwindGuideRenderer } from '../components/TailwindGuideRenderer';
|
|
16
16
|
|
|
17
17
|
function UIGuideContent() {
|
|
18
18
|
const { currentCategory, setCurrentCategory } = useShowcase();
|
|
@@ -5,25 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
'use client';
|
|
7
7
|
|
|
8
|
-
import React
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import type { ComponentCategory } from './config';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
import { Sidebar } from '../components/layout/Sidebar';
|
|
10
|
+
import { Header } from '../components/layout/Header';
|
|
11
|
+
import { MobileOverlay } from '../components/layout/MobileOverlay';
|
|
12
|
+
import { generateAIContext } from '../config';
|
|
13
|
+
import { useShowcase } from '../context';
|
|
14
|
+
import type { UILayoutProps } from '../types';
|
|
16
15
|
|
|
17
|
-
export interface UILayoutProps {
|
|
18
|
-
children: ReactNode;
|
|
19
|
-
title?: string;
|
|
20
|
-
description?: string;
|
|
21
|
-
categories: ComponentCategory[];
|
|
22
|
-
currentCategory?: string;
|
|
23
|
-
onCategoryChange?: (categoryId: string) => void;
|
|
24
|
-
logo?: ReactNode;
|
|
25
|
-
projectName?: string;
|
|
26
|
-
}
|
|
27
16
|
|
|
28
17
|
/**
|
|
29
18
|
* UILayout - Main layout component for UI documentation
|
|
@@ -57,7 +46,6 @@ export function UILayout({
|
|
|
57
46
|
projectName = "Django CFG UI",
|
|
58
47
|
}: UILayoutProps) {
|
|
59
48
|
const { isSidebarOpen, toggleSidebar, closeSidebar } = useShowcase();
|
|
60
|
-
const { copyToClipboard } = useCopy();
|
|
61
49
|
|
|
62
50
|
const handleCategoryChange = (categoryId: string) => {
|
|
63
51
|
onCategoryChange?.(categoryId);
|
|
@@ -65,8 +53,7 @@ export function UILayout({
|
|
|
65
53
|
};
|
|
66
54
|
|
|
67
55
|
const handleCopyForAI = () => {
|
|
68
|
-
|
|
69
|
-
copyToClipboard(aiContext);
|
|
56
|
+
return generateAIContext();
|
|
70
57
|
};
|
|
71
58
|
|
|
72
59
|
return (
|
|
@@ -116,7 +103,3 @@ export function UILayout({
|
|
|
116
103
|
</div>
|
|
117
104
|
);
|
|
118
105
|
}
|
|
119
|
-
|
|
120
|
-
// Legacy export for backward compatibility
|
|
121
|
-
export { UILayout as ComponentShowcaseLayout };
|
|
122
|
-
export type { UILayoutProps as ComponentShowcaseLayoutProps };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UILayout Hooks
|
|
3
|
+
* Custom hooks for UILayout functionality
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export { useSidebarState } from './useSidebarState';
|
|
7
|
+
export { useAIExport } from './useAIExport';
|
|
8
|
+
export { useCategoryNavigation } from './useCategoryNavigation';
|
|
9
|
+
export { useComponentSearch } from './useComponentSearch';
|