rn-native-wrapper 1.0.2 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # RN Native Wrapper
2
2
 
3
- A React Native wrapper component for the native date picker, providing a simple and type-safe interface to `react-native-date-picker`.
3
+ React Native wrapper package providing:
4
+
5
+ - **RNNativeDatePicker** – type-safe interface to `react-native-date-picker`
6
+ - **Speech recognition** – re-exports from `expo-speech-recognition` (APIs, hooks, constants, types)
7
+ - **Permission helpers** – internal helpers to check/request microphone and speech recognition permissions
4
8
 
5
9
  ## Installation
6
10
 
@@ -12,16 +16,28 @@ yarn add rn-native-wrapper
12
16
 
13
17
  ### Peer Dependencies
14
18
 
15
- This package requires the following peer dependencies:
16
-
17
19
  ```bash
18
20
  npm install react react-native
19
21
  ```
20
22
 
21
- The package automatically includes `react-native-date-picker` as a dependency.
23
+ The package includes `react-native-date-picker` and `expo-speech-recognition` as dependencies.
24
+
25
+ ---
26
+
27
+ ## Exports (from `index.ts`)
28
+
29
+ | Category | Exports |
30
+ |----------|--------|
31
+ | **Date Picker** | `RNNativeDatePicker` (default + named) |
32
+ | **Permission helpers** | `checkSpeechRecognitionPermissions`, `requestSpeechRecognitionPermissions`, `checkMicrophonePermission`, `requestMicrophonePermission`, `checkSpeechRecognizerPermission`, `requestSpeechRecognizerPermission`, `ensureSpeechRecognitionPermissions`, `SpeechRecognitionPermissionResult` |
33
+ | **Speech recognition** | All exports from `expo-speech-recognition` (e.g. `ExpoSpeechRecognitionModule`, `ExpoWebSpeechRecognition`, `useSpeechRecognitionEvent`, constants, types) |
34
+
35
+ ---
22
36
 
23
37
  ## Usage
24
38
 
39
+ ### RNNativeDatePicker
40
+
25
41
  ```tsx
26
42
  import React, { useState } from 'react';
27
43
  import { View, Button } from 'react-native';
@@ -34,7 +50,6 @@ export default function App() {
34
50
  return (
35
51
  <View>
36
52
  <Button title="Open Date Picker" onPress={() => setOpen(true)} />
37
-
38
53
  <RNNativeDatePicker
39
54
  modal
40
55
  open={open}
@@ -43,32 +58,78 @@ export default function App() {
43
58
  setOpen(false);
44
59
  setDate(selectedDate);
45
60
  }}
46
- onCancel={() => {
47
- setOpen(false);
48
- }}
61
+ onCancel={() => setOpen(false)}
49
62
  />
50
63
  </View>
51
64
  );
52
65
  }
53
66
  ```
54
67
 
