Skip to content

Commit 09bcb62

Browse files
committed
Merge branch 'hotfix' of github.com:nativeui-org/ui into hotfix
2 parents 6f72d65 + 6e49e24 commit 09bcb62

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+214
-139
lines changed

app/(site)/docs/components/badge/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default function BadgePage() {
2121
{
2222
"title": "Sizes",
2323
"value": "sizes",
24-
"content": "import { Badge } from \"@nativeui/ui\";\n\nexport default function BadgeSizes() {\n return (\n <div className=\"flex items-center gap-4\">\n <Badge size=\"default\">Default</Badge>\n <Badge size=\"sm\">Sm</Badge>\n <Badge size=\"lg\">Lg</Badge>\n </div>\n );\n}",
24+
"content": "import { Badge } from \"@components/ui\";\n\nexport default function BadgeSizes() {\n return (\n <div className=\"flex items-center gap-4\">\n <Badge size=\"default\">Default</Badge>\n <Badge size=\"sm\">Sm</Badge>\n <Badge size=\"lg\">Lg</Badge>\n </div>\n );\n}",
2525
"language": "tsx"
2626
}
2727
]}

app/(site)/docs/components/button/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export default function ButtonPage() {
2121
{
2222
"title": "Sizes",
2323
"value": "sizes",
24-
"content": "import { Button } from \"@nativeui/ui\";\n\nexport default function ButtonSizes() {\n return (\n <div className=\"flex items-center gap-4\">\n <Button size=\"default\">Default</Button>\n <Button size=\"sm\">Sm</Button>\n <Button size=\"lg\">Lg</Button>\n <Button size=\"icon\">👋</Button>\n </div>\n );\n}",
24+
"content": "import { Button } from \"@components/ui\";\n\nexport default function ButtonSizes() {\n return (\n <div className=\"flex items-center gap-4\">\n <Button size=\"default\">Default</Button>\n <Button size=\"sm\">Sm</Button>\n <Button size=\"lg\">Lg</Button>\n <Button size=\"icon\">👋</Button>\n </div>\n );\n}",
2525
"language": "tsx"
2626
}
2727
]}

app/(site)/docs/installation/page.tsx

Lines changed: 95 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -6,46 +6,93 @@ import { InstallationTabs } from "@/components/docs/installation-tabs";
66
import Image from "next/image";
77
import { useTheme } from "next-themes";
88

