expo-paste-input 0.1.1 → 0.1.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.
Files changed (2) hide show
  1. package/README.md +114 -78
  2. package/package.json +10 -3
package/README.md CHANGED
@@ -1,123 +1,159 @@
1
- # TextInputWrapper
1
+ # expo-paste-input
2
2
 
3
- A native Expo module for cross-platform paste event handling in React Native TextInput components.
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
- ## Demo
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
- | iOS | Android |
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
- ## Overview
13
+ ---
12
14
 
13
- This is **not a published npm package**. This is the exact production-ready module used inside a real app. If you want to use it, copy the `modules/text-input-wrapper` folder into your Expo project and run it.
15
+ ## Features
14
16
 
15
- ### What it does
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
- - Intercepts paste events on TextInput components
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
- ### Paste event payload
26
+ ## Installation
23
27
 
24
- ```typescript
25
- type PasteEventPayload =
26
- | { type: "text"; value: string }
27
- | { type: "images"; uris: string[] }
28
- | { type: "unsupported" };
29
- ```
28
+ ### Quick install
30
29
 
31
- ## How it works
30
+ ```bash
31
+ npx expo install expo-paste-input
32
+ ````
32
33
 
33
- The module wraps a TextInput and intercepts paste events at the native level before they reach the default handler.
34
+ or
34
35
 
35
- ### iOS
36
+ ```bash
37
+ yarn add expo-paste-input
38
+ ```
36
39
 
37
- On iOS, the module uses method swizzling to intercept `paste(_:)` on the underlying `UITextField`/`UITextView`. When a paste is detected:
40
+ ### Rebuild the app (required)
38
41
 
39
- - For **images**: The paste is intercepted, images are extracted from `UIPasteboard`, saved to temp files, and URIs are sent to JavaScript. GIFs are preserved as-is.
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
- The wrapper view is transparent and passes through all touch events to children.
44
+ ```bash
45
+ npx expo run:ios
46
+ npx expo run:android
47
+ ```
43
48
 
44
- ### Android
49
+ (Expo Go will not work)
45
50
 
46
- On Android, the module uses `OnReceiveContentListener` (API 31+) and a custom `ActionMode.Callback` to intercept paste events from both keyboard and context menu:
51
+ ---
47
52
 
48
- - For **images**: The paste is consumed before Android shows the "Can't add images" toast. Images are decoded, saved to cache, and URIs are sent to JavaScript.
49
- - For **text**: The original paste proceeds normally, and the pasted text is forwarded to JavaScript.
53
+ ## Usage
50
54
 
51
- The wrapper view is non-interactive and delegates all touch events to children.
55
+ Wrap your `TextInput` with `TextInputWrapper`:
52
56
 
53
- ## Usage
57
+ ```tsx
58
+ import { TextInputWrapper } from "expo-paste-input";
59
+ import { TextInput } from "react-native";
54
60
 
55
- ### 1. Copy the module
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
- Copy the `modules/text-input-wrapper` folder into your Expo project's `modules` directory.
74
+ ---
58
75
 
59
- ### 2. Run prebuild / pod install
76
+ ## Props
60
77
 
61
- ```bash
62
- # If using Expo prebuild
63
- npx expo prebuild
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
- # Or if you already have native projects
66
- cd ios && pod install
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
- ### 3. Import and use
94
+ * `text` pasted text
95
+ * `images` → local file URIs (`file://...`)
96
+ * `unsupported` → anything else
70
97
 
71
- Wrap your TextInput with `TextInputWrapper` and handle the `onPaste` callback:
98
+ ---
72
99
 
73
- ```tsx
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
- function MyInput() {
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
- return (
92
- <TextInputWrapper onPaste={handlePaste}>
93
- <TextInput placeholder="Paste here..." />
94
- </TextInputWrapper>
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
- - This is **intentionally not packaged as a library**
102
- - It's meant to be copied, modified, and extended for your specific needs
103
- - The image URIs point to temporary files move or copy them if you need persistence
104
- - Text paste events fire _after_ the text is inserted into the input
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
- If you want to build a library on top of this, feel free. Please credit **Arunabh Verma** as inspiration.
145
+ ---
108
146
 
109
147
  ## Inspiration
110
148
 
111
- This project exists because of inspiration from:
149
+ Inspired by work from:
112
150
 
113
- - **[Fernando Rojo](https://x.com/fernandorojo)** — Inspired by his blog post how paste input is implemented in the v0 app
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
- Their work on pushing React Native closer to native platform conventions was the catalyst for building this.
153
+ - **v0.dev** real-world product pushing React Native closer to native UX
119
154
 
120
155
  ---
121
156
 
122
- Built by **Arunabh Verma**
123
- Demo: [X post ↗](https://x.com/iamarunabh/status/1997738168247062774)
157
+ ## License
158
+
159
+ MIT
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-paste-input",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "A wrapper around React Native TextInput to paste images and GIFs from the clipboard (iOS, Android, Web)",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -18,8 +18,15 @@
18
18
  "keywords": [
19
19
  "react-native",
20
20
  "expo",
21
- "expo-paste-input",
22
- "ExpoPasteInput"
21
+ "react-native-paste-input",
22
+ "react-native-image-paste-input",
23
+ "clipboard",
24
+ "paste",
25
+ "paste-image",
26
+ "paste-gif",
27
+ "textinput",
28
+ "react-native-clipboard",
29
+ "react-native-textinput"
23
30
  ],
24
31
  "repository": "https://github.com/arunabhverma/expo-paste-input",
25
32
  "bugs": {