react-native-transformer-text-input 0.1.0-alpha.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 (78) hide show
  1. package/LICENSE +20 -0
  2. package/README.md +141 -0
  3. package/RNTransformerTextInput.podspec +35 -0
  4. package/android/build.gradle +96 -0
  5. package/android/gradle.properties +5 -0
  6. package/android/spotless.gradle +19 -0
  7. package/android/src/main/AndroidManifest.xml +2 -0
  8. package/android/src/main/java/com/appandflow/transformertextinput/TextState.kt +15 -0
  9. package/android/src/main/java/com/appandflow/transformertextinput/TransformerTextInputDecoratorView.kt +160 -0
  10. package/android/src/main/java/com/appandflow/transformertextinput/TransformerTextInputDecoratorViewManager.kt +44 -0
  11. package/android/src/main/java/com/appandflow/transformertextinput/TransformerTextInputJni.kt +25 -0
  12. package/android/src/main/java/com/appandflow/transformertextinput/TransformerTextInputModule.kt +22 -0
  13. package/android/src/main/java/com/appandflow/transformertextinput/TransformerTextInputPackage.kt +53 -0
  14. package/android/src/main/jni/CMakeLists.txt +62 -0
  15. package/android/src/main/jni/TransformerTextInputJni.cpp +94 -0
  16. package/android/src/main/jni/rntti.h +17 -0
  17. package/cpp/TransformerTextInputDecoratorViewComponentDescriptor.h +16 -0
  18. package/cpp/TransformerTextInputDecoratorViewShadowNode.cpp +21 -0
  19. package/cpp/TransformerTextInputDecoratorViewShadowNode.h +40 -0
  20. package/cpp/TransformerTextInputRuntime.cpp +86 -0
  21. package/cpp/TransformerTextInputRuntime.h +31 -0
  22. package/ios/TransformerTextInputDecoratorView.h +9 -0
  23. package/ios/TransformerTextInputDecoratorView.mm +256 -0
  24. package/ios/TransformerTextInputModule.h +8 -0
  25. package/ios/TransformerTextInputModule.mm +28 -0
  26. package/lib/module/NativeTransformerTextInputModule.js +5 -0
  27. package/lib/module/NativeTransformerTextInputModule.js.map +1 -0
  28. package/lib/module/Transformer.js +15 -0
  29. package/lib/module/Transformer.js.map +1 -0
  30. package/lib/module/TransformerTextInput.js +86 -0
  31. package/lib/module/TransformerTextInput.js.map +1 -0
  32. package/lib/module/TransformerTextInputDecoratorViewNativeComponent.ts +31 -0
  33. package/lib/module/formatters/phone-number.js +315 -0
  34. package/lib/module/formatters/phone-number.js.map +1 -0
  35. package/lib/module/index.js +5 -0
  36. package/lib/module/index.js.map +1 -0
  37. package/lib/module/package.json +1 -0
  38. package/lib/module/registry.js +83 -0
  39. package/lib/module/registry.js.map +1 -0
  40. package/lib/module/selection.js +48 -0
  41. package/lib/module/selection.js.map +1 -0
  42. package/lib/module/utils/useMergeRefs.js +49 -0
  43. package/lib/module/utils/useMergeRefs.js.map +1 -0
  44. package/lib/module/utils/useRefEffect.js +37 -0
  45. package/lib/module/utils/useRefEffect.js.map +1 -0
  46. package/lib/typescript/package.json +1 -0
  47. package/lib/typescript/src/NativeTransformerTextInputModule.d.ts +7 -0
  48. package/lib/typescript/src/NativeTransformerTextInputModule.d.ts.map +1 -0
  49. package/lib/typescript/src/Transformer.d.ts +19 -0
  50. package/lib/typescript/src/Transformer.d.ts.map +1 -0
  51. package/lib/typescript/src/TransformerTextInput.d.ts +247 -0
  52. package/lib/typescript/src/TransformerTextInput.d.ts.map +1 -0
  53. package/lib/typescript/src/TransformerTextInputDecoratorViewNativeComponent.d.ts +12 -0
  54. package/lib/typescript/src/TransformerTextInputDecoratorViewNativeComponent.d.ts.map +1 -0
  55. package/lib/typescript/src/formatters/phone-number.d.ts +18 -0
  56. package/lib/typescript/src/formatters/phone-number.d.ts.map +1 -0
  57. package/lib/typescript/src/index.d.ts +3 -0
  58. package/lib/typescript/src/index.d.ts.map +1 -0
  59. package/lib/typescript/src/registry.d.ts +17 -0
  60. package/lib/typescript/src/registry.d.ts.map +1 -0
  61. package/lib/typescript/src/selection.d.ts +4 -0
  62. package/lib/typescript/src/selection.d.ts.map +1 -0
  63. package/lib/typescript/src/utils/useMergeRefs.d.ts +20 -0
  64. package/lib/typescript/src/utils/useMergeRefs.d.ts.map +1 -0
  65. package/lib/typescript/src/utils/useRefEffect.d.ts +24 -0
  66. package/lib/typescript/src/utils/useRefEffect.d.ts.map +1 -0
  67. package/package.json +199 -0
  68. package/react-native.config.js +13 -0
  69. package/src/NativeTransformerTextInputModule.ts +10 -0
  70. package/src/Transformer.ts +32 -0
  71. package/src/TransformerTextInput.tsx +147 -0
  72. package/src/TransformerTextInputDecoratorViewNativeComponent.ts +31 -0
  73. package/src/formatters/phone-number.ts +327 -0
  74. package/src/index.tsx +10 -0
  75. package/src/registry.ts +120 -0
  76. package/src/selection.ts +62 -0
  77. package/src/utils/useMergeRefs.ts +59 -0
  78. package/src/utils/useRefEffect.ts +42 -0
