react-native-typerich 1.0.0 → 2.2.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.
Files changed (41) hide show
  1. package/README.md +251 -10
  2. package/ReactNativeTypeRich.podspec +41 -0
  3. package/android/src/main/java/com/typerich/TypeRichTextInputView.kt +37 -10
  4. package/android/src/main/java/com/typerich/TypeRichTextInputViewManager.kt +5 -0
  5. package/ios/TypeRichTextInputView.h +27 -7
  6. package/ios/TypeRichTextInputView.mm +809 -26
  7. package/ios/cpp/TypeRichTextInputViewComponentDescriptor.h +19 -0
  8. package/ios/cpp/TypeRichTextInputViewShadowNode.h +44 -0
  9. package/ios/cpp/TypeRichTextInputViewShadowNode.mm +110 -0
  10. package/ios/cpp/TypeRichTextInputViewState.cpp +10 -0
  11. package/ios/cpp/TypeRichTextInputViewState.h +22 -0
  12. package/ios/inputTextView/TypeRichUITextView.h +14 -0
  13. package/ios/inputTextView/TypeRichUITextView.mm +100 -0
  14. package/ios/modules/commands/TypeRichTextInputCommands.h +24 -0
  15. package/ios/modules/commands/TypeRichTextInputCommands.mm +392 -0
  16. package/ios/utils/StringUtils.h +19 -0
  17. package/ios/utils/StringUtils.mm +15 -0
  18. package/ios/utils/TextInputUtils.h +26 -0
  19. package/ios/utils/TextInputUtils.mm +58 -0
  20. package/lib/module/TypeRichTextInput.js +13 -36
  21. package/lib/module/TypeRichTextInput.js.map +1 -1
  22. package/lib/module/TypeRichTextInputNativeComponent.ts +266 -52
  23. package/lib/module/index.js +1 -0
  24. package/lib/module/index.js.map +1 -1
  25. package/lib/module/types/TypeRichTextInput.js +4 -0
  26. package/lib/module/types/TypeRichTextInput.js.map +1 -0
  27. package/lib/typescript/src/TypeRichTextInput.d.ts +2 -22
  28. package/lib/typescript/src/TypeRichTextInput.d.ts.map +1 -1
  29. package/lib/typescript/src/TypeRichTextInputNativeComponent.d.ts +200 -14
  30. package/lib/typescript/src/TypeRichTextInputNativeComponent.d.ts.map +1 -1
  31. package/lib/typescript/src/index.d.ts +1 -1
  32. package/lib/typescript/src/index.d.ts.map +1 -1
  33. package/lib/typescript/src/types/TypeRichTextInput.d.ts +95 -0
  34. package/lib/typescript/src/types/TypeRichTextInput.d.ts.map +1 -0
  35. package/package.json +1 -1
  36. package/src/TypeRichTextInput.tsx +20 -70
  37. package/src/TypeRichTextInputNativeComponent.ts +266 -52
  38. package/src/index.tsx +1 -5
  39. package/src/types/TypeRichTextInput.tsx +116 -0
  40. package/TypeRichTextInput.podspec +0 -20
  41. package/ios/TypeRichTextInputViewManager.mm +0 -27
package/README.md CHANGED
@@ -8,39 +8,271 @@ currently available only for android
8
8
  ```sh
9
9
  npm install react-native-typerich
10
10
  ```
11
+ > [!NOTE]
12
+ > you will need to prebuild app in expo
11
13
 
12
14
  ## Usage
13
15
 
14
- ```js
16
+ ```jsx
15
17
  import { TypeRichTextInput } from 'react-native-typerich';
16
18
 
17
19
  // ...
18
-
19
20
  <TypeRichTextInput
20
- ref={ref}
21
+ ref={inputRef}
22
+ value={value}
21
23
  style={styles.typeRichTextInput}
22
24
  placeholder="Type here..."
23
25
  placeholderTextColor="rgb(0, 26, 114)"
24
- selectionColor="red"
25
- cursorColor="green"
26
+ editable={true}
27
+ selectionColor="deepskyblue"
28
+ cursorColor="dodgerblue"
26
29
  autoCapitalize="words"
