diff --git a/app/(site)/docs/components/button/page.tsx b/app/(site)/docs/components/button/page.tsx index eccbfee..983ec68 100644 --- a/app/(site)/docs/components/button/page.tsx +++ b/app/(site)/docs/components/button/page.tsx @@ -15,7 +15,7 @@ export default function ButtonPage() { { "title": "Variants", "value": "variants", - "content": "import { Button } from \"@nativeui/ui\";\n\nexport default function ButtonVariants() {\n return (\n
\n \n \n \n \n \n \n
\n );\n}", + "content": "import { Button } from \"@nativeui/ui\";\n\nexport default function ButtonVariants() {\n return (\n
\n \n \n \n \n \n \n \n
\n );\n}", "language": "tsx" }, { @@ -25,75 +25,97 @@ export default function ButtonPage() { "language": "tsx" } ]} - componentCode={`import * as React from "react"; + componentCode={`import { cn } from "@/lib/utils"; +import { cva, type VariantProps } from "class-variance-authority"; +import * as React from "react"; import { Pressable, - PressableProps as RNPressableProps, + type PressableStateCallbackType, + type PressableProps as RNPressableProps, View, - ViewStyle, - PressableStateCallbackType, + type ViewStyle, } from "react-native"; -import { cn } from "@/lib/utils"; - -import { cva, type VariantProps } from "class-variance-authority"; export const buttonVariants = cva( - "flex-row items-center justify-center rounded-md", - { - variants: { - variant: { - default: - "bg-primary text-primary-foreground dark:bg-primary dark:text-primary-foreground shadow", - destructive: - "bg-destructive text-destructive-foreground dark:bg-destructive dark:text-destructive-foreground shadow-sm", - outline: - "border border-input bg-background text-foreground dark:border-input dark:bg-background dark:text-foreground shadow-sm", - secondary: - "bg-secondary text-secondary-foreground dark:bg-secondary dark:text-secondary-foreground shadow-sm", - ghost: "text-foreground dark:text-foreground", - link: "text-primary dark:text-primary underline", - }, - size: { - default: "h-12 px-6", - sm: "h-10 px-4", - lg: "h-14 px-8", - icon: "h-12 w-12", - }, - }, - defaultVariants: { - variant: "default", - size: "default", - }, - } + "flex-row items-center justify-center rounded-lg", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground shadow-sm", + destructive: "bg-destructive text-destructive-foreground shadow-sm", + outline: "border-2 border-border bg-background text-foreground", + secondary: "bg-secondary text-secondary-foreground shadow-sm", + ghost: "text-foreground", + link: "text-primary underline", + selection: "border-2 border-border bg-background", + }, + size: { + default: "h-12 px-4", + sm: "h-10 px-3", + lg: "h-14 px-6", + icon: "h-12 w-12", + }, + selected: { + true: "", + false: "", + }, + }, + compoundVariants: [ + { + variant: "selection", + selected: true, + className: "border-primary bg-primary/5", + }, + { + variant: "outline", + selected: true, + className: "border-primary ring-1 ring-primary/20", + }, + ], + defaultVariants: { + variant: "default", + size: "default", + selected: false, + }, + }, ); export interface ButtonProps - extends Omit, - VariantProps { - className?: string; - style?: ViewStyle; - asChild?: boolean; + extends Omit, + VariantProps { + className?: string; + style?: ViewStyle; + asChild?: boolean; + selected?: boolean; } const Button = React.forwardRef( - ({ className, variant, size, asChild = false, children, ...props }, ref) => { - return ( - - {(state: PressableStateCallbackType) => ( - - {typeof children === "function" ? children(state) : children} - - )} - - ); - } + ( + { className, variant, size, selected, asChild = false, children, ...props }, + ref, + ) => { + const [isPressed, setIsPressed] = React.useState(false); + + return ( + setIsPressed(true)} + onPressOut={() => setIsPressed(false)} + {...props} + > + {(state: PressableStateCallbackType) => ( + + {typeof children === "function" ? children(state) : children} + + )} + + ); + }, ); Button.displayName = "Button"; diff --git a/app/(site)/docs/components/input/page.tsx b/app/(site)/docs/components/input/page.tsx index 94bf1d5..5925606 100644 --- a/app/(site)/docs/components/input/page.tsx +++ b/app/(site)/docs/components/input/page.tsx @@ -13,37 +13,40 @@ export default function InputPage() { "language": "tsx" } ]} - componentCode={`import * as React from "react" -import { TextInput, Platform } from "react-native" -import { cn } from "@/lib/utils" + componentCode={`import { cn } from "@/lib/utils"; +import * as React from "react"; +import { Platform, TextInput } from "react-native"; -const Input = React.forwardRef>( - ({ className, ...props }, ref) => { - const [isFocused, setIsFocused] = React.useState(false) +const Input = React.forwardRef< + TextInput, + React.ComponentProps +>(({ className, ...props }, ref) => { + const [isFocused, setIsFocused] = React.useState(false); - return ( - setIsFocused(true)} - onBlur={() => setIsFocused(false)} - {...props} - /> - ) - } -) + return ( + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + {...props} + /> + ); +}); -Input.displayName = "Input" +Input.displayName = "Input"; -export { Input } +export { Input }; `} previewCode={`import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; diff --git a/app/(site)/docs/layout.tsx b/app/(site)/docs/layout.tsx index d8b0535..af6d0c9 100644 --- a/app/(site)/docs/layout.tsx +++ b/app/(site)/docs/layout.tsx @@ -4,7 +4,7 @@ import React from "react"; import Link from "next/link"; import { usePathname } from "next/navigation"; import { cn } from "@/lib/utils"; -import registry from "@/registry.json"; +import registry from "@/public/r/registry.json"; const docsConfig = { sidebarNav: [ diff --git a/components/command-menu.tsx b/components/command-menu.tsx index 910515e..15bd5e5 100644 --- a/components/command-menu.tsx +++ b/components/command-menu.tsx @@ -9,7 +9,7 @@ import { CommandItem, CommandList, } from "@/components/ui/command"; -import registry from "@/registry.json"; +import registry from "@/public/r/registry.json"; export function CommandMenu() { const [open, setOpen] = React.useState(false); diff --git a/public/r/button.json b/public/r/button.json index 2a74b41..1099412 100644 --- a/public/r/button.json +++ b/public/r/button.json @@ -11,7 +11,7 @@ "files": [ { "path": "registry/button/button.tsx", - "content": "import * as React from \"react\";\nimport {\n Pressable,\n PressableProps as RNPressableProps,\n View,\n ViewStyle,\n PressableStateCallbackType,\n} from \"react-native\";\nimport { cn } from \"@/lib/utils\";\n\nimport { cva, type VariantProps } from \"class-variance-authority\";\n\nexport const buttonVariants = cva(\n \"flex-row items-center justify-center rounded-md\",\n {\n variants: {\n variant: {\n default:\n \"bg-primary text-primary-foreground dark:bg-primary dark:text-primary-foreground shadow\",\n destructive:\n \"bg-destructive text-destructive-foreground dark:bg-destructive dark:text-destructive-foreground shadow-sm\",\n outline:\n \"border border-input bg-background text-foreground dark:border-input dark:bg-background dark:text-foreground shadow-sm\",\n secondary:\n \"bg-secondary text-secondary-foreground dark:bg-secondary dark:text-secondary-foreground shadow-sm\",\n ghost: \"text-foreground dark:text-foreground\",\n link: \"text-primary dark:text-primary underline\",\n },\n size: {\n default: \"h-12 px-6\",\n sm: \"h-10 px-4\",\n lg: \"h-14 px-8\",\n icon: \"h-12 w-12\",\n },\n },\n defaultVariants: {\n variant: \"default\",\n size: \"default\",\n },\n }\n);\n\nexport interface ButtonProps\n extends Omit,\n VariantProps {\n className?: string;\n style?: ViewStyle;\n asChild?: boolean;\n}\n\nconst Button = React.forwardRef(\n ({ className, variant, size, asChild = false, children, ...props }, ref) => {\n return (\n \n {(state: PressableStateCallbackType) => (\n \n {typeof children === \"function\" ? children(state) : children}\n \n )}\n \n );\n }\n);\n\nButton.displayName = \"Button\";\n\nexport { Button };\n", + "content": "import { cn } from \"@/lib/utils\";\nimport { cva, type VariantProps } from \"class-variance-authority\";\nimport * as React from \"react\";\nimport {\n Pressable,\n type PressableStateCallbackType,\n type PressableProps as RNPressableProps,\n View,\n type ViewStyle,\n} from \"react-native\";\n\nexport const buttonVariants = cva(\n\t\"flex-row items-center justify-center rounded-lg\",\n\t{\n\t\tvariants: {\n\t\t\tvariant: {\n\t\t\t\tdefault: \"bg-primary text-primary-foreground shadow-sm\",\n\t\t\t\tdestructive: \"bg-destructive text-destructive-foreground shadow-sm\",\n\t\t\t\toutline: \"border-2 border-border bg-background text-foreground\",\n\t\t\t\tsecondary: \"bg-secondary text-secondary-foreground shadow-sm\",\n\t\t\t\tghost: \"text-foreground\",\n\t\t\t\tlink: \"text-primary underline\",\n\t\t\t\tselection: \"border-2 border-border bg-background\",\n\t\t\t},\n\t\t\tsize: {\n\t\t\t\tdefault: \"h-12 px-4\",\n\t\t\t\tsm: \"h-10 px-3\",\n\t\t\t\tlg: \"h-14 px-6\",\n\t\t\t\ticon: \"h-12 w-12\",\n\t\t\t},\n\t\t\tselected: {\n\t\t\t\ttrue: \"\",\n\t\t\t\tfalse: \"\",\n\t\t\t},\n\t\t},\n\t\tcompoundVariants: [\n\t\t\t{\n\t\t\t\tvariant: \"selection\",\n\t\t\t\tselected: true,\n\t\t\t\tclassName: \"border-primary bg-primary/5\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tvariant: \"outline\",\n\t\t\t\tselected: true,\n\t\t\t\tclassName: \"border-primary ring-1 ring-primary/20\",\n\t\t\t},\n\t\t],\n\t\tdefaultVariants: {\n\t\t\tvariant: \"default\",\n\t\t\tsize: \"default\",\n\t\t\tselected: false,\n\t\t},\n\t},\n);\n\nexport interface ButtonProps\n\textends Omit,\n\t\tVariantProps {\n\tclassName?: string;\n\tstyle?: ViewStyle;\n\tasChild?: boolean;\n\tselected?: boolean;\n}\n\nconst Button = React.forwardRef(\n\t(\n\t\t{ className, variant, size, selected, asChild = false, children, ...props },\n\t\tref,\n\t) => {\n\t\tconst [isPressed, setIsPressed] = React.useState(false);\n\n\t\treturn (\n\t\t\t setIsPressed(true)}\n\t\t\t\tonPressOut={() => setIsPressed(false)}\n\t\t\t\t{...props}\n\t\t\t>\n\t\t\t\t{(state: PressableStateCallbackType) => (\n\t\t\t\t\t\n\t\t\t\t\t\t{typeof children === \"function\" ? children(state) : children}\n\t\t\t\t\t\n\t\t\t\t)}\n\t\t\t\n\t\t);\n\t},\n);\n\nButton.displayName = \"Button\";\n\nexport { Button };\n", "type": "registry:ui" } ], diff --git a/public/r/input.json b/public/r/input.json index e220cd8..32e5161 100644 --- a/public/r/input.json +++ b/public/r/input.json @@ -9,7 +9,7 @@ "files": [ { "path": "registry/input/input.tsx", - "content": "import * as React from \"react\"\nimport { TextInput, Platform } from \"react-native\"\nimport { cn } from \"@/lib/utils\"\n\nconst Input = React.forwardRef>(\n ({ className, ...props }, ref) => {\n const [isFocused, setIsFocused] = React.useState(false)\n\n return (\n setIsFocused(true)}\n onBlur={() => setIsFocused(false)}\n {...props}\n />\n )\n }\n)\n\nInput.displayName = \"Input\"\n\nexport { Input }\n", + "content": "import { cn } from \"@/lib/utils\";\nimport * as React from \"react\";\nimport { Platform, TextInput } from \"react-native\";\n\nconst Input = React.forwardRef<\n\tTextInput,\n\tReact.ComponentProps\n>(({ className, ...props }, ref) => {\n\tconst [isFocused, setIsFocused] = React.useState(false);\n\n\treturn (\n\t\t setIsFocused(true)}\n\t\t\tonBlur={() => setIsFocused(false)}\n\t\t\t{...props}\n\t\t/>\n\t);\n});\n\nInput.displayName = \"Input\";\n\nexport { Input };\n", "type": "registry:ui" } ], diff --git a/registry.json b/public/r/registry.json similarity index 100% rename from registry.json rename to public/r/registry.json diff --git a/registry/CONTRIBUTING.md b/registry/CONTRIBUTING.md index 47ecc84..85cb3cf 100644 --- a/registry/CONTRIBUTING.md +++ b/registry/CONTRIBUTING.md @@ -61,7 +61,7 @@ registry/ After creating your component files, you need to add the component to the registry: 1. Create a JSON file in `public/r/[component-name].json` -2. Add the component entry to `registry.json` +2. Add the component entry to `public/r/registry.json` ### Generate component files diff --git a/scripts/generate-component-docs.js b/scripts/generate-component-docs.js index e22aa3c..d5d820e 100644 --- a/scripts/generate-component-docs.js +++ b/scripts/generate-component-docs.js @@ -10,34 +10,38 @@ if (!fs.existsSync(DOCS_COMPONENTS_DIR)) { fs.mkdirSync(DOCS_COMPONENTS_DIR, { recursive: true }); } -// Get all registry JSON files +// Get all registry JSON files, excluding registry.json itself const registryFiles = fs.readdirSync(PUBLIC_REGISTRY_DIR) - .filter(file => file.endsWith('.json')); + .filter(file => file.endsWith('.json') && file !== 'registry.json'); // Process each component registryFiles.forEach(jsonFile => { const componentName = path.basename(jsonFile, '.json'); const jsonPath = path.join(PUBLIC_REGISTRY_DIR, jsonFile); - + // Read the component JSON const componentJson = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); - + // Create component directory if it doesn't exist const componentDir = path.join(DOCS_COMPONENTS_DIR, componentName); if (!fs.existsSync(componentDir)) { fs.mkdirSync(componentDir, { recursive: true }); } + + if (!componentJson.files || !componentJson.files.length) { + console.log(`⚠️ No files found for ${componentName}, skipping...`); + return; + } - // Get component code from JSON const componentCode = componentJson.files[0].content; - + // Parse component code to extract variants and sizes const variants = extractVariants(componentCode); const sizes = extractSizes(componentCode); - + // Generate examples based on variants and sizes const examples = generateExamples(componentName, variants, sizes); - + // Generate page content const pageContent = generatePageContent( componentName, @@ -45,11 +49,11 @@ registryFiles.forEach(jsonFile => { examples, componentCode ); - + // Write the page file const pagePath = path.join(componentDir, 'page.tsx'); fs.writeFileSync(pagePath, pageContent); - + console.log(`✅ Generated documentation page for ${componentName}`); }); @@ -62,18 +66,18 @@ function extractVariants(code) { // Find the variants section inside buttonVariants (or similar) const variantsMatch = code.match(/variant:\s*{([^}]*)}/s); if (!variantsMatch) return ['default']; - + const variantsBlock = variantsMatch[1]; - + // Now extract each variant name defined in the component const variantRegex = /\s+(\w+):\s*["|']/g; const variantMatches = []; let match; - + while ((match = variantRegex.exec(variantsBlock)) !== null) { variantMatches.push(match[1]); } - + return variantMatches.length > 0 ? variantMatches : ['default']; } @@ -83,14 +87,14 @@ function extractVariants(code) { function extractSizes(code) { const sizesMatch = code.match(/size:\s*{([^}]*)}/s); if (!sizesMatch) return ['default']; - + const sizesBlock = sizesMatch[1]; // Extract only the size names (without the comments and values) const sizeMatches = Array.from( sizesBlock.matchAll(/\s+(\w+):\s*/g), m => m[1] ); - + // Return unique sizes return [...new Set(sizeMatches)]; } @@ -104,7 +108,7 @@ function generateExamples(componentName, variants, sizes) { .split('-') .map(part => part.charAt(0).toUpperCase() + part.slice(1)) .join(''); - + const examples = [ { title: "Default", @@ -121,7 +125,7 @@ export default function ${formattedComponentName}Demo() { language: "tsx", } ]; - + if (variants.length > 1) { // Add an example showing each variant examples.push({ @@ -139,7 +143,7 @@ export default function ${formattedComponentName}Variants() { language: "tsx", }); } - + if (sizes.length > 1) { // Add an example showing each size examples.push({ @@ -157,7 +161,7 @@ export default function ${formattedComponentName}Sizes() { language: "tsx", }); } - + return examples; } @@ -172,14 +176,14 @@ function generatePageContent(componentName, description, examples, componentCode .join(''); // Get component dependencies from registry.json - const registryContent = JSON.parse(fs.readFileSync('registry.json', 'utf8')); + const registryContent = JSON.parse(fs.readFileSync('./public/r/registry.json', 'utf8')); const componentInfo = registryContent.items.find(item => item.name === componentName); const changelog = componentInfo?.changelog || []; - + // Combine both types of dependencies const directDependencies = componentInfo?.dependencies || []; const registryDependencies = componentInfo?.registryDependencies || []; - + // Format registry dependencies to show they are from the registry const formattedRegistryDeps = registryDependencies.map(dep => { // Si c'est une URL, extraire juste le nom du composant @@ -189,17 +193,17 @@ function generatePageContent(componentName, description, examples, componentCode } return `@nativeui/ui/${dep}`; }); - + const allDependencies = [ ...directDependencies, ...formattedRegistryDeps ]; - + // Generate preview code // Lire le fichier JSON du composant pour obtenir customPreview const jsonPath = path.join(PUBLIC_REGISTRY_DIR, `${componentName}.json`); const componentJson = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); - + // Utiliser customPreview s'il existe, sinon utiliser le code généré par défaut const previewCode = componentJson.customPreview || `import { ${formattedComponentName} } from "@nativeui/ui";