react-native-enriched 0.4.1 → 0.5.1

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 (53) hide show
  1. package/README.md +27 -2
  2. package/ReactNativeEnriched.podspec +5 -1
  3. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +12 -0
  4. package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +4 -0
  5. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.cpp +149 -0
  6. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.h +146 -0
  7. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.cpp +16 -1
  8. package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.h +46 -0
  9. package/android/src/main/java/com/swmansion/enriched/common/GumboNormalizer.kt +5 -0
  10. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCheckboxListSpan.kt +3 -2
  11. package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnorderedListSpan.kt +2 -1
  12. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +166 -20
  13. package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewManager.kt +32 -0
  14. package/android/src/main/java/com/swmansion/enriched/textinput/MeasurementStore.kt +19 -2
  15. package/android/src/main/java/com/swmansion/enriched/textinput/events/OnContextMenuItemPressEvent.kt +35 -0
  16. package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedLineHeightSpan.kt +44 -0
  17. package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +16 -0
  18. package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpanState.kt +18 -12
  19. package/android/src/main/new_arch/CMakeLists.txt +9 -13
  20. package/android/src/main/new_arch/GumboNormalizerJni.cpp +14 -0
  21. package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/conversions.h +2 -21
  22. package/cpp/CMakeLists.txt +50 -0
  23. package/cpp/GumboParser/GumboParser.h +34043 -0
  24. package/cpp/README.md +59 -0
  25. package/cpp/parser/GumboNormalizer.c +915 -0
  26. package/cpp/parser/GumboParser.cpp +16 -0
  27. package/cpp/parser/GumboParser.hpp +23 -0
  28. package/cpp/tests/GumboParserTest.cpp +457 -0
  29. package/ios/EnrichedTextInputView.h +2 -0
  30. package/ios/EnrichedTextInputView.mm +152 -2
  31. package/ios/config/InputConfig.h +3 -0
  32. package/ios/config/InputConfig.mm +15 -0
  33. package/ios/extensions/LayoutManagerExtension.mm +34 -11
  34. package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.cpp +149 -0
  35. package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.h +146 -0
  36. package/ios/generated/ReactNativeEnrichedSpec/Props.cpp +16 -1
  37. package/ios/generated/ReactNativeEnrichedSpec/Props.h +46 -0
  38. package/ios/generated/ReactNativeEnrichedSpec/RCTComponentViewHelpers.h +29 -0
  39. package/ios/inputParser/InputParser.mm +27 -0
  40. package/ios/inputTextView/InputTextView.mm +3 -3
  41. package/lib/module/EnrichedTextInput.js +43 -30
  42. package/lib/module/EnrichedTextInput.js.map +1 -1
  43. package/lib/module/spec/EnrichedTextInputNativeComponent.ts +118 -22
  44. package/lib/typescript/src/EnrichedTextInput.d.ts +24 -6
  45. package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
  46. package/lib/typescript/src/index.d.ts +1 -1
  47. package/lib/typescript/src/index.d.ts.map +1 -1
  48. package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts +111 -21
  49. package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts.map +1 -1
  50. package/package.json +5 -5
  51. package/src/EnrichedTextInput.tsx +79 -40
  52. package/src/index.tsx +0 -1
  53. package/src/spec/EnrichedTextInputNativeComponent.ts +118 -22
package/README.md CHANGED
@@ -1,4 +1,4 @@
1
- <img src="https://github.com/user-attachments/assets/6963e203-38c8-4209-b1b2-1ff65f6765f9" alt="react-native-enriched by Software Mansion" width="100%">
1
+ <img src="https://github.com/user-attachments/assets/c8ba03bc-4ea8-48f4-9566-02ebac0c19d3" alt="react-native-enriched by Software Mansion" width="100%">
2
2
 
3
3
  # react-native-enriched
4
4
 