30
+ autoFocus
27
31
  onChangeText={(text: string) => console.log(text)}
28
32
  onFocus={() => console.log('focused')}
29
33
  onBlur={() => console.log('blurred')}
30
34
  onChangeSelection={(e: { start: number, end: number, text: string }) =>
31
35
  console.log(e)
32
36
  }
33
- androidExperimentalSynchronousEvents={true}
34
- multiline
35
- numberOfLines={5}
36
37
  onPasteImageData={(e) => {
38
+ setImage(e);
37
39
  console.log(e);
38
40
  }}
39
- defaultValue="TypeRichTextInput"
40
- keyboardAppearance="dark" // ios only
41
+ androidExperimentalSynchronousEvents={true} // not tested very well
42
+ multiline
43
+ numberOfLines={5}
44
+ lineHeight={22}
45
+ fontFamily="serif"
46
+ fontStyle="italic"
47
+ fontWeight={'700'}
48
+ fontSize={26}
49
+ color="darkgreen"
41
50
  />;
42
51
  ```
43
52
 
53
+ ## Props
54
+
55
+ - **Props that works Same as React Native's Default `TextInput`:**
56
+
57
+ ```ts
58
+ value?: string;
59
+ autoFocus?: boolean;
60
+ editable?: boolean;
61
+ defaultValue?: string;
62
+ placeholder?: string;
63
+ placeholderTextColor?: ColorValue;
64
+ cursorColor?: ColorValue;
65
+ selectionColor?: ColorValue;
66
+ autoCapitalize?: string;
67
+ scrollEnabled?: boolean;
68
+ secureTextEntry?: boolean;
69
+ ```
70
+
71
+ - **Styling Props you need to pass externally:**
72
+
73
+ ```ts
74
+ color?: ColorValue;
75
+ fontSize?: Float;
76
+ fontFamily?: string;
77
+ fontWeight?: string;
78
+ fontStyle?: string;
79
+ lineHeight?: Float;
80
+ ```
81
+
82
+ - **props that have some bugs:**
83
+
84
+ ```ts
85
+ multiline?: boolean;
86
+ numberOfLines?: Int32;
87
+ ```
88
+
89
+ > using this togather adds some extra height sometimes.
90
+ > use multline without numberOfLines and it works fine
91
+ > use `maxHeight` instead of number of lines
92
+
93
+ > [!NOTE]
94
+ > This is not a Major bug and the change is unnoticable
95
+
96
+ ## Events
97
+
98
+ ### 1. onFocus
99
+
100
+ callback signature
101
+
102
+ ```ts
103
+ onFocus?: () => void;
104
+ ```
105
+
106
+ ### 2. onBlur
107
+
108
+ callback signature
109
+
110
+ ```ts
111
+ onBlur?: () => void;
112
+ ```
113
+
114
+ ### 3. onChangeText
115
+
116
+ callback signature
117
+
118
+ ```ts
119
+ onChangeText?: (value: string) => void;
120
+ ```
121
+
122
+ ### 4. onChangeSelection
123
+
124
+ callback signature
125
+
126
+ ```ts
127
+ onChangeSelection?: (event: {
128
+ start: number;
129
+ end: number;
130
+ text: string;
131
+ }) => void;
132
+ ```
133
+
134
+ ### 4. onPasteImageData
135
+
136
+ > fires on when user paste image
137
+
138
+ callback signature
139
+
140
+ ```ts
141
+ onPasteImageData?: (data: {
142
+ uri: string;
143
+ type: string;
144
+ fileName: string;
145
+ fileSize: Double;
146
+ source: 'keyboard' | 'clipboard' | 'context_menu'; // it never receives source as 'context_menu' and will be removed in future we suggest not using it
147
+ error?: { message: string };
148
+ }) => void;
149
+ ```
150
+
151
+ ### Event props
152
+
153
+ - `uri`: uri of the image, can be directly used or passed to Image comp
154
+ - `type`: mime type of image
155
+ - `fileName`: File name of image (always starts with `typerich_` prefix)
156
+ - `fileSize`: File Size in bytes
157
+ - `source`: its enum with two possible values
158
+ - `keyboard`: if its pasted from gboard's clipboard or is a sticker or something similar
159
+ - `clipboard`: if its pasted from context menu (long press)
160
+ - `error`: error message if there is any
161
+
162
+ ## Commands
163
+
164
+ ### 1. focus()
165
+
166
+ > use to get programmatic focus on TextInput
167
+
168
+ Command signature
169
+
170
+ ```ts
171
+ focus: () => void;
172
+ ```
173
+
174
+ useage
175
+
176
+ ```ts
177
+ inputRef.current?.focus();
178
+ ```
179
+
180
+ ### 2. blur()
181
+
182
+ > use to programmatically blur TextInput
183
+
184
+ Command signature
185
+
186
+ ```ts
187
+ blur: () => void;
188
+ ```
189
+
190
+ useage
191
+
192
+ ```ts
193
+ inputRef.current?.blur();
194
+ ```
195
+
196
+ ### 3. setText(text)
197
+
198
+ > use to set the value of TextInput (replaces whole content)
199
+
200
+ > [!NOTE]
201
+ > it does not updates selection automatically use have to call `setSelection()`
202
+
203
+ Command signature
204
+
205
+ ```ts
206
+ setText: (text: string) => void;
207
+ ```
208
+
209
+ useage
210
+
211
+ ```ts
212
+ inputRef.current?.setText('This is Text');
213
+ ```
214
+
215
+ ### 4. insertTextAt(start, end, text)
216
+
217
+ > use to insert value at specific position (keeps content of TextInput)
218
+
219
+ > [!NOTE]
220
+ > it preserves the cursor and updates the selection
221
+ > no need to call the `setSelection` after this
222
+
223
+ Command signature
224
+
225
+ ```ts
226
+ insertTextAt: (start: number, end: number, text: string) => void;
227
+ ```
228
+
229
+ useage
230
+
231
+ ```ts
232
+ inputRef.current?.insertTextAt(4, 6, 'This is Text');
233
+ ```
234
+
235
+ ### 5. setSelection(start, end)
236
+
237
+ > use to set the value of TextInput (replaces whole content)
238
+
239
+ Command signature
240
+
241
+ ```ts
242
+ setSelection: (start: number, end: number) => void;
243
+ ```
244
+
245
+ useage
246
+
247
+ ```ts
248
+ inputRef.current?.setSelection(4, 6);
249
+ ```
250
+
251
+ ### 6. getNativeRef()
252
+
253
+ > use to get the internal ref object of the TypeRichTextInput
254
+
255
+ > [!NOTE]
256
+ > you mostly does not need to use this
257
+ > only use this when you need to use the ref in certain cases like following
258
+
259
+ ```ts
260
+ const hostRef = input?.getNativeRef?.();
261
+ const node = findNodeHandle(hostRef); // findNodeHandle is from 'react-native'
262
+ ```
263
+
264
+ Command signature
265
+
266
+ ```ts
267
+ getNativeRef: () => any | null;
268
+ ```
269
+
270
+ useage
271
+
272
+ ```ts
273
+ inputRef.current?.getNativeRef();
274
+ ```
275
+
44
276
  ## Contributing
45
277
 
46
278
  - [Development workflow](CONTRIBUTING.md#development-workflow)
@@ -54,3 +286,12 @@ MIT
54
286
  ---
55
287
 
56
288
  Made with [create-react-native-library](https://github.com/callstack/react-native-builder-bob)
289
+
290
+ ## Credits
291
+
292
+ - **Divyanshu Patil** – Author & maintainer
293
+ - Built with help from the open-source community ❤️
294
+
295
+ ### special thanks to [Software-Mansion](https://github.com/software-mansion) for the custom shadow node code
296
+
297
+ checkout [react-native-enriched](https://github.com/software-mansion/react-native-enriched) by [software-mansion](https://github.com/software-mansion)
@@ -0,0 +1,41 @@
1
+ require "json"
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, "package.json")))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = "ReactNativeTypeRich"
7
+ s.version = package["version"]
8
+ s.summary = package["description"]
9
+ s.homepage = package["homepage"]
10
+ s.license = package["license"]
11
+ s.authors = package["author"]
12
+
13
+ s.platforms = { :ios => min_ios_version_supported }
14
+ s.source = { :git => "https://github.com/divyanshu-patil/react-native-typerich.git", :tag => "#{s.version}" }
15
+
16
+ # s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
+ s.source_files = "ios/**/*.{h,m,mm,cpp}"
18
+ s.private_header_files = "ios/**/*.h"
19
+
20
+ s.public_header_files = [
21
+ "ios/**/*.h",
22
+ "ios/cpp/**/*.h"
23
+ ]
24
+
25
+ s.header_mappings_dir = "ios"
26
+
27
+ s.pod_target_xcconfig = {
28
+ "HEADER_SEARCH_PATHS" => '"$(PODS_TARGET_SRCROOT)/ios"',
29
+ "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
30
+ }
31
+
32
+ # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
33
+ # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
34
+ if respond_to?(:install_modules_dependencies, true)
35
+ install_modules_dependencies(s)
36
+ else
37
+ s.dependency "React-Core"
38
+ end
39
+
40
+ end
41
+
@@ -64,6 +64,7 @@ class TypeRichTextInputView : AppCompatEditText {
64
64
  private var lineHeightPx: Int? = null
65
65
  private var isSettingTextFromJS = false
66
66
  private var isInitialized = false
67
+ private var disableImagePasting = false
67
68
 
68
69
  constructor(context: Context) : super(context) {
69
70
  prepareComponent()
@@ -140,23 +141,34 @@ class TypeRichTextInputView : AppCompatEditText {
140
141
  override fun onCreateInputConnection(outAttrs: EditorInfo): InputConnection? {
141
142
  val ic = super.onCreateInputConnection(outAttrs) ?: return null
142
143
 
143
- EditorInfoCompat.setContentMimeTypes(
144
- outAttrs,
145
- arrayOf("image/png", "image/jpg", "image/jpeg", "image/gif", "image/webp")
146
- )
144
+ if (!disableImagePasting) {
145
+ EditorInfoCompat.setContentMimeTypes(
146
+ outAttrs,
147
+ arrayOf("image/png", "image/jpg", "image/jpeg", "image/gif", "image/webp")
148
+ )
149
+
150
+ return InputConnectionCompat.createWrapper(ic, outAttrs, onCommitContent)
151
+ }
147
152
 
148
- return InputConnectionCompat.createWrapper(ic, outAttrs, onCommitContent)
153
+ return ic
149
154
  }
150
155
 
151
156
  private val onCommitContent = InputConnectionCompat.OnCommitContentListener { info, flags, _ ->
152
157
  try {
153
- // request permission if needed
154
- if ((flags and InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0) {
158
+ val hasPermission =
159
+ (flags and InputConnectionCompat.INPUT_CONTENT_GRANT_READ_URI_PERMISSION) != 0
160
+
161
+ if (hasPermission) {
155
162
  try {
156
163
  info.requestPermission()
157
- } catch (ex: Exception) {
158
- // permission failed
164
+ } catch (_: Exception) {}
165
+ }
166
+
167
+ if (disableImagePasting) {
168
+ if (hasPermission) {
169
+ try { info.releasePermission() } catch (_: Exception) {}
159
170
  }
171
+ return@OnCommitContentListener false
160
172
  }
161
173
 
162
174
  val uri = info.contentUri
@@ -189,7 +201,7 @@ class TypeRichTextInputView : AppCompatEditText {
189
201
  }
190
202
  }
191
203
 
192
- // paste handler
204
+ // context menu paste handler
193
205
  override fun onTextContextMenuItem(id: Int): Boolean {
194
206
  if (id == android.R.id.paste || id == android.R.id.pasteAsPlainText) {
195
207
  val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
@@ -198,6 +210,12 @@ class TypeRichTextInputView : AppCompatEditText {
198
210
  val clip = clipboard.primaryClip ?: return super.onTextContextMenuItem(id)
199
211
  val item = clip.getItemAt(0)
200
212
 
213
+ if (disableImagePasting) {
214
+ if (item.uri != null || item.intent?.data != null) {
215
+ return true
216
+ }
217
+ }
218
+
201
219
  // uri
202
220
  item.uri?.let { uri ->
203
221
  val source = EnumPasteSource.CLIPBOARD.value
@@ -496,6 +514,7 @@ class TypeRichTextInputView : AppCompatEditText {
496
514
  val sizePx = ceil(PixelUtil.toPixelFromSP(size))
497
515
  fontSize = sizePx
498
516
  setTextSize(TypedValue.COMPLEX_UNIT_PX, sizePx)
517
+ layoutManager.invalidateLayout()
499
518
  }
500
519
 
501
520
  fun setFontFamily(family: String?) {
@@ -539,6 +558,10 @@ class TypeRichTextInputView : AppCompatEditText {
539
558
  }
540
559
 
541
560
  fun setSecureTextEntry(isSecure: Boolean) {
561
+ if (isSecure) {
562
+ setMultiline(false)
563
+ maxLines = 1
564
+ }
542
565
  transformationMethod =
543
566
  if (isSecure)
544
567
  android.text.method.PasswordTransformationMethod.getInstance()
@@ -572,6 +595,10 @@ class TypeRichTextInputView : AppCompatEditText {
572
595
  }
573
596
  }
574
597
 
598
+ fun setDisableImagePasting(disabled: Boolean){
599
+ this.disableImagePasting = disabled
600
+ }
601
+
575
602
  override fun isLayoutRequested(): Boolean {
576
603
  return false
577
604
  }
@@ -145,6 +145,11 @@ class TypeRichTextInputViewManager :
145
145
  view?.setLineHeightReact(lineHeight)
146
146
  }
147
147
 
148
+ @ReactProp(name = "disableImagePasting")
149
+ override fun setDisableImagePasting(view: TypeRichTextInputView?, value: Boolean) {
150
+ view?.setDisableImagePasting(value)
151
+ }
152
+
148
153
  override fun onAfterUpdateTransaction(view: TypeRichTextInputView) {
149
154
  super.onAfterUpdateTransaction(view)
150
155
  view.afterUpdateTransaction()
@@ -1,14 +1,34 @@
1
1
  #import <React/RCTViewComponentView.h>
2
+ #import <react/renderer/core/State.h>
2
3
  #import <UIKit/UIKit.h>
3
4
 
4
- #ifndef TypeRichTextInputViewNativeComponent_h
5
- #define TypeRichTextInputViewNativeComponent_h
6
-
7
5
  NS_ASSUME_NONNULL_BEGIN
8
6
 
9
- @interface TypeRichTextInputView : RCTViewComponentView
10
- @end
7
+ @interface TypeRichTextInputView : RCTViewComponentView <UITextViewDelegate>
11
8
 
12
- NS_ASSUME_NONNULL_END
9
+ @property(nonatomic, assign) BOOL blockEmitting;
10
+ @property (atomic, assign) BOOL isUserTyping;
11
+ @property (atomic, assign) CFTimeInterval lastTypingTime;
12
+
13
+ - (CGSize)measureSize:(CGFloat)maxWidth;
14
+
15
+ // events
16
+ - (void)emitPasteImageEventWith:(NSString *)uri
17
+ type:(NSString *)type
18
+ fileName:(NSString *)fileName
19
+ fileSize:(NSUInteger)fileSize;
13
20
 
14
- #endif /* TypeRichTextInputViewNativeComponent_h */
21
+ // commands
22
+ - (void)handleCommand:(NSString *)commandName
23
+ args:(NSArray *)args;
24
+
25
+ // helpers used by commands
26
+ - (BOOL)isTouchInProgress;
27
+ //- (BOOL)isHandlingUserInput;
28
+ - (void)invalidateTextLayoutFromCommand;
29
+ - (void)updatePlaceholderVisibilityFromCommand;
30
+ - (void)dispatchSelectionChangeIfNeeded;
31
+ - (BOOL)isDisableImagePasting;
32
+
33
+ @end
34
+ NS_ASSUME_NONNULL_END