Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '18'
node-version: '23'
cache: 'npm'

- name: Install dependencies
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ jobs:
build-and-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4

- name: Setup Node.js
uses: actions/setup-node@v3
uses: actions/setup-node@v4
with:
node-version: '18'
node-version: '23'
registry-url: 'https://registry.npmjs.org'
cache: 'npm'

Expand Down
9 changes: 0 additions & 9 deletions .prettierrc.js

This file was deleted.

2 changes: 1 addition & 1 deletion .prettierrc → .prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@
"tabWidth": 2,
"arrowParens": "avoid",
"endOfLine": "auto"
}
}
2 changes: 0 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@ npm run test
We follow a test-first approach for component development:

1. **Test in Example Apps First**: Before adding a new component to the library, you should first implement and test it in our example applications:

- Expo Example App: `/examples/expo/nativecn`
- React Native CLI Example App: _(coming soon)_

Expand All @@ -128,7 +127,6 @@ We follow a test-first approach for component development:
To add a new component:

1. Add component template files in `packages/cli/templates/<component-name>/`:

- `index.tsx.template`: Component template
- `styles.ts.template`: Styles template

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ Creates a nativecn-preset.js file with default theme colors.

| Category | Progress | Completed | Total |
| ----------------------------- | ------------------------ | --------- | ------ |
| Essential UI Primitives | ⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜ | 1 | 10 |
| Essential UI Primitives | ⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜ | 7 | 10 |
| Common Components | ⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜ | 6 | 12 |
| Navigation & Structure | ⬛⬛⬜⬜⬜⬜⬜⬜ | 2 | 8 |
| Advanced Interaction | ⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜ | 1 | 10 |
| Data & Specialized Components | ⬛⬛⬜⬜⬜⬜ | 2 | 6 |
| **Overall** | ⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜ | **12** | **46** |
| **Overall** | ⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜ | **18** | **46** |

Check out the [Roadmap](ROADMAP.md) for more details.

Expand Down
16 changes: 8 additions & 8 deletions ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,23 +14,23 @@ NativeCN is in early development with a foundation of core components and infras

| Category | Progress | Completed | Total |
| ----------------------------- | ------------------------ | --------- | ------ |
| Essential UI Primitives | ⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜ | 1 | 10 |
| Essential UI Primitives | ⬛⬛⬛⬛⬛⬛⬛⬜⬜⬜ | 7 | 10 |
| Common Components | ⬛⬛⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜ | 6 | 12 |
| Navigation & Structure | ⬛⬛⬜⬜⬜⬜⬜⬜ | 2 | 8 |
| Advanced Interaction | ⬛⬜⬜⬜⬜⬜⬜⬜⬜⬜ | 1 | 10 |
| Data & Specialized Components | ⬛⬛⬜⬜⬜⬜ | 2 | 6 |
| **Overall** | ⬛⬛⬜⬜⬜⬜⬜⬜⬜⬜ | **12** | **46** |
| **Overall** | ⬛⬛⬛⬛⬜⬜⬜⬜⬜⬜ | **18** | **46** |

#### Essential UI Primitives

