react-native-earl-accessory-view 1.0.0

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ORDOVEZ, E,R
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 ADDED
@@ -0,0 +1,453 @@
1
+ # react-native-earl-accessory-view
2
+
3
+ [![npm version](https://img.shields.io/npm/v/react-native-earl-accessory-view.svg)](https://www.npmjs.com/package/react-native-earl-accessory-view)
4
+ [![license](https://img.shields.io/npm/l/react-native-earl-accessory-view.svg)](https://github.com/ordovez-er/react-native-earl-accessory-view/blob/main/LICENSE)
5
+ [![platform](https://img.shields.io/badge/platform-iOS%20%7C%20Android%20%7C%20Web-blue.svg)]()
6
+
7
+ A **keyboard input preview bar** for React Native. When the keyboard covers your input field, this bar sits above the keyboard showing what the user is typing in real-time — so they never lose sight of their input.
8
+
9
+ Fully customizable with **action buttons**, smooth animations, and cross-platform support on **iOS**, **Android**, and **Web**.
10
+
11
+ ---
12
+
13
+ ## The Problem
14
+
15
+ On mobile, the soft keyboard frequently covers `TextInput` fields — especially inputs near the bottom of the screen. Users end up typing blind, unsure what they've entered.
16
+
17
+ ## The Solution
18
+
19
+ `react-native-earl-accessory-view` adds a slim bar above the keyboard that **mirrors what the user is typing** in real-time. When the keyboard goes away, the bar animates out smoothly.
20
+
21
+ ---
22
+
23
+ ## Features
24
+
25
+ - **Live input preview** — shows the current value above the keyboard in real-time
26
+ - **Action buttons** — add send, attach, navigate, or any custom buttons with icons
27
+ - **Fully customizable** — colors, fonts, height, borders, shadows, padding — everything
28
+ - **Smooth animations** — fade + slide transitions synced with keyboard events
29
+ - **Text positioning** — place preview text on the left or right
30
+ - **Dismiss button** — auto-positions opposite to the text, fully styleable, or hide it
31
+ - **Field label** — optional label like "Email" or "Password" next to the preview
32
+ - **Character counter** — built-in with warning/error color thresholds
33
+ - **Cross-platform** — iOS `InputAccessoryView`, Android absolute positioning, Web fallback
34
+ - **Safe area aware** — respects bottom safe areas on notched devices
35
+ - **Custom children** — override the default layout with any React Native content
36
+ - **TypeScript first** — full type definitions with JSDoc
37
+
38
+ ---
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ npm install react-native-earl-accessory-view
44
+ # or
45
+ yarn add react-native-earl-accessory-view
46
+ ```
47
+
48
+ > **Peer dependencies:** `react >= 16.8.0` and `react-native >= 0.60.0`
49
+
50
+ ---
51
+
52
+ ## Quick Start
53
+
54
+ ```tsx
55
+ import React, { useState } from "react";
56
+ import { TextInput, View } from "react-native";
57
+ import { AccessoryView } from "react-native-earl-accessory-view";
58
+
59
+ export default function App() {
60
+ const [text, setText] = useState("");
61
+
62
+ return (
63
+ <View style={{ flex: 1, justifyContent: "flex-end", padding: 20 }}>
64
+ <TextInput
65
+ value={text}
66
+ onChangeText={setText}
67
+ placeholder="Type something..."
68
+ style={{
69
+ borderWidth: 1,
70
+ borderColor: "#ccc",
71
+ padding: 12,
72
+ borderRadius: 8,
73
+ }}
74
+ />
75
+
76
+ {/* This bar appears above the keyboard, previewing what you type */}
77
+ <AccessoryView value={text} onValueChange={setText} />
78
+ </View>
79
+ );
80
+ }
81
+ ```
82
+
83
+ ---
84
+
85
+ ## Usage Examples
86
+
87
+ ### Basic Preview
88
+
89
+ ```tsx
90
+ const [text, setText] = useState("");
91
+
92
+ <TextInput value={text} onChangeText={setText} />
93
+ <AccessoryView value={text} onValueChange={setText} />
94
+ ```
95
+
96
+ ### Send Button
97
+
98
+ Replace the dismiss button with a send action:
99
+
100
+ ```tsx
101
+ <AccessoryView
102
+ value={text}
103
+ onValueChange={setText}
104
+ actionButtons={[
105
+ {
106
+ id: "send",
107
+ content: "Send",
108
+ onPress: () => {
109
+ console.log("Sending:", text);
110
+ setText("");
111
+ },
112
+ disabled: !text.trim(),
113
+ position: "right",
114
+ },
115
+ ]}
116
+ />
117
+ ```
118
+
119
+ ### Send with Custom Icon
120
+
121
+ Use any icon library (lucide-react-native, @expo/vector-icons, etc.):
122
+
123
+ ```tsx
124
+ import { Send } from "lucide-react-native";
125
+
126
+ <AccessoryView
127
+ value={text}
128
+ onValueChange={setText}
129
+ actionButtons={[
130
+ {
131
+ id: "send",
132
+ content: <Send size={20} color="#007AFF" />,
133
+ onPress: handleSend,
134
+ disabled: !text.trim(),
135
+ },
136
+ ]}
137
+ />;
138
+ ```
139
+
140
+ ### Switch Between Inputs (Prev / Next)
141
+
142
+ Navigate between form fields with any icon library:
143
+
144
+ ```tsx
145
+ import { ChevronLeft, ChevronRight } from "lucide-react-native";
146
+
147
+ <AccessoryView
148
+ value={currentValue}
149
+ onValueChange={setCurrentValue}
150
+ label={currentField === "email" ? "Email" : "Password"}
151
+ actionButtons={[
152
+ {
153
+ id: "prev",
154
+ content: <ChevronLeft size={20} color="#6366F1" />,
155
+ onPress: () => emailRef.current?.focus(),
156
+ disabled: currentField === "email",
157
+ position: "left",
158
+ },
159
+ {
160
+ id: "next",
161
+ content: <ChevronRight size={20} color="#6366F1" />,
162
+ onPress: () => passwordRef.current?.focus(),
163
+ disabled: currentField === "password",
164
+ position: "right",
165
+ },
166
+ ]}
167
+ showDismissButton={true}
168
+ />;
169
+ ```
170
+
171
+ ### Multi-Action Bar (Attach + Camera + Send)
172
+
173
+ Combine multiple actions — `content` accepts **any ReactNode** and `onPress` accepts **any function**:
174
+
175
+ ```tsx
176
+ import { Paperclip, Camera, Send } from "lucide-react-native";
177
+
178
+ <AccessoryView
179
+ value={text}
180
+ onValueChange={setText}
181
+ actionButtons={[
182
+ {
183
+ id: "attach",
184
+ content: <Paperclip size={20} color="#6B7280" />,
185
+ onPress: openFilePicker,
186
+ position: "left",
187
+ },
188
+ {
189
+ id: "camera",
190
+ content: <Camera size={20} color="#6B7280" />,
191
+ onPress: openCamera,
192
+ position: "left",
193
+ },
194
+ {
195
+ id: "send",
196
+ content: <Send size={20} color="#007AFF" />,
197
+ onPress: handleSend,
198
+ disabled: !text.trim(),
199
+ position: "right",
200
+ },
201
+ ]}
202
+ />;
203
+ ```
204
+
205
+ ### Right-Aligned Text
206
+
207
+ ```tsx
208
+ <AccessoryView
209
+ value={text}
210
+ onValueChange={setText}
211
+ textPosition="right"
212
+ placeholder="Waiting for input..."
213
+ />
214
+ ```
215
+
216
+ ### No Dismiss Button
217
+
218
+ ```tsx
219
+ <AccessoryView value={text} onValueChange={setText} showDismissButton={false} />
220
+ ```
221
+
222
+ ### With a Field Label
223
+
224
+ ```tsx
225
+ <AccessoryView value={text} onValueChange={setText} label="Email" />
226
+ ```
227
+
228
+ ### Character Counter
229
+
230
+ ```tsx
231
+ <AccessoryView
232
+ value={text}
233
+ onValueChange={setText}
234
+ charCount={text.length}
235
+ maxCharCount={280}
236
+ charCountWarningThreshold={0.8}
237
+ />
238
+ ```
239
+
240
+ ### Custom Dismiss Icon (any icon library)
241
+
242
+ ```tsx
243
+ import { X } from "lucide-react-native";
244
+
245
+ <AccessoryView
246
+ value={text}
247
+ onValueChange={setText}
248
+ dismissButtonContent={<X size={18} color="#666" />}
249
+ />;
250
+ ```
251
+
252
+ ### Dark Theme
253
+
254
+ ```tsx
255
+ <AccessoryView
256
+ value={text}
257
+ onValueChange={setText}
258
+ backgroundColor="#1A1A2E"
259
+ valueStyle={{ color: "#FFFFFF" }}
260
+ placeholderStyle={{ color: "#555577" }}
261
+ dismissButtonTextStyle={{ color: "#888899" }}
262
+ borderTopColor="#2A2A44"
263
+ />
264
+ ```
265
+
266
+ ### Custom Children (Full Control)
267
+
268
+ ```tsx
269
+ <AccessoryView backgroundColor="#FFFFFF" height={50}>
270
+ <View
271
+ style={{
272
+ flexDirection: "row",
273
+ alignItems: "center",
274
+ paddingHorizontal: 16,
275
+ flex: 1,
276
+ }}
277
+ >
278
+ <Text style={{ color: "#aaa", fontSize: 11, fontWeight: "700" }}>
279
+ PREVIEW
280
+ </Text>
281
+ <Text style={{ flex: 1, marginLeft: 8 }}>
282
+ {text || "Nothing yet..."}
283
+ </Text>
284
+ <Text style={{ color: "#bbb" }}>{text.length}</Text>
285
+ </View>
286
+ </AccessoryView>
287
+ ```
288
+
289
+ ### iOS InputAccessoryView Connection
290
+
291
+ ```tsx
292
+ import { ACCESSORY_VIEW_NATIVE_ID } from "react-native-earl-accessory-view";
293
+
294
+ <TextInput inputAccessoryViewID={ACCESSORY_VIEW_NATIVE_ID} />
295
+ <AccessoryView value={text} onValueChange={setText} />
296
+ ```
297
+
298
+ ---
299
+
300
+ ## API Reference
301
+
302
+ ### `<AccessoryView />` Props
303
+
304
+ #### Preview Content
305
+
306
+ | Prop | Type | Default | Description |
307
+ | ------------------ | ------------------- | --------------------- | --------------------------------------------- |
308
+ | `value` | `string` | — | Current input value to preview (primary prop) |
309
+ | `placeholder` | `string` | `'Type something...'` | Shown when value is empty |
310
+ | `valueStyle` | `TextStyle` | — | Style for the preview text |
311
+ | `placeholderStyle` | `TextStyle` | — | Style for the placeholder |
312
+ | `editable` | `boolean` | `true` | Allow editing/pasting in the preview bar |
313
+ | `onValueChange` | `(value) => void` | — | Called when preview value changes |
314
+ | `onPress` | `() => void` | — | Called when non-editable preview is tapped |
315
+ | `label` | `string` | — | Optional label (e.g. "Email") |
316
+ | `labelStyle` | `TextStyle` | — | Style for the label |
317
+ | `textPosition` | `'left' \| 'right'` | `'left'` | Preview text side |
318
+ | `textStyle` | `TextStyle` | — | Style for the text |
319
+ | `children` | `ReactNode` | — | Custom content (overrides default layout) |
320
+
321
+ #### Action Buttons
322
+
323
+ | Prop | Type | Default | Description |
324
+ | --------------- | ------------------------- | ------- | ------------------------------------------------------------------------ |
325
+ | `actionButtons` | `AccessoryActionButton[]` | — | Array of action buttons (send, switch input, etc.). See below for shape. |
326
+
327
+ **`AccessoryActionButton` shape:**
328
+
329
+ | Field | Type | Default | Description |
330
+ | -------------------- | --------------------- | --------- | ---------------------------------------------------------- |
331
+ | `id` | `string` | — | Unique key (required) |
332
+ | `onPress` | `() => void` | — | **Any** function — send, attach, navigate, etc. (required) |
333
+ | `content` | `ReactNode \| string` | — | **Any** icon, component, image, or text (required) |
334
+ | `position` | `'left' \| 'right'` | `'right'` | Where to place the button |
335
+ | `disabled` | `boolean` | `false` | Disable the button |
336
+ | `style` | `ViewStyle` | — | Container style override |
337
+ | `textStyle` | `TextStyle` | — | Text style (when `content` is a string) |
338
+ | `accessibilityLabel` | `string` | — | Accessibility label |
339
+ | `testID` | `string` | — | Test ID |
340
+
341
+ > **Note:** When `actionButtons` is provided, the dismiss button is automatically hidden unless `showDismissButton` is explicitly set to `true`.
342
+
343
+ #### Dismiss Button
344
+
345
+ | Prop | Type | Default | Description |
346
+ | ------------------------ | ----------------------------- | ------------------ | -------------------------------------------- |
347
+ | `showDismissButton` | `boolean` | `true` \* | Show/hide the close button |
348
+ | `dismissButtonPosition` | `'auto' \| 'left' \| 'right'` | `'auto'` | Button position (`auto` = opposite of text) |
349
+ | `onDismiss` | `() => void` | `Keyboard.dismiss` | Dismiss callback |
350
+ | `dismissButtonContent` | `ReactNode` | `✕` text | Custom button content (use any icon library) |
351
+ | `dismissButtonStyle` | `ViewStyle` | — | Button container style |
352
+ | `dismissButtonTextStyle` | `TextStyle` | — | Default ✕ text style |
353
+
354
+ \* Defaults to `false` when `actionButtons` is provided.
355
+
356
+ #### Character Counter
357
+
358
+ | Prop | Type | Default | Description |
359
+ | --------------------------- | ------------------------------- | ----------- | ----------------------- |
360
+ | `charCount` | `number` | — | Current count |
361
+ | `maxCharCount` | `number` | — | Max count |
362
+ | `charCountPosition` | `'left' \| 'right' \| 'center'` | `'right'` | Counter position |
363
+ | `charCountWarningThreshold` | `number` | `0.9` | Warning threshold (0–1) |
364
+ | `charCountWarningColor` | `string` | `'#FF9800'` | Warning color |
365
+ | `charCountErrorColor` | `string` | `'#F44336'` | Over-limit color |
366
+
367
+ #### Appearance
368
+
369
+ | Prop | Type | Default | Description |
370
+ | ----------------------- | ----------- | --------------- | ---------------- |
371
+ | `backgroundColor` | `string` | `'#FFFFFF'` | Background color |
372
+ | `height` | `number` | `44` | Bar height |
373
+ | `borderTopWidth` | `number` | `hairlineWidth` | Top border |
374
+ | `borderTopColor` | `string` | `'#E0E0E0'` | Border color |
375
+ | `elevation` | `number` | `4` | Shadow depth |
376
+ | `containerStyle` | `ViewStyle` | — | Outer style |
377
+ | `contentContainerStyle` | `ViewStyle` | — | Inner style |
378
+
379
+ #### Behavior & Animation
380
+
381
+ | Prop | Type | Default | Description |
382
+ | ------------------- | --------- | ------- | ----------------------------- |
383
+ | `animationEnabled` | `boolean` | `true` | Animate transitions |
384
+ | `animationDuration` | `number` | `250` | Duration (ms) |
385
+ | `alwaysVisible` | `boolean` | `false` | Keep visible without keyboard |
386
+ | `safeAreaEnabled` | `boolean` | `true` | Respect safe area (iOS) |
387
+
388
+ #### Callbacks
389
+
390
+ | Prop | Type | Description |
391
+ | ----------------- | ------------------ | ------------------ |
392
+ | `onKeyboardShow` | `(height) => void` | Keyboard appeared |
393
+ | `onKeyboardHide` | `() => void` | Keyboard dismissed |
394
+ | `onAccessoryShow` | `() => void` | Bar appeared |
395
+ | `onAccessoryHide` | `() => void` | Bar hidden |
396
+
397
+ ---
398
+
399
+ ### `<ActionButton />` Component
400
+
401
+ A standalone action button component you can also use independently:
402
+
403
+ ```tsx
404
+ import { ActionButton } from "react-native-earl-accessory-view";
405
+
406
+ <ActionButton
407
+ id="send"
408
+ content="Send"
409
+ onPress={handleSend}
410
+ disabled={!text.trim()}
411
+ />;
412
+ ```
413
+
414
+ ---
415
+
416
+ ### `useKeyboardAccessory(options?)` Hook
417
+
418
+ Use this hook directly for custom keyboard-aware UIs.
419
+
420
+ ```tsx
421
+ const { keyboardVisible, keyboardHeight } = useKeyboardAccessory({
422
+ onKeyboardShow: (height) => console.log("Keyboard height:", height),
423
+ onKeyboardHide: () => console.log("Keyboard hidden"),
424
+ });
425
+ ```
426
+
427
+ ---
428
+
429
+ ### Constants
430
+
431
+ | Constant | Usage |
432
+ | -------------------------- | ----------------------------------------------- |
433
+ | `ACCESSORY_VIEW_NATIVE_ID` | Pass to `TextInput.inputAccessoryViewID` on iOS |
434
+
435
+ ---
436
+
437
+ ## Showcase
438
+
439
+ A ready-to-use demo screen is included at [`example/showcase.tsx`](example/showcase.tsx). Drop it into your app to try every feature interactively — including send buttons, input switching, and multi-action bars.
440
+
441
+ > The showcase uses [lucide-react-native](https://lucide.dev/) for icons. Install it with:
442
+ >
443
+ > ```bash
444
+ > npm install lucide-react-native react-native-svg
445
+ > ```
446
+
447
+ ---
448
+
449
+ ## License
450
+
451
+ MIT © [ORDOVEZ, E,R](https://github.com/ordovez-er)
452
+
453
+ **Author:** ORDOVEZ, E,R
@@ -0,0 +1,33 @@
1
+ import React from "react";
2
+ import { AccessoryViewProps } from "./types";
3
+ declare const ACCESSORY_NATIVE_ID = "earl-accessory-view-native";
4
+ /**
5
+ * AccessoryView — a keyboard input preview bar for React Native.
6
+ *
7
+ * Shows a live preview of what the user is typing above the keyboard,
8
+ * so the input is always visible even when the text field is obscured.
9
+ * The preview is interactive — paste directly or edit the text right
10
+ * in the bar. Changes sync back via onValueChange.
11
+ *
12
+ * Works on iOS, Android, and Web.
13
+ *
14
+ * @example
15
+ * ```tsx
16
+ * const [text, setText] = useState('');
17
+ *
18
+ * <TextInput value={text} onChangeText={setText} />
19
+ * <AccessoryView value={text} onValueChange={setText} />
20
+ * ```
21
+ */
22
+ export declare const AccessoryView: React.FC<AccessoryViewProps>;
23
+ /**
24
+ * The nativeID to use on iOS TextInput for connecting to this accessory view.
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * import { ACCESSORY_VIEW_NATIVE_ID } from 'react-native-earl-accessory-view';
29
+ * <TextInput inputAccessoryViewID={ACCESSORY_VIEW_NATIVE_ID} />
30
+ * ```
31
+ */
32
+ export { ACCESSORY_NATIVE_ID as ACCESSORY_VIEW_NATIVE_ID };
33
+ //# sourceMappingURL=AccessoryView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccessoryView.d.ts","sourceRoot":"","sources":["../src/AccessoryView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAMN,MAAM,OAAO,CAAC;AAaf,OAAO,EAAE,kBAAkB,EAAmC,MAAM,SAAS,CAAC;AAQ9E,QAAA,MAAM,mBAAmB,+BAA+B,CAAC;AAEzD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,kBAAkB,CA8ZtD,CAAC;AAEF;;;;;;;;GAQG;AACH,OAAO,EAAE,mBAAmB,IAAI,wBAAwB,EAAE,CAAC"}