9-
export default function InstallationPage() {
10-
const [selectedPlatform, setSelectedPlatform] = React.useState("expo");
11-
const { resolvedTheme } = useTheme();
12-
const [mounted, setMounted] = React.useState(false);
13-
14-
React.useEffect(() => {
15-
setMounted(true);
16-
}, []);
9+
const InstallationPageSkeleton = () => {
10+
return (
11+
<div className="container max-w-3xl py-10 animate-pulse">
12+
{/* Header skeleton */}
13+
<div className="space-y-6">
14+
<div className="space-y-4">
15+
<div className="h-10 bg-muted rounded-md w-1/2"></div>
16+
<div className="h-6 bg-muted rounded-md w-full"></div>
17+
<div className="h-6 bg-muted rounded-md w-4/5"></div>
18+
</div>
1719

18-
if (!mounted) {
19-
return (
20-
<div className="space-y-8">
21-
<div>
22-
<h1 className="text-3xl font-bold tracking-tight">Installation</h1>
23-
<p className="text-muted-foreground text-lg mt-2">
24-
How to install dependencies and structure your app.
25-
</p>
20+
{/* Info banner skeleton */}
21+
<div className="rounded-lg border-2 p-4">
22+
<div className="flex items-center gap-3">
23+
<div className="rounded-full bg-muted p-2 w-9 h-9"></div>
24+
<div className="h-5 bg-muted rounded-md flex-1"></div>
25+
</div>
2626
</div>
27+
</div>
28+
29+
{/* Platform selection skeleton */}
30+
<div className="mt-12 space-y-8">
2731
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
28-
<div className="relative overflow-hidden rounded-lg border p-6">
32+
<div className="rounded-lg border p-6">
2933
<div className="flex flex-col items-center space-y-4">
30-
<div className="h-16 w-16 relative">
31-
<Image
32-
src={resolvedTheme === 'dark' ? "/images/expo-logo-dark.svg" : "/images/expo-logo.svg"}
33-
alt="Expo"
34-
fill
35-
className="object-contain"
36-
/>
34+
<div className="h-16 w-16 bg-muted rounded"></div>
35+
<div className="text-center space-y-2">
36+
<div className="h-6 bg-muted rounded-md w-32"></div>
37+
<div className="h-4 bg-muted rounded-md w-40"></div>
3738
</div>
38-
<div className="text-center">
39-
<h3 className="font-bold text-xl">Expo (Recommended)</h3>
40-
<p className="text-sm text-muted-foreground mt-1">
41-
Quick setup with better developer experience
42-
</p>
39+
</div>
40+
</div>
41+
<div className="rounded-lg border p-6 opacity-50">
42+
<div className="flex flex-col items-center space-y-4">
43+
<div className="h-16 w-16 bg-muted rounded"></div>
44+
<div className="text-center space-y-2">
45+
<div className="h-6 bg-muted rounded-md w-32"></div>
46+
<div className="h-4 bg-muted rounded-md w-40"></div>
4347
</div>
4448
</div>
4549
</div>
4650
</div>
51+
52+
{/* Installation steps skeleton */}
53+
<div className="space-y-8">
54+
<div className="space-y-4">
55+
<div className="h-8 bg-muted rounded-md w-1/3"></div>
56+
<div className="h-5 bg-muted rounded-md w-full"></div>
57+
<div className="h-5 bg-muted rounded-md w-3/4"></div>
58+
</div>
59+
60+
{/* Multiple step skeletons */}
61+
{Array.from({ length: 6 }).map((_, i) => (
62+
<div key={i} className="space-y-4">
63+
<div className="h-6 bg-muted rounded-md w-1/4"></div>
64+
<div className="h-4 bg-muted rounded-md w-full"></div>
65+
<div className="h-32 bg-muted rounded-md"></div>
66+
</div>
67+
))}
68+
</div>
4769
</div>
48-
);
70+
</div>
71+
);
72+
};
73+
74+
export default function InstallationPage() {
75+
const [selectedPlatform, setSelectedPlatform] = React.useState("expo");
76+
const { resolvedTheme } = useTheme();
77+
const [mounted, setMounted] = React.useState(false);
78+
const [isLoading, setIsLoading] = React.useState(true);
79+
80+
React.useEffect(() => {
81+
const loadResources = async () => {
82+
setIsLoading(true);
83+
84+
// Simulate loading time for theme and resources
85+
await new Promise(resolve => setTimeout(resolve, 800));
86+
87+
setMounted(true);
88+
setIsLoading(false);
89+
};
90+
91+
loadResources();
92+
}, []);
93+
94+
if (isLoading || !mounted) {
95+
return <InstallationPageSkeleton />;
4996
}
5097

5198
return (
@@ -136,7 +183,7 @@ export default function InstallationPage() {
136183
<div className="mt-8 space-y-12">
137184
<div className="space-y-4">
138185
<h3 className="text-xl font-semibold">1. Create Expo Project</h3>
139-
<InstallationTabs command="create-expo-app my-app --template default" />
186+
<InstallationTabs command="create-expo-app my-app" />
140187
</div>
141188

142189
<div className="space-y-4">
@@ -473,7 +520,7 @@ export function cn(...inputs: ClassValue[]) {
473520
"strict": true,
474521
"paths": {
475522
"@/*": [
476-
"./"
523+
"./*"
477524
]
478525
}
479526
},
@@ -634,41 +681,34 @@ function AppContent() {
634681
Add this code in any of your components to test that everything is working:
635682
</p>
636683
<CodeBlock
637-
language="typescript"
684+
language="tsx"
638685
collapsible
639-
title="app/components/TestComponent.tsx"
640-
code={`import { Button } from '@/components/ui/button';
641-
import { Text } from 'react-native';
686+
title="Test Component"
687+
code={`import { View, Text } from 'react-native';
642688
643-
// ... rest of your imports ...
644-
645-
return (
646-
<Button>
647-
<Text className="text-primary-foreground">Click me</Text>
648-
</Button>
649-
);`}
689+
export default function TestComponent() {
690+
return (
691+
<View className="flex-1 justify-center items-center bg-background">
692+
<Text className="text-2xl font-bold text-foreground">
693+
NativeUI is working! 🎉
694+
</Text>
695+
</View>
696+
);
697+
}`}
650698
/>
651699
</div>
652700
</div>
653701
</div>
654702
</div>
655703
) : (
656-
<div className="rounded-lg border-2 border-muted p-8 text-center">
657-
<h2 className="text-2xl font-bold tracking-tight mb-4">React Native CLI Support</h2>
658-
<p className="text-muted-foreground text-lg leading-7">
659-
Support for React Native CLI is coming soon. We recommend using Expo for now.
704+
<div className="text-center py-12">
705+
<h3 className="text-xl font-semibold mb-2">React Native CLI</h3>
706+
<p className="text-muted-foreground">
707+
Support for React Native CLI is coming soon. Stay tuned!
660708
</p>
661709
</div>
662710
)}
663711
</div>
664-
665-
<div className="mt-12 space-y-4">
666-
<h2 className="text-2xl font-bold tracking-tight">Next Steps</h2>
667-
<p className="text-muted-foreground leading-7">
668-
Now that you have set up your project, you can start adding components from our collection.
669-
Visit the components section to explore available components and learn how to use them.
670-
</p>
671-
</div>
672712
</div>
673713
);
674714
}

app/(site)/docs/page.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export default function DocsPage() {
4747
</div>
4848
</Link>
4949
<Link
50-
href="/docs/components"
50+
href="/components"
5151
className="group relative overflow-hidden rounded-lg border p-6 hover:border-foreground/10 transition-colors"
5252
>
5353
<div className="flex flex-col justify-between space-y-2">

app/(site)/layout.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ export default function SiteLayout({
1313
}: Readonly<{
1414
children: React.ReactNode;
1515
}>) {
16-
const { resolvedTheme } = useTheme(); // Suppression de theme non utilisé
16+
const { resolvedTheme, theme } = useTheme(); // Suppression de theme non utilisé
1717
const [mounted, setMounted] = React.useState(false);
1818

19+
console.log(theme);
20+
21+
1922
React.useEffect(() => {
2023
setMounted(true);
2124
}, []);

components/ui/code-block.tsx

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,16 @@ export function CodeBlock({
4646
maxVisibleLines = 10,
4747
defaultExpanded = false,
4848
}: CodeBlockProps) {
49-
const { theme: applicationTheme } = useTheme();
49+
const { resolvedTheme } = useTheme();
5050
const [copied, setCopied] = React.useState(false);
5151
const [localActiveTab, setLocalActiveTab] = React.useState<string | undefined>(activeTab || (tabs && tabs.length > 0 ? tabs[0].value : undefined));
5252
const [isExpanded, setIsExpanded] = React.useState(defaultExpanded);
53+
const [mounted, setMounted] = React.useState(false);
54+
55+
// Fix hydration issues with next-themes
56+
React.useEffect(() => {
57+
setMounted(true);
58+
}, []);
5359

5460
const handleCopy = () => {
5561
const textToCopy = tabs && localActiveTab
@@ -79,7 +85,7 @@ export function CodeBlock({
7985

8086
const codeLines = activeContent.split("\n");
8187
const shouldCollapse = collapsible && codeLines.length > maxVisibleLines;
82-
const displayedCode = shouldCollapse && !isExpanded
88+
const displayedCode = shouldCollapse && !isExpanded
8389
? codeLines.slice(0, maxVisibleLines).join("\n")
8490
: activeContent;
8591

@@ -110,7 +116,7 @@ export function CodeBlock({
110116
case 'shell':
111117
return (
112118
<svg className="h-4 w-4" viewBox="0 0 24 24" fill="currentColor">
113-
<path d="M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2m0 2v14h14V5H5m2 2h2v2H7V7m3 0h2v2h-2V7m3 0h2v2h-2V7m3 0h2v2h-2V7m3 0h2v2h-2V7M7 10h2v2H7v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2M7 13h2v2H7v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2M7 16h2v2H7v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2" />
119+
<path d="M5 3h14a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2m0 2v14h14V5H5m2 2h2v2H7V7m3 0h2v2h-2V7m3 0h2v2h-2V7m3 0h2v2h-2V7m3 0h2v2h-2V7m3 0h2v2h-2V7m3 0h2v2h-2V7m3 0h2v2h-2V7M7 10h2v2H7v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2M7 16h2v2H7v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2m3 0h2v2h-2v-2" />
114120
</svg>
115121
);
116122
case 'css':
@@ -212,7 +218,32 @@ export function CodeBlock({
212218
]
213219
};
214220

215-
const theme = applicationTheme === "dark" ? customDarkTheme : themes.github;
221+
const theme = resolvedTheme === "dark" ? customDarkTheme : themes.github;
222+
223+
// Don't render until mounted to avoid hydration mismatch
224+
if (!mounted) {
225+
return (
226+
<div className={cn("relative group rounded-md overflow-hidden border border-border", className)}>
227+
{showHeader && (
228+
<div className="flex items-center justify-between border-b border-border bg-muted/50 px-4 py-2">
229+
<div className="flex items-center gap-2">
230+
{headerPrefix}
231+
{getLanguageIcon(activeLanguage)}
232+
{title && <div className="text-sm font-medium">{title}</div>}
233+
</div>
234+
<div className="rounded-md p-1">
235+
<Copy className="h-4 w-4" />
236+
</div>
237+
</div>
238+
)}
239+
<div className="relative">
240+
<pre className="text-sm p-4 overflow-x-auto bg-muted">
241+
<code>{displayedCode.trim()}</code>
242+
</pre>
243+
</div>
244+
</div>
245+
);
246+
}
216247

217248
return (
218249
<div className={cn("relative group rounded-md overflow-hidden border border-border", className)}>
@@ -310,7 +341,8 @@ export function CodeBlock({
310341
)}
311342
style={{
312343
...style,
313-
backgroundColor: applicationTheme === "dark" ? "#1a1a1a" : style.backgroundColor,
344+
// Remove hardcoded background, let CSS variables handle it
345+
backgroundColor: undefined,
314346
}}
315347
>
316348
{tokens.map((line, i) => {
@@ -332,7 +364,7 @@ export function CodeBlock({
332364
</pre>
333365
)}
334366
</Highlight>
335-
367+
336368
{shouldCollapse && !isExpanded && (
337369
<div className="absolute bottom-0 left-0 right-0 h-12 bg-gradient-to-t from-background to-transparent pointer-events-none" />
338370
)}

public/r/accordion.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"$schema": "https://ui.shadcn.com/schema/registry-item.json",
33
"name": "accordion",
4-
"type": "registry:component",
4+
"type": "registry:ui",
55
"title": "Accordion",
66
"description": "A accordion component for React Native applications.",
77
"dependencies": [],
@@ -10,7 +10,7 @@
1010
{
1111
"path": "registry/accordion/accordion.tsx",
1212
"content": "import * as React from 'react';\nimport { Pressable, View, Text, LayoutAnimation, Platform, UIManager } from 'react-native';\nimport { Feather } from '@expo/vector-icons';\nimport { cn } from '@/lib/utils';\n\n// Enable layout animation for Android\nif (Platform.OS === 'android') {\n if (UIManager.setLayoutAnimationEnabledExperimental) {\n UIManager.setLayoutAnimationEnabledExperimental(true);\n }\n}\n\ninterface AccordionContextValue {\n value: string[];\n onValueChange: (itemValue: string) => void;\n type: 'single' | 'multiple';\n}\n\nconst AccordionContext = React.createContext<AccordionContextValue | null>(null);\n\nexport interface AccordionProps {\n type?: 'single' | 'multiple';\n collapsible?: boolean;\n value?: string[];\n onValueChange?: (value: string[]) => void;\n defaultValue?: string[];\n className?: string;\n children: React.ReactNode;\n}\n\nconst Accordion = ({\n type = 'single',\n collapsible = false,\n value,\n onValueChange,\n defaultValue,\n className,\n children,\n}: AccordionProps) => {\n const [state, setState] = React.useState<string[]>(value || defaultValue || []);\n\n const isControlled = value !== undefined;\n const accordionValue = isControlled ? value : state;\n\n const handleValueChange = React.useCallback((itemValue: string) => {\n const isSelected = accordionValue.includes(itemValue);\n\n let newValue: string[] = [];\n\n if (type === 'single') {\n if (isSelected) {\n newValue = collapsible ? [] : [itemValue];\n } else {\n newValue = [itemValue];\n }\n } else {\n if (isSelected) {\n newValue = accordionValue.filter((v) => v !== itemValue);\n } else {\n newValue = [...accordionValue, itemValue];\n }\n }\n\n if (!isControlled) {\n setState(newValue);\n }\n\n onValueChange?.(newValue);\n }, [accordionValue, collapsible, isControlled, onValueChange, type]);\n\n return (\n <AccordionContext.Provider value={{ value: accordionValue, onValueChange: handleValueChange, type }}>\n <View className={cn(\"w-full\", className)}>\n {children}\n </View>\n </AccordionContext.Provider>\n );\n};\n\ninterface AccordionItemProps {\n value: string;\n className?: string;\n children: React.ReactNode;\n}\n\nconst AccordionItem = ({ value, className, children }: AccordionItemProps) => {\n const context = React.useContext(AccordionContext);\n\n if (!context) {\n throw new Error('AccordionItem must be used within an Accordion');\n }\n\n const isExpanded = context.value.includes(value);\n\n return (\n <View className={cn(\"border-b border-border\", className)}>\n {React.Children.map(children, (child) => {\n if (React.isValidElement(child)) {\n return React.cloneElement(child as React.ReactElement<any>, {\n value,\n isExpanded,\n });\n }\n return child;\n })}\n </View>\n );\n};\n\ninterface AccordionTriggerProps {\n className?: string;\n children: React.ReactNode;\n value?: string;\n isExpanded?: boolean;\n}\n\nconst AccordionTrigger = ({\n className,\n children,\n value,\n isExpanded,\n}: AccordionTriggerProps) => {\n const context = React.useContext(AccordionContext);\n\n if (!context || value === undefined) {\n return null;\n }\n\n const iconRotation = isExpanded ? 180 : 0;\n\n const handlePress = () => {\n LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);\n context.onValueChange(value);\n };\n\n return (\n <Pressable\n onPress={handlePress}\n className={cn(\n \"flex-row items-center justify-between py-4\",\n className\n )}\n accessibilityRole=\"button\"\n accessibilityState={{ expanded: isExpanded }}\n accessibilityHint=\"Toggle accordion section\"\n >\n <View className=\"flex-1\">\n {typeof children === 'string' ? (\n <Text className=\"text-base font-medium text-foreground\">{children}</Text>\n ) : (\n children\n )}\n </View>\n <View style={{ transform: [{ rotate: `${iconRotation}deg` }] }}>\n <Feather name=\"chevron-down\" size={20} color=\"#888\" />\n </View>\n </Pressable>\n );\n};\n\ninterface AccordionContentProps {\n className?: string;\n children: React.ReactNode;\n value?: string;\n isExpanded?: boolean;\n}\n\nconst AccordionContent = ({\n className,\n children,\n value,\n isExpanded,\n}: AccordionContentProps) => {\n if (!isExpanded) {\n return null;\n }\n\n return (\n <View\n className={cn(\"pb-4 pt-0\", className)}\n >\n {typeof children === 'string' ? (\n <Text className=\"text-base text-muted-foreground\">{children}</Text>\n ) : (\n children\n )}\n </View>\n );\n};\n\nAccordion.displayName = 'Accordion';\nAccordionItem.displayName = 'AccordionItem';\nAccordionTrigger.displayName = 'AccordionTrigger';\nAccordionContent.displayName = 'AccordionContent';\n\nexport { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; ",
13-
"type": "registry:component"
13+
"type": "registry:ui"
1414
}
1515
],
1616
"changelog": [],

0 commit comments

Comments
 (0)