react-native-marcus 0.1.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 +201 -0
- package/README.md +227 -0
- package/RNMarcus.podspec +78 -0
- package/android/build.gradle +220 -0
- package/android/proguard-rules.pro +1 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/cpp/CMakeLists.txt +41 -0
- package/android/src/main/cpp/MarkdownParser.cpp +49 -0
- package/android/src/main/cpp/MarkdownParser.h +56 -0
- package/android/src/main/cpp/OnLoad.cpp +17 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusModule.kt +28 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusPackage.kt +31 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorView.kt +63 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorViewManager.kt +32 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorViewManagerSpec.kt +21 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownFormatter.kt +333 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownParseResult.kt +12 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownParser.kt +89 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownRange.kt +11 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownStyle.kt +81 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownTextWatcher.kt +17 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownUtils.kt +84 -0
- package/android/src/main/java/app/getbullet/marcus/ReactLineHeight.java +29 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundColorSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundSpan.kt +105 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBlockIndentSpan.kt +67 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBoldSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontFamilySpan.kt +28 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontSizeSpan.kt +7 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownForegroundColorSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownGapSpan.kt +57 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownItalicSpan.kt +6 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownLineHeightSpan.kt +21 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownSpan.kt +7 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownStrikethroughSpan.kt +5 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownUnderlineSpan.kt +5 -0
- package/android/src/main/new_arch/CMakeLists.txt +70 -0
- package/android/src/main/new_arch/RNMarcusSpec.cpp +43 -0
- package/android/src/main/new_arch/RNMarcusSpec.h +36 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorShadowNode.cpp +218 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorShadowNode.h +54 -0
- package/android/src/reactNativeVersionPatch/CustomMountingManager/81/app/getbullet/marcus/CustomFabricUIManager.java +156 -0
- package/android/src/reactNativeVersionPatch/CustomMountingManager/latest/app/getbullet/marcus/CustomFabricUIManager.java +130 -0
- package/android/src/test/java/app/getbullet/marcus/MarkdownFormatterTest.kt +205 -0
- package/android/src/test/java/app/getbullet/marcus/RenderModel.kt +78 -0
- package/android/src/test/resources/render-model.txt +583 -0
- package/apple/MarcusModule.h +8 -0
- package/apple/MarcusModule.mm +26 -0
- package/apple/MarcusRange.h +15 -0
- package/apple/MarcusRange.mm +15 -0
- package/apple/MarcusTextInputDecoratorComponentView.h +12 -0
- package/apple/MarcusTextInputDecoratorComponentView.mm +121 -0
- package/apple/MarcusTextInputDecoratorShadowNode.cpp +251 -0
- package/apple/MarcusTextInputDecoratorShadowNode.h +65 -0
- package/apple/MarcusTextInputDecoratorShadowNode.mm +204 -0
- package/apple/MarcusTextInputDecoratorViewManager.h +8 -0
- package/apple/MarcusTextInputDecoratorViewManager.mm +11 -0
- package/apple/MarkdownAdaptiveImageGlyph.swift +44 -0
- package/apple/MarkdownAttributes.h +31 -0
- package/apple/MarkdownAttributes.m +9 -0
- package/apple/MarkdownBackedTextInputDelegate.swift +117 -0
- package/apple/MarkdownDecorator.swift +316 -0
- package/apple/MarkdownFonts.swift +21 -0
- package/apple/MarkdownFormatter+React.swift +51 -0
- package/apple/MarkdownFormatter.swift +509 -0
- package/apple/MarkdownLog.h +10 -0
- package/apple/MarkdownLog.mm +6 -0
- package/apple/MarkdownParser.swift +200 -0
- package/apple/MarkdownSwiftInterop.h +23 -0
- package/apple/MarkdownTextBackground.swift +17 -0
- package/apple/MarkdownTextFieldObserver.swift +90 -0
- package/apple/MarkdownTextLayoutFragment.swift +205 -0
- package/apple/MarkdownTextLayoutManagerDelegate.swift +84 -0
- package/apple/MarkdownTextStorageDelegate.swift +32 -0
- package/apple/MarkdownTextViewObserver.swift +30 -0
- package/apple/RCTMarcusStyle+Codegen.h +18 -0
- package/apple/RCTMarcusStyle+Codegen.mm +64 -0
- package/apple/RCTMarcusStyle.h +41 -0
- package/apple/RCTMarcusStyle.m +5 -0
- package/apple/RCTMarcusUtils.h +26 -0
- package/apple/RCTMarcusUtils.mm +26 -0
- package/apple/RCTTextInput+AdaptiveImageGlyph.h +16 -0
- package/apple/RCTTextInput+AdaptiveImageGlyph.mm +79 -0
- package/apple/RCTTextInputComponentView+Marcus.h +11 -0
- package/apple/RCTTextInputComponentView+Marcus.mm +79 -0
- package/cpp/MarkdownCxx.modulemap +8 -0
- package/cpp/MarkdownGlobal.cpp +48 -0
- package/cpp/MarkdownGlobal.h +33 -0
- package/cpp/MarkdownWorkletParser.cpp +93 -0
- package/cpp/MarkdownWorkletParser.h +45 -0
- package/cpp/RuntimeDecorator.cpp +39 -0
- package/cpp/RuntimeDecorator.h +14 -0
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorState.h +33 -0
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorViewComponentDescriptor.h +17 -0
- package/lib/commonjs/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
- package/lib/commonjs/MarkdownTextInput.js +99 -0
- package/lib/commonjs/MarkdownTextInput.js.map +1 -0
- package/lib/commonjs/MarkdownTextInput.web.js +618 -0
- package/lib/commonjs/MarkdownTextInput.web.js.map +1 -0
- package/lib/commonjs/NativeMarcusModule.js +9 -0
- package/lib/commonjs/NativeMarcusModule.js.map +1 -0
- package/lib/commonjs/commonTypes.js +2 -0
- package/lib/commonjs/commonTypes.js.map +1 -0
- package/lib/commonjs/global.d.js +2 -0
- package/lib/commonjs/global.d.js.map +1 -0
- package/lib/commonjs/index.js +27 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/parser/index.js +7030 -0
- package/lib/commonjs/parser/index.js.map +7 -0
- package/lib/commonjs/rangeUtils.js +71 -0
- package/lib/commonjs/rangeUtils.js.map +1 -0
- package/lib/commonjs/react-native-web.d.js +2 -0
- package/lib/commonjs/react-native-web.d.js.map +1 -0
- package/lib/commonjs/styleUtils.js +91 -0
- package/lib/commonjs/styleUtils.js.map +1 -0
- package/lib/commonjs/web/InputHistory.js +111 -0
- package/lib/commonjs/web/InputHistory.js.map +1 -0
- package/lib/commonjs/web/MarkdownTextInput.css +72 -0
- package/lib/commonjs/web/utils/blockLayout.js +132 -0
- package/lib/commonjs/web/utils/blockLayout.js.map +1 -0
- package/lib/commonjs/web/utils/blockUtils.js +219 -0
- package/lib/commonjs/web/utils/blockUtils.js.map +1 -0
- package/lib/commonjs/web/utils/browserUtils.js +40 -0
- package/lib/commonjs/web/utils/browserUtils.js.map +1 -0
- package/lib/commonjs/web/utils/cursorUtils.js +157 -0
- package/lib/commonjs/web/utils/cursorUtils.js.map +1 -0
- package/lib/commonjs/web/utils/inputUtils.js +152 -0
- package/lib/commonjs/web/utils/inputUtils.js.map +1 -0
- package/lib/commonjs/web/utils/measureUtils.js +0 -0
- package/lib/commonjs/web/utils/measureUtils.js.map +1 -0
- package/lib/commonjs/web/utils/parserUtils.js +552 -0
- package/lib/commonjs/web/utils/parserUtils.js.map +1 -0
- package/lib/commonjs/web/utils/treeUtils.js +109 -0
- package/lib/commonjs/web/utils/treeUtils.js.map +1 -0
- package/lib/commonjs/web/utils/webStyleUtils.js +59 -0
- package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -0
- package/lib/module/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
- package/lib/module/MarkdownTextInput.js +94 -0
- package/lib/module/MarkdownTextInput.js.map +1 -0
- package/lib/module/MarkdownTextInput.web.js +612 -0
- package/lib/module/MarkdownTextInput.web.js.map +1 -0
- package/lib/module/NativeMarcusModule.js +5 -0
- package/lib/module/NativeMarcusModule.js.map +1 -0
- package/lib/module/commonTypes.js +2 -0
- package/lib/module/commonTypes.js.map +1 -0
- package/lib/module/global.d.js +2 -0
- package/lib/module/global.d.js.map +1 -0
- package/lib/module/index.js +5 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/parser/index.js +7029 -0
- package/lib/module/parser/index.js.map +7 -0
- package/lib/module/rangeUtils.js +66 -0
- package/lib/module/rangeUtils.js.map +1 -0
- package/lib/module/react-native-web.d.js +2 -0
- package/lib/module/react-native-web.d.js.map +1 -0
- package/lib/module/styleUtils.js +88 -0
- package/lib/module/styleUtils.js.map +1 -0
- package/lib/module/web/InputHistory.js +106 -0
- package/lib/module/web/InputHistory.js.map +1 -0
- package/lib/module/web/MarkdownTextInput.css +72 -0
- package/lib/module/web/utils/blockLayout.js +128 -0
- package/lib/module/web/utils/blockLayout.js.map +1 -0
- package/lib/module/web/utils/blockUtils.js +210 -0
- package/lib/module/web/utils/blockUtils.js.map +1 -0
- package/lib/module/web/utils/browserUtils.js +36 -0
- package/lib/module/web/utils/browserUtils.js.map +1 -0
- package/lib/module/web/utils/cursorUtils.js +150 -0
- package/lib/module/web/utils/cursorUtils.js.map +1 -0
- package/lib/module/web/utils/inputUtils.js +144 -0
- package/lib/module/web/utils/inputUtils.js.map +1 -0
- package/lib/module/web/utils/measureUtils.js +0 -0
- package/lib/module/web/utils/measureUtils.js.map +1 -0
- package/lib/module/web/utils/parserUtils.js +547 -0
- package/lib/module/web/utils/parserUtils.js.map +1 -0
- package/lib/module/web/utils/treeUtils.js +103 -0
- package/lib/module/web/utils/treeUtils.js.map +1 -0
- package/lib/module/web/utils/webStyleUtils.js +54 -0
- package/lib/module/web/utils/webStyleUtils.js.map +1 -0
- package/lib/typescript/commonjs/e2e/checks.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/checks.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/e2e/editing.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/editing.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/e2e/helpers.d.ts +23 -0
- package/lib/typescript/commonjs/e2e/helpers.d.ts.map +1 -0
- package/lib/typescript/commonjs/e2e/smoke.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/smoke.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/package.json +1 -0
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +63 -0
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts +22 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts +41 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/NativeMarcusModule.d.ts +7 -0
- package/lib/typescript/commonjs/src/NativeMarcusModule.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/commonTypes.d.ts +10 -0
- package/lib/typescript/commonjs/src/commonTypes.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/index.d.ts +5 -0
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/parser/index.d.ts +8 -0
- package/lib/typescript/commonjs/src/parser/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts +8 -0
- package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/rangeUtils.d.ts +6 -0
- package/lib/typescript/commonjs/src/rangeUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/styleUtils.d.ts +8 -0
- package/lib/typescript/commonjs/src/styleUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/InputHistory.d.ts +24 -0
- package/lib/typescript/commonjs/src/web/InputHistory.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts +41 -0
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts +33 -0
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/browserUtils.d.ts +7 -0
- package/lib/typescript/commonjs/src/web/utils/browserUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/cursorUtils.d.ts +12 -0
- package/lib/typescript/commonjs/src/web/utils/cursorUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/inputUtils.d.ts +12 -0
- package/lib/typescript/commonjs/src/web/utils/inputUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/measureUtils.d.ts +22 -0
- package/lib/typescript/commonjs/src/web/utils/measureUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts +44 -0
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts +25 -0
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts +7 -0
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts.map +1 -0
- package/lib/typescript/module/e2e/checks.test.d.ts +2 -0
- package/lib/typescript/module/e2e/checks.test.d.ts.map +1 -0
- package/lib/typescript/module/e2e/editing.test.d.ts +2 -0
- package/lib/typescript/module/e2e/editing.test.d.ts.map +1 -0
- package/lib/typescript/module/e2e/helpers.d.ts +23 -0
- package/lib/typescript/module/e2e/helpers.d.ts.map +1 -0
- package/lib/typescript/module/e2e/smoke.test.d.ts +2 -0
- package/lib/typescript/module/e2e/smoke.test.d.ts.map +1 -0
- package/lib/typescript/module/package.json +1 -0
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +63 -0
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownTextInput.d.ts +22 -0
- package/lib/typescript/module/src/MarkdownTextInput.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts +41 -0
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts.map +1 -0
- package/lib/typescript/module/src/NativeMarcusModule.d.ts +7 -0
- package/lib/typescript/module/src/NativeMarcusModule.d.ts.map +1 -0
- package/lib/typescript/module/src/commonTypes.d.ts +10 -0
- package/lib/typescript/module/src/commonTypes.d.ts.map +1 -0
- package/lib/typescript/module/src/index.d.ts +5 -0
- package/lib/typescript/module/src/index.d.ts.map +1 -0
- package/lib/typescript/module/src/parser/index.d.ts +8 -0
- package/lib/typescript/module/src/parser/index.d.ts.map +1 -0
- package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts +8 -0
- package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts.map +1 -0
- package/lib/typescript/module/src/rangeUtils.d.ts +6 -0
- package/lib/typescript/module/src/rangeUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/styleUtils.d.ts +8 -0
- package/lib/typescript/module/src/styleUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/InputHistory.d.ts +24 -0
- package/lib/typescript/module/src/web/InputHistory.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts +41 -0
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts +33 -0
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/browserUtils.d.ts +7 -0
- package/lib/typescript/module/src/web/utils/browserUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/cursorUtils.d.ts +12 -0
- package/lib/typescript/module/src/web/utils/cursorUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/inputUtils.d.ts +12 -0
- package/lib/typescript/module/src/web/utils/inputUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/measureUtils.d.ts +22 -0
- package/lib/typescript/module/src/web/utils/measureUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts +44 -0
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts +25 -0
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts +7 -0
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts.map +1 -0
- package/package.json +167 -0
- package/react-native.config.js +10 -0
- package/src/MarcusTextInputDecoratorViewNativeComponent.ts +77 -0
- package/src/MarkdownTextInput.tsx +139 -0
- package/src/MarkdownTextInput.web.tsx +958 -0
- package/src/NativeMarcusModule.ts +8 -0
- package/src/commonTypes.ts +30 -0
- package/src/global.d.ts +1 -0
- package/src/index.tsx +4 -0
- package/src/parser/index.ts +556 -0
- package/src/parser/micromark-extension-mentions.ts +82 -0
- package/src/rangeUtils.ts +68 -0
- package/src/react-native-web.d.ts +1 -0
- package/src/styleUtils.ts +100 -0
- package/src/web/InputHistory.ts +137 -0
- package/src/web/MarkdownTextInput.css +72 -0
- package/src/web/utils/blockLayout.ts +163 -0
- package/src/web/utils/blockUtils.ts +245 -0
- package/src/web/utils/browserUtils.ts +41 -0
- package/src/web/utils/cursorUtils.ts +204 -0
- package/src/web/utils/inputUtils.ts +206 -0
- package/src/web/utils/measureUtils.ts +0 -0
- package/src/web/utils/parserUtils.ts +813 -0
- package/src/web/utils/treeUtils.ts +149 -0
- package/src/web/utils/webStyleUtils.ts +69 -0
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
#import <React/RCTFabricComponentsPlugins.h>
|
|
2
|
+
#import <React/RCTTextInputComponentView.h>
|
|
3
|
+
#import <react/debug/react_native_assert.h>
|
|
4
|
+
#import <react/renderer/components/RNMarcusSpec/Props.h>
|
|
5
|
+
|
|
6
|
+
#import <RNMarcus/MarkdownSwiftInterop.h>
|
|
7
|
+
#import <RNMarcus/MarcusTextInputDecoratorComponentView.h>
|
|
8
|
+
#import <RNMarcus/MarcusTextInputDecoratorViewComponentDescriptor.h>
|
|
9
|
+
#import <RNMarcus/RCTMarcusStyle+Codegen.h>
|
|
10
|
+
#import <RNMarcus/RCTTextInput+AdaptiveImageGlyph.h>
|
|
11
|
+
|
|
12
|
+
using namespace facebook::react;
|
|
13
|
+
|
|
14
|
+
// Thin Objective-C++ shell around MarkdownDecorator.
|
|
15
|
+
//
|
|
16
|
+
// Only the parts that RCTComponentViewProtocol forces into Objective-C++ live
|
|
17
|
+
// here -- the component descriptor, the C++ props update, and view recycling.
|
|
18
|
+
// Everything else is delegated to Swift.
|
|
19
|
+
@implementation MarcusTextInputDecoratorComponentView {
|
|
20
|
+
MarkdownDecorator *_decorator;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
24
|
+
return concreteComponentDescriptorProvider<
|
|
25
|
+
MarcusTextInputDecoratorViewComponentDescriptor>();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Needed because of this: https://github.com/facebook/react-native/pull/37274
|
|
29
|
+
+ (void)load {
|
|
30
|
+
[super load];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
34
|
+
if (self = [super initWithFrame:frame]) {
|
|
35
|
+
static const auto defaultProps =
|
|
36
|
+
std::make_shared<const MarcusTextInputDecoratorViewProps>();
|
|
37
|
+
_props = defaultProps;
|
|
38
|
+
_decorator = [MarkdownDecorator new];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return self;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
- (void)didAddSubview:(UIView *)subview {
|
|
45
|
+
[super didAddSubview:subview];
|
|
46
|
+
|
|
47
|
+
react_native_assert(!_decorator.isAttached && "MarcusTextInputDecoratorComponentView tried to add "
|
|
48
|
+
"TextInput observers while they were attached");
|
|
49
|
+
react_native_assert(
|
|
50
|
+
[subview isKindOfClass:[RCTTextInputComponentView class]] &&
|
|
51
|
+
"Child component of MarcusTextInputDecoratorComponentView is not an "
|
|
52
|
+
"instance of RCTTextInputComponentView."
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
[_decorator attachTo:subview];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
- (void)willRemoveSubview:(UIView *)subview {
|
|
59
|
+
[_decorator detach];
|
|
60
|
+
[super willRemoveSubview:subview];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
- (void)reattachTextInputObservers {
|
|
64
|
+
// React Native replaces its backed text input view in place when `multiline`
|
|
65
|
+
// changes, which never touches this view's own subviews. Called from the
|
|
66
|
+
// RCTTextInputComponentView swizzle so the observers follow the new view
|
|
67
|
+
// instead of staying bound to the discarded one.
|
|
68
|
+
UIView *textInputComponentView = self.subviews.firstObject;
|
|
69
|
+
if (textInputComponentView == nil) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
[_decorator attachTo:textInputComponentView];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
- (void)updateProps:(Props::Shared const &)props
|
|
77
|
+
oldProps:(Props::Shared const &)oldProps {
|
|
78
|
+
const auto &oldViewProps =
|
|
79
|
+
*std::static_pointer_cast<MarcusTextInputDecoratorViewProps const>(
|
|
80
|
+
_props
|
|
81
|
+
);
|
|
82
|
+
const auto &newViewProps =
|
|
83
|
+
*std::static_pointer_cast<MarcusTextInputDecoratorViewProps const>(
|
|
84
|
+
props
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
if (oldViewProps.parserId != newViewProps.parserId) {
|
|
88
|
+
_decorator.parserId = @(newViewProps.parserId);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Codegen only emits operator== for the markdownStyle struct under
|
|
92
|
+
// RN_SERIALIZABLE_STATE, which is not defined on iOS, so there is no cheap
|
|
93
|
+
// way to tell whether the style actually changed. Rebuilding it and
|
|
94
|
+
// re-formatting on every props update is wasteful -- Fabric emits one
|
|
95
|
+
// whenever the props object is recreated -- but hand-written equality over
|
|
96
|
+
// ~27 fields would rot silently the moment a style prop is added.
|
|
97
|
+
_decorator.markdownStyle =
|
|
98
|
+
[[RCTMarcusStyle alloc] initWithStruct:newViewProps.markdownStyle];
|
|
99
|
+
|
|
100
|
+
[_decorator applyNewStyles];
|
|
101
|
+
|
|
102
|
+
[super updateProps:props oldProps:oldProps];
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
- (void)prepareForRecycle {
|
|
106
|
+
react_native_assert(!_decorator.isAttached && "MarcusTextInputDecoratorComponentView was being "
|
|
107
|
+
"recycled with TextInput observers still attached");
|
|
108
|
+
[super prepareForRecycle];
|
|
109
|
+
|
|
110
|
+
static const auto defaultProps =
|
|
111
|
+
std::make_shared<const MarcusTextInputDecoratorViewProps>();
|
|
112
|
+
_props = defaultProps;
|
|
113
|
+
[_decorator reset];
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
Class<RCTComponentViewProtocol>
|
|
117
|
+
MarcusTextInputDecoratorViewCls(void) {
|
|
118
|
+
return MarcusTextInputDecoratorComponentView.class;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@end
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#include "MarcusTextInputDecoratorShadowNode.h"
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/components/view/conversions.h>
|
|
4
|
+
#include <react/renderer/core/ComponentDescriptor.h>
|
|
5
|
+
#include <yoga/Yoga.h>
|
|
6
|
+
|
|
7
|
+
namespace facebook {
|
|
8
|
+
namespace react {
|
|
9
|
+
|
|
10
|
+
extern const char MarcusTextInputDecoratorViewComponentName[] =
|
|
11
|
+
"MarcusTextInputDecoratorView";
|
|
12
|
+
|
|
13
|
+
MarcusTextInputDecoratorShadowNode::MarcusTextInputDecoratorShadowNode(
|
|
14
|
+
ShadowNodeFragment const &fragment,
|
|
15
|
+
ShadowNodeFamily::Shared const &family,
|
|
16
|
+
ShadowNodeTraits traits
|
|
17
|
+
)
|
|
18
|
+
: ConcreteViewShadowNode(fragment, family, traits) {
|
|
19
|
+
initialize();
|
|
20
|
+
makeChildNodeMutable();
|
|
21
|
+
|
|
22
|
+
if (fragment.children) {
|
|
23
|
+
overwriteMeasureCallbackConnector();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
MarcusTextInputDecoratorShadowNode::MarcusTextInputDecoratorShadowNode(
|
|
28
|
+
ShadowNode const &sourceShadowNode,
|
|
29
|
+
ShadowNodeFragment const &fragment
|
|
30
|
+
)
|
|
31
|
+
: ConcreteViewShadowNode(sourceShadowNode, fragment) {
|
|
32
|
+
// Carry the re-measure flag over from the node being cloned. It has to be set
|
|
33
|
+
// before makeChildNodeMutable() below, which is what reads it.
|
|
34
|
+
const auto &source =
|
|
35
|
+
static_cast<const MarcusTextInputDecoratorShadowNode &>(sourceShadowNode);
|
|
36
|
+
needsRemeasure_ = source.needsRemeasure_;
|
|
37
|
+
|
|
38
|
+
initialize();
|
|
39
|
+
makeChildNodeMutable();
|
|
40
|
+
|
|
41
|
+
if (fragment.children) {
|
|
42
|
+
overwriteMeasureCallbackConnector();
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void
|
|
47
|
+
MarcusTextInputDecoratorShadowNode::initialize() {
|
|
48
|
+
// Setting display: contents style results in ForceFlattenView trait being set
|
|
49
|
+
// on the shadow node. This trait causes the node not to have a host view. By
|
|
50
|
+
// removing the trait, it's possible to force RN to create a host view, layout
|
|
51
|
+
// of which can then be customized.
|
|
52
|
+
ShadowNode::traits_.unset(ShadowNodeTraits::ForceFlattenView);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
void
|
|
56
|
+
MarcusTextInputDecoratorShadowNode::makeChildNodeMutable() {
|
|
57
|
+
// When the decorator is cloned and has a child node, the child node should be
|
|
58
|
+
// cloned as well to ensure it is mutable.
|
|
59
|
+
const auto &children = getChildren();
|
|
60
|
+
if (!children.empty()) {
|
|
61
|
+
react_native_assert(
|
|
62
|
+
children.size() == 1 &&
|
|
63
|
+
"MarcusTextInputDecoratorView received more than one child"
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const auto clonedChild = children[0]->clone({});
|
|
67
|
+
replaceChild(*children[0], clonedChild);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
void
|
|
72
|
+
MarcusTextInputDecoratorShadowNode::overwriteMeasureCallbackConnector() {
|
|
73
|
+
const auto &children = getChildren();
|
|
74
|
+
if (children.empty()) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
react_native_assert(
|
|
78
|
+
children.size() == 1 &&
|
|
79
|
+
"MarcusTextInputDecoratorView received more than one child"
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const auto child = std::dynamic_pointer_cast<const TextInputShadowNode>(children[0]);
|
|
83
|
+
react_native_assert(
|
|
84
|
+
child != nullptr &&
|
|
85
|
+
"MarcusTextInputDecoratorView received child other than a TextInput"
|
|
86
|
+
);
|
|
87
|
+
child->ensureUnsealed();
|
|
88
|
+
|
|
89
|
+
// This is obviously not correct, but since both MarcusTextInputDecoratorShadowNode and
|
|
90
|
+
// TextInputShadowNode inherit from YogaLayoutableShadowNode by doing this cast it's
|
|
91
|
+
// possible to access protected members from TextInputShadowNode like yogaNode_.
|
|
92
|
+
// As only things from YogaLayoutableShadowNode are accessed, it should be safe,
|
|
93
|
+
// since the vtable should be the same between them.
|
|
94
|
+
const auto &nodeWithAccessibleYogaNode =
|
|
95
|
+
std::reinterpret_pointer_cast<const MarcusTextInputDecoratorShadowNode>(child);
|
|
96
|
+
|
|
97
|
+
// decorator node cannot have a measure function since it's not a leaf node
|
|
98
|
+
// but we can redirect measuring of the child input to call measureContent
|
|
99
|
+
// on the decorator
|
|
100
|
+
const auto &yogaNode = &nodeWithAccessibleYogaNode->yogaNode_;
|
|
101
|
+
YGNodeSetMeasureFunc(yogaNode, yogaNodeMeasureCallbackConnector);
|
|
102
|
+
|
|
103
|
+
// If a background parse finished since the last layout, the size Yoga has for
|
|
104
|
+
// the child was measured from unformatted text. Nothing dirties this part of
|
|
105
|
+
// the tree on its own -- Yoga never measures the decorator -- so the stale
|
|
106
|
+
// height would stick until something else changed the layout.
|
|
107
|
+
//
|
|
108
|
+
// Both nodes are marked by hand rather than via YGNodeMarkDirty(): the child
|
|
109
|
+
// is usually already dirty from completeClone(), which stops the flag
|
|
110
|
+
// propagating upwards and would leave the decorator clean. Parents pick it up
|
|
111
|
+
// themselves when updateYogaChildren() runs.
|
|
112
|
+
if (needsRemeasure_ != nullptr && needsRemeasure_->exchange(false)) {
|
|
113
|
+
yogaNode->setDirty(true);
|
|
114
|
+
yogaNode_.setDirty(true);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
void
|
|
119
|
+
MarcusTextInputDecoratorShadowNode::appendChild(
|
|
120
|
+
const std::shared_ptr<const ShadowNode> &child
|
|
121
|
+
) {
|
|
122
|
+
YogaLayoutableShadowNode::appendChild(child);
|
|
123
|
+
|
|
124
|
+
overwriteMeasureCallbackConnector();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
void
|
|
128
|
+
MarcusTextInputDecoratorShadowNode::replaceChild(
|
|
129
|
+
const ShadowNode &oldChild, const std::shared_ptr<const ShadowNode> &newChild, size_t suggestedIndex
|
|
130
|
+
) {
|
|
131
|
+
YogaLayoutableShadowNode::replaceChild(oldChild, newChild, suggestedIndex);
|
|
132
|
+
|
|
133
|
+
overwriteMeasureCallbackConnector();
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
Size
|
|
137
|
+
MarcusTextInputDecoratorShadowNode::measureContent(
|
|
138
|
+
const LayoutContext &layoutContext,
|
|
139
|
+
const LayoutConstraints &layoutConstraints
|
|
140
|
+
) const {
|
|
141
|
+
const auto &children = getChildren();
|
|
142
|
+
react_native_assert(
|
|
143
|
+
children.size() == 1 &&
|
|
144
|
+
"MarcusTextInputDecoratorView received wrong number of children"
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const auto child =
|
|
148
|
+
std::static_pointer_cast<const TextInputShadowNode>(children[0]);
|
|
149
|
+
|
|
150
|
+
child->ensureUnsealed();
|
|
151
|
+
|
|
152
|
+
// apply markdown formatting before measuring the child
|
|
153
|
+
const auto &mutableChild =
|
|
154
|
+
std::const_pointer_cast<TextInputShadowNode>(child);
|
|
155
|
+
applyMarkdownFormattingToTextInputState(mutableChild, layoutContext);
|
|
156
|
+
|
|
157
|
+
const auto childWithMeasureContentAccess =
|
|
158
|
+
std::static_pointer_cast<const YogaLayoutableShadowNode>(child);
|
|
159
|
+
return childWithMeasureContentAccess->measureContent(layoutContext, layoutConstraints);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
void
|
|
163
|
+
MarcusTextInputDecoratorShadowNode::layout(LayoutContext layoutContext) {
|
|
164
|
+
YogaLayoutableShadowNode::layout(layoutContext);
|
|
165
|
+
|
|
166
|
+
const auto &children = getChildren();
|
|
167
|
+
react_native_assert(
|
|
168
|
+
children.size() == 1 &&
|
|
169
|
+
"MarcusTextInputDecoratorView didn't receive exactly one child"
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const auto child =
|
|
173
|
+
std::static_pointer_cast<const TextInputShadowNode>(children[0]);
|
|
174
|
+
|
|
175
|
+
child->ensureUnsealed();
|
|
176
|
+
|
|
177
|
+
const auto &mutableChild =
|
|
178
|
+
std::const_pointer_cast<TextInputShadowNode>(child);
|
|
179
|
+
|
|
180
|
+
// TODO: this may not be the correct way to do this
|
|
181
|
+
// Since nodes with display: contents are skipped during layout, they have
|
|
182
|
+
// zero-layout. To properly display the view, assign the layout metrics from
|
|
183
|
+
// the child (text input, which was calculated by Yoga) to the decorator view.
|
|
184
|
+
auto childMetrics = child->getLayoutMetrics();
|
|
185
|
+
setLayoutMetrics(childMetrics);
|
|
186
|
+
|
|
187
|
+
// Then, it's also needed to update the metrics on the child as the position
|
|
188
|
+
// is relative to the parent, which was just moved above. By zeroing the
|
|
189
|
+
// origin, the child is effectively moved to the same position it was before
|
|
190
|
+
// the manipulation here.
|
|
191
|
+
childMetrics.frame.origin = Point{};
|
|
192
|
+
mutableChild->setLayoutMetrics(childMetrics);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// this is private in YogaLayoutableShadowNode
|
|
196
|
+
YGSize
|
|
197
|
+
MarcusTextInputDecoratorShadowNode::yogaNodeMeasureCallbackConnector(
|
|
198
|
+
YGNodeConstRef yogaNode, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode
|
|
199
|
+
) {
|
|
200
|
+
|
|
201
|
+
auto minimumSize = Size{0, 0};
|
|
202
|
+
auto maximumSize = Size{std::numeric_limits<Float>::infinity(), std::numeric_limits<Float>::infinity()};
|
|
203
|
+
|
|
204
|
+
switch (widthMode) {
|
|
205
|
+
case YGMeasureModeUndefined:
|
|
206
|
+
break;
|
|
207
|
+
case YGMeasureModeExactly:
|
|
208
|
+
minimumSize.width = floatFromYogaFloat(width);
|
|
209
|
+
maximumSize.width = floatFromYogaFloat(width);
|
|
210
|
+
break;
|
|
211
|
+
case YGMeasureModeAtMost:
|
|
212
|
+
maximumSize.width = floatFromYogaFloat(width);
|
|
213
|
+
break;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
switch (heightMode) {
|
|
217
|
+
case YGMeasureModeUndefined:
|
|
218
|
+
break;
|
|
219
|
+
case YGMeasureModeExactly:
|
|
220
|
+
minimumSize.height = floatFromYogaFloat(height);
|
|
221
|
+
maximumSize.height = floatFromYogaFloat(height);
|
|
222
|
+
break;
|
|
223
|
+
case YGMeasureModeAtMost:
|
|
224
|
+
maximumSize.height = floatFromYogaFloat(height);
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// This is where changes begin compared to the copied code
|
|
229
|
+
const auto &decoratorYogaNode = YGNodeGetParent(const_cast<YGNodeRef>(yogaNode));
|
|
230
|
+
const auto &decoratorShadowNode = shadowNodeFromContext(decoratorYogaNode);
|
|
231
|
+
|
|
232
|
+
LayoutContext context{};
|
|
233
|
+
context.fontSizeMultiplier = fontSizeMultiplier();
|
|
234
|
+
|
|
235
|
+
const auto size = decoratorShadowNode.measureContent(context, {minimumSize, maximumSize});
|
|
236
|
+
|
|
237
|
+
return YGSize{yogaFloatFromFloat(size.width), yogaFloatFromFloat(size.height)};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// this is private in YogaLayoutableShadowNode
|
|
241
|
+
YogaLayoutableShadowNode &
|
|
242
|
+
MarcusTextInputDecoratorShadowNode::shadowNodeFromContext(
|
|
243
|
+
YGNodeConstRef yogaNode
|
|
244
|
+
) {
|
|
245
|
+
return dynamic_cast<YogaLayoutableShadowNode &>(
|
|
246
|
+
*static_cast<ShadowNode *>(YGNodeGetContext(yogaNode))
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
} // namespace react
|
|
251
|
+
} // namespace facebook
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <react/renderer/components/RNMarcusSpec/EventEmitters.h>
|
|
4
|
+
#include <react/renderer/components/RNMarcusSpec/MarcusTextInputDecoratorState.h>
|
|
5
|
+
#include <react/renderer/components/RNMarcusSpec/Props.h>
|
|
6
|
+
#include <react/renderer/components/iostextinput/TextInputShadowNode.h>
|
|
7
|
+
|
|
8
|
+
#include <react/renderer/components/view/ConcreteViewShadowNode.h>
|
|
9
|
+
#include <react/renderer/core/LayoutContext.h>
|
|
10
|
+
|
|
11
|
+
#include <atomic>
|
|
12
|
+
#include <memory>
|
|
13
|
+
|
|
14
|
+
namespace facebook {
|
|
15
|
+
namespace react {
|
|
16
|
+
|
|
17
|
+
JSI_EXPORT extern const char MarcusTextInputDecoratorViewComponentName[];
|
|
18
|
+
|
|
19
|
+
class JSI_EXPORT MarcusTextInputDecoratorShadowNode final
|
|
20
|
+
: public ConcreteViewShadowNode<MarcusTextInputDecoratorViewComponentName, MarcusTextInputDecoratorViewProps, MarcusTextInputDecoratorViewEventEmitter, MarcusTextInputDecoratorState> {
|
|
21
|
+
public:
|
|
22
|
+
MarcusTextInputDecoratorShadowNode(ShadowNodeFragment const &fragment, ShadowNodeFamily::Shared const &family, ShadowNodeTraits traits);
|
|
23
|
+
|
|
24
|
+
MarcusTextInputDecoratorShadowNode(ShadowNode const &sourceShadowNode, ShadowNodeFragment const &fragment);
|
|
25
|
+
|
|
26
|
+
void
|
|
27
|
+
appendChild(const std::shared_ptr<const ShadowNode> &child) override;
|
|
28
|
+
void
|
|
29
|
+
replaceChild(const ShadowNode &oldChild, const std::shared_ptr<const ShadowNode> &newChild, size_t suggestedIndex = SIZE_MAX) override;
|
|
30
|
+
void
|
|
31
|
+
layout(LayoutContext layoutContext) override;
|
|
32
|
+
Size
|
|
33
|
+
measureContent(const LayoutContext &layoutContext, const LayoutConstraints &layoutConstraints) const override;
|
|
34
|
+
|
|
35
|
+
private:
|
|
36
|
+
void
|
|
37
|
+
initialize();
|
|
38
|
+
void
|
|
39
|
+
overwriteMeasureCallbackConnector();
|
|
40
|
+
void
|
|
41
|
+
makeChildNodeMutable();
|
|
42
|
+
void
|
|
43
|
+
applyMarkdownFormattingToTextInputState(
|
|
44
|
+
std::shared_ptr<TextInputShadowNode> node,
|
|
45
|
+
const LayoutContext &layoutContext
|
|
46
|
+
) const;
|
|
47
|
+
static YGSize
|
|
48
|
+
yogaNodeMeasureCallbackConnector(YGNodeConstRef yogaNode, float width, YGMeasureMode widthMode, float height, YGMeasureMode heightMode);
|
|
49
|
+
static YogaLayoutableShadowNode &
|
|
50
|
+
shadowNodeFromContext(YGNodeConstRef yogaNode);
|
|
51
|
+
|
|
52
|
+
// Defined in the Objective-C++ half of this class; wraps
|
|
53
|
+
// RCTFontSizeMultiplier so the Yoga measure connector can stay in plain C++.
|
|
54
|
+
static Float
|
|
55
|
+
fontSizeMultiplier();
|
|
56
|
+
|
|
57
|
+
// Set from any thread when a background parse finishes. The next
|
|
58
|
+
// overwriteMeasureCallbackConnector() marks the child's Yoga node dirty so it
|
|
59
|
+
// is measured again, this time with markdown applied. Shared across clones so
|
|
60
|
+
// a parse that lands mid-layout is not lost.
|
|
61
|
+
mutable std::shared_ptr<std::atomic_bool> needsRemeasure_;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
} // namespace react
|
|
65
|
+
} // namespace facebook
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// Objective-C++ half of MarcusTextInputDecoratorShadowNode.
|
|
2
|
+
//
|
|
3
|
+
// Everything that needs to cross into Objective-C lives here; the rest of the
|
|
4
|
+
// class is plain C++ in MarcusTextInputDecoratorShadowNode.cpp. Splitting a
|
|
5
|
+
// class across translation units is fine -- these are just two object files
|
|
6
|
+
// that link together.
|
|
7
|
+
|
|
8
|
+
#include "MarcusTextInputDecoratorShadowNode.h"
|
|
9
|
+
|
|
10
|
+
#import <React/RCTUtils.h>
|
|
11
|
+
#include <react/renderer/core/ConcreteState.h>
|
|
12
|
+
#include <react/renderer/textlayoutmanager/RCTAttributedTextUtils.h>
|
|
13
|
+
|
|
14
|
+
#include <atomic>
|
|
15
|
+
#include <memory>
|
|
16
|
+
|
|
17
|
+
#import "RCTMarcusStyle+Codegen.h"
|
|
18
|
+
#import <RNMarcus/MarkdownSwiftInterop.h>
|
|
19
|
+
|
|
20
|
+
namespace facebook {
|
|
21
|
+
namespace react {
|
|
22
|
+
|
|
23
|
+
namespace {
|
|
24
|
+
|
|
25
|
+
using DecoratorState = ConcreteState<MarcusTextInputDecoratorState>;
|
|
26
|
+
|
|
27
|
+
// Formats `string` in place.
|
|
28
|
+
//
|
|
29
|
+
// On the main thread only already-cached ranges are used; the parser is never
|
|
30
|
+
// run there. Running it waits on the markdown runtime, and on a Yoga measure
|
|
31
|
+
// pass that wait can outlast the watchdog
|
|
32
|
+
// (Expensify/react-native-live-markdown#772). On a cache miss the text is left
|
|
33
|
+
// unformatted for this pass and parsed in the background instead, then
|
|
34
|
+
// `needsRemeasure` and a state update ask for another measure so the size
|
|
35
|
+
// measured from unformatted text -- wrong for h1, code fonts, blockquote
|
|
36
|
+
// indents and emoji -- does not stick.
|
|
37
|
+
//
|
|
38
|
+
// This is rare in practice: text changes are normally parsed off the main
|
|
39
|
+
// thread first, so the main thread finds the ranges already cached.
|
|
40
|
+
void
|
|
41
|
+
ApplyMarkdownFormatting(
|
|
42
|
+
NSMutableAttributedString *string,
|
|
43
|
+
NSDictionary<NSAttributedStringKey, id> *defaultTextAttributes,
|
|
44
|
+
RCTMarcusStyle *markdownStyle,
|
|
45
|
+
NSNumber *parserId,
|
|
46
|
+
MarkdownParser *parser,
|
|
47
|
+
std::shared_ptr<std::atomic_bool> needsRemeasure,
|
|
48
|
+
std::shared_ptr<const DecoratorState> state
|
|
49
|
+
) {
|
|
50
|
+
NSString *text = string.string;
|
|
51
|
+
NSArray<MarcusRange *> *markdownRanges =
|
|
52
|
+
[parser cachedRangesForText:text
|
|
53
|
+
withParserId:parserId];
|
|
54
|
+
|
|
55
|
+
if (markdownRanges == nil) {
|
|
56
|
+
if ([NSThread isMainThread]) {
|
|
57
|
+
[parser
|
|
58
|
+
warmCacheAsyncForText:text
|
|
59
|
+
withParserId:parserId
|
|
60
|
+
completion:^{
|
|
61
|
+
// Only ever runs for the newest text, so this cannot
|
|
62
|
+
// loop: the next measure finds the ranges cached, or the
|
|
63
|
+
// text changed again and a new measure was needed
|
|
64
|
+
// anyway.
|
|
65
|
+
if (needsRemeasure != nullptr) {
|
|
66
|
+
needsRemeasure->store(true);
|
|
67
|
+
}
|
|
68
|
+
if (state != nullptr) {
|
|
69
|
+
// Changes nothing; it exists only to schedule a
|
|
70
|
+
// commit. updateState() is safe from any thread and
|
|
71
|
+
// handles an already-gone family.
|
|
72
|
+
state->updateState(MarcusTextInputDecoratorState{});
|
|
73
|
+
}
|
|
74
|
+
}];
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Background threads may parse inline: the watchdog only applies to the
|
|
79
|
+
// main thread, and parsing no longer holds a lock the main thread waits on.
|
|
80
|
+
markdownRanges = [parser parse:text withParserId:parserId];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
[MarkdownFormatter formatAttributedString:string
|
|
84
|
+
defaultTextAttributes:defaultTextAttributes
|
|
85
|
+
ranges:markdownRanges
|
|
86
|
+
style:markdownStyle];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
} // namespace
|
|
90
|
+
|
|
91
|
+
Float
|
|
92
|
+
MarcusTextInputDecoratorShadowNode::fontSizeMultiplier() {
|
|
93
|
+
return RCTFontSizeMultiplier();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
void
|
|
97
|
+
MarcusTextInputDecoratorShadowNode::
|
|
98
|
+
applyMarkdownFormattingToTextInputState(
|
|
99
|
+
std::shared_ptr<TextInputShadowNode> textInput,
|
|
100
|
+
const LayoutContext &layoutContext
|
|
101
|
+
) const {
|
|
102
|
+
|
|
103
|
+
const auto &textInputState =
|
|
104
|
+
*std::static_pointer_cast<const react::ConcreteState<TextInputState>>(
|
|
105
|
+
textInput->getState()
|
|
106
|
+
);
|
|
107
|
+
const auto &stateData = textInputState.getData();
|
|
108
|
+
const auto fontSizeMultiplier = layoutContext.fontSizeMultiplier;
|
|
109
|
+
|
|
110
|
+
const auto &decoratorProps =
|
|
111
|
+
*std::static_pointer_cast<MarcusTextInputDecoratorViewProps const>(
|
|
112
|
+
getProps()
|
|
113
|
+
);
|
|
114
|
+
const auto &textInputProps =
|
|
115
|
+
*std::static_pointer_cast<TextInputProps const>(textInput->getProps());
|
|
116
|
+
|
|
117
|
+
const auto defaultTextAttributes =
|
|
118
|
+
textInputProps.getEffectiveTextAttributes(fontSizeMultiplier);
|
|
119
|
+
const auto defaultNSTextAttributes =
|
|
120
|
+
RCTNSTextAttributesFromTextAttributes(defaultTextAttributes);
|
|
121
|
+
|
|
122
|
+
// The parser is shared process-wide, so its cache survives the cloning that
|
|
123
|
+
// happens during layout without this node having to hold on to it.
|
|
124
|
+
MarkdownParser *parser = [MarkdownParser sharedParser];
|
|
125
|
+
|
|
126
|
+
if (!needsRemeasure_) {
|
|
127
|
+
needsRemeasure_ = std::make_shared<std::atomic_bool>(false);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
RCTMarcusStyle *markdownStyle =
|
|
131
|
+
[[RCTMarcusStyle alloc] initWithStruct:decoratorProps.markdownStyle];
|
|
132
|
+
NSNumber *parserId = [NSNumber numberWithInt:decoratorProps.parserId];
|
|
133
|
+
|
|
134
|
+
// convert the attibuted string stored in state to
|
|
135
|
+
// NSAttributedString
|
|
136
|
+
auto nsAttributedString = RCTNSAttributedStringFromAttributedStringBox(
|
|
137
|
+
stateData.attributedStringBox
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
auto newStateData = TextInputState(stateData);
|
|
141
|
+
|
|
142
|
+
if (stateData.attributedStringBox.getMode() ==
|
|
143
|
+
AttributedStringBox::Mode::Value) {
|
|
144
|
+
|
|
145
|
+
// Handles the first render, where the text stored in props is
|
|
146
|
+
// different than the one stored in state. The one in state is empty,
|
|
147
|
+
// while the one in props is passed from JS. If we don't update the
|
|
148
|
+
// state here, we'll end up with a one-default-line-sized text input
|
|
149
|
+
if (textInputState.getRevision() == State::initialRevisionValue) {
|
|
150
|
+
auto plainStringFromState =
|
|
151
|
+
std::string([[nsAttributedString string] UTF8String]);
|
|
152
|
+
|
|
153
|
+
if (plainStringFromState != textInputProps.text) {
|
|
154
|
+
// creates new AttributedString from props, adapted from
|
|
155
|
+
// TextInputShadowNode (ios one, text inputs are
|
|
156
|
+
// platform-specific)
|
|
157
|
+
auto attributedString = AttributedString{};
|
|
158
|
+
attributedString.appendFragment(AttributedString::Fragment{textInputProps.text, defaultTextAttributes});
|
|
159
|
+
|
|
160
|
+
auto attachments = BaseTextShadowNode::Attachments{};
|
|
161
|
+
BaseTextShadowNode::buildAttributedString(
|
|
162
|
+
defaultTextAttributes, *textInput, attributedString, attachments
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
// convert the newly created attributed string to
|
|
166
|
+
// NSAttributedString
|
|
167
|
+
nsAttributedString = RCTNSAttributedStringFromAttributedStringBox(
|
|
168
|
+
AttributedStringBox{attributedString}
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// apply markdown
|
|
174
|
+
NSMutableAttributedString *newString = [nsAttributedString mutableCopy];
|
|
175
|
+
ApplyMarkdownFormatting(
|
|
176
|
+
newString, defaultNSTextAttributes, markdownStyle, parserId, parser, needsRemeasure_, std::static_pointer_cast<const DecoratorState>(getState())
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
// create a clone of the old TextInputState and update the
|
|
180
|
+
// attributed string box to point to the string with markdown
|
|
181
|
+
// applied
|
|
182
|
+
newStateData.attributedStringBox =
|
|
183
|
+
RCTAttributedStringBoxFromNSAttributedString(newString);
|
|
184
|
+
} else if (stateData.attributedStringBox.getMode() ==
|
|
185
|
+
AttributedStringBox::Mode::OpaquePointer) {
|
|
186
|
+
|
|
187
|
+
// apply markdown
|
|
188
|
+
NSMutableAttributedString *newString = [nsAttributedString mutableCopy];
|
|
189
|
+
ApplyMarkdownFormatting(
|
|
190
|
+
newString, defaultNSTextAttributes, markdownStyle, parserId, parser, needsRemeasure_, std::static_pointer_cast<const DecoratorState>(getState())
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
// create a clone of the old TextInputState and update the
|
|
194
|
+
// attributed string box to point to the string with markdown
|
|
195
|
+
// applied
|
|
196
|
+
newStateData.attributedStringBox =
|
|
197
|
+
RCTAttributedStringBoxFromNSAttributedString(newString);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
textInput->setStateData(std::move(newStateData));
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
} // namespace react
|
|
204
|
+
} // namespace facebook
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#import <RNMarcus/MarcusTextInputDecoratorViewManager.h>
|
|
2
|
+
|
|
3
|
+
@implementation MarcusTextInputDecoratorViewManager
|
|
4
|
+
|
|
5
|
+
RCT_EXPORT_MODULE(MarcusTextInputDecoratorView)
|
|
6
|
+
|
|
7
|
+
RCT_EXPORT_VIEW_PROPERTY(markdownStyle, NSDictionary)
|
|
8
|
+
|
|
9
|
+
RCT_EXPORT_VIEW_PROPERTY(parserId, NSNumber)
|
|
10
|
+
|
|
11
|
+
@end
|