@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,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Navigation Types
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Navigation State
|
|
7
|
+
* Tracks navigation history and current position
|
|
8
|
+
*/
|
|
9
|
+
export interface NavigationState {
|
|
10
|
+
/** Current category ID */
|
|
11
|
+
currentCategory: string;
|
|
12
|
+
/** Navigation history */
|
|
13
|
+
history: string[];
|
|
14
|
+
/** Can navigate back */
|
|
15
|
+
canGoBack: boolean;
|
|
16
|
+
/** Can navigate forward */
|
|
17
|
+
canGoForward: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Sidebar State
|
|
22
|
+
* Tracks sidebar open/close state
|
|
23
|
+
*/
|
|
24
|
+
export interface SidebarState {
|
|
25
|
+
/** Is sidebar open (mobile) */
|
|
26
|
+
isOpen: boolean;
|
|
27
|
+
/** Toggle sidebar */
|
|
28
|
+
toggle: () => void;
|
|
29
|
+
/** Open sidebar */
|
|
30
|
+
open: () => void;
|
|
31
|
+
/** Close sidebar */
|
|
32
|
+
close: () => void;
|
|
33
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Export Formatters
|
|
3
|
+
* Formatting utilities for AI documentation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ComponentConfig } from '../../types';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Format component as markdown
|
|
10
|
+
*/
|
|
11
|
+
export function formatComponentAsMarkdown(component: ComponentConfig): string {
|
|
12
|
+
return `
|
|
13
|
+
### ${component.name}
|
|
14
|
+
${component.description}
|
|
15
|
+
|
|
16
|
+
\`\`\`tsx
|
|
17
|
+
${component.importPath}
|
|
18
|
+
|
|
19
|
+
${component.example}
|
|
20
|
+
\`\`\`
|
|
21
|
+
`.trim();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Format multiple components as markdown list
|
|
26
|
+
*/
|
|
27
|
+
export function formatComponentsAsMarkdownList(
|
|
28
|
+
components: ComponentConfig[]
|
|
29
|
+
): string {
|
|
30
|
+
return components.map(formatComponentAsMarkdown).join('\n\n');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Format import statements
|
|
35
|
+
*/
|
|
36
|
+
export function formatImports(components: ComponentConfig[]): string {
|
|
37
|
+
const uniqueImports = new Set(components.map((c) => c.importPath));
|
|
38
|
+
return Array.from(uniqueImports).join('\n');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Format code block
|
|
43
|
+
*/
|
|
44
|
+
export function formatCodeBlock(code: string, language = 'tsx'): string {
|
|
45
|
+
return `\`\`\`${language}\n${code}\n\`\`\``;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Format section header
|
|
50
|
+
*/
|
|
51
|
+
export function formatSectionHeader(
|
|
52
|
+
title: string,
|
|
53
|
+
level: number = 2,
|
|
54
|
+
count?: number
|
|
55
|
+
): string {
|
|
56
|
+
const hashes = '#'.repeat(level);
|
|
57
|
+
const countStr = count !== undefined ? ` (${count})` : '';
|
|
58
|
+
return `${hashes} ${title}${countStr}`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Format list
|
|
63
|
+
*/
|
|
64
|
+
export function formatList(items: string[], ordered = false): string {
|
|
65
|
+
return items
|
|
66
|
+
.map((item, index) => {
|
|
67
|
+
const prefix = ordered ? `${index + 1}. ` : '- ';
|
|
68
|
+
return `${prefix}${item}`;
|
|
69
|
+
})
|
|
70
|
+
.join('\n');
|
|
71
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Export Generators
|
|
3
|
+
* Functions for generating AI-friendly documentation
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { ComponentConfig } from '../../types';
|
|
7
|
+
import type { TailwindGuide } from '../../config/tailwind.config';
|
|
8
|
+
|
|
9
|
+
export interface UILibraryConfig {
|
|
10
|
+
projectName: string;
|
|
11
|
+
version: string;
|
|
12
|
+
description: string;
|
|
13
|
+
totalComponents: number;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Generate complete AI context from components and config
|
|
18
|
+
*/
|
|
19
|
+
export function generateAIContext(
|
|
20
|
+
components: ComponentConfig[],
|
|
21
|
+
tailwindGuide: TailwindGuide,
|
|
22
|
+
config: UILibraryConfig
|
|
23
|
+
): string {
|
|
24
|
+
const { projectName, version, description } = config;
|
|
25
|
+
|
|
26
|
+
let output = `# ${projectName} v${version}\n\n`;
|
|
27
|
+
output += `${description}\n\n`;
|
|
28
|
+
|
|
29
|
+
// Tailwind Guide Section
|
|
30
|
+
output += generateTailwindSection(tailwindGuide);
|
|
31
|
+
|
|
32
|
+
// Components by Category
|
|
33
|
+
output += generateComponentsSection(components);
|
|
34
|
+
|
|
35
|
+
return output;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Generate Tailwind CSS section
|
|
40
|
+
*/
|
|
41
|
+
export function generateTailwindSection(guide: TailwindGuide): string {
|
|
42
|
+
let output = `## Tailwind CSS v${guide.version} Guidelines\n\n`;
|
|
43
|
+
|
|
44
|
+
output += `### Key Changes\n`;
|
|
45
|
+
guide.keyChanges.forEach((change) => {
|
|
46
|
+
output += `- ${change}\n`;
|
|
47
|
+
});
|
|
48
|
+
output += `\n`;
|
|
49
|
+
|
|
50
|
+
output += `### Best Practices\n`;
|
|
51
|
+
guide.bestPractices.forEach((practice) => {
|
|
52
|
+
output += `- ${practice}\n`;
|
|
53
|
+
});
|
|
54
|
+
output += `\n`;
|
|
55
|
+
|
|
56
|
+
output += `### Migration Steps\n`;
|
|
57
|
+
guide.migrationSteps.forEach((step, index) => {
|
|
58
|
+
output += `${index + 1}. ${step}\n`;
|
|
59
|
+
});
|
|
60
|
+
output += `\n`;
|
|
61
|
+
|
|
62
|
+
output += `### Examples\n\n`;
|
|
63
|
+
guide.examples.forEach((example) => {
|
|
64
|
+
output += `#### ${example.title}\n`;
|
|
65
|
+
output += `${example.description}\n\n`;
|
|
66
|
+
output += `\`\`\`css\n${example.code}\n\`\`\`\n\n`;
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
return output;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generate components section grouped by category
|
|
74
|
+
*/
|
|
75
|
+
export function generateComponentsSection(components: ComponentConfig[]): string {
|
|
76
|
+
let output = '';
|
|
77
|
+
|
|
78
|
+
// Group components by category
|
|
79
|
+
const byCategory = groupByCategory(components);
|
|
80
|
+
|
|
81
|
+
// Generate section for each category
|
|
82
|
+
Object.entries(byCategory).forEach(([category, comps]) => {
|
|
83
|
+
output += `## ${formatCategoryName(category)} (${comps.length})\n\n`;
|
|
84
|
+
|
|
85
|
+
comps.forEach((comp) => {
|
|
86
|
+
output += generateComponentDoc(comp);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
return output;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generate documentation for a single component
|
|
95
|
+
*/
|
|
96
|
+
export function generateComponentDoc(component: ComponentConfig): string {
|
|
97
|
+
let output = `### ${component.name}\n`;
|
|
98
|
+
output += `${component.description}\n\n`;
|
|
99
|
+
output += `\`\`\`tsx\n`;
|
|
100
|
+
output += `${component.importPath}\n\n`;
|
|
101
|
+
output += `${component.example}\n`;
|
|
102
|
+
output += `\`\`\`\n\n`;
|
|
103
|
+
|
|
104
|
+
return output;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Group components by category
|
|
109
|
+
*/
|
|
110
|
+
function groupByCategory(
|
|
111
|
+
components: ComponentConfig[]
|
|
112
|
+
): Record<string, ComponentConfig[]> {
|
|
113
|
+
return components.reduce(
|
|
114
|
+
(acc, comp) => {
|
|
115
|
+
if (!acc[comp.category]) {
|
|
116
|
+
acc[comp.category] = [];
|
|
117
|
+
}
|
|
118
|
+
acc[comp.category].push(comp);
|
|
119
|
+
return acc;
|
|
120
|
+
},
|
|
121
|
+
{} as Record<string, ComponentConfig[]>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Format category name for display
|
|
127
|
+
*/
|
|
128
|
+
function formatCategoryName(category: string): string {
|
|
129
|
+
return category.charAt(0).toUpperCase() + category.slice(1);
|
|
130
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Filter Utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ComponentConfig } from '../../types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Filter options
|
|
9
|
+
*/
|
|
10
|
+
export interface FilterOptions {
|
|
11
|
+
/** Category to filter by */
|
|
12
|
+
category?: string;
|
|
13
|
+
/** Tags to filter by (OR logic) */
|
|
14
|
+
tags?: string[];
|
|
15
|
+
/** Search query */
|
|
16
|
+
query?: string;
|
|
17
|
+
/** Search fields */
|
|
18
|
+
searchFields?: Array<keyof ComponentConfig>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Apply multiple filters to components
|
|
23
|
+
*/
|
|
24
|
+
export function applyFilters(
|
|
25
|
+
components: ComponentConfig[],
|
|
26
|
+
options: FilterOptions
|
|
27
|
+
): ComponentConfig[] {
|
|
28
|
+
let filtered = components;
|
|
29
|
+
|
|
30
|
+
// Filter by category
|
|
31
|
+
if (options.category) {
|
|
32
|
+
filtered = filtered.filter((comp) => comp.category === options.category);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Filter by tags
|
|
36
|
+
if (options.tags && options.tags.length > 0) {
|
|
37
|
+
filtered = filtered.filter((comp) => {
|
|
38
|
+
if (!comp.tags) return false;
|
|
39
|
+
return options.tags!.some((tag) => comp.tags?.includes(tag));
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Filter by search query
|
|
44
|
+
if (options.query && options.query.trim()) {
|
|
45
|
+
const lowerQuery = options.query.toLowerCase();
|
|
46
|
+
const searchFields = options.searchFields || [
|
|
47
|
+
'name',
|
|
48
|
+
'description',
|
|
49
|
+
'category',
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
filtered = filtered.filter((comp) => {
|
|
53
|
+
return searchFields.some((field) => {
|
|
54
|
+
const value = comp[field];
|
|
55
|
+
if (typeof value === 'string') {
|
|
56
|
+
return value.toLowerCase().includes(lowerQuery);
|
|
57
|
+
}
|
|
58
|
+
if (Array.isArray(value)) {
|
|
59
|
+
return value.some((item) =>
|
|
60
|
+
String(item).toLowerCase().includes(lowerQuery)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return filtered;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Sort components by field
|
|
73
|
+
*/
|
|
74
|
+
export function sortComponents(
|
|
75
|
+
components: ComponentConfig[],
|
|
76
|
+
field: keyof ComponentConfig,
|
|
77
|
+
order: 'asc' | 'desc' = 'asc'
|
|
78
|
+
): ComponentConfig[] {
|
|
79
|
+
return [...components].sort((a, b) => {
|
|
80
|
+
const aVal = String(a[field]);
|
|
81
|
+
const bVal = String(b[field]);
|
|
82
|
+
|
|
83
|
+
if (order === 'asc') {
|
|
84
|
+
return aVal.localeCompare(bVal);
|
|
85
|
+
} else {
|
|
86
|
+
return bVal.localeCompare(aVal);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Group components by field
|
|
93
|
+
*/
|
|
94
|
+
export function groupComponentsBy<K extends keyof ComponentConfig>(
|
|
95
|
+
components: ComponentConfig[],
|
|
96
|
+
field: K
|
|
97
|
+
): Record<string, ComponentConfig[]> {
|
|
98
|
+
return components.reduce(
|
|
99
|
+
(acc, comp) => {
|
|
100
|
+
const key = String(comp[field]);
|
|
101
|
+
if (!acc[key]) {
|
|
102
|
+
acc[key] = [];
|
|
103
|
+
}
|
|
104
|
+
acc[key].push(comp);
|
|
105
|
+
return acc;
|
|
106
|
+
},
|
|
107
|
+
{} as Record<string, ComponentConfig[]>
|
|
108
|
+
);
|
|
109
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Component Search Utilities
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { ComponentConfig } from '../../types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Search components by query
|
|
9
|
+
*/
|
|
10
|
+
export function searchComponents(
|
|
11
|
+
components: ComponentConfig[],
|
|
12
|
+
query: string,
|
|
13
|
+
fields: Array<keyof ComponentConfig> = ['name', 'description', 'category']
|
|
14
|
+
): ComponentConfig[] {
|
|
15
|
+
if (!query.trim()) {
|
|
16
|
+
return components;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const lowerQuery = query.toLowerCase();
|
|
20
|
+
|
|
21
|
+
return components.filter((component) => {
|
|
22
|
+
return fields.some((field) => {
|
|
23
|
+
const value = component[field];
|
|
24
|
+
if (typeof value === 'string') {
|
|
25
|
+
return value.toLowerCase().includes(lowerQuery);
|
|
26
|
+
}
|
|
27
|
+
if (Array.isArray(value)) {
|
|
28
|
+
return value.some((item) =>
|
|
29
|
+
String(item).toLowerCase().includes(lowerQuery)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
return false;
|
|
33
|
+
});
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Filter components by category
|
|
39
|
+
*/
|
|
40
|
+
export function filterByCategory(
|
|
41
|
+
components: ComponentConfig[],
|
|
42
|
+
category: string
|
|
43
|
+
): ComponentConfig[] {
|
|
44
|
+
return components.filter((comp) => comp.category === category);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Filter components by tags
|
|
49
|
+
*/
|
|
50
|
+
export function filterByTags(
|
|
51
|
+
components: ComponentConfig[],
|
|
52
|
+
tags: string[]
|
|
53
|
+
): ComponentConfig[] {
|
|
54
|
+
if (tags.length === 0) {
|
|
55
|
+
return components;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return components.filter((comp) => {
|
|
59
|
+
if (!comp.tags) return false;
|
|
60
|
+
return tags.some((tag) => comp.tags?.includes(tag));
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Get unique categories from components
|
|
66
|
+
*/
|
|
67
|
+
export function getUniqueCategories(
|
|
68
|
+
components: ComponentConfig[]
|
|
69
|
+
): string[] {
|
|
70
|
+
const categories = new Set(components.map((c) => c.category));
|
|
71
|
+
return Array.from(categories).sort();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Get unique tags from components
|
|
76
|
+
*/
|
|
77
|
+
export function getUniqueTags(components: ComponentConfig[]): string[] {
|
|
78
|
+
const tags = new Set<string>();
|
|
79
|
+
components.forEach((comp) => {
|
|
80
|
+
comp.tags?.forEach((tag) => tags.add(tag));
|
|
81
|
+
});
|
|
82
|
+
return Array.from(tags).sort();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Find component by name
|
|
87
|
+
*/
|
|
88
|
+
export function findComponentByName(
|
|
89
|
+
components: ComponentConfig[],
|
|
90
|
+
name: string
|
|
91
|
+
): ComponentConfig | undefined {
|
|
92
|
+
return components.find(
|
|
93
|
+
(comp) => comp.name.toLowerCase() === name.toLowerCase()
|
|
94
|
+
);
|
|
95
|
+
}
|