@djangocfg/layouts 1.2.17 → 1.2.19
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 +53 -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 -8
- package/src/layouts/UILayout/context/ShowcaseContext.tsx +1 -11
- package/src/layouts/UILayout/{UIGuideLanding.tsx → core/UIGuideLanding.tsx} +1 -1
- package/src/layouts/UILayout/{UIGuideView.tsx → core/UIGuideView.tsx} +6 -6
- package/src/layouts/UILayout/core/UILayout.tsx +102 -0
- 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 +57 -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/UILayout.tsx +0 -122
- 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
|
+
}
|
|
@@ -147,12 +147,13 @@ export const NAVIGATION_COMPONENTS: ComponentConfig[] = [
|
|
|
147
147
|
{
|
|
148
148
|
name: 'Tabs',
|
|
149
149
|
category: 'navigation',
|
|
150
|
-
description: 'Tab navigation for switching between different views',
|
|
150
|
+
description: 'Tab navigation for switching between different views. Supports mobile sheet mode, sticky positioning, and auto-responsive behavior.',
|
|
151
151
|
importPath: `import { Tabs, TabsContent, TabsList, TabsTrigger } from '@djangocfg/ui';`,
|
|
152
|
-
example:
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
<TabsTrigger value="
|
|
152
|
+
example: `// Basic tabs with equal-width tabs
|
|
153
|
+
<Tabs defaultValue="account" className="w-[400px]">
|
|
154
|
+
<TabsList fullWidth>
|
|
155
|
+
<TabsTrigger value="account" flexEqual>Account</TabsTrigger>
|
|
156
|
+
<TabsTrigger value="password" flexEqual>Password</TabsTrigger>
|
|
156
157
|
</TabsList>
|
|
157
158
|
<TabsContent value="account">
|
|
158
159
|
<div className="p-4 border rounded-md">
|
|
@@ -164,12 +165,32 @@ export const NAVIGATION_COMPONENTS: ComponentConfig[] = [
|
|
|
164
165
|
<p className="text-sm">Change your password here.</p>
|
|
165
166
|
</div>
|
|
166
167
|
</TabsContent>
|
|
168
|
+
</Tabs>
|
|
169
|
+
|
|
170
|
+
// Mobile-responsive tabs with sticky positioning
|
|
171
|
+
<Tabs
|
|
172
|
+
defaultValue="account"
|
|
173
|
+
mobileSheet
|
|
174
|
+
mobileTitleText="Settings"
|
|
175
|
+
mobileSheetTitle="Navigation"
|
|
176
|
+
sticky
|
|
177
|
+
>
|
|
178
|
+
<TabsList fullWidth>
|
|
179
|
+
<TabsTrigger value="account" flexEqual>Account</TabsTrigger>
|
|
180
|
+
<TabsTrigger value="password" flexEqual>Password</TabsTrigger>
|
|
181
|
+
</TabsList>
|
|
182
|
+
<TabsContent value="account">
|
|
183
|
+
Account content
|
|
184
|
+
</TabsContent>
|
|
185
|
+
<TabsContent value="password">
|
|
186
|
+
Password content
|
|
187
|
+
</TabsContent>
|
|
167
188
|
</Tabs>`,
|
|
168
189
|
preview: (
|
|
169
190
|
<Tabs defaultValue="account" className="w-[400px]">
|
|
170
|
-
<TabsList
|
|
171
|
-
<TabsTrigger value="account">Account</TabsTrigger>
|
|
172
|
-
<TabsTrigger value="password">Password</TabsTrigger>
|
|
191
|
+
<TabsList fullWidth>
|
|
192
|
+
<TabsTrigger value="account" flexEqual>Account</TabsTrigger>
|
|
193
|
+
<TabsTrigger value="password" flexEqual>Password</TabsTrigger>
|
|
173
194
|
</TabsList>
|
|
174
195
|
<TabsContent value="account">
|
|
175
196
|
<div className="p-4 border rounded-md">
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Showcase Context
|
|
3
|
-
* Manages navigation state for
|
|
3
|
+
* Manages navigation state for UILayout
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
'use client';
|
|
@@ -10,9 +10,6 @@ import React, { createContext, useContext, useState, ReactNode } from 'react';
|
|
|
10
10
|
interface ShowcaseContextValue {
|
|
11
11
|
currentCategory: string;
|
|
12
12
|
setCurrentCategory: (category: string) => void;
|
|
13
|
-
isSidebarOpen: boolean;
|
|
14
|
-
toggleSidebar: () => void;
|
|
15
|
-
closeSidebar: () => void;
|
|
16
13
|
}
|
|
17
14
|
|
|
18
15
|
const ShowcaseContext = createContext<ShowcaseContextValue | undefined>(undefined);
|
|
@@ -24,19 +21,12 @@ interface ShowcaseProviderProps {
|
|
|
24
21
|
|
|
25
22
|
export function ShowcaseProvider({ children, defaultCategory = 'overview' }: ShowcaseProviderProps) {
|
|
26
23
|
const [currentCategory, setCurrentCategory] = useState(defaultCategory);
|
|
27
|
-
const [isSidebarOpen, setIsSidebarOpen] = useState(false);
|
|
28
|
-
|
|
29
|
-
const toggleSidebar = () => setIsSidebarOpen(prev => !prev);
|
|
30
|
-
const closeSidebar = () => setIsSidebarOpen(false);
|
|
31
24
|
|
|
32
25
|
return (
|
|
33
26
|
<ShowcaseContext.Provider
|
|
34
27
|
value={{
|
|
35
28
|
currentCategory,
|
|
36
29
|
setCurrentCategory,
|
|
37
|
-
isSidebarOpen,
|
|
38
|
-
toggleSidebar,
|
|
39
|
-
closeSidebar,
|
|
40
30
|
}}
|
|
41
31
|
>
|
|
42
32
|
{children}
|
|
@@ -8,16 +8,16 @@
|
|
|
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();
|
|
19
19
|
|
|
20
|
-
// For overview, show
|
|
20
|
+
// For overview, show landing page without layout
|
|
21
21
|
if (currentCategory === 'overview') {
|
|
22
22
|
return <UIGuideLanding />;
|
|
23
23
|
}
|
|
@@ -44,7 +44,7 @@ function UIGuideContent() {
|
|
|
44
44
|
{currentCategory === 'tailwind4' && <TailwindGuideRenderer />}
|
|
45
45
|
|
|
46
46
|
{/* All other categories use CategoryRenderer */}
|
|
47
|
-
{currentCategory !== 'tailwind4' &&
|
|
47
|
+
{currentCategory !== 'tailwind4' && (
|
|
48
48
|
<CategoryRenderer categoryId={currentCategory} />
|
|
49
49
|
)}
|
|
50
50
|
</div>
|