{"_id":"@alex8203/rn-components","_rev":"2-c76c6710885e818d53dc9d2cb8c55b2f","name":"@alex8203/rn-components","dist-tags":{"latest":"0.2.0"},"versions":{"0.1.0":{"name":"@alex8203/rn-components","version":"0.1.0","keywords":["react-native","components","ui","theme","dark-mode","typescript"],"license":"MIT","_id":"@alex8203/rn-components@0.1.0","maintainers":[{"name":"alex8203","email":"ahwebstudio6703@gmail.com"}],"dist":{"shasum":"4ec0ea355e6e090943aaf14e5345bd0c407227b9","tarball":"https://registry.npmjs.org/@alex8203/rn-components/-/rn-components-0.1.0.tgz","fileCount":8,"integrity":"sha512-g8gg3w2OxyCnztgMPAiN/2T2yhsWVMLqbQVeDJtOCzQn9VAyg8MpA1wHYneb88phOTCGOjs7V81L3r7dIDbwkw==","signatures":[{"sig":"MEUCIQDNXNWLxufiYEPV5VR61SmXOCwrePXhYh5vX6SGy8SyWwIgAJGqPWvkcDcmPz7wb/r0IYjrKIxIw8ODJOFWieDobCU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":347491},"main":"dist/index.js","types":"dist/index.d.ts","module":"dist/index.mjs","source":"src/index.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"gitHead":"5afad4eedca904188aee1d0c7c5d2b6f37b8db0d","scripts":{"lint":"eslint src","size":"size-limit","test":"jest","build":"tsup","format":"prettier --write \"src/**/*.{ts,tsx}\"","release":"bun run build && changeset publish","version":"changeset version","lint:fix":"eslint src --fix","typecheck":"tsc --noEmit","test:watch":"jest --watch","build:watch":"tsup --watch","test:coverage":"jest --coverage","prepublishOnly":"bun run build && bun run typecheck"},"_npmUser":{"name":"alex8203","email":"ahwebstudio6703@gmail.com"},"size-limit":[{"path":"dist/index.mjs","limit":"15 kB"},{"path":"dist/index.js","limit":"15 kB"}],"_npmVersion":"10.9.4","description":"A lightweight, native React Native component library with dark/light theme support","directories":{},"_nodeVersion":"22.22.0","_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","tsup":"^8.5.1","react":"^19.2.4","eslint":"^10.0.3","prettier":"^3.8.1","@types/bun":"latest","babel-jest":"^30.2.0","size-limit":"^12.0.1","typescript":"^5.9.3","@babel/core":"^7.29.0","@types/jest":"^30.0.0","@types/react":"^19.2.14","react-native":"^0.84.1","@changesets/cli":"^2.30.0","@babel/preset-env":"^7.29.0","@types/react-native":"^0.73.0","eslint-plugin-react":"^7.37.5","react-test-renderer":"^19.2.4","eslint-config-prettier":"^10.1.8","@babel/preset-typescript":"^7.28.5","@typescript-eslint/parser":"^8.56.1","babel-preset-react-native":"^4.0.1","@react-native/babel-preset":"^0.84.1","eslint-plugin-react-native":"^5.0.0","@size-limit/preset-small-lib":"^12.0.1","@testing-library/react-native":"^13.3.3","@typescript-eslint/eslint-plugin":"^8.56.1"},"peerDependencies":{"react":">=18.0.0","react-native":">=0.73.0","react-hook-form":""},"peerDependenciesMeta":{"react-hook-form":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/rn-components_0.1.0_1773074300489_0.3991768308712207","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@alex8203/rn-components","version":"0.2.0","description":"A lightweight, native React Native component library with dark/light theme support","source":"src/index.ts","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"build":"tsup","build:watch":"tsup --watch","test":"jest","test:watch":"jest --watch","test:coverage":"jest --coverage","lint":"eslint src","lint:fix":"eslint src --fix","format":"prettier --write \"src/**/*.{ts,tsx}\"","typecheck":"tsc --noEmit","prepublishOnly":"bun run build && bun run typecheck","version":"changeset version","release":"bun run build && changeset publish","size":"size-limit"},"size-limit":[{"path":"dist/index.mjs","limit":"15 kB"},{"path":"dist/index.js","limit":"15 kB"}],"peerDependencies":{"react":">=18.0.0","react-hook-form":"","react-native":">=0.73.0","@react-native-community/datetimepicker":">=7.0.0","react-native-gesture-handler":"^2.20.0","react-native-reanimated":"^3.16.0"},"peerDependenciesMeta":{"react-hook-form":{"optional":true},"react-native-reanimated":{"optional":true},"react-native-gesture-handler":{"optional":true}},"devDependencies":{"@babel/core":"^7.29.0","@babel/preset-env":"^7.29.0","@babel/preset-typescript":"^7.28.5","@changesets/cli":"^2.30.0","@react-native-community/datetimepicker":"^8.6.0","@react-native/babel-preset":"^0.84.1","@size-limit/preset-small-lib":"^12.0.1","@testing-library/react-native":"^13.3.3","@types/bun":"latest","@types/jest":"^30.0.0","@types/react":"^19.2.14","@types/react-native":"^0.73.0","@typescript-eslint/eslint-plugin":"^8.56.1","@typescript-eslint/parser":"^8.56.1","babel-jest":"^30.2.0","babel-preset-react-native":"^4.0.1","eslint":"^10.0.3","eslint-config-prettier":"^10.1.8","eslint-plugin-react":"^7.37.5","eslint-plugin-react-native":"^5.0.0","jest":"^30.2.0","prettier":"^3.8.1","react":"^19.2.4","react-native":"^0.84.1","react-test-renderer":"^19.2.4","size-limit":"^12.0.1","tsup":"^8.5.1","typescript":"^5.9.3"},"keywords":["react-native","components","ui","theme","dark-mode","typescript"],"license":"MIT","_id":"@alex8203/rn-components@0.2.0","gitHead":"73ba45eecf325a26bd9b199a5ed1f6845652b986","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-iCBb9QX92Z/ZrX3wptzdvEynPVIjxHKOJqHx9FcgY5rCclGIcUNZ8X0lN4A/J43YCxVKhPEUk1yj501pzfNNeA==","shasum":"f162caa683edfc0bedbaa7e75b0bcd05a6d8561a","tarball":"https://registry.npmjs.org/@alex8203/rn-components/-/rn-components-0.2.0.tgz","fileCount":8,"unpackedSize":434439,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQDs0oC7ES0Pswc5xPOueoTdpqiurYk2nGbdcQw5C7oj6AIgJ6CAAY/J5cyw71ttkapnaEeo88OUvunaza72KlHR0nE="}]},"_npmUser":{"name":"alex8203","email":"ahwebstudio6703@gmail.com"},"directories":{},"maintainers":[{"name":"alex8203","email":"ahwebstudio6703@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/rn-components_0.2.0_1773077995446_0.6433524167581302"},"_hasShrinkwrap":false}},"time":{"created":"2026-03-09T16:38:20.378Z","modified":"2026-03-09T17:39:55.748Z","0.1.0":"2026-03-09T16:38:20.638Z","0.2.0":"2026-03-09T17:39:55.640Z"},"license":"MIT","keywords":["react-native","components","ui","theme","dark-mode","typescript"],"description":"A lightweight, native React Native component library with dark/light theme support","maintainers":[{"name":"alex8203","email":"ahwebstudio6703@gmail.com"}],"readme":"# rn-components\n\nA lightweight React Native component library with dark/light theme support, built with TypeScript strict mode and native platform best practices.\n\n- ✅ iOS & Android native feel (Pressable, android_ripple, Platform.select)\n- ✅ Dark / Light / System theme via `ThemeProvider`\n- ✅ Optional [react-hook-form](https://react-hook-form.com/) integration\n- ✅ Fully typed — `strict: true`, zero `any`\n- ✅ Tree-shakeable — CJS + ESM builds\n\n---\n\n## Installation\n\n```bash\nnpm install @alex8203/rn-components\n# or\npnpm add @alex8203/rn-components\n# or\nbun add @alex8203/rn-components\n```\n\n### Peer dependencies\n\n```bash\nnpm install react react-native\n# react-hook-form is optional\nnpm install react-hook-form\n```\n\n---\n\n## Setup\n\nWrap your app with `ThemeProvider`. That's it.\n\n```tsx\nimport { ThemeProvider } from 'rn-components';\n\nexport default function App() {\n  return (\n    <ThemeProvider mode=\"system\">\n      {/* your app */}\n    </ThemeProvider>\n  );\n}\n```\n\n| `mode` | Description |\n|--------|-------------|\n| `\"system\"` | Follows device dark/light setting (default) |\n| `\"light\"` | Always light |\n| `\"dark\"` | Always dark |\n\n### Custom theme\n\n```tsx\nimport { ThemeProvider } from 'rn-components';\nimport type { PartialTheme } from 'rn-components';\n\nconst myTheme: PartialTheme = {\n  colors: {\n    primary: '#6366F1',\n    secondary: '#8B5CF6',\n  },\n  radii: {\n    md: 12,\n  },\n};\n\n<ThemeProvider mode=\"system\" theme={myTheme}>\n  {/* your app */}\n</ThemeProvider>\n```\n\n### useTheme\n\n```tsx\nimport { useTheme } from 'rn-components';\n\nfunction MyComponent() {\n  const { theme, mode } = useTheme();\n  return <View style={{ backgroundColor: theme.colors.background }} />;\n}\n```\n\n---\n\n## Components\n\n### Button\n\n```tsx\nimport { Button } from 'rn-components';\n\n<Button label=\"Save\" variant=\"primary\" onPress={handleSave} />\n<Button label=\"Delete\" variant=\"destructive\" />\n<Button label=\"Loading...\" loading />\n<Button label=\"Full width\" fullWidth />\n```\n\n| Prop | Type | Default |\n|------|------|---------|\n| `label` | `string` | required |\n| `variant` | `primary \\| secondary \\| outline \\| ghost \\| destructive` | `primary` |\n| `size` | `sm \\| md \\| lg` | `md` |\n| `loading` | `boolean` | `false` |\n| `disabled` | `boolean` | `false` |\n| `fullWidth` | `boolean` | `false` |\n| `leftIcon` | `ReactNode` | — |\n| `rightIcon` | `ReactNode` | — |\n\n---\n\n### IconButton\n\n```tsx\nimport { IconButton } from 'rn-components';\nimport { Ionicons } from '@expo/vector-icons';\n\n<IconButton\n  icon={<Ionicons name=\"heart\" size={20} color=\"#fff\" />}\n  accessibilityLabel=\"Like\"\n  variant=\"primary\"\n/>\n```\n\n| Prop | Type | Default |\n|------|------|---------|\n| `icon` | `ReactNode` | required |\n| `accessibilityLabel` | `string` | required |\n| `variant` | same as Button | `primary` |\n| `size` | `sm \\| md \\| lg` | `md` |\n\n---\n\n### Text\n\n```tsx\nimport { Text } from 'rn-components';\n\n<Text variant=\"h1\">Heading 1</Text>\n<Text variant=\"body\" color=\"#666\">Paragraph</Text>\n```\n\n| `variant` | Usage |\n|-----------|-------|\n| `h1` `h2` `h3` | Headings |\n| `body` `bodySmall` | Paragraphs |\n| `label` | Form labels |\n| `caption` | Helper / hint text |\n| `overline` | Section labels (uppercase) |\n\n---\n\n### TextInput\n\n```tsx\nimport { TextInput } from 'rn-components';\n\n<TextInput\n  label=\"Email\"\n  placeholder=\"you@example.com\"\n  value={email}\n  onChangeText={setEmail}\n  helperText=\"We'll never share your email\"\n  keyboardType=\"email-address\"\n  autoCapitalize=\"none\"\n/>\n\n// With error\n<TextInput\n  label=\"Username\"\n  value={username}\n  onChangeText={setUsername}\n  errorMessage=\"Username already taken\"\n/>\n```\n\n#### With react-hook-form\n\n```tsx\nimport { useForm } from 'react-hook-form';\nimport { TextInput } from 'rn-components';\n\nconst { control } = useForm();\n\n<TextInput\n  label=\"Email\"\n  name=\"email\"\n  control={control}\n  rules={{ required: 'Required', pattern: { value: /\\S+@\\S+/, message: 'Invalid email' } }}\n/>\n```\n\n---\n\n### PasswordInput\n\n```tsx\nimport { PasswordInput } from 'rn-components';\n\n// Built-in eye icon\n<PasswordInput label=\"Password\" value={password} onChangeText={setPassword} />\n\n// Custom icon (e.g. Ionicons)\n<PasswordInput\n  label=\"Password\"\n  value={password}\n  onChangeText={setPassword}\n  renderEyeIcon={({ visible, color }) => (\n    <Ionicons name={visible ? 'eye' : 'eye-off'} size={20} color={color} />\n  )}\n/>\n```\n\n---\n\n### Checkbox\n\n```tsx\nimport { Checkbox } from 'rn-components';\n\n<Checkbox\n  state={checked}\n  label=\"Accept terms and conditions\"\n  onChange={(isChecked) => setChecked(isChecked ? 'checked' : 'unchecked')}\n/>\n```\n\n| `state` | `checked \\| unchecked \\| indeterminate` |\n|---------|-----------------------------------------|\n\n---\n\n### Switch\n\n```tsx\nimport { Switch } from 'rn-components';\n\n<Switch label=\"Notifications\" value={enabled} onValueChange={setEnabled} />\n```\n\n> Uses React Native's native Switch — looks different on iOS/Android intentionally.\n> For react-hook-form, wrap with `<Controller>`.\n\n---\n\n### Select\n\n```tsx\nimport { Select } from 'rn-components';\n\n<Select\n  label=\"Country\"\n  placeholder=\"Select a country\"\n  value={country}\n  onValueChange={setCountry}\n  options={[\n    { label: 'Cuba', value: 'cu' },\n    { label: 'Mexico', value: 'mx' },\n    { label: 'Spain', value: 'es' },\n  ]}\n/>\n```\n\n---\n\n### SearchSelect\n\nLike `Select` but with a search input to filter options.\n\n```tsx\nimport { SearchSelect } from 'rn-components';\nimport { Ionicons } from '@expo/vector-icons';\n\n<SearchSelect\n  label=\"Country\"\n  options={countries}\n  value={country}\n  onValueChange={setCountry}\n  searchIcon={<Ionicons name=\"search\" size={16} color=\"#999\" style={{ marginRight: 8 }} />}\n  clearIcon={<Ionicons name=\"close-circle\" size={16} color=\"#999\" />}\n/>\n```\n\n---\n\n### OTPInput\n\n```tsx\nimport { OTPInput } from 'rn-components';\n\n<OTPInput\n  length={6}\n  value={code}\n  onChangeText={setCode}\n  onComplete={(value) => verifyCode(value)}\n/>\n```\n\n---\n\n### Avatar\n\n```tsx\nimport { Avatar } from 'rn-components';\n\n// Image with fallback to initials\n<Avatar uri=\"https://example.com/photo.jpg\" name=\"John Doe\" size=\"md\" />\n\n// Initials only\n<Avatar name=\"Ana García\" size=\"lg\" backgroundColor=\"#5856D6\" />\n```\n\n| `size` | Dimensions |\n|--------|-----------|\n| `sm` | 32px |\n| `md` | 40px |\n| `lg` | 56px |\n| `xl` | 72px |\n\n---\n\n### Badge\n\n```tsx\nimport { Badge } from 'rn-components';\n\n<Badge label=\"New\" variant=\"success\" />\n<Badge label=\"3\" variant=\"error\" size=\"sm\" />\n```\n\n| `variant` | `info \\| success \\| warning \\| error \\| neutral` |\n\n---\n\n### Card\n\n```tsx\nimport { Card } from 'rn-components';\n\n<Card variant=\"elevated\">\n  <Text variant=\"label\">Title</Text>\n  <Text variant=\"caption\">Subtitle</Text>\n</Card>\n```\n\n| `variant` | Description |\n|-----------|-------------|\n| `elevated` | Shadow (iOS) / elevation (Android) |\n| `outlined` | Border, no shadow |\n| `filled` | Surface background |\n\n---\n\n### Divider\n\n```tsx\nimport { Divider } from 'rn-components';\n\n<Divider />\n<Divider orientation=\"vertical\" style={{ height: 24 }} />\n```\n\n---\n\n### Spinner\n\n```tsx\nimport { Spinner } from 'rn-components';\n\n<Spinner size=\"md\" />\n<Spinner size=\"lg\" color=\"#34C759\" />\n```\n\n---\n\n### Modal\n\n```tsx\nimport { Modal } from 'rn-components';\n\n<Modal visible={open} onClose={() => setOpen(false)}>\n  <Text variant=\"h3\">Confirm action</Text>\n  <Button label=\"Confirm\" onPress={handleConfirm} />\n</Modal>\n```\n\n---\n\n### Toast\n\nWrap your app with `ToastProvider` and use the `useToast` hook anywhere.\n\n```tsx\n// App.tsx\nimport { ToastProvider } from 'rn-components';\n\n<ThemeProvider mode=\"system\">\n  <ToastProvider>\n    <App />\n  </ToastProvider>\n</ThemeProvider>\n```\n\n```tsx\n// Any component\nimport { useToast } from 'rn-components';\n\nfunction MyComponent() {\n  const { show } = useToast();\n\n  return (\n    <Button\n      label=\"Save\"\n      onPress={() => show({ message: 'Saved!', variant: 'success' })}\n    />\n  );\n}\n```\n\n| Option | Type | Default |\n|--------|------|---------|\n| `message` | `string` | required |\n| `variant` | `info \\| success \\| warning \\| error` | `info` |\n| `position` | `top \\| bottom` | `bottom` |\n| `duration` | `number` (ms) | `3000` |\n\n---\n\n### SafeAreaView\n\n```tsx\nimport { SafeAreaView } from 'rn-components';\n\n<SafeAreaView>\n  {/* content */}\n</SafeAreaView>\n```\n\nHandles safe area insets on both iOS and Android automatically.\n\n---\n\n### KeyboardAvoidingView\n\n```tsx\nimport { KeyboardAvoidingView } from 'rn-components';\n\n<SafeAreaView>\n  <KeyboardAvoidingView>\n    <ScrollView>\n      <TextInput label=\"Email\" ... />\n      <PasswordInput label=\"Password\" ... />\n    </ScrollView>\n  </KeyboardAvoidingView>\n</SafeAreaView>\n```\n\nUses `behavior=\"padding\"` on iOS and `behavior=\"height\"` on Android automatically.\n\n---\n\n## react-hook-form\n\n`TextInput`, `PasswordInput`, and `Checkbox` have built-in RHF support via `name` + `control` props.\n\nFor `Switch` and `Select`, use RHF's `<Controller>` directly:\n\n```tsx\nimport { Controller, useForm } from 'react-hook-form';\nimport { Switch, Select } from 'rn-components';\n\nconst { control } = useForm();\n\n<Controller\n  name=\"notifications\"\n  control={control}\n  render={({ field: { value, onChange } }) => (\n    <Switch label=\"Notifications\" value={value} onValueChange={onChange} />\n  )}\n/>\n\n<Controller\n  name=\"country\"\n  control={control}\n  render={({ field: { value, onChange } }) => (\n    <Select options={countries} value={value} onValueChange={onChange} />\n  )}\n/>\n```\n\n---\n\n## License\n\nMIT\n","readmeFilename":"README.md"}