expo-paste-input 0.1.0 → 0.1.2
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/LICENSE +21 -0
- package/README.md +114 -78
- package/package.json +1 -1
- package/android/.gradle/8.9/checksums/checksums.lock +0 -0
- package/android/.gradle/8.9/dependencies-accessors/gc.properties +0 -0
- package/android/.gradle/8.9/fileChanges/last-build.bin +0 -0
- package/android/.gradle/8.9/fileHashes/fileHashes.lock +0 -0
- package/android/.gradle/8.9/gc.properties +0 -0
- package/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/android/.gradle/buildOutputCleanup/cache.properties +0 -2
- package/android/.gradle/vcs-1/gc.properties +0 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Arunabh Verma
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,123 +1,159 @@
|
|
|
1
|
-
#
|
|
1
|
+
# expo-paste-input
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`expo-paste-input` is a lightweight wrapper around React Native `TextInput` that lets users paste images and GIFs directly from the system clipboard on **iOS and Android**.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
It works at the native level to intercept paste events before React Native handles them, giving you access to pasted media as local file URIs while keeping full control over your own `TextInput` component.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
See the original demo on [Twitter](https://x.com/iamarunabh/status/1997738168247062774)
|
|
8
|
+
|
|
9
|
+
| iOS | Android |
|
|
10
|
+
| --- | --- |
|
|
9
11
|
| <video src="https://github.com/user-attachments/assets/b54b15ac-5b98-4dc7-84d7-2e7d48e53e24" /> | <video src="https://github.com/user-attachments/assets/4d709a2c-2dca-431d-8972-05f01b7e5276" /> |
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
---
|
|
12
14
|
|
|
13
|
-
|
|
15
|
+
## Features
|
|
14
16
|
|
|
15
|
-
|
|
17
|
+
- Paste **text, images, and multiple GIFs**
|
|
18
|
+
- Works on **iOS and Android**
|
|
19
|
+
- True wrapper around `TextInput` (bring your own input)
|
|
20
|
+
- No custom UI, no opinionated styles
|
|
21
|
+
- Returns local file URIs for pasted media
|
|
22
|
+
- Safe to import on Web (no crash, no-op)
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
- Handles pasted text and images consistently across iOS and Android
|
|
19
|
-
- Supports pasting multiple images, including GIFs
|
|
20
|
-
- Returns file URIs for pasted images that you can use directly
|
|
24
|
+
---
|
|
21
25
|
|
|
22
|
-
|
|
26
|
+
## Installation
|
|
23
27
|
|
|
24
|
-
|
|
25
|
-
type PasteEventPayload =
|
|
26
|
-
| { type: "text"; value: string }
|
|
27
|
-
| { type: "images"; uris: string[] }
|
|
28
|
-
| { type: "unsupported" };
|
|
29
|
-
```
|
|
28
|
+
### Quick install
|
|
30
29
|
|
|
31
|
-
|
|
30
|
+
```bash
|
|
31
|
+
npx expo install expo-paste-input
|
|
32
|
+
````
|
|
32
33
|
|
|
33
|
-
|
|
34
|
+
or
|
|
34
35
|
|
|
35
|
-
|
|
36
|
+
```bash
|
|
37
|
+
yarn add expo-paste-input
|
|
38
|
+
```
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
### Rebuild the app (required)
|
|
38
41
|
|
|
39
|
-
|
|
40
|
-
- For **text**: The original paste proceeds normally, and the pasted text is forwarded to JavaScript.
|
|
42
|
+
This library uses native code, so you must rebuild.
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
```bash
|
|
45
|
+
npx expo run:ios
|
|
46
|
+
npx expo run:android
|
|
47
|
+
```
|
|
43
48
|
|
|
44
|
-
|
|
49
|
+
(Expo Go will not work)
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
---
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
- For **text**: The original paste proceeds normally, and the pasted text is forwarded to JavaScript.
|
|
53
|
+
## Usage
|
|
50
54
|
|
|
51
|
-
|
|
55
|
+
Wrap your `TextInput` with `TextInputWrapper`:
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
```tsx
|
|
58
|
+
import { TextInputWrapper } from "expo-paste-input";
|
|
59
|
+
import { TextInput } from "react-native";
|
|
54
60
|
|
|
55
|
-
|
|
61
|
+
export default function MyInput() {
|
|
62
|
+
return (
|
|
63
|
+
<TextInputWrapper
|
|
64
|
+
onPaste={(payload) => {
|
|
65
|
+
console.log(payload);
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
<TextInput placeholder="Paste here..." />
|
|
69
|
+
</TextInputWrapper>
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
56
73
|
|
|
57
|
-
|
|
74
|
+
---
|
|
58
75
|
|
|
59
|
-
|
|
76
|
+
## Props
|
|
60
77
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
78
|
+
| Prop | Type | Description |
|
|
79
|
+
| -------- | -------------------------------------- | ---------------------------------------------------------------------------------- |
|
|
80
|
+
| children | `React.ReactElement` | The `TextInput` (or any custom input) you want to wrap. |
|
|
81
|
+
| onPaste | `(payload: PasteEventPayload) => void` | Callback fired when a paste event is detected. Receives pasted text or image URIs. |
|
|
82
|
+
|
|
83
|
+
---
|
|
64
84
|
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
## Types
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
type PasteEventPayload =
|
|
89
|
+
| { type: "text"; value: string }
|
|
90
|
+
| { type: "images"; uris: string[] }
|
|
91
|
+
| { type: "unsupported" };
|
|
67
92
|
```
|
|
68
93
|
|
|
69
|
-
|
|
94
|
+
* `text` → pasted text
|
|
95
|
+
* `images` → local file URIs (`file://...`)
|
|
96
|
+
* `unsupported` → anything else
|
|
70
97
|
|
|
71
|
-
|
|
98
|
+
---
|
|
72
99
|
|
|
73
|
-
|
|
74
|
-
import {
|
|
75
|
-
TextInputWrapper,
|
|
76
|
-
PasteEventPayload,
|
|
77
|
-
} from "@/modules/text-input-wrapper";
|
|
78
|
-
import { TextInput } from "react-native";
|
|
100
|
+
## Why a wrapper?
|
|
79
101
|
|
|
80
|
-
|
|
81
|
-
const handlePaste = (payload: PasteEventPayload) => {
|
|
82
|
-
if (payload.type === "images") {
|
|
83
|
-
// payload.uris contains file:// URIs for each pasted image
|
|
84
|
-
console.log("Pasted images:", payload.uris);
|
|
85
|
-
} else if (payload.type === "text") {
|
|
86
|
-
// payload.value contains the pasted text
|
|
87
|
-
console.log("Pasted text:", payload.value);
|
|
88
|
-
}
|
|
89
|
-
};
|
|
102
|
+
This library does **not** reimplement `TextInput`.
|
|
90
103
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
104
|
+
Instead:
|
|
105
|
+
|
|
106
|
+
```tsx
|
|
107
|
+
<TextInputWrapper>
|
|
108
|
+
<TextInput />
|
|
109
|
+
</TextInputWrapper>
|
|
97
110
|
```
|
|
98
111
|
|
|
112
|
+
This means:
|
|
113
|
+
|
|
114
|
+
* you keep full control of your input
|
|
115
|
+
* works with any custom TextInput
|
|
116
|
+
* no prop mirroring
|
|
117
|
+
* future-proof with RN updates
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## Platform behavior
|
|
122
|
+
|
|
123
|
+
### iOS
|
|
124
|
+
|
|
125
|
+
* Intercepts native `paste(_:)`
|
|
126
|
+
* Extracts images from `UIPasteboard`
|
|
127
|
+
* Saves to temp files
|
|
128
|
+
* Preserves GIFs
|
|
129
|
+
|
|
130
|
+
### Android
|
|
131
|
+
|
|
132
|
+
* Uses `OnReceiveContentListener` + `ActionMode`
|
|
133
|
+
* Prevents Android "Can't paste images" toast
|
|
134
|
+
* Saves pasted media to cache
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
99
138
|
## Notes
|
|
100
139
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
- Image paste events _prevent_ the default paste (since TextInput can't display images)
|
|
140
|
+
* Image URIs are temporary files, move them if you need persistence.
|
|
141
|
+
* Text paste events fire after the text is inserted.
|
|
142
|
+
* Image paste events prevent default paste (since TextInput can't render images).
|
|
143
|
+
* Web is currently a no-op implementation.
|
|
106
144
|
|
|
107
|
-
|
|
145
|
+
---
|
|
108
146
|
|
|
109
147
|
## Inspiration
|
|
110
148
|
|
|
111
|
-
|
|
149
|
+
Inspired by work from:
|
|
112
150
|
|
|
113
|
-
- **
|
|
114
|
-
- Blog + context: how native paste handling works in v0
|
|
115
|
-
- 𝕏: **[How we built the v0 iOS app](https://x.com/fernandorojo/status/1993098916456452464)**
|
|
116
|
-
- **[v0](https://v0.dev)** — The real-world product discussed in Fernando Rojo’s writing
|
|
151
|
+
- **Fernando Rojo** — [native paste handling in the v0 app](https://x.com/fernandorojo/status/1993098916456452464)
|
|
117
152
|
|
|
118
|
-
|
|
153
|
+
- **v0.dev** — real-world product pushing React Native closer to native UX
|
|
119
154
|
|
|
120
155
|
---
|
|
121
156
|
|
|
122
|
-
|
|
123
|
-
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT
|
package/package.json
CHANGED
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
File without changes
|