68
+ ### Permission helpers (speech / microphone)
69
+
70
+ Request or check permissions before starting speech recognition:
71
+
72
+ ```tsx
73
+ import {
74
+ ensureSpeechRecognitionPermissions,
75
+ type SpeechRecognitionPermissionResult,
76
+ } from 'rn-native-wrapper';
77
+
78
+ // Before starting recognition: check, then request if needed
79
+ const result = await ensureSpeechRecognitionPermissions();
80
+ if (result.granted) {
81
+ // Start speech recognition
82
+ } else if (!result.canAskAgain) {
83
+ // Open app settings
84
+ } else {
85
+ // User denied; show message
86
+ }
87
+ ```
88
+
89
+ Available helpers:
90
+
91
+ - **`checkSpeechRecognitionPermissions()`** – Check both speech + microphone (no dialog).
92
+ - **`requestSpeechRecognitionPermissions()`** – Request both (shows system dialog).
93
+ - **`ensureSpeechRecognitionPermissions()`** – Check first; request if not granted.
94
+ - **`checkMicrophonePermission()`** / **`requestMicrophonePermission()`** – Microphone only.
95
+ - **`checkSpeechRecognizerPermission()`** / **`requestSpeechRecognizerPermission()`** – Speech recognizer only (mainly iOS).
96
+
97
+ **`SpeechRecognitionPermissionResult`**: `{ granted, canAskAgain, status, response }`.
98
+
99
+ ### Speech recognition (expo-speech-recognition)
100
+
101
+ All APIs from `expo-speech-recognition` are re-exported. Use them as you would from that package:
102
+
103
+ ```tsx
104
+ import {
105
+ ExpoSpeechRecognitionModule,
106
+ useSpeechRecognitionEvent,
107
+ } from 'rn-native-wrapper';
108
+ ```
109
+
110
+ See [expo-speech-recognition](https://www.npmjs.com/package/expo-speech-recognition) for full API and setup (e.g. config plugin, permissions in `app.json`).
111
+
112
+ ---
113
+
55
114
  ## API
56
115
 
57
- The `RNNativeDatePicker` component accepts all props from [`react-native-date-picker`](https://github.com/henninghall/react-native-date-picker). Please refer to their documentation for detailed prop descriptions.
116
+ ### RNNativeDatePicker
58
117
 
59
- ### Common Props
118
+ Accepts all props from [`react-native-date-picker`](https://github.com/henninghall/react-native-date-picker).
60
119
 
61
- - `modal`: boolean - Display as modal
62
- - `open`: boolean - Modal open state
63
- - `date`: Date - Currently selected date
64
- - `onConfirm`: (date: Date) => void - Callback when date is confirmed
65
- - `onCancel`: () => void - Callback when picker is cancelled
66
- - `mode`: 'date' | 'time' | 'datetime' - Picker mode
67
- - `locale`: string - Locale for date formatting
120
+ | Prop | Type | Description |
121
+ |------|------|--------------|
122
+ | `modal` | boolean | Display as modal |
123
+ | `open` | boolean | Modal open state |
124
+ | `date` | Date | Currently selected date |
125
+ | `onConfirm` | (date: Date) => void | Called when date is confirmed |
126
+ | `onCancel` | () => void | Called when picker is cancelled |
127
+ | `mode` | 'date' \| 'time' \| 'datetime' | Picker mode |
128
+ | `locale` | string | Locale for date formatting |
68
129
 
69
130
  ## TypeScript
70
131
 
71
- This package is written in TypeScript and includes full type definitions out of the box.
132
+ This package is written in TypeScript and ships with type definitions.
72
133
 
73
134
  ## License
74
135
 
package/ios/Podfile.lock CHANGED
@@ -57,6 +57,8 @@ PODS:
57
57
  - ReactCommon/turbomodule/core
58
58
  - ReactNativeDependencies
59
59
  - Yoga
60
+ - ExpoSpeechRecognition (3.1.0):
61
+ - ExpoModulesCore
60
62
  - FBLazyVector (0.81.5)
61
63
  - hermes-engine (0.81.5):
62
64
  - hermes-engine/Pre-built (= 0.81.5)
@@ -1813,6 +1815,7 @@ DEPENDENCIES:
1813
1815
  - ExpoFont (from `../node_modules/expo-font/ios`)
1814
1816
  - ExpoKeepAwake (from `../node_modules/expo-keep-awake/ios`)
1815
1817
  - ExpoModulesCore (from `../node_modules/expo-modules-core`)
1818
+ - ExpoSpeechRecognition (from `../node_modules/expo-speech-recognition/ios`)
1816
1819
  - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
1817
1820
  - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`)
1818
1821
  - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`)
@@ -1900,6 +1903,8 @@ EXTERNAL SOURCES:
1900
1903
  :path: "../node_modules/expo-keep-awake/ios"
1901
1904
  ExpoModulesCore:
1902
1905
  :path: "../node_modules/expo-modules-core"
1906
+ ExpoSpeechRecognition:
1907
+ :path: "../node_modules/expo-speech-recognition/ios"
1903
1908
  FBLazyVector:
1904
1909
  :path: "../node_modules/react-native/Libraries/FBLazyVector"
1905
1910
  hermes-engine:
@@ -2050,6 +2055,7 @@ SPEC CHECKSUMS:
2050
2055
  ExpoFont: f543ce20a228dd702813668b1a07b46f51878d47
2051
2056
  ExpoKeepAwake: 55f75eca6499bb9e4231ebad6f3e9cb8f99c0296
2052
2057
  ExpoModulesCore: f3da4f1ab5a8375d0beafab763739dbee8446583
2058
+ ExpoSpeechRecognition: d8dfddcc6d998399f57803a9547aa7a0c63777b9
2053
2059
  FBLazyVector: e95a291ad2dadb88e42b06e0c5fb8262de53ec12
2054
2060
  hermes-engine: 9f4dfe93326146a1c99eb535b1cb0b857a3cd172
2055
2061
  RCTDeprecation: 943572d4be82d480a48f4884f670135ae30bf990
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rn-native-wrapper",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "React Native wrapper for native date picker component",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/index.js",
@@ -10,6 +10,7 @@
10
10
  "files": [
11
11
  "lib",
12
12
  "src",
13
+ "scripts",
13
14
  "ios",
14
15
  "android",
15
16
  "!**/__tests__",
@@ -21,6 +22,7 @@
21
22
  "!android/.gradle"
22
23
  ],
23
24
  "scripts": {
25
+ "postinstall": "node ./scripts/postinstall.js",
24
26
  "start": "expo start",
25
27
  "reset-project": "node ./scripts/reset-project.js",
26
28
  "android": "expo run:android",
@@ -32,12 +34,13 @@
32
34
  "clean": "rm -rf lib"
33
35
  },
34
36
  "dependencies": {
35
- "expo-speech-recognition": "^3.1.0",
36
- "react-native-date-picker": "^5.0.13"
37
+
37
38
  },
38
39
  "peerDependencies": {
39
40
  "react": "*",
40
- "react-native": "*"
41
+ "react-native": "*",
42
+ "expo-speech-recognition": "*",
43
+ "react-native-date-picker": "*"
41
44
  },
42
45
  "devDependencies": {
43
46
  "@types/react": "~19.1.0",
@@ -0,0 +1,90 @@
1
+ /**
2
+ * When rn-native-wrapper is installed as a dependency (directly or via another library),
3
+ * install peer packages expo-speech-recognition and react-native-date-picker in the
4
+ * host/root project so the app has them available.
5
+ */
6
+ const path = require('path');
7
+ const fs = require('fs');
8
+ const { execSync } = require('child_process');
9
+
10
+ const PACKAGES_TO_INSTALL = ['expo-speech-recognition', 'react-native-date-picker'];
11
+
12
+ function findHostProjectRoot() {
13
+ // This script runs from rn-native-wrapper/scripts, so __dirname is .../rn-native-wrapper/scripts
14
+ // Package root = rn-native-wrapper
15
+ let dir = path.resolve(__dirname, '..');
16
+
17
+ while (dir !== path.dirname(dir)) {
18
+ const parent = path.dirname(dir);
19
+ const parentBasename = path.basename(parent);
20
+ // We're inside node_modules when our parent folder is named "node_modules"
21
+ if (parentBasename === 'node_modules') {
22
+ const candidateRoot = path.dirname(parent);
23
+ const pkgJson = path.join(candidateRoot, 'package.json');
24
+ if (fs.existsSync(pkgJson)) {
25
+ return candidateRoot;
26
+ }
27
+ }
28
+ dir = parent;
29
+ }
30
+
31
+ return null;
32
+ }
33
+
34
+ function getPackageManager(root) {
35
+ if (fs.existsSync(path.join(root, 'yarn.lock'))) return 'yarn';
36
+ if (fs.existsSync(path.join(root, 'pnpm-lock.yaml'))) return 'pnpm';
37
+ return 'npm';
38
+ }
39
+
40
+ function installInHost(hostRoot, packagesToAdd) {
41
+ const pm = getPackageManager(hostRoot);
42
+ const packages = packagesToAdd.join(' ');
43
+
44
+ try {
45
+ if (pm === 'yarn') {
46
+ execSync(`yarn add ${packages}`, { cwd: hostRoot, stdio: 'inherit' });
47
+ } else if (pm === 'pnpm') {
48
+ execSync(`pnpm add ${packages}`, { cwd: hostRoot, stdio: 'inherit' });
49
+ } else {
50
+ execSync(`npm install ${packages}`, { cwd: hostRoot, stdio: 'inherit' });
51
+ }
52
+ } catch (err) {
53
+ console.warn(
54
+ '[rn-native-wrapper] postinstall: could not auto-install peer packages in root project. Please run:\n' +
55
+ ` ${pm} add ${packages}\n` +
56
+ ' in your project root.'
57
+ );
58
+ }
59
+ }
60
+
61
+ function main() {
62
+ // Only run when we are installed as a dependency (inside node_modules)
63
+ const packageRoot = path.resolve(__dirname, '..');
64
+ if (!packageRoot.includes('node_modules')) {
65
+ return;
66
+ }
67
+
68
+ const hostRoot = findHostProjectRoot();
69
+ if (!hostRoot) {
70
+ return;
71
+ }
72
+
73
+ const pkgJsonPath = path.join(hostRoot, 'package.json');
74
+ let pkgJson;
75
+ try {
76
+ pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf8'));
77
+ } catch {
78
+ return;
79
+ }
80
+
81
+ const deps = { ...pkgJson.dependencies, ...pkgJson.devDependencies };
82
+ const missing = PACKAGES_TO_INSTALL.filter((p) => !deps[p]);
83
+ if (missing.length === 0) {
84
+ return;
85
+ }
86
+
87
+ installInHost(hostRoot, missing);
88
+ }
89
+
90
+ main();
@@ -0,0 +1,112 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * This script is used to reset the project to a blank state.
5
+ * It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file.
6
+ * You can remove the `reset-project` script from package.json and safely delete this file after running it.
7
+ */
8
+
9
+ const fs = require("fs");
10
+ const path = require("path");
11
+ const readline = require("readline");
12
+
13
+ const root = process.cwd();
14
+ const oldDirs = ["app", "components", "hooks", "constants", "scripts"];
15
+ const exampleDir = "app-example";
16
+ const newAppDir = "app";
17
+ const exampleDirPath = path.join(root, exampleDir);
18
+
19
+ const indexContent = `import { Text, View } from "react-native";
20
+
21
+ export default function Index() {
22
+ return (
23
+ <View
24
+ style={{
25
+ flex: 1,
26
+ justifyContent: "center",
27
+ alignItems: "center",
28
+ }}
29
+ >
30
+ <Text>Edit app/index.tsx to edit this screen.</Text>
31
+ </View>
32
+ );
33
+ }
34
+ `;
35
+
36
+ const layoutContent = `import { Stack } from "expo-router";
37
+
38
+ export default function RootLayout() {
39
+ return <Stack />;
40
+ }
41
+ `;
42
+
43
+ const rl = readline.createInterface({
44
+ input: process.stdin,
45
+ output: process.stdout,
46
+ });
47
+
48
+ const moveDirectories = async (userInput) => {
49
+ try {
50
+ if (userInput === "y") {
51
+ // Create the app-example directory
52
+ await fs.promises.mkdir(exampleDirPath, { recursive: true });
53
+ console.log(`📁 /${exampleDir} directory created.`);
54
+ }
55
+
56
+ // Move old directories to new app-example directory or delete them
57
+ for (const dir of oldDirs) {
58
+ const oldDirPath = path.join(root, dir);
59
+ if (fs.existsSync(oldDirPath)) {
60
+ if (userInput === "y") {
61
+ const newDirPath = path.join(root, exampleDir, dir);
62
+ await fs.promises.rename(oldDirPath, newDirPath);
63
+ console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`);
64
+ } else {
65
+ await fs.promises.rm(oldDirPath, { recursive: true, force: true });
66
+ console.log(`❌ /${dir} deleted.`);
67
+ }
68
+ } else {
69
+ console.log(`➡️ /${dir} does not exist, skipping.`);
70
+ }
71
+ }
72
+
73
+ // Create new /app directory
74
+ const newAppDirPath = path.join(root, newAppDir);
75
+ await fs.promises.mkdir(newAppDirPath, { recursive: true });
76
+ console.log("\n📁 New /app directory created.");
77
+
78
+ // Create index.tsx
79
+ const indexPath = path.join(newAppDirPath, "index.tsx");
80
+ await fs.promises.writeFile(indexPath, indexContent);
81
+ console.log("📄 app/index.tsx created.");
82
+
83
+ // Create _layout.tsx
84
+ const layoutPath = path.join(newAppDirPath, "_layout.tsx");
85
+ await fs.promises.writeFile(layoutPath, layoutContent);
86
+ console.log("📄 app/_layout.tsx created.");
87
+
88
+ console.log("\n✅ Project reset complete. Next steps:");
89
+ console.log(
90
+ `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${
91
+ userInput === "y"
92
+ ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.`
93
+ : ""
94
+ }`
95
+ );
96
+ } catch (error) {
97
+ console.error(`❌ Error during script execution: ${error.message}`);
98
+ }
99
+ };
100
+
101
+ rl.question(
102
+ "Do you want to move existing files to /app-example instead of deleting them? (Y/n): ",
103
+ (answer) => {
104
+ const userInput = answer.trim().toLowerCase() || "y";
105
+ if (userInput === "y" || userInput === "n") {
106
+ moveDirectories(userInput).finally(() => rl.close());
107
+ } else {
108
+ console.log("❌ Invalid input. Please enter 'Y' or 'N'.");
109
+ rl.close();
110
+ }
111
+ }
112
+ );