@fendent/react-native-enriched 0.5.2-fork.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.
- package/LICENSE +20 -0
- package/README.md +343 -0
- package/ReactNativeEnriched.podspec +31 -0
- package/android/build.gradle +106 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +197 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +72 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ComponentDescriptors.cpp +22 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ComponentDescriptors.h +24 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.cpp +434 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.h +391 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.cpp +173 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.h +833 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ShadowNodes.cpp +17 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/ShadowNodes.h +23 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/States.cpp +16 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/States.h +20 -0
- package/android/gradle.properties +5 -0
- package/android/lint.gradle +70 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/AndroidManifestNew.xml +2 -0
- package/android/src/main/java/com/swmansion/enriched/ReactNativeEnrichedPackage.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/AsyncDrawable.kt +126 -0
- package/android/src/main/java/com/swmansion/enriched/common/CheckboxDrawable.kt +81 -0
- package/android/src/main/java/com/swmansion/enriched/common/EnrichedConstants.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/common/EnrichedStyle.kt +57 -0
- package/android/src/main/java/com/swmansion/enriched/common/ForceRedrawSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/common/GumboNormalizer.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/common/MentionStyle.kt +7 -0
- package/android/src/main/java/com/swmansion/enriched/common/ResourceManager.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedParser.java +956 -0
- package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +79 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBlockQuoteSpan.kt +53 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedBoldSpan.kt +12 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCheckboxListSpan.kt +92 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedCodeBlockSpan.kt +81 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH1Span.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH2Span.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH3Span.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH4Span.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH5Span.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedH6Span.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedImageSpan.kt +184 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedInlineCodeSpan.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedItalicSpan.kt +12 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedLinkSpan.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedMentionSpan.kt +35 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedOrderedListSpan.kt +79 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedStrikeThroughSpan.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnderlineSpan.kt +11 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedUnorderedListSpan.kt +62 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedBlockSpan.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedHeadingSpan.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedInlineSpan.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedParagraphSpan.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedSpan.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/interfaces/EnrichedZeroWidthSpaceSpan.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputConnectionWrapper.kt +140 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +83 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +1120 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewLayoutManager.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputViewManager.kt +478 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/MeasurementStore.kt +225 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/MentionHandler.kt +55 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeHtmlEvent.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeSelectionEvent.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeStateEvent.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnChangeTextEvent.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnContextMenuItemPressEvent.kt +35 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputBlurEvent.kt +25 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputFocusEvent.kt +25 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnInputKeyPressEvent.kt +27 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnLinkDetectedEvent.kt +32 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionDetectedEvent.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionEvent.kt +34 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnPasteImagesEvent.kt +47 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnRequestHtmlResultEvent.kt +32 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnSubmitEditingEvent.kt +29 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBlockQuoteSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputBoldSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCheckboxListSpan.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputCodeBlockSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH1Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH2Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH3Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH4Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH5Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputH6Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputImageSpan.kt +36 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputInlineCodeSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputItalicSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputLinkSpan.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputMentionSpan.kt +18 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputOrderedListSpan.kt +21 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputStrikeThroughSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnderlineSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedInputUnorderedListSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedLineHeightSpan.kt +44 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedSpans.kt +241 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/interfaces/EnrichedInputSpan.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/HtmlStyle.kt +372 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +164 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +263 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +434 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +394 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedEditableFactory.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSelection.kt +320 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpanState.kt +310 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +106 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannableStringBuilder.kt +24 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/RichContentReceiver.kt +127 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +106 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedSpanWatcher.kt +107 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedTextWatcher.kt +74 -0
- package/android/src/main/new_arch/CMakeLists.txt +62 -0
- package/android/src/main/new_arch/GumboNormalizerJni.cpp +14 -0
- package/android/src/main/new_arch/ReactNativeEnrichedSpec.cpp +11 -0
- package/android/src/main/new_arch/ReactNativeEnrichedSpec.h +15 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputComponentDescriptor.h +35 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputMeasurementManager.cpp +53 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputMeasurementManager.h +25 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputShadowNode.cpp +35 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputShadowNode.h +53 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputState.cpp +9 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputState.h +24 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/conversions.h +27 -0
- package/android/src/main/res/drawable/broken_image.xml +10 -0
- package/cpp/CMakeLists.txt +50 -0
- package/cpp/GumboParser/GumboParser.h +34043 -0
- package/cpp/README.md +59 -0
- package/cpp/parser/GumboNormalizer.c +915 -0
- package/cpp/parser/GumboParser.cpp +16 -0
- package/cpp/parser/GumboParser.hpp +23 -0
- package/cpp/tests/GumboParserTest.cpp +457 -0
- package/ios/EnrichedTextInputView.h +53 -0
- package/ios/EnrichedTextInputView.mm +2360 -0
- package/ios/EnrichedTextInputViewManager.mm +13 -0
- package/ios/attributesManager/AttributesManager.h +17 -0
- package/ios/attributesManager/AttributesManager.mm +195 -0
- package/ios/config/InputConfig.h +104 -0
- package/ios/config/InputConfig.mm +664 -0
- package/ios/extensions/ColorExtension.h +7 -0
- package/ios/extensions/ColorExtension.mm +38 -0
- package/ios/extensions/FontExtension.h +11 -0
- package/ios/extensions/FontExtension.mm +72 -0
- package/ios/extensions/ImageExtension.h +34 -0
- package/ios/extensions/ImageExtension.mm +165 -0
- package/ios/extensions/LayoutManagerExtension.h +6 -0
- package/ios/extensions/LayoutManagerExtension.mm +443 -0
- package/ios/extensions/StringExtension.h +15 -0
- package/ios/extensions/StringExtension.mm +69 -0
- package/ios/generated/ReactNativeEnrichedSpec/ComponentDescriptors.cpp +22 -0
- package/ios/generated/ReactNativeEnrichedSpec/ComponentDescriptors.h +24 -0
- package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.cpp +434 -0
- package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.h +391 -0
- package/ios/generated/ReactNativeEnrichedSpec/Props.cpp +173 -0
- package/ios/generated/ReactNativeEnrichedSpec/Props.h +833 -0
- package/ios/generated/ReactNativeEnrichedSpec/RCTComponentViewHelpers.h +582 -0
- package/ios/generated/ReactNativeEnrichedSpec/ShadowNodes.cpp +17 -0
- package/ios/generated/ReactNativeEnrichedSpec/ShadowNodes.h +23 -0
- package/ios/generated/ReactNativeEnrichedSpec/States.cpp +16 -0
- package/ios/generated/ReactNativeEnrichedSpec/States.h +20 -0
- package/ios/inputParser/InputParser.h +11 -0
- package/ios/inputParser/InputParser.mm +1463 -0
- package/ios/inputTextView/InputTextView.h +6 -0
- package/ios/inputTextView/InputTextView.mm +285 -0
- package/ios/interfaces/AttributeEntry.h +9 -0
- package/ios/interfaces/AttributeEntry.mm +4 -0
- package/ios/interfaces/BaseStyleProtocol.h +17 -0
- package/ios/interfaces/ImageAttachment.h +11 -0
- package/ios/interfaces/ImageAttachment.mm +107 -0
- package/ios/interfaces/ImageData.h +10 -0
- package/ios/interfaces/ImageData.mm +4 -0
- package/ios/interfaces/LinkData.h +11 -0
- package/ios/interfaces/LinkData.mm +29 -0
- package/ios/interfaces/LinkRegexConfig.h +19 -0
- package/ios/interfaces/LinkRegexConfig.mm +37 -0
- package/ios/interfaces/MediaAttachment.h +23 -0
- package/ios/interfaces/MediaAttachment.mm +31 -0
- package/ios/interfaces/MentionParams.h +8 -0
- package/ios/interfaces/MentionParams.mm +4 -0
- package/ios/interfaces/MentionStyleProps.h +13 -0
- package/ios/interfaces/MentionStyleProps.mm +63 -0
- package/ios/interfaces/StyleBase.h +36 -0
- package/ios/interfaces/StyleBase.mm +256 -0
- package/ios/interfaces/StyleHeaders.h +102 -0
- package/ios/interfaces/StylePair.h +9 -0
- package/ios/interfaces/StylePair.mm +4 -0
- package/ios/interfaces/StyleTypeEnum.h +26 -0
- package/ios/interfaces/TextDecorationLineEnum.h +6 -0
- package/ios/interfaces/TextDecorationLineEnum.mm +4 -0
- package/ios/internals/EnrichedTextInputViewComponentDescriptor.h +19 -0
- package/ios/internals/EnrichedTextInputViewShadowNode.h +44 -0
- package/ios/internals/EnrichedTextInputViewShadowNode.mm +103 -0
- package/ios/internals/EnrichedTextInputViewState.cpp +10 -0
- package/ios/internals/EnrichedTextInputViewState.h +22 -0
- package/ios/styles/BlockQuoteStyle.mm +55 -0
- package/ios/styles/BoldStyle.mm +37 -0
- package/ios/styles/CheckboxListStyle.mm +153 -0
- package/ios/styles/CodeBlockStyle.mm +49 -0
- package/ios/styles/H1Style.mm +20 -0
- package/ios/styles/H2Style.mm +20 -0
- package/ios/styles/H3Style.mm +20 -0
- package/ios/styles/H4Style.mm +20 -0
- package/ios/styles/H5Style.mm +20 -0
- package/ios/styles/H6Style.mm +20 -0
- package/ios/styles/HeadingStyleBase.mm +65 -0
- package/ios/styles/ImageStyle.mm +146 -0
- package/ios/styles/InlineCodeStyle.mm +65 -0
- package/ios/styles/ItalicStyle.mm +37 -0
- package/ios/styles/LinkStyle.mm +532 -0
- package/ios/styles/MentionStyle.mm +538 -0
- package/ios/styles/OrderedListStyle.mm +86 -0
- package/ios/styles/StrikethroughStyle.mm +25 -0
- package/ios/styles/UnderlineStyle.mm +24 -0
- package/ios/styles/UnorderedListStyle.mm +86 -0
- package/ios/utils/CheckboxHitTestUtils.h +10 -0
- package/ios/utils/CheckboxHitTestUtils.mm +122 -0
- package/ios/utils/DotReplacementUtils.h +10 -0
- package/ios/utils/DotReplacementUtils.mm +68 -0
- package/ios/utils/KeyboardUtils.h +7 -0
- package/ios/utils/KeyboardUtils.mm +31 -0
- package/ios/utils/OccurenceUtils.h +44 -0
- package/ios/utils/OccurenceUtils.mm +179 -0
- package/ios/utils/ParagraphAttributesUtils.h +15 -0
- package/ios/utils/ParagraphAttributesUtils.mm +257 -0
- package/ios/utils/RangeUtils.h +12 -0
- package/ios/utils/RangeUtils.mm +183 -0
- package/ios/utils/TextBlockTapGestureRecognizer.h +17 -0
- package/ios/utils/TextBlockTapGestureRecognizer.mm +56 -0
- package/ios/utils/TextInsertionUtils.h +17 -0
- package/ios/utils/TextInsertionUtils.mm +64 -0
- package/ios/utils/WordsUtils.h +7 -0
- package/ios/utils/WordsUtils.mm +98 -0
- package/ios/utils/ZeroWidthSpaceUtils.h +9 -0
- package/ios/utils/ZeroWidthSpaceUtils.mm +270 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/native/EnrichedTextInput.js +304 -0
- package/lib/module/native/EnrichedTextInput.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/spec/EnrichedTextInputNativeComponent.ts +517 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils/EnrichedTextInputDefaultProps.js +12 -0
- package/lib/module/utils/EnrichedTextInputDefaultProps.js.map +1 -0
- package/lib/module/utils/normalizeHtmlStyle.js +155 -0
- package/lib/module/utils/normalizeHtmlStyle.js.map +1 -0
- package/lib/module/utils/nullthrows.js +9 -0
- package/lib/module/utils/nullthrows.js.map +1 -0
- package/lib/module/utils/regexParser.js +46 -0
- package/lib/module/utils/regexParser.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/index.d.ts +3 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/native/EnrichedTextInput.d.ts +3 -0
- package/lib/typescript/src/native/EnrichedTextInput.d.ts.map +1 -0
- package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts +397 -0
- package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +447 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts +10 -0
- package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts.map +1 -0
- package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +4 -0
- package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -0
- package/lib/typescript/src/utils/nullthrows.d.ts +2 -0
- package/lib/typescript/src/utils/nullthrows.d.ts.map +1 -0
- package/lib/typescript/src/utils/regexParser.d.ts +3 -0
- package/lib/typescript/src/utils/regexParser.d.ts.map +1 -0
- package/package.json +226 -0
- package/react-native.config.js +13 -0
- package/src/index.tsx +20 -0
- package/src/native/EnrichedTextInput.tsx +370 -0
- package/src/spec/EnrichedTextInputNativeComponent.ts +517 -0
- package/src/types.ts +499 -0
- package/src/utils/EnrichedTextInputDefaultProps.ts +9 -0
- package/src/utils/normalizeHtmlStyle.ts +199 -0
- package/src/utils/nullthrows.ts +7 -0
- package/src/utils/regexParser.ts +56 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
package com.swmansion.enriched.textinput.utils
|
|
2
|
+
|
|
3
|
+
import android.text.Spannable
|
|
4
|
+
import android.text.SpannableString
|
|
5
|
+
import android.text.SpannableStringBuilder
|
|
6
|
+
import com.swmansion.enriched.common.spans.interfaces.EnrichedBlockSpan
|
|
7
|
+
import com.swmansion.enriched.common.spans.interfaces.EnrichedParagraphSpan
|
|
8
|
+
|
|
9
|
+
fun Spannable.getSafeSpanBoundaries(
|
|
10
|
+
start: Int,
|
|
11
|
+
end: Int,
|
|
12
|
+
): Pair<Int, Int> {
|
|
13
|
+
val safeStart = start.coerceAtMost(end).coerceAtLeast(0)
|
|
14
|
+
val safeEnd = end.coerceAtLeast(start).coerceAtMost(this.length)
|
|
15
|
+
|
|
16
|
+
return Pair(safeStart, safeEnd)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
fun Spannable.getParagraphBounds(
|
|
20
|
+
start: Int,
|
|
21
|
+
end: Int,
|
|
22
|
+
): Pair<Int, Int> {
|
|
23
|
+
var startPosition = start.coerceAtLeast(0).coerceAtMost(this.length)
|
|
24
|
+
var endPosition = end.coerceAtLeast(0).coerceAtMost(this.length)
|
|
25
|
+
|
|
26
|
+
// Find the start of the paragraph
|
|
27
|
+
while (startPosition > 0 && this[startPosition - 1] != '\n') {
|
|
28
|
+
startPosition--
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Find the end of the paragraph
|
|
32
|
+
while (endPosition < this.length && this[endPosition] != '\n') {
|
|
33
|
+
endPosition++
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (startPosition >= endPosition) {
|
|
37
|
+
// If the start position is equal or greater than the end position, return the same position
|
|
38
|
+
startPosition = endPosition
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return Pair(startPosition, endPosition)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fun Spannable.getParagraphBounds(index: Int): Pair<Int, Int> = this.getParagraphBounds(index, index)
|
|
45
|
+
|
|
46
|
+
fun Spannable.mergeSpannables(
|
|
47
|
+
start: Int,
|
|
48
|
+
end: Int,
|
|
49
|
+
string: String,
|
|
50
|
+
): Spannable = this.mergeSpannables(start, end, SpannableString(string))
|
|
51
|
+
|
|
52
|
+
fun Spannable.mergeSpannables(
|
|
53
|
+
start: Int,
|
|
54
|
+
end: Int,
|
|
55
|
+
spannable: Spannable,
|
|
56
|
+
): Spannable {
|
|
57
|
+
var finalStart = start
|
|
58
|
+
var finalEnd = end
|
|
59
|
+
|
|
60
|
+
val builder = SpannableStringBuilder(this)
|
|
61
|
+
val startBlockSpans = spannable.getSpans(0, 0, EnrichedBlockSpan::class.java)
|
|
62
|
+
val startParagraphSpans = spannable.getSpans(0, 0, EnrichedParagraphSpan::class.java)
|
|
63
|
+
val endBlockSpans = spannable.getSpans(this.length, this.length, EnrichedBlockSpan::class.java)
|
|
64
|
+
val endParagraphSpans = spannable.getSpans(this.length, this.length, EnrichedParagraphSpan::class.java)
|
|
65
|
+
val (paragraphStart, paragraphEnd) = this.getParagraphBounds(start, end)
|
|
66
|
+
val isNewLineStart = startBlockSpans.isNotEmpty() || startParagraphSpans.isNotEmpty()
|
|
67
|
+
val isNewLineEnd = endBlockSpans.isNotEmpty() || endParagraphSpans.isNotEmpty()
|
|
68
|
+
|
|
69
|
+
val pastedHasOwnStyles =
|
|
70
|
+
spannable.getSpans(0, spannable.length, EnrichedBlockSpan::class.java).isNotEmpty() ||
|
|
71
|
+
spannable.getSpans(0, spannable.length, EnrichedParagraphSpan::class.java).isNotEmpty()
|
|
72
|
+
|
|
73
|
+
if (isNewLineStart && start != paragraphStart) {
|
|
74
|
+
builder.insert(start, "\n")
|
|
75
|
+
finalStart = start + 1
|
|
76
|
+
finalEnd = end + 1
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (isNewLineEnd && end != paragraphEnd) {
|
|
80
|
+
builder.insert(finalEnd, "\n")
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
builder.replace(finalStart, finalEnd, spannable)
|
|
84
|
+
|
|
85
|
+
// Manually extend existing paragraph/block spans to cover the pasted text.
|
|
86
|
+
if (!pastedHasOwnStyles) {
|
|
87
|
+
val pasteEnd = finalStart + spannable.length
|
|
88
|
+
|
|
89
|
+
val affectedParagraphSpans = builder.getSpans(finalStart, finalStart, EnrichedParagraphSpan::class.java)
|
|
90
|
+
val affectedBlockSpans = builder.getSpans(finalStart, finalStart, EnrichedBlockSpan::class.java)
|
|
91
|
+
val affectedSpans = affectedBlockSpans.toList() + affectedParagraphSpans.toList()
|
|
92
|
+
|
|
93
|
+
for (span in affectedSpans) {
|
|
94
|
+
val spanStart = builder.getSpanStart(span)
|
|
95
|
+
val spanEnd = builder.getSpanEnd(span)
|
|
96
|
+
if (spanStart == -1 || spanEnd >= pasteEnd) continue
|
|
97
|
+
|
|
98
|
+
val (_, newParagraphEnd) = builder.getParagraphBounds(spanStart, pasteEnd)
|
|
99
|
+
val flags = builder.getSpanFlags(span)
|
|
100
|
+
builder.removeSpan(span)
|
|
101
|
+
builder.setSpan(span, spanStart, newParagraphEnd, flags)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return builder
|
|
106
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
package com.swmansion.enriched.textinput.utils
|
|
2
|
+
|
|
3
|
+
import android.text.SpannableStringBuilder
|
|
4
|
+
import com.swmansion.enriched.common.EnrichedConstants
|
|
5
|
+
|
|
6
|
+
fun CharSequence.zwsCountBefore(index: Int): Int {
|
|
7
|
+
var count = 0
|
|
8
|
+
for (i in 0 until index) {
|
|
9
|
+
if (this[i] == EnrichedConstants.ZWS) count++
|
|
10
|
+
}
|
|
11
|
+
return count
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Removes zero-width spaces from the given range in the SpannableStringBuilder without affecting spans
|
|
15
|
+
fun SpannableStringBuilder.removeZWS(
|
|
16
|
+
start: Int,
|
|
17
|
+
end: Int,
|
|
18
|
+
) {
|
|
19
|
+
for (i in (end - 1) downTo start) {
|
|
20
|
+
if (this[i] == EnrichedConstants.ZWS) {
|
|
21
|
+
delete(i, i + 1)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
package com.swmansion.enriched.textinput.utils
|
|
2
|
+
|
|
3
|
+
import android.content.ClipData
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.graphics.BitmapFactory
|
|
6
|
+
import android.net.Uri
|
|
7
|
+
import android.os.Handler
|
|
8
|
+
import android.os.Looper
|
|
9
|
+
import android.util.Log
|
|
10
|
+
import android.view.View
|
|
11
|
+
import android.webkit.MimeTypeMap
|
|
12
|
+
import androidx.core.view.ContentInfoCompat
|
|
13
|
+
import androidx.core.view.OnReceiveContentListener
|
|
14
|
+
import com.facebook.react.bridge.ReactContext
|
|
15
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
16
|
+
import com.swmansion.enriched.textinput.EnrichedTextInputView
|
|
17
|
+
import com.swmansion.enriched.textinput.events.OnPasteImagesEvent
|
|
18
|
+
import java.io.File
|
|
19
|
+
import java.io.FileOutputStream
|
|
20
|
+
import kotlin.io.copyTo
|
|
21
|
+
|
|
22
|
+
class RichContentReceiver(
|
|
23
|
+
private val view: EnrichedTextInputView,
|
|
24
|
+
private val context: ReactContext,
|
|
25
|
+
) : OnReceiveContentListener {
|
|
26
|
+
override fun onReceiveContent(
|
|
27
|
+
view: View,
|
|
28
|
+
contentInfo: ContentInfoCompat,
|
|
29
|
+
): ContentInfoCompat? {
|
|
30
|
+
val split = contentInfo.partition { item: ClipData.Item -> item.uri != null }
|
|
31
|
+
val uriContent = split.first
|
|
32
|
+
val remaining = split.second
|
|
33
|
+
|
|
34
|
+
if (uriContent != null) {
|
|
35
|
+
processClipDataInBackground(uriContent.clip)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (remaining != null && remaining.clip.itemCount > 0) {
|
|
39
|
+
val item = remaining.clip.getItemAt(0)
|
|
40
|
+
|
|
41
|
+
this.view.handleTextPaste(item)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return null
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
private fun processClipDataInBackground(clip: ClipData) {
|
|
48
|
+
Thread {
|
|
49
|
+
val results = mutableListOf<OnPasteImagesEvent.Companion.PastedImage>()
|
|
50
|
+
|
|
51
|
+
for (i in 0 until clip.itemCount) {
|
|
52
|
+
val item = clip.getItemAt(i)
|
|
53
|
+
val uri = item.uri ?: continue
|
|
54
|
+
val mimeType = getMimeTypeFromUri(uri) ?: continue
|
|
55
|
+
|
|
56
|
+
if (mimeType.startsWith("image/")) {
|
|
57
|
+
val result = saveUriToTempFile(context, uri, mimeType)
|
|
58
|
+
if (result != null) {
|
|
59
|
+
results.add(result)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (results.isNotEmpty()) {
|
|
65
|
+
Handler(Looper.getMainLooper()).post {
|
|
66
|
+
emitOnPasteImageEvent(results)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}.start()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
private fun emitOnPasteImageEvent(images: List<OnPasteImagesEvent.Companion.PastedImage>) {
|
|
73
|
+
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
74
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
|
|
75
|
+
dispatcher?.dispatchEvent(OnPasteImagesEvent(surfaceId, view.id, images, false))
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
private fun saveUriToTempFile(
|
|
79
|
+
context: Context,
|
|
80
|
+
uri: Uri,
|
|
81
|
+
mimeType: String,
|
|
82
|
+
): OnPasteImagesEvent.Companion.PastedImage? {
|
|
83
|
+
return try {
|
|
84
|
+
val resolver = context.contentResolver
|
|
85
|
+
val ext = MimeTypeMap.getFileExtensionFromUrl(uri.toString())
|
|
86
|
+
val file = File.createTempFile("temp", ".$ext", context.cacheDir)
|
|
87
|
+
|
|
88
|
+
// Copy Stream
|
|
89
|
+
resolver.openInputStream(uri).use { input ->
|
|
90
|
+
if (input == null) return null
|
|
91
|
+
FileOutputStream(file).use { output ->
|
|
92
|
+
input.copyTo(output)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Decode Dimensions
|
|
97
|
+
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
|
|
98
|
+
BitmapFactory.decodeFile(file.absolutePath, options)
|
|
99
|
+
|
|
100
|
+
OnPasteImagesEvent.Companion.PastedImage(
|
|
101
|
+
uri = "file://${file.absolutePath}",
|
|
102
|
+
type = mimeType,
|
|
103
|
+
width = options.outWidth.toDouble(),
|
|
104
|
+
height = options.outHeight.toDouble(),
|
|
105
|
+
)
|
|
106
|
+
} catch (e: Exception) {
|
|
107
|
+
Log.e("RichContentReceiver", "Failed to save URI: $uri", e)
|
|
108
|
+
null
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private fun getMimeTypeFromUri(uri: Uri): String? {
|
|
113
|
+
var mimeType = context.contentResolver.getType(uri)
|
|
114
|
+
|
|
115
|
+
if (mimeType == null) {
|
|
116
|
+
val extension = MimeTypeMap.getFileExtensionFromUrl(uri.toString())
|
|
117
|
+
|
|
118
|
+
mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return mimeType
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
companion object {
|
|
125
|
+
val MIME_TYPES = arrayOf("image/*", "text/*")
|
|
126
|
+
}
|
|
127
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
package com.swmansion.enriched.textinput.utils
|
|
2
|
+
|
|
3
|
+
import android.annotation.SuppressLint
|
|
4
|
+
import android.text.Selection
|
|
5
|
+
import android.text.Spannable
|
|
6
|
+
import android.text.Spanned
|
|
7
|
+
import android.util.Log
|
|
8
|
+
import android.view.MotionEvent
|
|
9
|
+
import android.widget.TextView
|
|
10
|
+
import com.swmansion.enriched.textinput.spans.EnrichedInputCheckboxListSpan
|
|
11
|
+
import org.json.JSONObject
|
|
12
|
+
|
|
13
|
+
fun jsonStringToStringMap(json: String): Map<String, String> {
|
|
14
|
+
val result = mutableMapOf<String, String>()
|
|
15
|
+
try {
|
|
16
|
+
val jsonObject = JSONObject(json)
|
|
17
|
+
for (key in jsonObject.keys()) {
|
|
18
|
+
val value = jsonObject.opt(key)
|
|
19
|
+
if (value is String) {
|
|
20
|
+
result[key] = value
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} catch (e: Exception) {
|
|
24
|
+
Log.w("ReactNativeEnrichedView", "Failed to parse JSON string to Map: $json", e)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return result
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Sets a touch listener on TextView which is responsible for detecting touches on checkbox icons
|
|
31
|
+
// We don't use ClickableSpan because it works fine only when LinkMovementMethod is set on TextView
|
|
32
|
+
// Which breaks text selection and other features
|
|
33
|
+
@SuppressLint("ClickableViewAccessibility")
|
|
34
|
+
fun TextView.setCheckboxClickListener() {
|
|
35
|
+
var isDownOnCheckbox = false
|
|
36
|
+
|
|
37
|
+
setOnTouchListener { v, event ->
|
|
38
|
+
val tv = v as TextView
|
|
39
|
+
val layout = tv.layout ?: return@setOnTouchListener false
|
|
40
|
+
val spannable = tv.text as? Spanned ?: return@setOnTouchListener false
|
|
41
|
+
|
|
42
|
+
// Get touch coordinates relative to the text content
|
|
43
|
+
val x = event.x.toInt() - tv.totalPaddingLeft + tv.scrollX
|
|
44
|
+
val y = event.y.toInt() - tv.totalPaddingTop + tv.scrollY
|
|
45
|
+
|
|
46
|
+
// Identify the line and whether it's the first line of the span
|
|
47
|
+
val line = layout.getLineForVertical(y)
|
|
48
|
+
val lineStart = layout.getLineStart(line)
|
|
49
|
+
|
|
50
|
+
// Find spans for specific line
|
|
51
|
+
val spans = spannable.getSpans(lineStart, lineStart, EnrichedInputCheckboxListSpan::class.java)
|
|
52
|
+
if (spans.isEmpty()) return@setOnTouchListener false
|
|
53
|
+
|
|
54
|
+
// There should be only one span per line as we don't support nested lists
|
|
55
|
+
val span = spans[0]
|
|
56
|
+
val isFirstLine = spannable.getSpanStart(span) == lineStart
|
|
57
|
+
val marginWidth = span.getLeadingMargin(true)
|
|
58
|
+
|
|
59
|
+
// Check if touch is on checkbox icon area (which is in the leading margin on the first line)
|
|
60
|
+
val isInHotZone = isFirstLine && x in 0..marginWidth
|
|
61
|
+
|
|
62
|
+
when (event.action) {
|
|
63
|
+
MotionEvent.ACTION_DOWN -> {
|
|
64
|
+
if (isInHotZone) {
|
|
65
|
+
isDownOnCheckbox = true
|
|
66
|
+
return@setOnTouchListener true
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
MotionEvent.ACTION_UP -> {
|
|
71
|
+
if (isDownOnCheckbox && isInHotZone) {
|
|
72
|
+
val spannable = tv.text as? Spannable
|
|
73
|
+
if (spannable != null) {
|
|
74
|
+
val start = spannable.getSpanStart(span)
|
|
75
|
+
val end = spannable.getSpanEnd(span)
|
|
76
|
+
val flags = spannable.getSpanFlags(span)
|
|
77
|
+
span.isChecked = !span.isChecked
|
|
78
|
+
|
|
79
|
+
// Reapply span so changes are visible without need to redraw entire TextView
|
|
80
|
+
spannable.removeSpan(span)
|
|
81
|
+
spannable.setSpan(span, start, end, flags)
|
|
82
|
+
|
|
83
|
+
// For focused input, ensure cursor is active for affected paragraph
|
|
84
|
+
if (tv.isFocused) {
|
|
85
|
+
val currentCursor = Selection.getSelectionEnd(spannable)
|
|
86
|
+
if (currentCursor < start || currentCursor > end) {
|
|
87
|
+
Selection.setSelection(spannable, end)
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
isDownOnCheckbox = false
|
|
93
|
+
return@setOnTouchListener true
|
|
94
|
+
}
|
|
95
|
+
isDownOnCheckbox = false
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
MotionEvent.ACTION_CANCEL -> {
|
|
99
|
+
isDownOnCheckbox = false
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Let TextView handle other touches (e.g., for selection)
|
|
104
|
+
false
|
|
105
|
+
}
|
|
106
|
+
}
|
package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedSpanWatcher.kt
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
package com.swmansion.enriched.textinput.watchers
|
|
2
|
+
|
|
3
|
+
import android.text.SpanWatcher
|
|
4
|
+
import android.text.Spannable
|
|
5
|
+
import android.text.style.ParagraphStyle
|
|
6
|
+
import com.facebook.react.bridge.ReactContext
|
|
7
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
8
|
+
import com.swmansion.enriched.common.parser.EnrichedParser
|
|
9
|
+
import com.swmansion.enriched.common.spans.interfaces.EnrichedHeadingSpan
|
|
10
|
+
import com.swmansion.enriched.textinput.EnrichedTextInputView
|
|
11
|
+
import com.swmansion.enriched.textinput.events.OnChangeHtmlEvent
|
|
12
|
+
import com.swmansion.enriched.textinput.spans.EnrichedInputOrderedListSpan
|
|
13
|
+
import com.swmansion.enriched.textinput.spans.interfaces.EnrichedInputSpan
|
|
14
|
+
import com.swmansion.enriched.textinput.utils.getSafeSpanBoundaries
|
|
15
|
+
|
|
16
|
+
class EnrichedSpanWatcher(
|
|
17
|
+
private val view: EnrichedTextInputView,
|
|
18
|
+
) : SpanWatcher {
|
|
19
|
+
private var previousHtml: String? = null
|
|
20
|
+
|
|
21
|
+
override fun onSpanAdded(
|
|
22
|
+
text: Spannable,
|
|
23
|
+
what: Any,
|
|
24
|
+
start: Int,
|
|
25
|
+
end: Int,
|
|
26
|
+
) {
|
|
27
|
+
updateNextLineLayout(what, text, end)
|
|
28
|
+
updateUnorderedListSpans(what, text, end)
|
|
29
|
+
emitEvent(text, what)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
override fun onSpanRemoved(
|
|
33
|
+
text: Spannable,
|
|
34
|
+
what: Any,
|
|
35
|
+
start: Int,
|
|
36
|
+
end: Int,
|
|
37
|
+
) {
|
|
38
|
+
updateNextLineLayout(what, text, end)
|
|
39
|
+
updateUnorderedListSpans(what, text, end)
|
|
40
|
+
emitEvent(text, what)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
override fun onSpanChanged(
|
|
44
|
+
text: Spannable,
|
|
45
|
+
what: Any,
|
|
46
|
+
ostart: Int,
|
|
47
|
+
oend: Int,
|
|
48
|
+
nstart: Int,
|
|
49
|
+
nend: Int,
|
|
50
|
+
) {
|
|
51
|
+
// Do nothing for now
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private fun updateUnorderedListSpans(
|
|
55
|
+
what: Any,
|
|
56
|
+
text: Spannable,
|
|
57
|
+
end: Int,
|
|
58
|
+
) {
|
|
59
|
+
if (what is EnrichedInputOrderedListSpan) {
|
|
60
|
+
view.listStyles?.updateOrderedListIndexes(text, end)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// After adding/removing heading span, we have to manually set empty paragraph span to the following text
|
|
65
|
+
// This allows us to update the layout (as it's not updated automatically - looks like an Android issue)
|
|
66
|
+
private fun updateNextLineLayout(
|
|
67
|
+
what: Any,
|
|
68
|
+
text: Spannable,
|
|
69
|
+
end: Int,
|
|
70
|
+
) {
|
|
71
|
+
class EmptySpan : ParagraphStyle
|
|
72
|
+
|
|
73
|
+
if (what is EnrichedHeadingSpan) {
|
|
74
|
+
val finalStart = (end + 1)
|
|
75
|
+
val finalEnd = text.length
|
|
76
|
+
val (safeStart, safeEnd) = text.getSafeSpanBoundaries(finalStart, finalEnd)
|
|
77
|
+
text.setSpan(EmptySpan(), safeStart, safeEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
fun emitEvent(
|
|
82
|
+
s: Spannable,
|
|
83
|
+
what: Any?,
|
|
84
|
+
) {
|
|
85
|
+
// Do not parse spannable and emit event if onChangeHtml is not provided
|
|
86
|
+
if (!view.shouldEmitHtml) return
|
|
87
|
+
|
|
88
|
+
// Emit event only if we change one of ours spans
|
|
89
|
+
if (what != null && what !is EnrichedInputSpan) return
|
|
90
|
+
|
|
91
|
+
val html = EnrichedParser.toHtml(s)
|
|
92
|
+
if (html == previousHtml) return
|
|
93
|
+
|
|
94
|
+
previousHtml = html
|
|
95
|
+
val context = view.context as ReactContext
|
|
96
|
+
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
97
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
|
|
98
|
+
dispatcher?.dispatchEvent(
|
|
99
|
+
OnChangeHtmlEvent(
|
|
100
|
+
surfaceId,
|
|
101
|
+
view.id,
|
|
102
|
+
html,
|
|
103
|
+
view.experimentalSynchronousEvents,
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
}
|
|
107
|
+
}
|
package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedTextWatcher.kt
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
package com.swmansion.enriched.textinput.watchers
|
|
2
|
+
|
|
3
|
+
import android.text.Editable
|
|
4
|
+
import android.text.TextWatcher
|
|
5
|
+
import com.facebook.react.bridge.ReactContext
|
|
6
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
7
|
+
import com.swmansion.enriched.textinput.EnrichedTextInputView
|
|
8
|
+
import com.swmansion.enriched.textinput.events.OnChangeTextEvent
|
|
9
|
+
|
|
10
|
+
class EnrichedTextWatcher(
|
|
11
|
+
private val view: EnrichedTextInputView,
|
|
12
|
+
) : TextWatcher {
|
|
13
|
+
private var endCursorPosition: Int = 0
|
|
14
|
+
private var startCursorPosition: Int = 0
|
|
15
|
+
private var previousTextLength: Int = 0
|
|
16
|
+
|
|
17
|
+
override fun beforeTextChanged(
|
|
18
|
+
s: CharSequence?,
|
|
19
|
+
start: Int,
|
|
20
|
+
count: Int,
|
|
21
|
+
after: Int,
|
|
22
|
+
) {
|
|
23
|
+
previousTextLength = s?.length ?: 0
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
override fun onTextChanged(
|
|
27
|
+
s: CharSequence?,
|
|
28
|
+
start: Int,
|
|
29
|
+
before: Int,
|
|
30
|
+
count: Int,
|
|
31
|
+
) {
|
|
32
|
+
startCursorPosition = start
|
|
33
|
+
endCursorPosition = start + count
|
|
34
|
+
view.layoutManager.invalidateLayout()
|
|
35
|
+
view.isRemovingMany = !view.isDuringTransaction && before > count + 1
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
override fun afterTextChanged(s: Editable?) {
|
|
39
|
+
if (s == null) return
|
|
40
|
+
emitEvents(s)
|
|
41
|
+
|
|
42
|
+
if (view.isDuringTransaction) return
|
|
43
|
+
applyStyles(s)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private fun applyStyles(s: Editable) {
|
|
47
|
+
view.inlineStyles?.afterTextChanged(s, endCursorPosition)
|
|
48
|
+
view.paragraphStyles?.afterTextChanged(s, endCursorPosition, previousTextLength)
|
|
49
|
+
view.listStyles?.afterTextChanged(s, endCursorPosition, previousTextLength)
|
|
50
|
+
view.parametrizedStyles?.afterTextChanged(s, startCursorPosition, endCursorPosition)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private fun emitChangeText(editable: Editable) {
|
|
54
|
+
if (!view.shouldEmitOnChangeText) {
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
val context = view.context as ReactContext
|
|
58
|
+
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
59
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(context, view.id)
|
|
60
|
+
dispatcher?.dispatchEvent(
|
|
61
|
+
OnChangeTextEvent(
|
|
62
|
+
surfaceId,
|
|
63
|
+
view.id,
|
|
64
|
+
editable,
|
|
65
|
+
view.experimentalSynchronousEvents,
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
private fun emitEvents(s: Editable) {
|
|
71
|
+
emitChangeText(s)
|
|
72
|
+
view.spanWatcher?.emitEvent(s, null)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.13)
|
|
2
|
+
set(CMAKE_VERBOSE_MAKEFILE on)
|
|
3
|
+
|
|
4
|
+
set(LIB_LITERAL ReactNativeEnrichedSpec)
|
|
5
|
+
set(LIB_TARGET_NAME react_codegen_${LIB_LITERAL})
|
|
6
|
+
|
|
7
|
+
set(LIB_ANDROID_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../..)
|
|
8
|
+
set(LIB_ANDROID_GENERATED_JNI_DIR ${LIB_ANDROID_DIR}/generated/jni)
|
|
9
|
+
set(LIB_ANDROID_GENERATED_COMPONENTS_DIR ${LIB_ANDROID_GENERATED_JNI_DIR}/react/renderer/components/${LIB_LITERAL})
|
|
10
|
+
|
|
11
|
+
set(LIB_CPP_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../../../cpp)
|
|
12
|
+
|
|
13
|
+
file(GLOB LIB_MODULE_SRCS CONFIGURE_DEPENDS *.cpp react/renderer/components/${LIB_LITERAL}/*.cpp)
|
|
14
|
+
file(GLOB LIB_CODEGEN_SRCS CONFIGURE_DEPENDS ${LIB_ANDROID_GENERATED_COMPONENTS_DIR}/*.cpp)
|
|
15
|
+
file(GLOB LIB_CPP_SRCS CONFIGURE_DEPENDS ${LIB_CPP_DIR}/parser/GumboParser.cpp ${LIB_CPP_DIR}/parser/GumboNormalizer.c)
|
|
16
|
+
|
|
17
|
+
set_source_files_properties(${LIB_CPP_DIR}/parser/GumboNormalizer.c PROPERTIES LANGUAGE C COMPILE_FLAGS "-std=c99")
|
|
18
|
+
|
|
19
|
+
if(NOT DEFINED REACT_NATIVE_MINOR_VERSION)
|
|
20
|
+
set(REACT_NATIVE_MINOR_VERSION ${ReactAndroid_VERSION_MINOR})
|
|
21
|
+
endif()
|
|
22
|
+
|
|
23
|
+
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DREACT_NATIVE_MINOR_VERSION=${REACT_NATIVE_MINOR_VERSION}")
|
|
24
|
+
|
|
25
|
+
add_library(
|
|
26
|
+
${LIB_TARGET_NAME}
|
|
27
|
+
SHARED
|
|
28
|
+
${LIB_MODULE_SRCS}
|
|
29
|
+
${LIB_CUSTOM_SRCS}
|
|
30
|
+
${LIB_CODEGEN_SRCS}
|
|
31
|
+
${LIB_CPP_SRCS}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
target_include_directories(
|
|
35
|
+
${LIB_TARGET_NAME}
|
|
36
|
+
PUBLIC
|
|
37
|
+
.
|
|
38
|
+
./react/renderer/components/${LIB_LITERAL}
|
|
39
|
+
${LIB_ANDROID_GENERATED_JNI_DIR}
|
|
40
|
+
${LIB_ANDROID_GENERATED_COMPONENTS_DIR}
|
|
41
|
+
${LIB_CPP_DIR}
|
|
42
|
+
${LIB_CPP_DIR}/parser
|
|
43
|
+
${LIB_CPP_DIR}/GumboParser
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
find_package(fbjni REQUIRED CONFIG)
|
|
47
|
+
find_package(ReactAndroid REQUIRED CONFIG)
|
|
48
|
+
|
|
49
|
+
target_link_libraries(
|
|
50
|
+
${LIB_TARGET_NAME}
|
|
51
|
+
fbjni::fbjni
|
|
52
|
+
ReactAndroid::jsi
|
|
53
|
+
ReactAndroid::reactnative
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
target_compile_reactnative_options(${LIB_TARGET_NAME} PRIVATE)
|
|
57
|
+
|
|
58
|
+
target_include_directories(
|
|
59
|
+
${CMAKE_PROJECT_NAME}
|
|
60
|
+
PUBLIC
|
|
61
|
+
${CMAKE_CURRENT_SOURCE_DIR}
|
|
62
|
+
)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#include "GumboParser.hpp"
|
|
2
|
+
#include <jni.h>
|
|
3
|
+
#include <string>
|
|
4
|
+
|
|
5
|
+
extern "C" JNIEXPORT jstring JNICALL
|
|
6
|
+
Java_com_swmansion_enriched_common_GumboNormalizer_normalizeHtml(
|
|
7
|
+
JNIEnv *env, jclass /*cls*/, jstring htmlJString) {
|
|
8
|
+
const char *htmlChars = env->GetStringUTFChars(htmlJString, nullptr);
|
|
9
|
+
std::string result = GumboParser::normalizeHtml(htmlChars);
|
|
10
|
+
env->ReleaseStringUTFChars(htmlJString, htmlChars);
|
|
11
|
+
if (result.empty())
|
|
12
|
+
return nullptr;
|
|
13
|
+
return env->NewStringUTF(result.c_str());
|
|
14
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#include "ReactNativeEnrichedSpec.h"
|
|
2
|
+
|
|
3
|
+
namespace facebook::react {
|
|
4
|
+
|
|
5
|
+
std::shared_ptr<TurboModule> ReactNativeEnrichedSpec_ModuleProvider(
|
|
6
|
+
const std::string &moduleName, const JavaTurboModule::InitParams ¶ms) {
|
|
7
|
+
|
|
8
|
+
return nullptr;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <ReactCommon/JavaTurboModule.h>
|
|
4
|
+
#include <ReactCommon/TurboModule.h>
|
|
5
|
+
#include <jsi/jsi.h>
|
|
6
|
+
|
|
7
|
+
#include <react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextInputComponentDescriptor.h>
|
|
8
|
+
|
|
9
|
+
namespace facebook::react {
|
|
10
|
+
|
|
11
|
+
JSI_EXPORT
|
|
12
|
+
std::shared_ptr<TurboModule> ReactNativeEnrichedSpec_ModuleProvider(
|
|
13
|
+
const std::string &moduleName, const JavaTurboModule::InitParams ¶ms);
|
|
14
|
+
|
|
15
|
+
} // namespace facebook::react
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "EnrichedTextInputMeasurementManager.h"
|
|
4
|
+
#include "EnrichedTextInputShadowNode.h"
|
|
5
|
+
|
|
6
|
+
#include <react/renderer/core/ConcreteComponentDescriptor.h>
|
|
7
|
+
|
|
8
|
+
namespace facebook::react {
|
|
9
|
+
|
|
10
|
+
class EnrichedTextInputComponentDescriptor final
|
|
11
|
+
: public ConcreteComponentDescriptor<EnrichedTextInputShadowNode> {
|
|
12
|
+
public:
|
|
13
|
+
EnrichedTextInputComponentDescriptor(
|
|
14
|
+
const ComponentDescriptorParameters ¶meters)
|
|
15
|
+
: ConcreteComponentDescriptor(parameters),
|
|
16
|
+
measurementsManager_(
|
|
17
|
+
std::make_shared<EnrichedTextInputMeasurementManager>(
|
|
18
|
+
contextContainer_)) {}
|
|
19
|
+
|
|
20
|
+
void adopt(ShadowNode &shadowNode) const override {
|
|
21
|
+
ConcreteComponentDescriptor::adopt(shadowNode);
|
|
22
|
+
auto &editorShadowNode =
|
|
23
|
+
static_cast<EnrichedTextInputShadowNode &>(shadowNode);
|
|
24
|
+
|
|
25
|
+
// `EnrichedTextInputShadowNode` uses
|
|
26
|
+
// `EnrichedTextInputMeasurementManager` to provide measurements to Yoga.
|
|
27
|
+
editorShadowNode.setMeasurementsManager(measurementsManager_);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private:
|
|
31
|
+
const std::shared_ptr<EnrichedTextInputMeasurementManager>
|
|
32
|
+
measurementsManager_;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
} // namespace facebook::react
|