- [x] [Button component](https://github.com/tailwiinder/nativecn/tree/main/packages/cli/templates/button)
- [ ] Checkbox component
- [ ] Input component
- [ ] Label component
- [ ] Radio Group component
- [x] [Checkbox component](https://github.com/tailwiinder/nativecn/tree/main/packages/cli/templates/checkbox)
- [x] [Input component](https://github.com/tailwiinder/nativecn/tree/main/packages/cli/templates/input)
- [x] [Label component](https://github.com/tailwiinder/nativecn/tree/main/packages/cli/templates/label)
- [x] [Radio Group component](https://github.com/tailwiinder/nativecn/tree/main/packages/cli/templates/radio-group)
- [ ] Select component
- [ ] Separator component
- [ ] Switch component
- [x] [Separator component](https://github.com/tailwiinder/nativecn/tree/main/packages/cli/templates/separator)
- [x] [Switch component](https://github.com/tailwiinder/nativecn/tree/main/packages/cli/templates/switch)
- [ ] Textarea component
- [ ] Tooltip component

Expand Down
10 changes: 9 additions & 1 deletion eslint.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { readFileSync } from 'fs';
import { fileURLToPath } from 'url';
import path from 'path';

import tseslint from 'typescript-eslint';
import eslintPluginPrettier from 'eslint-plugin-prettier';
import eslintPluginImport from 'eslint-plugin-import';
import eslintPluginUnusedImports from 'eslint-plugin-unused-imports';
import eslintPluginEslintComments from 'eslint-plugin-eslint-comments';
import prettierConfig from './.prettierrc.js';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

const prettierConfig = JSON.parse(readFileSync(path.join(__dirname, '.prettierrc.json'), 'utf8'));

export default [
{
Expand Down
19 changes: 11 additions & 8 deletions examples/expo/nativecn/app/_layout.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,20 @@
import React from 'react';
import { Stack } from 'expo-router';
import { ThemeProvider } from '../lib/ThemeContext';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import '../global.css';

export default function RootLayout() {
return (
<ThemeProvider>
<Stack
screenOptions={{
headerShown: false,
animation: 'slide_from_right',
}}
/>
</ThemeProvider>
<SafeAreaProvider>
<ThemeProvider>
<Stack
screenOptions={{
headerShown: false,
animation: 'slide_from_right',
}}
/>
</ThemeProvider>
</SafeAreaProvider>
);
}
14 changes: 13 additions & 1 deletion examples/expo/nativecn/app/component/[id].tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React from 'react';
import { View, Text, SafeAreaView, ScrollView } from 'react-native';
import { View, Text, ScrollView } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useLocalSearchParams } from 'expo-router';
import { useTheme } from '../../lib/ThemeContext';
import Header from '../components/Header';
Expand All @@ -19,6 +20,12 @@ import CarouselShowcase from './showcases/CarouselShowcase';
import OTPInputShowCase from './showcases/InputOtpShowCase';
import SkeletonShowcase from './showcases/SkeletonShowcase';

import InputShowcase from './showcases/InputShowcase';
import CheckboxShowcase from './showcases/CheckboxShowcase';
import SwitchShowcase from './showcases/SwitchShowcase';
import RadioGroupShowcase from './showcases/RadioGroupShowcase';
import SeparatorShowcase from './showcases/SeparatorShowcase';

// Component mapping for dynamic rendering
const COMPONENT_SHOWCASES: Record<string, React.ComponentType> = {
accordion: AccordionShowcase,
Expand All @@ -33,6 +40,11 @@ const COMPONENT_SHOWCASES: Record<string, React.ComponentType> = {
carousel: CarouselShowcase,
OTPInput: OTPInputShowCase,
Skeleton: SkeletonShowcase,
input: InputShowcase,
checkbox: CheckboxShowcase,
switch: SwitchShowcase,
'radio-group': RadioGroupShowcase,
separator: SeparatorShowcase,
};

export default function ComponentDetails() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useState } from 'react';
import { View, Text } from 'react-native';
import { useTheme } from '../../../lib/ThemeContext';
import { Checkbox } from '../../../components/ui/checkbox';
import { Label } from '../../../components/ui/label';

const CheckboxShowcase: React.FC = () => {
const { theme } = useTheme();
const isDark = theme === 'dark';
const mode = isDark ? 'dark' : 'light';

const [terms, setTerms] = useState(false);
const [marketing, setMarketing] = useState(true);

return (
<View className="gap-6 pb-10">
<Text className={`text-xl font-bold ${isDark ? 'text-dark-foreground' : 'text-foreground'}`}>
Basic Checkbox
</Text>

<View className="flex-row items-center gap-3">
<Checkbox mode={mode} checked={terms} onCheckedChange={setTerms} />
<Label mode={mode} onPress={() => setTerms(!terms)}>
Accept terms and conditions
</Label>
</View>

<Text
className={`text-xs ml-8 ${isDark ? 'text-dark-muted-foreground' : 'text-muted-foreground'}`}
>
You agree to our Terms of Service and Privacy Policy.
</Text>

<Text
className={`text-xl font-bold mt-4 ${isDark ? 'text-dark-foreground' : 'text-foreground'}`}
>
Checked by Default
</Text>

<View className="flex-row items-center gap-3">
<Checkbox mode={mode} checked={marketing} onCheckedChange={setMarketing} />
<Label mode={mode} onPress={() => setMarketing(!marketing)}>
Receive marketing emails
</Label>
</View>

<Text
className={`text-xl font-bold mt-4 ${isDark ? 'text-dark-foreground' : 'text-foreground'}`}
>
Disabled State
</Text>

<View className="flex-row items-center gap-3">
<Checkbox mode={mode} checked={true} disabled />
<Label mode={mode} className="opacity-50">
Disabled (Checked)
</Label>
</View>

<View className="flex-row items-center gap-3 mt-2">
<Checkbox mode={mode} checked={false} disabled />
<Label mode={mode} className="opacity-50">
Disabled (Unchecked)
</Label>
</View>

<Text
className={`text-xl font-bold mt-4 ${isDark ? 'text-dark-foreground' : 'text-foreground'}`}
>
Error State
</Text>

<View className="flex-row items-center gap-3">
<Checkbox mode={mode} checked={false} error />
<Label mode={mode} className="text-destructive">
Required Field
</Label>
</View>
</View>
);
};

export default CheckboxShowcase;
73 changes: 73 additions & 0 deletions examples/expo/nativecn/app/component/showcases/InputShowcase.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import React, { useState } from 'react';
import { View, Text } from 'react-native';
import { useTheme } from '../../../lib/ThemeContext';
import { Input } from '../../../components/ui/input';
import { Label } from '../../../components/ui/label';

const InputShowcase: React.FC = () => {
const { theme } = useTheme();
const isDark = theme === 'dark';
const mode = isDark ? 'dark' : 'light';

const [email, setEmail] = useState('');
const [password, setPassword] = useState('');

return (
<View className="gap-6 pb-10">
<Text className={`text-xl font-bold ${isDark ? 'text-dark-foreground' : 'text-foreground'}`}>
Basic Input
</Text>

<View className="gap-2">
<Label mode={mode}>Email</Label>
<Input
mode={mode}
placeholder="Email address"
value={email}
onChangeText={setEmail}
keyboardType="email-address"
autoCapitalize="none"
/>
<Text
className={`text-xs ${isDark ? 'text-dark-muted-foreground' : 'text-muted-foreground'}`}
>
Enter your email address to continue.
</Text>
</View>

<Text
className={`text-xl font-bold mt-4 ${isDark ? 'text-dark-foreground' : 'text-foreground'}`}
>
Error State
</Text>

<View className="gap-2">
<Label mode={mode} className="text-destructive">
Password
</Label>
<Input
mode={mode}
placeholder="Password"
value={password}
onChangeText={setPassword}
secureTextEntry
error
/>
<Text className="text-xs text-destructive">Password must be at least 8 characters.</Text>
</View>

<Text
className={`text-xl font-bold mt-4 ${isDark ? 'text-dark-foreground' : 'text-foreground'}`}
>
Disabled State
</Text>

<View className="gap-2">
<Label mode={mode}>Username (Read Only)</Label>
<Input mode={mode} placeholder="bronzontech" editable={false} />
</View>
</View>
);
};

export default InputShowcase;
Loading