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";