@@ -30,6 +30,7 @@ We can help you build your next dream product –
30
30
  - [Inline Images](#inline-images)
31
31
  - [Style Detection](#style-detection)
32
32
  - [Other Events](#other-events)
33
+ - [Context Menu Items](#context-menu-items)
33
34
  - [Customizing \<EnrichedTextInput /> styles](#customizing-enrichedtextinput--styles)
34
35
  - [API Reference](#api-reference)
35
36
  - [Known limitations](#known-limitations)
@@ -40,7 +41,8 @@ We can help you build your next dream product –
40
41
  ## Prerequisites
41
42
 
42
43
  - `react-native-enriched` currently supports only Android and iOS platforms
43
- - It works only with [the React Native New Architecture (Fabric)](https://reactnative.dev/architecture/landing-page) and supports following React Native releases: `0.79`, `0.80`, `0.81`, `0.82`, `0.83` and `0.84`.
44
+ - It works only with [the React Native New Architecture (Fabric)](https://reactnative.dev/architecture/landing-page) and supports following React Native releases: `0.81`, `0.82`, `0.83` and `0.84`.
45
+ - If you would like to use `react-native-enriched` in React Native `0.79` or `0.80` use `react-native-enriched 0.4.x`.
44
46
 
45
47
  ## Installation
46
48
 
@@ -232,6 +234,29 @@ You can find some examples in the [usage section](#usage) or in the example app.
232
234
  - [onKeyPress](docs/API_REFERENCE.md#onkeypress) - emits whenever a key is pressed. Follows react-native TextInput's onKeyPress event [spec](https://reactnative.dev/docs/textinput#onkeypress).
233
235
  - [onPasteImages](docs/API_REFERENCE.md#onpasteimages) - returns an array of images details whenever an image/GIF is pasted into the input.
234
236
 
237
+ ## Context Menu Items
238
+
239
+ > **Note:** This feature is currently supported on Android and iOS 16+.
240
+
241
+ You can extend the native text editing menu with custom items using the [contextMenuItems](docs/API_REFERENCE.md#contextmenuitems) prop. Each item has a `text` (title), `visible` flag and an `onPress` callback. Items appear in the specified order, before the system actions.
242
+
243
+ ```tsx
244
+ <EnrichedTextInput
245
+ ref={ref}
246
+ contextMenuItems={[
247
+ {
248
+ text: 'Paste Link',
249
+ onPress: ({ text, selection, styleState }) => {
250
+ if (!styleState.link.isBlocking) {
251
+ ref.current?.setLink(selection.start, selection.end, text, url);
252
+ }
253
+ },
254
+ visible: true,
255
+ },
256
+ ]}
257
+ />
258
+ ```
259
+
235
260
  ## Customizing \<EnrichedTextInput /> styles
236
261
 
237
262
  `react-native-enriched` allows customizing styles of the `<EnrichedTextInput />` component. See [htmlStyle](docs/API_REFERENCE.md#htmlstyle) prop.
@@ -13,8 +13,12 @@ Pod::Spec.new do |s|
13
13
  s.platforms = { :ios => min_ios_version_supported }
14
14
  s.source = { :git => "https://github.com/software-mansion/react-native-enriched.git", :tag => "#{s.version}" }
15
15
 
16
- s.source_files = "ios/**/*.{h,m,mm,cpp}"
16
+ s.source_files = ["ios/**/*.{h,m,mm,cpp}", "cpp/**/*.{h,hpp,c,cpp}"]
17
+ s.exclude_files = ["cpp/tests/**"]
17
18
  s.private_header_files = "ios/**/*.h"
19
+ s.pod_target_xcconfig = {
20
+ 'HEADER_SEARCH_PATHS' => '"${PODS_TARGET_SRCROOT}/cpp/parser" "${PODS_TARGET_SRCROOT}/cpp/GumboParser"'
21
+ }
18
22
 
19
23
  # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0.
20
24
  # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79.
@@ -61,12 +61,18 @@ public class EnrichedTextInputViewManagerDelegate<T extends View, U extends Base
61
61
  case "linkRegex":
62
62
  mViewManager.setLinkRegex(view, (ReadableMap) value);
63
63
  break;
64
+ case "contextMenuItems":
65
+ mViewManager.setContextMenuItems(view, (ReadableArray) value);
66
+ break;
64
67
  case "color":
65
68
  mViewManager.setColor(view, ColorPropConverter.getColor(value, view.getContext()));
66
69
  break;
67
70
  case "fontSize":
68
71
  mViewManager.setFontSize(view, value == null ? 0f : ((Double) value).floatValue());
69
72
  break;
73
+ case "lineHeight":
74
+ mViewManager.setLineHeight(view, value == null ? 0f : ((Double) value).floatValue());
75
+ break;
70
76
  case "fontFamily":
71
77
  mViewManager.setFontFamily(view, value == null ? null : (String) value);
72
78
  break;
@@ -85,6 +91,9 @@ public class EnrichedTextInputViewManagerDelegate<T extends View, U extends Base
85
91
  case "androidExperimentalSynchronousEvents":
86
92
  mViewManager.setAndroidExperimentalSynchronousEvents(view, value == null ? false : (boolean) value);
87
93
  break;
94
+ case "useHtmlNormalizer":
95
+ mViewManager.setUseHtmlNormalizer(view, value == null ? false : (boolean) value);
96
+ break;
88
97
  default:
89
98
  super.setProperty(view, propName, value);
90
99
  }
@@ -156,6 +165,9 @@ public class EnrichedTextInputViewManagerDelegate<T extends View, U extends Base
156
165
  case "addLink":
157
166
  mViewManager.addLink(view, args.getInt(0), args.getInt(1), args.getString(2), args.getString(3));
158
167
  break;
168
+ case "removeLink":
169
+ mViewManager.removeLink(view, args.getInt(0), args.getInt(1));
170
+ break;
159
171
  case "addImage":
160
172
  mViewManager.addImage(view, args.getString(0), (float) args.getDouble(1), (float) args.getDouble(2));
161
173
  break;
@@ -28,14 +28,17 @@ public interface EnrichedTextInputViewManagerInterface<T extends View> extends V
28
28
  void setHtmlStyle(T view, @Nullable ReadableMap value);
29
29
  void setScrollEnabled(T view, boolean value);
30
30
  void setLinkRegex(T view, @Nullable ReadableMap value);
31
+ void setContextMenuItems(T view, @Nullable ReadableArray value);
31
32
  void setColor(T view, @Nullable Integer value);
32
33
  void setFontSize(T view, float value);
34
+ void setLineHeight(T view, float value);
33
35
  void setFontFamily(T view, @Nullable String value);
34
36
  void setFontWeight(T view, @Nullable String value);
35
37
  void setFontStyle(T view, @Nullable String value);
36
38
  void setIsOnChangeHtmlSet(T view, boolean value);
37
39
  void setIsOnChangeTextSet(T view, boolean value);
38
40
  void setAndroidExperimentalSynchronousEvents(T view, boolean value);
41
+ void setUseHtmlNormalizer(T view, boolean value);
39
42
  void focus(T view);
40
43
  void blur(T view);
41
44
  void setValue(T view, String text);
@@ -57,6 +60,7 @@ public interface EnrichedTextInputViewManagerInterface<T extends View> extends V
57
60
  void toggleUnorderedList(T view);
58
61
  void toggleCheckboxList(T view, boolean checked);
59
62
  void addLink(T view, int start, int end, String text, String url);
63
+ void removeLink(T view, int start, int end);
60
64
  void addImage(T view, String uri, float width, float height);
61
65
  void startMention(T view, String indicator);
62
66
  void addMention(T view, String indicator, String text, String payload);
@@ -273,4 +273,153 @@ imagesObject.setProperty(runtime, "height", imagesValue.height);
273
273
  });
274
274
  }
275
275
 
276
+
277
+ void EnrichedTextInputViewEventEmitter::onContextMenuItemPress(OnContextMenuItemPress event) const {
278
+ dispatchEvent("contextMenuItemPress", [event=std::move(event)](jsi::Runtime &runtime) {
279
+ auto payload = jsi::Object(runtime);
280
+ payload.setProperty(runtime, "itemText", event.itemText);
281
+ payload.setProperty(runtime, "selectedText", event.selectedText);
282
+ payload.setProperty(runtime, "selectionStart", event.selectionStart);
283
+ payload.setProperty(runtime, "selectionEnd", event.selectionEnd);
284
+ {
285
+ auto styleState = jsi::Object(runtime);
286
+ {
287
+ auto bold = jsi::Object(runtime);
288
+ bold.setProperty(runtime, "isActive", event.styleState.bold.isActive);
289
+ bold.setProperty(runtime, "isConflicting", event.styleState.bold.isConflicting);
290
+ bold.setProperty(runtime, "isBlocking", event.styleState.bold.isBlocking);
291
+ styleState.setProperty(runtime, "bold", bold);
292
+ }
293
+ {
294
+ auto italic = jsi::Object(runtime);
295
+ italic.setProperty(runtime, "isActive", event.styleState.italic.isActive);
296
+ italic.setProperty(runtime, "isConflicting", event.styleState.italic.isConflicting);
297
+ italic.setProperty(runtime, "isBlocking", event.styleState.italic.isBlocking);
298
+ styleState.setProperty(runtime, "italic", italic);
299
+ }
300
+ {
301
+ auto underline = jsi::Object(runtime);
302
+ underline.setProperty(runtime, "isActive", event.styleState.underline.isActive);
303
+ underline.setProperty(runtime, "isConflicting", event.styleState.underline.isConflicting);
304
+ underline.setProperty(runtime, "isBlocking", event.styleState.underline.isBlocking);
305
+ styleState.setProperty(runtime, "underline", underline);
306
+ }
307
+ {
308
+ auto strikeThrough = jsi::Object(runtime);
309
+ strikeThrough.setProperty(runtime, "isActive", event.styleState.strikeThrough.isActive);
310
+ strikeThrough.setProperty(runtime, "isConflicting", event.styleState.strikeThrough.isConflicting);
311
+ strikeThrough.setProperty(runtime, "isBlocking", event.styleState.strikeThrough.isBlocking);
312
+ styleState.setProperty(runtime, "strikeThrough", strikeThrough);
313
+ }
314
+ {
315
+ auto inlineCode = jsi::Object(runtime);
316
+ inlineCode.setProperty(runtime, "isActive", event.styleState.inlineCode.isActive);
317
+ inlineCode.setProperty(runtime, "isConflicting", event.styleState.inlineCode.isConflicting);
318
+ inlineCode.setProperty(runtime, "isBlocking", event.styleState.inlineCode.isBlocking);
319
+ styleState.setProperty(runtime, "inlineCode", inlineCode);
320
+ }
321
+ {
322
+ auto h1 = jsi::Object(runtime);
323
+ h1.setProperty(runtime, "isActive", event.styleState.h1.isActive);
324
+ h1.setProperty(runtime, "isConflicting", event.styleState.h1.isConflicting);
325
+ h1.setProperty(runtime, "isBlocking", event.styleState.h1.isBlocking);
326
+ styleState.setProperty(runtime, "h1", h1);
327
+ }
328
+ {
329
+ auto h2 = jsi::Object(runtime);
330
+ h2.setProperty(runtime, "isActive", event.styleState.h2.isActive);
331
+ h2.setProperty(runtime, "isConflicting", event.styleState.h2.isConflicting);
332
+ h2.setProperty(runtime, "isBlocking", event.styleState.h2.isBlocking);
333
+ styleState.setProperty(runtime, "h2", h2);
334
+ }
335
+ {
336
+ auto h3 = jsi::Object(runtime);
337
+ h3.setProperty(runtime, "isActive", event.styleState.h3.isActive);
338
+ h3.setProperty(runtime, "isConflicting", event.styleState.h3.isConflicting);
339
+ h3.setProperty(runtime, "isBlocking", event.styleState.h3.isBlocking);
340
+ styleState.setProperty(runtime, "h3", h3);
341
+ }
342
+ {
343
+ auto h4 = jsi::Object(runtime);
344
+ h4.setProperty(runtime, "isActive", event.styleState.h4.isActive);
345
+ h4.setProperty(runtime, "isConflicting", event.styleState.h4.isConflicting);
346
+ h4.setProperty(runtime, "isBlocking", event.styleState.h4.isBlocking);
347
+ styleState.setProperty(runtime, "h4", h4);
348
+ }
349
+ {
350
+ auto h5 = jsi::Object(runtime);
351
+ h5.setProperty(runtime, "isActive", event.styleState.h5.isActive);
352
+ h5.setProperty(runtime, "isConflicting", event.styleState.h5.isConflicting);
353
+ h5.setProperty(runtime, "isBlocking", event.styleState.h5.isBlocking);
354
+ styleState.setProperty(runtime, "h5", h5);
355
+ }
356
+ {
357
+ auto h6 = jsi::Object(runtime);
358
+ h6.setProperty(runtime, "isActive", event.styleState.h6.isActive);
359
+ h6.setProperty(runtime, "isConflicting", event.styleState.h6.isConflicting);
360
+ h6.setProperty(runtime, "isBlocking", event.styleState.h6.isBlocking);
361
+ styleState.setProperty(runtime, "h6", h6);
362
+ }
363
+ {
364
+ auto codeBlock = jsi::Object(runtime);
365
+ codeBlock.setProperty(runtime, "isActive", event.styleState.codeBlock.isActive);
366
+ codeBlock.setProperty(runtime, "isConflicting", event.styleState.codeBlock.isConflicting);
367
+ codeBlock.setProperty(runtime, "isBlocking", event.styleState.codeBlock.isBlocking);
368
+ styleState.setProperty(runtime, "codeBlock", codeBlock);
369
+ }
370
+ {
371
+ auto blockQuote = jsi::Object(runtime);
372
+ blockQuote.setProperty(runtime, "isActive", event.styleState.blockQuote.isActive);
373
+ blockQuote.setProperty(runtime, "isConflicting", event.styleState.blockQuote.isConflicting);
374
+ blockQuote.setProperty(runtime, "isBlocking", event.styleState.blockQuote.isBlocking);
375
+ styleState.setProperty(runtime, "blockQuote", blockQuote);
376
+ }
377
+ {
378
+ auto orderedList = jsi::Object(runtime);
379
+ orderedList.setProperty(runtime, "isActive", event.styleState.orderedList.isActive);
380
+ orderedList.setProperty(runtime, "isConflicting", event.styleState.orderedList.isConflicting);
381
+ orderedList.setProperty(runtime, "isBlocking", event.styleState.orderedList.isBlocking);
382
+ styleState.setProperty(runtime, "orderedList", orderedList);
383
+ }
384
+ {
385
+ auto unorderedList = jsi::Object(runtime);
386
+ unorderedList.setProperty(runtime, "isActive", event.styleState.unorderedList.isActive);
387
+ unorderedList.setProperty(runtime, "isConflicting", event.styleState.unorderedList.isConflicting);
388
+ unorderedList.setProperty(runtime, "isBlocking", event.styleState.unorderedList.isBlocking);
389
+ styleState.setProperty(runtime, "unorderedList", unorderedList);
390
+ }
391
+ {
392
+ auto link = jsi::Object(runtime);
393
+ link.setProperty(runtime, "isActive", event.styleState.link.isActive);
394
+ link.setProperty(runtime, "isConflicting", event.styleState.link.isConflicting);
395
+ link.setProperty(runtime, "isBlocking", event.styleState.link.isBlocking);
396
+ styleState.setProperty(runtime, "link", link);
397
+ }
398
+ {
399
+ auto image = jsi::Object(runtime);
400
+ image.setProperty(runtime, "isActive", event.styleState.image.isActive);
401
+ image.setProperty(runtime, "isConflicting", event.styleState.image.isConflicting);
402
+ image.setProperty(runtime, "isBlocking", event.styleState.image.isBlocking);
403
+ styleState.setProperty(runtime, "image", image);
404
+ }
405
+ {
406
+ auto mention = jsi::Object(runtime);
407
+ mention.setProperty(runtime, "isActive", event.styleState.mention.isActive);
408
+ mention.setProperty(runtime, "isConflicting", event.styleState.mention.isConflicting);
409
+ mention.setProperty(runtime, "isBlocking", event.styleState.mention.isBlocking);
410
+ styleState.setProperty(runtime, "mention", mention);
411
+ }
412
+ {
413
+ auto checkboxList = jsi::Object(runtime);
414
+ checkboxList.setProperty(runtime, "isActive", event.styleState.checkboxList.isActive);
415
+ checkboxList.setProperty(runtime, "isConflicting", event.styleState.checkboxList.isConflicting);
416
+ checkboxList.setProperty(runtime, "isBlocking", event.styleState.checkboxList.isBlocking);
417
+ styleState.setProperty(runtime, "checkboxList", checkboxList);
418
+ }
419
+ payload.setProperty(runtime, "styleState", styleState);
420
+ }
421
+ return payload;
422
+ });
423
+ }
424
+
276
425
  } // namespace facebook::react
@@ -212,6 +212,150 @@ class EnrichedTextInputViewEventEmitter : public ViewEventEmitter {
212
212
  struct OnPasteImages {
213
213
  std::vector<OnPasteImagesImages> images;
214
214
  };
215
+
216
+ struct OnContextMenuItemPressStyleStateBold {
217
+ bool isActive;
218
+ bool isConflicting;
219
+ bool isBlocking;
220
+ };
221
+
222
+ struct OnContextMenuItemPressStyleStateItalic {
223
+ bool isActive;
224
+ bool isConflicting;
225
+ bool isBlocking;
226
+ };
227
+
228
+ struct OnContextMenuItemPressStyleStateUnderline {
229
+ bool isActive;
230
+ bool isConflicting;
231
+ bool isBlocking;
232
+ };
233
+
234
+ struct OnContextMenuItemPressStyleStateStrikeThrough {
235
+ bool isActive;
236
+ bool isConflicting;
237
+ bool isBlocking;
238
+ };
239
+
240
+ struct OnContextMenuItemPressStyleStateInlineCode {
241
+ bool isActive;
242
+ bool isConflicting;
243
+ bool isBlocking;
244
+ };
245
+
246
+ struct OnContextMenuItemPressStyleStateH1 {
247
+ bool isActive;
248
+ bool isConflicting;
249
+ bool isBlocking;
250
+ };
251
+
252
+ struct OnContextMenuItemPressStyleStateH2 {
253
+ bool isActive;
254
+ bool isConflicting;
255
+ bool isBlocking;
256
+ };
257
+
258
+ struct OnContextMenuItemPressStyleStateH3 {
259
+ bool isActive;
260
+ bool isConflicting;
261
+ bool isBlocking;
262
+ };
263
+
264
+ struct OnContextMenuItemPressStyleStateH4 {
265
+ bool isActive;
266
+ bool isConflicting;
267
+ bool isBlocking;
268
+ };
269
+
270
+ struct OnContextMenuItemPressStyleStateH5 {
271
+ bool isActive;
272
+ bool isConflicting;
273
+ bool isBlocking;
274
+ };
275
+
276
+ struct OnContextMenuItemPressStyleStateH6 {
277
+ bool isActive;
278
+ bool isConflicting;
279
+ bool isBlocking;
280
+ };
281
+
282
+ struct OnContextMenuItemPressStyleStateCodeBlock {
283
+ bool isActive;
284
+ bool isConflicting;
285
+ bool isBlocking;
286
+ };
287
+
288
+ struct OnContextMenuItemPressStyleStateBlockQuote {
289
+ bool isActive;
290
+ bool isConflicting;
291
+ bool isBlocking;
292
+ };
293
+
294
+ struct OnContextMenuItemPressStyleStateOrderedList {
295
+ bool isActive;
296
+ bool isConflicting;
297
+ bool isBlocking;
298
+ };
299
+
300
+ struct OnContextMenuItemPressStyleStateUnorderedList {
301
+ bool isActive;
302
+ bool isConflicting;
303
+ bool isBlocking;
304
+ };
305
+
306
+ struct OnContextMenuItemPressStyleStateLink {
307
+ bool isActive;
308
+ bool isConflicting;
309
+ bool isBlocking;
310
+ };
311
+
312
+ struct OnContextMenuItemPressStyleStateImage {
313
+ bool isActive;
314
+ bool isConflicting;
315
+ bool isBlocking;
316
+ };
317
+
318
+ struct OnContextMenuItemPressStyleStateMention {
319
+ bool isActive;
320
+ bool isConflicting;
321
+ bool isBlocking;
322
+ };
323
+
324
+ struct OnContextMenuItemPressStyleStateCheckboxList {
325
+ bool isActive;
326
+ bool isConflicting;
327
+ bool isBlocking;
328
+ };
329
+
330
+ struct OnContextMenuItemPressStyleState {
331
+ OnContextMenuItemPressStyleStateBold bold;
332
+ OnContextMenuItemPressStyleStateItalic italic;
333
+ OnContextMenuItemPressStyleStateUnderline underline;
334
+ OnContextMenuItemPressStyleStateStrikeThrough strikeThrough;
335
+ OnContextMenuItemPressStyleStateInlineCode inlineCode;
336
+ OnContextMenuItemPressStyleStateH1 h1;
337
+ OnContextMenuItemPressStyleStateH2 h2;
338
+ OnContextMenuItemPressStyleStateH3 h3;
339
+ OnContextMenuItemPressStyleStateH4 h4;
340
+ OnContextMenuItemPressStyleStateH5 h5;
341
+ OnContextMenuItemPressStyleStateH6 h6;
342
+ OnContextMenuItemPressStyleStateCodeBlock codeBlock;
343
+ OnContextMenuItemPressStyleStateBlockQuote blockQuote;
344
+ OnContextMenuItemPressStyleStateOrderedList orderedList;
345
+ OnContextMenuItemPressStyleStateUnorderedList unorderedList;
346
+ OnContextMenuItemPressStyleStateLink link;
347
+ OnContextMenuItemPressStyleStateImage image;
348
+ OnContextMenuItemPressStyleStateMention mention;
349
+ OnContextMenuItemPressStyleStateCheckboxList checkboxList;
350
+ };
351
+
352
+ struct OnContextMenuItemPress {
353
+ std::string itemText;
354
+ std::string selectedText;
355
+ int selectionStart;
356
+ int selectionEnd;
357
+ OnContextMenuItemPressStyleState styleState;
358
+ };
215
359
  void onInputFocus(OnInputFocus value) const;
216
360
 
217
361
  void onInputBlur(OnInputBlur value) const;
@@ -235,5 +379,7 @@ class EnrichedTextInputViewEventEmitter : public ViewEventEmitter {
235
379
  void onInputKeyPress(OnInputKeyPress value) const;
236
380
 
237
381
  void onPasteImages(OnPasteImages value) const;
382
+
383
+ void onContextMenuItemPress(OnContextMenuItemPress value) const;
238
384
  };
239
385
  } // namespace facebook::react
@@ -32,14 +32,17 @@ EnrichedTextInputViewProps::EnrichedTextInputViewProps(
32
32
  htmlStyle(convertRawProp(context, rawProps, "htmlStyle", sourceProps.htmlStyle, {})),
33
33
  scrollEnabled(convertRawProp(context, rawProps, "scrollEnabled", sourceProps.scrollEnabled, {false})),
34
34
  linkRegex(convertRawProp(context, rawProps, "linkRegex", sourceProps.linkRegex, {})),
35
+ contextMenuItems(convertRawProp(context, rawProps, "contextMenuItems", sourceProps.contextMenuItems, {})),
35
36
  color(convertRawProp(context, rawProps, "color", sourceProps.color, {})),
36
37
  fontSize(convertRawProp(context, rawProps, "fontSize", sourceProps.fontSize, {0.0})),
38
+ lineHeight(convertRawProp(context, rawProps, "lineHeight", sourceProps.lineHeight, {0.0})),
37
39
  fontFamily(convertRawProp(context, rawProps, "fontFamily", sourceProps.fontFamily, {})),
38
40
  fontWeight(convertRawProp(context, rawProps, "fontWeight", sourceProps.fontWeight, {})),
39
41
  fontStyle(convertRawProp(context, rawProps, "fontStyle", sourceProps.fontStyle, {})),
40
42
  isOnChangeHtmlSet(convertRawProp(context, rawProps, "isOnChangeHtmlSet", sourceProps.isOnChangeHtmlSet, {false})),
41
43
  isOnChangeTextSet(convertRawProp(context, rawProps, "isOnChangeTextSet", sourceProps.isOnChangeTextSet, {false})),
42
- androidExperimentalSynchronousEvents(convertRawProp(context, rawProps, "androidExperimentalSynchronousEvents", sourceProps.androidExperimentalSynchronousEvents, {false})) {}
44
+ androidExperimentalSynchronousEvents(convertRawProp(context, rawProps, "androidExperimentalSynchronousEvents", sourceProps.androidExperimentalSynchronousEvents, {false})),
45
+ useHtmlNormalizer(convertRawProp(context, rawProps, "useHtmlNormalizer", sourceProps.useHtmlNormalizer, {false})) {}
43
46
 
44
47
  #ifdef RN_SERIALIZABLE_STATE
45
48
  ComponentName EnrichedTextInputViewProps::getDiffPropsImplementationTarget() const {
@@ -105,6 +108,10 @@ folly::dynamic EnrichedTextInputViewProps::getDiffProps(
105
108
  result["linkRegex"] = toDynamic(linkRegex);
106
109
  }
107
110
 
111
+ if (contextMenuItems != oldProps->contextMenuItems) {
112
+ result["contextMenuItems"] = toDynamic(contextMenuItems);
113
+ }
114
+
108
115
  if (color != oldProps->color) {
109
116
  result["color"] = *color;
110
117
  }
@@ -113,6 +120,10 @@ folly::dynamic EnrichedTextInputViewProps::getDiffProps(
113
120
  result["fontSize"] = fontSize;
114
121
  }
115
122
 
123
+ if ((lineHeight != oldProps->lineHeight) && !(std::isnan(lineHeight) && std::isnan(oldProps->lineHeight))) {
124
+ result["lineHeight"] = lineHeight;
125
+ }
126
+
116
127
  if (fontFamily != oldProps->fontFamily) {
117
128
  result["fontFamily"] = fontFamily;
118
129
  }
@@ -136,6 +147,10 @@ folly::dynamic EnrichedTextInputViewProps::getDiffProps(
136
147
  if (androidExperimentalSynchronousEvents != oldProps->androidExperimentalSynchronousEvents) {
137
148
  result["androidExperimentalSynchronousEvents"] = androidExperimentalSynchronousEvents;
138
149
  }
150
+
151
+ if (useHtmlNormalizer != oldProps->useHtmlNormalizer) {
152
+ result["useHtmlNormalizer"] = useHtmlNormalizer;
153
+ }
139
154
  return result;
140
155
  }
141
156
  #endif
@@ -746,6 +746,49 @@ static inline folly::dynamic toDynamic(const EnrichedTextInputViewLinkRegexStruc
746
746
  return value.toDynamic();
747
747
  }
748
748
  #endif
749
+
750
+ struct EnrichedTextInputViewContextMenuItemsStruct {
751
+ std::string text{};
752
+
753
+ #ifdef RN_SERIALIZABLE_STATE
754
+ bool operator==(const EnrichedTextInputViewContextMenuItemsStruct&) const = default;
755
+
756
+ folly::dynamic toDynamic() const {
757
+ folly::dynamic result = folly::dynamic::object();
758
+ result["text"] = text;
759
+ return result;
760
+ }
761
+ #endif
762
+ };
763
+
764
+ static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, EnrichedTextInputViewContextMenuItemsStruct &result) {
765
+ auto map = (std::unordered_map<std::string, RawValue>)value;
766
+
767
+ auto tmp_text = map.find("text");
768
+ if (tmp_text != map.end()) {
769
+ fromRawValue(context, tmp_text->second, result.text);
770
+ }
771
+ }
772
+
773
+ static inline std::string toString(const EnrichedTextInputViewContextMenuItemsStruct &value) {
774
+ return "[Object EnrichedTextInputViewContextMenuItemsStruct]";
775
+ }
776
+
777
+ #ifdef RN_SERIALIZABLE_STATE
778
+ static inline folly::dynamic toDynamic(const EnrichedTextInputViewContextMenuItemsStruct &value) {
779
+ return value.toDynamic();
780
+ }
781
+ #endif
782
+
783
+ static inline void fromRawValue(const PropsParserContext& context, const RawValue &value, std::vector<EnrichedTextInputViewContextMenuItemsStruct> &result) {
784
+ auto items = (std::vector<RawValue>)value;
785
+ for (const auto &item : items) {
786
+ EnrichedTextInputViewContextMenuItemsStruct newItem;
787
+ fromRawValue(context, item, newItem);
788
+ result.emplace_back(newItem);
789
+ }
790
+ }
791
+
749
792
  class EnrichedTextInputViewProps final : public ViewProps {
750
793
  public:
751
794
  EnrichedTextInputViewProps() = default;
@@ -765,14 +808,17 @@ class EnrichedTextInputViewProps final : public ViewProps {
765
808
  EnrichedTextInputViewHtmlStyleStruct htmlStyle{};
766
809
  bool scrollEnabled{false};
767
810
  EnrichedTextInputViewLinkRegexStruct linkRegex{};
811
+ std::vector<EnrichedTextInputViewContextMenuItemsStruct> contextMenuItems{};
768
812
  SharedColor color{};
769
813
  Float fontSize{0.0};
814
+ Float lineHeight{0.0};
770
815
  std::string fontFamily{};
771
816
  std::string fontWeight{};
772
817
  std::string fontStyle{};
773
818
  bool isOnChangeHtmlSet{false};
774
819
  bool isOnChangeTextSet{false};
775
820
  bool androidExperimentalSynchronousEvents{false};
821
+ bool useHtmlNormalizer{false};
776
822
 
777
823
  #ifdef RN_SERIALIZABLE_STATE
778
824
  ComponentName getDiffPropsImplementationTarget() const override;
@@ -0,0 +1,5 @@
1
+ package com.swmansion.enriched.common
2
+
3
+ object GumboNormalizer {
4
+ external fun normalizeHtml(html: String): String?
5
+ }
@@ -80,8 +80,9 @@ open class EnrichedCheckboxListSpan(
80
80
  if (spannedText.getSpanStart(this) == start) {
81
81
  checkboxDrawable.update(isChecked)
82
82
 
83
- val lineCenter = (top + bottom) / 2f
84
- val drawableTop = lineCenter - (enrichedStyle.ulCheckboxBoxSize / 2f)
83
+ val fm = paint.fontMetricsInt
84
+ val textCenter = baseline + (fm.ascent + fm.descent) / 2f
85
+ val drawableTop = textCenter - (enrichedStyle.ulCheckboxBoxSize / 2f)
85
86
 
86
87
  canvas.withTranslation(x.toFloat() + enrichedStyle.ulCheckboxMarginLeft, drawableTop) {
87
88
  checkboxDrawable.draw(this)
@@ -49,7 +49,8 @@ open class EnrichedUnorderedListSpan(
49
49
  paint.style = Paint.Style.FILL
50
50
 
51
51
  val bulletRadius = enrichedStyle.ulBulletSize / 2f
52
- val yPosition = (top + bottom) / 2f
52
+ val fm = paint.fontMetricsInt
53
+ val yPosition = baseline + (fm.ascent + fm.descent) / 2f
53
54
  val xPosition = x + dir * bulletRadius + enrichedStyle.ulMarginLeft
54
55
 
55
56
  canvas.drawCircle(xPosition, yPosition, bulletRadius, paint)