@@ -0,0 +1,62 @@
1
+ cmake_minimum_required(VERSION 3.13)
2
+ set(CMAKE_VERBOSE_MAKEFILE ON)
3
+
4
+ set(LIB_LITERAL rntti)
5
+ set(LIB_TARGET_NAME react_codegen_${LIB_LITERAL})
6
+
7
+ set(LIB_ANDROID_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
8
+ set(LIB_COMMON_DIR ${LIB_ANDROID_DIR}/../cpp)
9
+ set(LIB_ANDROID_GENERATED_JNI_DIR ${LIB_ANDROID_DIR}/build/generated/source/codegen/jni)
10
+ set(LIB_ANDROID_GENERATED_COMPONENTS_DIR ${LIB_ANDROID_GENERATED_JNI_DIR}/react/renderer/components/${LIB_LITERAL})
11
+ set(REACT_NATIVE_WORKLETS_DIR ${LIB_ANDROID_DIR}/../node_modules/react-native-worklets)
12
+
13
+ file(GLOB LIB_CUSTOM_SRCS CONFIGURE_DEPENDS *.cpp ${LIB_COMMON_DIR}/*.cpp)
14
+ file(GLOB LIB_CODEGEN_SRCS CONFIGURE_DEPENDS ${LIB_ANDROID_GENERATED_JNI_DIR}/*.cpp ${LIB_ANDROID_GENERATED_COMPONENTS_DIR}/*.cpp)
15
+
16
+ add_library(
17
+ ${LIB_TARGET_NAME}
18
+ SHARED
19
+ ${LIB_CUSTOM_SRCS}
20
+ ${LIB_CODEGEN_SRCS}
21
+ )
22
+
23
+ target_include_directories(
24
+ ${LIB_TARGET_NAME}
25
+ PUBLIC
26
+ .
27
+ ${LIB_COMMON_DIR}
28
+ ${LIB_ANDROID_GENERATED_JNI_DIR}
29
+ ${LIB_ANDROID_GENERATED_COMPONENTS_DIR}
30
+ "${REACT_COMMON_DIR}"
31
+ "${REACT_COMMON_DIR}/jsiexecutor"
32
+ "${REACT_NATIVE_WORKLETS_DIR}/Common/cpp"
33
+ "${REACT_NATIVE_WORKLETS_DIR}/android/src/main/cpp"
34
+ )
35
+
36
+ message(STATUS "ANDROID_NDK_BUILD_TYPE = ${ANDROID_NDK_BUILD_TYPE}")
37
+
38
+
39
+ add_library(worklets SHARED IMPORTED)
40
+
41
+ set_target_properties(
42
+ worklets
43
+ PROPERTIES
44
+ IMPORTED_LOCATION
45
+ "${REACT_NATIVE_WORKLETS_DIR}/android/build/intermediates/cmake/debug/obj/${ANDROID_ABI}/libworklets.so"
46
+ )
47
+
48
+ target_link_libraries(
49
+ ${LIB_TARGET_NAME}
50
+ fbjni
51
+ jsi
52
+ reactnative
53
+ worklets
54
+ )
55
+
56
+ target_include_directories(
57
+ ${CMAKE_PROJECT_NAME}
58
+ PUBLIC
59
+ ${CMAKE_CURRENT_SOURCE_DIR}
60
+ )
61
+
62
+ target_compile_reactnative_options(${LIB_TARGET_NAME} PUBLIC)
@@ -0,0 +1,94 @@
1
+ #include <fbjni/fbjni.h>
2
+ #include <jsi/jsi.h>
3
+ #include <worklets/NativeModules/WorkletsModuleProxy.h>
4
+ #include <worklets/android/WorkletsModule.h>
5
+
6
+ #include "TransformerTextInputRuntime.h"
7
+
8
+ #include <memory>
9
+
10
+ using namespace facebook;
11
+
12
+ namespace rntti {
13
+
14
+ struct JTextSelection : public jni::JavaClass<JTextSelection> {
15
+ static auto constexpr kJavaDescriptor =
16
+ "Lcom/appandflow/transformertextinput/TextSelection;";
17
+
18
+ static jni::local_ref<JTextSelection> create(int start, int end) {
19
+ return newInstance(start, end);
20
+ }
21
+ };
22
+
23
+ struct JTextState : public jni::JavaClass<JTextState> {
24
+ static auto constexpr kJavaDescriptor =
25
+ "Lcom/appandflow/transformertextinput/TextState;";
26
+
27
+ static jni::local_ref<JTextState> create(
28
+ std::string const &value,
29
+ jni::local_ref<JTextSelection> selection) {
30
+ return newInstance(value, selection);
31
+ }
32
+ };
33
+
34
+ class JTransformerTextInputJni
35
+ : public jni::HybridClass<JTransformerTextInputJni> {
36
+ public:
37
+ static auto constexpr kJavaDescriptor =
38
+ "Lcom/appandflow/transformertextinput/TransformerTextInputJni;";
39
+
40
+ static void setWorkletsModule(
41
+ jni::alias_ref<jni::JClass> jClazz,
42
+ jni::alias_ref<worklets::WorkletsModule::javaobject> module) {
43
+ if (!module) {
44
+ return;
45
+ }
46
+ auto proxy = module->cthis()->getWorkletsModuleProxy();
47
+ if (!proxy) {
48
+ return;
49
+ }
50
+ auto uiRuntime = proxy->getUIWorkletRuntime();
51
+ if (!uiRuntime) {
52
+ return;
53
+ }
54
+ rntti::SetUIWorkletRuntime(uiRuntime);
55
+ }
56
+
57
+ static jni::local_ref<JTextState> transform(
58
+ jni::alias_ref<jni::JClass> jClazz,
59
+ jint transformerId,
60
+ jni::alias_ref<jni::JString> value,
61
+ jint selectionStart,
62
+ jint selectionEnd) {
63
+ auto transformer = rntti::LookupTransformer(transformerId);
64
+ if (!transformer) {
65
+ return nullptr;
66
+ }
67
+ const auto currentValue = value->toStdString();
68
+ rntti::SelectionRange selection{
69
+ static_cast<int>(selectionStart), static_cast<int>(selectionEnd)};
70
+ auto result = rntti::RunTransformer(transformer, currentValue, selection);
71
+ if (!result) {
72
+ return nullptr;
73
+ }
74
+
75
+ return JTextState::create(
76
+ result->value,
77
+ JTextSelection::create(result->selection.start, result->selection.end));
78
+ }
79
+
80
+ static void registerNatives() {
81
+ registerHybrid({
82
+ makeNativeMethod(
83
+ "setWorkletsModule", JTransformerTextInputJni::setWorkletsModule),
84
+ makeNativeMethod("transform", JTransformerTextInputJni::transform),
85
+ });
86
+ }
87
+ };
88
+
89
+ } // namespace rntti
90
+
91
+ JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *) {
92
+ return facebook::jni::initialize(
93
+ vm, [] { rntti::JTransformerTextInputJni::registerNatives(); });
94
+ }
@@ -0,0 +1,17 @@
1
+ #pragma once
2
+
3
+ #include <ReactCommon/JavaTurboModule.h>
4
+ #include <ReactCommon/TurboModule.h>
5
+ #include <jsi/jsi.h>
6
+ #include "TransformerTextInputDecoratorViewComponentDescriptor.h"
7
+
8
+ namespace facebook {
9
+ namespace react {
10
+
11
+ JSI_EXPORT
12
+ std::shared_ptr<TurboModule> rntti_ModuleProvider(
13
+ const std::string &moduleName,
14
+ const JavaTurboModule::InitParams &params);
15
+
16
+ } // namespace react
17
+ } // namespace facebook
@@ -0,0 +1,16 @@
1
+ #pragma once
2
+
3
+ #include <react/renderer/core/ConcreteComponentDescriptor.h>
4
+
5
+ #include "TransformerTextInputDecoratorViewShadowNode.h"
6
+
7
+ namespace facebook::react {
8
+
9
+ class TransformerTextInputDecoratorViewComponentDescriptor final
10
+ : public ConcreteComponentDescriptor<
11
+ TransformerTextInputDecoratorViewShadowNode> {
12
+ public:
13
+ using ConcreteComponentDescriptor::ConcreteComponentDescriptor;
14
+ };
15
+
16
+ } // namespace facebook::react
@@ -0,0 +1,21 @@
1
+ #include "TransformerTextInputDecoratorViewShadowNode.h"
2
+
3
+ namespace facebook::react {
4
+
5
+ const char TransformerTextInputDecoratorViewComponentName[] =
6
+ "TransformerTextInputDecoratorView";
7
+
8
+ void TransformerTextInputDecoratorViewShadowNode::initialize() {
9
+ // Setting display: contents style results in ForceFlattenView trait being set
10
+ // on the shadow node. This trait causes the node not to have a host view. By
11
+ // removing the trait, it's possible to force RN to create a host view, layout
12
+ // of which can then be customized.
13
+ ShadowNode::traits_.unset(ShadowNodeTraits::ForceFlattenView);
14
+ }
15
+
16
+ void TransformerTextInputDecoratorViewShadowNode::layout(
17
+ LayoutContext layoutContext) {
18
+ ConcreteViewShadowNode::layout(layoutContext);
19
+ }
20
+
21
+ } // namespace facebook::react
@@ -0,0 +1,40 @@
1
+ #pragma once
2
+
3
+ #include <react/renderer/components/rntti/EventEmitters.h>
4
+ #include <react/renderer/components/rntti/Props.h>
5
+ #include <react/renderer/components/view/ConcreteViewShadowNode.h>
6
+ #include <react/renderer/core/LayoutContext.h>
7
+ #include <react/renderer/core/ShadowNodeFamily.h>
8
+
9
+ namespace facebook::react {
10
+
11
+ extern const char TransformerTextInputDecoratorViewComponentName[];
12
+
13
+ class TransformerTextInputDecoratorViewShadowNode final
14
+ : public ConcreteViewShadowNode<
15
+ TransformerTextInputDecoratorViewComponentName,
16
+ TransformerTextInputDecoratorViewProps,
17
+ TransformerTextInputDecoratorViewEventEmitter> {
18
+ public:
19
+ TransformerTextInputDecoratorViewShadowNode(
20
+ ShadowNodeFragment const &fragment,
21
+ ShadowNodeFamily::Shared const &family,
22
+ ShadowNodeTraits traits)
23
+ : ConcreteViewShadowNode(fragment, family, traits) {
24
+ initialize();
25
+ }
26
+
27
+ TransformerTextInputDecoratorViewShadowNode(
28
+ ShadowNode const &sourceShadowNode,
29
+ ShadowNodeFragment const &fragment)
30
+ : ConcreteViewShadowNode(sourceShadowNode, fragment) {
31
+ initialize();
32
+ }
33
+
34
+ void layout(LayoutContext layoutContext) override;
35
+
36
+ private:
37
+ void initialize();
38
+ };
39
+
40
+ } // namespace facebook::react
@@ -0,0 +1,86 @@
1
+ #include "TransformerTextInputRuntime.h"
2
+
3
+ namespace rntti {
4
+
5
+ namespace {
6
+ std::shared_ptr<worklets::WorkletRuntime> gUiRuntime;
7
+ } // namespace
8
+
9
+ void SetUIWorkletRuntime(std::shared_ptr<worklets::WorkletRuntime> runtime) {
10
+ if (!runtime) {
11
+ gUiRuntime.reset();
12
+ return;
13
+ }
14
+ runtime->schedule([runtime]() { gUiRuntime = runtime; });
15
+ }
16
+
17
+ std::optional<jsi::WeakObject> LookupTransformer(int transformerId) {
18
+ auto uiRuntime = gUiRuntime;
19
+ if (!uiRuntime) {
20
+ return std::nullopt;
21
+ }
22
+ auto &runtime = uiRuntime->getJSIRuntime();
23
+ auto transformerRegistry = runtime.global().getPropertyAsObject(
24
+ runtime, "__rntti_registerTransformerRegistry");
25
+ auto transformerRegistryGet =
26
+ transformerRegistry.getPropertyAsFunction(runtime, "get");
27
+ auto transformerValue =
28
+ transformerRegistryGet.call(runtime, jsi::Value(transformerId));
29
+
30
+ if (transformerValue.isNull() || transformerValue.isUndefined() ||
31
+ !transformerValue.isObject()) {
32
+ return std::nullopt;
33
+ }
34
+
35
+ return jsi::WeakObject(runtime, transformerValue.asObject(runtime));
36
+ }
37
+
38
+ std::optional<TransformResult> RunTransformer(
39
+ const std::optional<jsi::WeakObject> &transformer,
40
+ const std::string &value,
41
+ SelectionRange selection) {
42
+ if (!transformer) {
43
+ return std::nullopt;
44
+ }
45
+
46
+ auto uiRuntime = gUiRuntime;
47
+ if (!uiRuntime) {
48
+ return std::nullopt;
49
+ }
50
+
51
+ auto &jsiRuntime = uiRuntime->getJSIRuntime();
52
+
53
+ auto transformerValue = transformer->lock(jsiRuntime);
54
+ if (transformerValue.isUndefined()) {
55
+ return std::nullopt;
56
+ }
57
+
58
+ auto transformerFunction =
59
+ transformerValue.asObject(jsiRuntime).asFunction(jsiRuntime);
60
+ auto resultValue = uiRuntime->runSync(
61
+ transformerFunction,
62
+ jsi::String::createFromUtf8(jsiRuntime, value),
63
+ jsi::Value(selection.start),
64
+ jsi::Value(selection.end));
65
+
66
+ TransformResult result;
67
+ auto resultObject = resultValue.asObject(jsiRuntime);
68
+ auto valueProp = resultObject.getProperty(jsiRuntime, "value");
69
+ if (valueProp.isString()) {
70
+ result.value = valueProp.asString(jsiRuntime).utf8(jsiRuntime);
71
+ }
72
+
73
+ auto selectionProp = resultObject.getProperty(jsiRuntime, "selection");
74
+ if (selectionProp.isObject()) {
75
+ auto selectionObject = selectionProp.asObject(jsiRuntime);
76
+ auto startProp = selectionObject.getProperty(jsiRuntime, "start");
77
+ auto endProp = selectionObject.getProperty(jsiRuntime, "end");
78
+ result.selection = SelectionRange{
79
+ static_cast<int>(startProp.asNumber()),
80
+ static_cast<int>(endProp.asNumber())};
81
+ }
82
+
83
+ return result;
84
+ }
85
+
86
+ } // namespace rntti
@@ -0,0 +1,31 @@
1
+ #pragma once
2
+
3
+ #include <jsi/jsi.h>
4
+ #include <worklets/WorkletRuntime/WorkletRuntime.h>
5
+
6
+ #include <memory>
7
+ #include <optional>
8
+ #include <string>
9
+
10
+ namespace rntti {
11
+
12
+ struct SelectionRange {
13
+ int start{0};
14
+ int end{0};
15
+ };
16
+
17
+ struct TransformResult {
18
+ std::string value;
19
+ SelectionRange selection;
20
+ };
21
+
22
+ void SetUIWorkletRuntime(std::shared_ptr<worklets::WorkletRuntime> runtime);
23
+
24
+ std::optional<jsi::WeakObject> LookupTransformer(int transformerId);
25
+
26
+ std::optional<TransformResult> RunTransformer(
27
+ const std::optional<jsi::WeakObject> &transformer,
28
+ const std::string &value,
29
+ SelectionRange selection);
30
+
31
+ } // namespace rntti
@@ -0,0 +1,9 @@
1
+ #import <React/RCTViewComponentView.h>
2
+ #import <UIKit/UIKit.h>
3
+
4
+ NS_ASSUME_NONNULL_BEGIN
5
+
6
+ @interface TransformerTextInputDecoratorView : RCTViewComponentView
7
+ @end
8
+
9
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,256 @@
1
+ #import "TransformerTextInputDecoratorView.h"
2
+
3
+ #import <React/RCTBackedTextInputDelegate.h>
4
+ #import <React/RCTBackedTextInputViewProtocol.h>
5
+ #import <React/RCTConversions.h>
6
+ #import <React/RCTTextInputComponentView.h>
7
+ #import <react/renderer/components/rntti/EventEmitters.h>
8
+ #import <react/renderer/components/rntti/Props.h>
9
+ #import <react/renderer/components/rntti/RCTComponentViewHelpers.h>
10
+ #import "TransformerTextInputDecoratorViewComponentDescriptor.h"
11
+ #import "TransformerTextInputRuntime.h"
12
+
13
+ using namespace facebook::react;
14
+
15
+ struct RNTTITextState {
16
+ NSString *value;
17
+ NSRange selection;
18
+ };
19
+
20
+ @interface TransformerTextInputDecoratorView () <
21
+ RCTTransformerTextInputDecoratorViewViewProtocol,
22
+ RCTBackedTextInputDelegate>
23
+
24
+ @end
25
+
26
+ @implementation TransformerTextInputDecoratorView {
27
+ std::optional<jsi::WeakObject> _transformer;
28
+ bool _observersAdded;
29
+ __weak id<RCTBackedTextInputDelegate> _baseDelegate;
30
+ __weak UIView<RCTBackedTextInputViewProtocol> *_backedTextInput;
31
+ }
32
+
33
+ + (ComponentDescriptorProvider)componentDescriptorProvider
34
+ {
35
+ return concreteComponentDescriptorProvider<TransformerTextInputDecoratorViewComponentDescriptor>();
36
+ }
37
+
38
+ - (instancetype)initWithFrame:(CGRect)frame
39
+ {
40
+ if (self = [super initWithFrame:frame]) {
41
+ _props = TransformerTextInputDecoratorViewShadowNode::defaultSharedProps();
42
+ _observersAdded = false;
43
+ }
44
+
45
+ return self;
46
+ }
47
+
48
+ - (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
49
+ {
50
+ const auto &oldViewProps = *std::static_pointer_cast<TransformerTextInputDecoratorViewProps const>(_props);
51
+ const auto &newViewProps = *std::static_pointer_cast<TransformerTextInputDecoratorViewProps const>(props);
52
+
53
+ if (oldViewProps.transformerId != newViewProps.transformerId) {
54
+ _transformer = rntti::LookupTransformer(newViewProps.transformerId);
55
+ }
56
+
57
+ [super updateProps:props oldProps:oldProps];
58
+ }
59
+
60
+ - (void)applyValue:(NSString *)newValue
61
+ {
62
+ NSMutableAttributedString *newAttributedText =
63
+ [[NSMutableAttributedString alloc] initWithAttributedString:_backedTextInput.attributedText];
64
+
65
+ [newAttributedText replaceCharactersInRange:NSMakeRange(0, newAttributedText.length) withString:newValue];
66
+ _backedTextInput.attributedText = newAttributedText;
67
+ }
68
+
69
+ - (NSRange)currentSelection
70
+ {
71
+ UITextRange *selectedTextRange = _backedTextInput.selectedTextRange;
72
+ if (!selectedTextRange) {
73
+ return NSMakeRange(0, 0);
74
+ }
75
+ NSInteger start = [_backedTextInput offsetFromPosition:_backedTextInput.beginningOfDocument
76
+ toPosition:selectedTextRange.start];
77
+ NSInteger end = [_backedTextInput offsetFromPosition:_backedTextInput.beginningOfDocument
78
+ toPosition:selectedTextRange.end];
79
+ return NSMakeRange(start, end - start);
80
+ }
81
+
82
+ - (NSString *)currentValue
83
+ {
84
+ return _backedTextInput.attributedText.string ?: @"";
85
+ }
86
+
87
+ - (void)applySelection:(NSRange)selection
88
+ {
89
+ UITextPosition *startPosition = [_backedTextInput positionFromPosition:_backedTextInput.beginningOfDocument
90
+ offset:selection.location];
91
+ UITextPosition *endPosition = [_backedTextInput positionFromPosition:_backedTextInput.beginningOfDocument
92
+ offset:selection.location + selection.length];
93
+ if (!startPosition || !endPosition) {
94
+ return;
95
+ }
96
+ UITextRange *range = [_backedTextInput textRangeFromPosition:startPosition toPosition:endPosition];
97
+ if (range) {
98
+ [_backedTextInput setSelectedTextRange:range notifyDelegate:NO];
99
+ }
100
+ }
101
+
102
+ - (RNTTITextState)transformTextState:(RNTTITextState)state
103
+ {
104
+ if (!_transformer) {
105
+ return state;
106
+ }
107
+
108
+ rntti::SelectionRange selectionRange{
109
+ static_cast<int>(state.selection.location), static_cast<int>(state.selection.location + state.selection.length)};
110
+ auto transformResult = rntti::RunTransformer(_transformer, RCTStringFromNSString(state.value), selectionRange);
111
+ if (!transformResult) {
112
+ return state;
113
+ }
114
+
115
+ NSString *value = RCTNSStringFromString(transformResult->value);
116
+ NSRange selection =
117
+ NSMakeRange(transformResult->selection.start, transformResult->selection.end - transformResult->selection.start);
118
+
119
+ return RNTTITextState{value, selection};
120
+ }
121
+
122
+ - (void)didAddSubview:(UIView *)subview
123
+ {
124
+ [super didAddSubview:subview];
125
+
126
+ [self addTextInputObservers];
127
+ }
128
+
129
+ - (void)willRemoveSubview:(UIView *)subview
130
+ {
131
+ [super willRemoveSubview:subview];
132
+
133
+ [self removeTextInputObservers];
134
+ }
135
+
136
+ - (void)addTextInputObservers
137
+ {
138
+ react_native_assert(
139
+ !_observersAdded &&
140
+ "MarkdownTextInputDecoratorComponentView tried to add TextInput observers while they were attached");
141
+ react_native_assert(
142
+ self.subviews.count > 0 && "MarkdownTextInputDecoratorComponentView is mounted without any children");
143
+ UIView *childView = self.subviews[0];
144
+ react_native_assert(
145
+ [childView isKindOfClass:[RCTTextInputComponentView class]] &&
146
+ "Child component of MarkdownTextInputDecoratorComponentView is not an instance of RCTTextInputComponentView.");
147
+ RCTTextInputComponentView *textInputComponentView = (RCTTextInputComponentView *)childView;
148
+ UIView<RCTBackedTextInputViewProtocol> *backedTextInputView =
149
+ [textInputComponentView valueForKey:@"_backedTextInputView"];
150
+
151
+ _backedTextInput = backedTextInputView;
152
+ _baseDelegate = backedTextInputView.textInputDelegate;
153
+ backedTextInputView.textInputDelegate = self;
154
+
155
+ _observersAdded = true;
156
+ }
157
+
158
+ - (void)removeTextInputObservers
159
+ {
160
+ _backedTextInput = nil;
161
+ _baseDelegate = nil;
162
+ _observersAdded = false;
163
+ _transformer = std::nullopt;
164
+ }
165
+
166
+ - (void)textInputDidBeginEditing
167
+ {
168
+ [_baseDelegate textInputDidBeginEditing];
169
+ }
170
+
171
+ - (void)textInputDidChange
172
+ {
173
+ // Current values
174
+ NSString *currentValue = [self currentValue];
175
+ NSRange currentSelection = [self currentSelection];
176
+ RNTTITextState current{currentValue, currentSelection};
177
+ RNTTITextState next = [self transformTextState:current];
178
+ bool didTransformValue = ![next.value isEqualToString:current.value];
179
+ if (didTransformValue) {
180
+ [self applyValue:next.value];
181
+ }
182
+ if (didTransformValue || !NSEqualRanges(next.selection, current.selection)) {
183
+ [self applySelection:next.selection];
184
+ }
185
+
186
+ [_baseDelegate textInputDidChange];
187
+ }
188
+
189
+ - (void)textInputDidChangeSelection
190
+ {
191
+ [_baseDelegate textInputDidChangeSelection];
192
+ }
193
+
194
+ - (void)textInputDidEndEditing
195
+ {
196
+ [_baseDelegate textInputDidEndEditing];
197
+ }
198
+
199
+ - (void)textInputDidReturn
200
+ {
201
+ [_baseDelegate textInputDidReturn];
202
+ }
203
+
204
+ - (BOOL)textInputShouldBeginEditing
205
+ {
206
+ return [_baseDelegate textInputShouldBeginEditing];
207
+ }
208
+
209
+ - (nonnull NSString *)textInputShouldChangeText:(nonnull NSString *)text inRange:(NSRange)range
210
+ {
211
+ return [_baseDelegate textInputShouldChangeText:text inRange:range];
212
+ }
213
+
214
+ - (BOOL)textInputShouldEndEditing
215
+ {
216
+ return [_baseDelegate textInputShouldEndEditing];
217
+ }
218
+
219
+ - (BOOL)textInputShouldReturn
220
+ {
221
+ return [_baseDelegate textInputShouldReturn];
222
+ }
223
+
224
+ - (BOOL)textInputShouldSubmitOnReturn
225
+ {
226
+ return [_baseDelegate textInputShouldSubmitOnReturn];
227
+ }
228
+
229
+ - (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
230
+ {
231
+ RCTTransformerTextInputDecoratorViewHandleCommand(self, commandName, args);
232
+ }
233
+
234
+ - (void)update:(BOOL)transform
235
+ value:(NSString *)value
236
+ selectionStart:(NSInteger)selectionStart
237
+ selectionEnd:(NSInteger)selectionEnd
238
+ {
239
+ NSString *currentValue = [self currentValue];
240
+ NSRange currentSelection = [self currentSelection];
241
+ NSString *providedValue = value ?: currentValue;
242
+ NSRange providedSelection = NSMakeRange(selectionStart, selectionEnd - selectionStart);
243
+ RNTTITextState provided{providedValue, providedSelection};
244
+ RNTTITextState next = transform ? [self transformTextState:provided] : provided;
245
+ bool didTransformValue = ![next.value isEqualToString:currentValue];
246
+ if (didTransformValue) {
247
+ [self applyValue:next.value];
248
+ }
249
+ if (didTransformValue || !NSEqualRanges(next.selection, currentSelection)) {
250
+ [self applySelection:next.selection];
251
+ }
252
+
253
+ [_baseDelegate textInputDidChange];
254
+ }
255
+
256
+ @end
@@ -0,0 +1,8 @@
1
+ #import <ReactCodegen/rntti/rntti.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ @interface TransformerTextInputModule : NativeTransformerTextInputModuleSpecBase <NativeTransformerTextInputModuleSpec>
6
+ @end
7
+
8
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,28 @@
1
+ #import "TransformerTextInputModule.h"
2
+
3
+ #import <jsi/jsi.h>
4
+ #import <rnworklets/worklets/apple/WorkletsModule.h>
5
+
6
+ #import "TransformerTextInputRuntime.h"
7
+
8
+ @implementation TransformerTextInputModule
9
+
10
+ RCT_EXPORT_MODULE(TransformerTextInputModule)
11
+
12
+ @synthesize moduleRegistry = _moduleRegistry;
13
+
14
+ - (NSNumber *)install
15
+ {
16
+ WorkletsModule *workletsModule = [_moduleRegistry moduleForName:"WorkletsModule"];
17
+ auto uiRuntime = [workletsModule getWorkletsModuleProxy]->getUIWorkletRuntime();
18
+ rntti::SetUIWorkletRuntime(uiRuntime);
19
+ return @YES;
20
+ }
21
+
22
+ - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
23
+ (const facebook::react::ObjCTurboModule::InitParams &)params
24
+ {
25
+ return std::make_shared<facebook::react::NativeTransformerTextInputModuleSpecJSI>(params);
26
+ }
27
+
28
+ @end
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+
3
+ import { TurboModuleRegistry } from 'react-native';
4
+ export default TurboModuleRegistry.getEnforcing('TransformerTextInputModule');
5
+ //# sourceMappingURL=NativeTransformerTextInputModule.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"names":["TurboModuleRegistry","getEnforcing"],"sourceRoot":"../../src","sources":["NativeTransformerTextInputModule.ts"],"mappings":";;AACA,SAASA,mBAAmB,QAAQ,cAAc;AAMlD,eAAeA,mBAAmB,CAACC,YAAY,CAC7C,4BACF,CAAC","ignoreList":[]}