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 +79 -18
- package/ios/Podfile.lock +6 -0
- package/package.json +7 -4
- package/scripts/postinstall.js +90 -0
- package/scripts/reset-project.js +112 -0
package/README.md
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
# RN Native Wrapper
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
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
|
-
|
|
116
|
+
### RNNativeDatePicker
|
|
58
117
|
|
|
59
|
-
|
|
118
|
+
Accepts all props from [`react-native-date-picker`](https://github.com/henninghall/react-native-date-picker).
|
|
60
119
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
+
);
|