react-native-enriched-html 0.0.0 → 1.1.0-nightly-20260617-3dfd09072
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 +402 -0
- package/ReactNativeEnrichedHtml.podspec +31 -0
- package/android/build.gradle +106 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +203 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +74 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextViewManagerDelegate.java +70 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextViewManagerInterface.java +31 -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 +456 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/EventEmitters.h +410 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.cpp +272 -0
- package/android/generated/jni/react/renderer/components/ReactNativeEnrichedSpec/Props.h +1595 -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 +22 -0
- package/android/src/main/java/com/swmansion/enriched/common/AllowFontScaling.kt +36 -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 +16 -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 +9 -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 +1034 -0
- package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +82 -0
- package/android/src/main/java/com/swmansion/enriched/common/spans/EnrichedAlignmentSpan.kt +19 -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 +185 -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/text/EnrichedTextMovementMethod.kt +75 -0
- package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextSpanFactory.kt +83 -0
- package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextStyle.kt +202 -0
- package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt +361 -0
- package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextViewManager.kt +157 -0
- package/android/src/main/java/com/swmansion/enriched/text/MeasurementStore.kt +170 -0
- package/android/src/main/java/com/swmansion/enriched/text/events/OnLinkPressEvent.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/text/events/OnMentionPressEvent.kt +32 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextAlignmentSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextBlockQuoteSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextBoldSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextCheckboxListSpan.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextCodeBlockSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH1Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH2Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH3Span.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH4Span.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH5Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextH6Span.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextImageSpan.kt +57 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextInlineCodeSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextItalicSpan.kt +14 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextLinkSpan.kt +41 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextMentionSpan.kt +63 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextOrderedListSpan.kt +16 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextStrikeThroughSpan.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextUnderlineSpan.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/EnrichedTextUnorderedListSpan.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/interfaces/EnrichedTextClickableSpan.kt +9 -0
- package/android/src/main/java/com/swmansion/enriched/text/spans/interfaces/EnrichedTextSpan.kt +10 -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 +85 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +1141 -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 +500 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/MeasurementStore.kt +230 -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/EnrichedInputAlignmentSpan.kt +14 -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 +45 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/spans/EnrichedSpans.kt +240 -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/AlignmentStyles.kt +367 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/HtmlStyle.kt +371 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +232 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +260 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +439 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +408 -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 +340 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpanState.kt +318 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +156 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannableStringBuilder.kt +59 -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/ShortcutsHandler.kt +150 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/StyleUtils.kt +51 -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 +109 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/watchers/EnrichedTextWatcher.kt +106 -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 +16 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextComponentDescriptor.h +32 -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/EnrichedTextMeasurementManager.cpp +45 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextMeasurementManager.h +25 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextShadowNode.cpp +21 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/EnrichedTextShadowNode.h +39 -0
- package/android/src/main/new_arch/react/renderer/components/ReactNativeEnrichedSpec/conversions.h +44 -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 +54 -0
- package/ios/EnrichedTextInputView.mm +2170 -0
- package/ios/EnrichedTextInputViewManager.mm +13 -0
- package/ios/EnrichedTextView.h +35 -0
- package/ios/EnrichedTextView.mm +806 -0
- package/ios/EnrichedTextViewManager.mm +13 -0
- package/ios/config/EnrichedConfig.h +114 -0
- package/ios/config/EnrichedConfig.mm +727 -0
- package/ios/enrichedInputTextView/EnrichedInputTextView.h +6 -0
- package/ios/enrichedInputTextView/EnrichedInputTextView.mm +334 -0
- package/ios/enrichedTextTextView/EnrichedTextTextView.h +16 -0
- package/ios/enrichedTextTextView/EnrichedTextTextView.mm +71 -0
- package/ios/extensions/ColorExtension.h +8 -0
- package/ios/extensions/ColorExtension.mm +48 -0
- package/ios/extensions/FontExtension.h +11 -0
- package/ios/extensions/FontExtension.mm +72 -0
- package/ios/extensions/ImageExtension.h +33 -0
- package/ios/extensions/ImageExtension.mm +176 -0
- package/ios/extensions/LayoutManagerExtension.h +6 -0
- package/ios/extensions/LayoutManagerExtension.mm +454 -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 +456 -0
- package/ios/generated/ReactNativeEnrichedSpec/EventEmitters.h +410 -0
- package/ios/generated/ReactNativeEnrichedSpec/Props.cpp +272 -0
- package/ios/generated/ReactNativeEnrichedSpec/Props.h +1595 -0
- package/ios/generated/ReactNativeEnrichedSpec/RCTComponentViewHelpers.h +570 -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/htmlParser/HtmlParser.h +11 -0
- package/ios/htmlParser/HtmlParser.mm +1468 -0
- package/ios/inputAttributesManager/InputAttributesManager.h +18 -0
- package/ios/inputAttributesManager/InputAttributesManager.mm +222 -0
- package/ios/inputHtmlParser/InputHtmlParser.h +10 -0
- package/ios/inputHtmlParser/InputHtmlParser.mm +225 -0
- package/ios/interfaces/AlignmentEntry.h +9 -0
- package/ios/interfaces/AlignmentEntry.mm +4 -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/EnrichedTextStyleHeaders.h +62 -0
- package/ios/interfaces/EnrichedViewHost.h +26 -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 +43 -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 +17 -0
- package/ios/interfaces/MentionStyleProps.mm +80 -0
- package/ios/interfaces/StyleBase.h +38 -0
- package/ios/interfaces/StyleBase.mm +279 -0
- package/ios/interfaces/StyleHeaders.h +110 -0
- package/ios/interfaces/StylePair.h +9 -0
- package/ios/interfaces/StylePair.mm +4 -0
- package/ios/interfaces/StyleTypeEnum.h +27 -0
- package/ios/interfaces/TextDecorationLineEnum.h +6 -0
- package/ios/interfaces/TextDecorationLineEnum.mm +4 -0
- package/ios/internals/EnrichedTextComponentDescriptor.h +19 -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/internals/EnrichedTextViewShadowNode.h +34 -0
- package/ios/internals/EnrichedTextViewShadowNode.mm +72 -0
- package/ios/internals/EnrichedTextViewState.cpp +7 -0
- package/ios/internals/EnrichedTextViewState.h +18 -0
- package/ios/styles/AlignmentStyle.mm +203 -0
- package/ios/styles/BlockQuoteStyle.mm +54 -0
- package/ios/styles/BoldStyle.mm +37 -0
- package/ios/styles/CheckboxListStyle.mm +154 -0
- package/ios/styles/CodeBlockStyle.mm +49 -0
- package/ios/styles/EnrichedTextStyles.mm +61 -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 +96 -0
- package/ios/styles/ImageStyle.mm +150 -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 +541 -0
- package/ios/styles/OrderedListStyle.mm +48 -0
- package/ios/styles/StrikethroughStyle.mm +25 -0
- package/ios/styles/UnderlineStyle.mm +24 -0
- package/ios/styles/UnorderedListStyle.mm +48 -0
- package/ios/textHtmlParser/TextHtmlParser.h +10 -0
- package/ios/textHtmlParser/TextHtmlParser.mm +181 -0
- package/ios/utils/AlignmentUtils.h +20 -0
- package/ios/utils/AlignmentUtils.mm +111 -0
- package/ios/utils/AttachmentLayoutUtils.h +24 -0
- package/ios/utils/AttachmentLayoutUtils.mm +144 -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/EnrichedTextTouchHandler.h +14 -0
- package/ios/utils/EnrichedTextTouchHandler.mm +152 -0
- package/ios/utils/KeyboardUtils.h +7 -0
- package/ios/utils/KeyboardUtils.mm +30 -0
- package/ios/utils/OccurenceUtils.h +44 -0
- package/ios/utils/OccurenceUtils.mm +185 -0
- package/ios/utils/ParagraphAttributesUtils.h +18 -0
- package/ios/utils/ParagraphAttributesUtils.mm +289 -0
- package/ios/utils/RangeUtils.h +12 -0
- package/ios/utils/RangeUtils.mm +183 -0
- package/ios/utils/ShortcutsUtils.h +21 -0
- package/ios/utils/ShortcutsUtils.mm +486 -0
- package/ios/utils/StyleUtils.h +33 -0
- package/ios/utils/StyleUtils.mm +290 -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 +60 -0
- package/ios/utils/TextListsUtils.h +40 -0
- package/ios/utils/TextListsUtils.mm +93 -0
- package/ios/utils/WeakBox.h +3 -0
- package/ios/utils/WeakBox.m +4 -0
- package/ios/utils/WordsUtils.h +7 -0
- package/ios/utils/WordsUtils.mm +98 -0
- package/ios/utils/ZeroWidthSpaceUtils.h +14 -0
- package/ios/utils/ZeroWidthSpaceUtils.mm +310 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/index.native.js +7 -0
- package/lib/module/index.native.js.map +1 -0
- package/lib/module/native/EnrichedText.js +76 -0
- package/lib/module/native/EnrichedText.js.map +1 -0
- package/lib/module/native/EnrichedTextInput.js +306 -0
- package/lib/module/native/EnrichedTextInput.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/spec/EnrichedTextInputNativeComponent.ts +524 -0
- package/lib/module/spec/EnrichedTextNativeComponent.ts +110 -0
- package/lib/module/types.js +4 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/utils/EnrichedTextInputDefaultProps.js +20 -0
- package/lib/module/utils/EnrichedTextInputDefaultProps.js.map +1 -0
- package/lib/module/utils/defaultHtmlStyle.js +83 -0
- package/lib/module/utils/defaultHtmlStyle.js.map +1 -0
- package/lib/module/utils/expandMentionStylesForIndicators.js +15 -0
- package/lib/module/utils/expandMentionStylesForIndicators.js.map +1 -0
- package/lib/module/utils/isMentionStyleRecord.js +10 -0
- package/lib/module/utils/isMentionStyleRecord.js.map +1 -0
- package/lib/module/utils/normalizeHtmlStyle.js +126 -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/module/web/EnrichedTextInput.css +201 -0
- package/lib/module/web/EnrichedTextInput.js +314 -0
- package/lib/module/web/EnrichedTextInput.js.map +1 -0
- package/lib/module/web/adaptWebToNativeEvent.js +34 -0
- package/lib/module/web/adaptWebToNativeEvent.js.map +1 -0
- package/lib/module/web/checkboxHtmlNormalizer.js +54 -0
- package/lib/module/web/checkboxHtmlNormalizer.js.map +1 -0
- package/lib/module/web/formats/EnrichedBlockquote.js +32 -0
- package/lib/module/web/formats/EnrichedBlockquote.js.map +1 -0
- package/lib/module/web/formats/EnrichedBold.js +22 -0
- package/lib/module/web/formats/EnrichedBold.js.map +1 -0
- package/lib/module/web/formats/EnrichedCheckboxItem.js +32 -0
- package/lib/module/web/formats/EnrichedCheckboxItem.js.map +1 -0
- package/lib/module/web/formats/EnrichedCheckboxList.js +35 -0
- package/lib/module/web/formats/EnrichedCheckboxList.js.map +1 -0
- package/lib/module/web/formats/EnrichedCode.js +15 -0
- package/lib/module/web/formats/EnrichedCode.js.map +1 -0
- package/lib/module/web/formats/EnrichedCodeBlock.js +43 -0
- package/lib/module/web/formats/EnrichedCodeBlock.js.map +1 -0
- package/lib/module/web/formats/EnrichedHeading.js +27 -0
- package/lib/module/web/formats/EnrichedHeading.js.map +1 -0
- package/lib/module/web/formats/EnrichedImage.js +57 -0
- package/lib/module/web/formats/EnrichedImage.js.map +1 -0
- package/lib/module/web/formats/EnrichedImageNodeView.js +90 -0
- package/lib/module/web/formats/EnrichedImageNodeView.js.map +1 -0
- package/lib/module/web/formats/EnrichedItalic.js +22 -0
- package/lib/module/web/formats/EnrichedItalic.js.map +1 -0
- package/lib/module/web/formats/EnrichedLink.js +140 -0
- package/lib/module/web/formats/EnrichedLink.js.map +1 -0
- package/lib/module/web/formats/EnrichedListItem.js +19 -0
- package/lib/module/web/formats/EnrichedListItem.js.map +1 -0
- package/lib/module/web/formats/EnrichedMention.js +50 -0
- package/lib/module/web/formats/EnrichedMention.js.map +1 -0
- package/lib/module/web/formats/EnrichedOrderedList.js +32 -0
- package/lib/module/web/formats/EnrichedOrderedList.js.map +1 -0
- package/lib/module/web/formats/EnrichedStrike.js +22 -0
- package/lib/module/web/formats/EnrichedStrike.js.map +1 -0
- package/lib/module/web/formats/EnrichedUnderline.js +22 -0
- package/lib/module/web/formats/EnrichedUnderline.js.map +1 -0
- package/lib/module/web/formats/EnrichedUnorderedList.js +33 -0
- package/lib/module/web/formats/EnrichedUnorderedList.js.map +1 -0
- package/lib/module/web/formats/applyWrappingListToSelection.js +63 -0
- package/lib/module/web/formats/applyWrappingListToSelection.js.map +1 -0
- package/lib/module/web/formats/formatRules.js +48 -0
- package/lib/module/web/formats/formatRules.js.map +1 -0
- package/lib/module/web/formats/listKeyboard.js +50 -0
- package/lib/module/web/formats/listKeyboard.js.map +1 -0
- package/lib/module/web/formats/wrappedBlockKeyboard.js +58 -0
- package/lib/module/web/formats/wrappedBlockKeyboard.js.map +1 -0
- package/lib/module/web/pasteImages.js +78 -0
- package/lib/module/web/pasteImages.js.map +1 -0
- package/lib/module/web/pastedImageDimensions.js +49 -0
- package/lib/module/web/pastedImageDimensions.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/index.js +36 -0
- package/lib/module/web/pmPlugins/MentionPlugin/index.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.js +11 -0
- package/lib/module/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js +67 -0
- package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/mentionPluginKey.js +5 -0
- package/lib/module/web/pmPlugins/MentionPlugin/mentionPluginKey.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.js +56 -0
- package/lib/module/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/setMention.js +58 -0
- package/lib/module/web/pmPlugins/MentionPlugin/setMention.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/startMention.js +9 -0
- package/lib/module/web/pmPlugins/MentionPlugin/startMention.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.js +9 -0
- package/lib/module/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js +100 -0
- package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/types.js +4 -0
- package/lib/module/web/pmPlugins/MentionPlugin/types.js.map +1 -0
- package/lib/module/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.js +42 -0
- package/lib/module/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.js.map +1 -0
- package/lib/module/web/pmPlugins/ShortcutPlugin.js +123 -0
- package/lib/module/web/pmPlugins/ShortcutPlugin.js.map +1 -0
- package/lib/module/web/pmPlugins/StrictMarksPlugin.js +98 -0
- package/lib/module/web/pmPlugins/StrictMarksPlugin.js.map +1 -0
- package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js +56 -0
- package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js.map +1 -0
- package/lib/module/web/pmPlugins/StripMarksInCodeBlockPlugin.js +28 -0
- package/lib/module/web/pmPlugins/StripMarksInCodeBlockPlugin.js.map +1 -0
- package/lib/module/web/pmPlugins/StripMarksOnImagePlugin.js +29 -0
- package/lib/module/web/pmPlugins/StripMarksOnImagePlugin.js.map +1 -0
- package/lib/module/web/positionMapping.js +76 -0
- package/lib/module/web/positionMapping.js.map +1 -0
- package/lib/module/web/returnKeyTypeToEnterKeyHint.js +20 -0
- package/lib/module/web/returnKeyTypeToEnterKeyHint.js.map +1 -0
- package/lib/module/web/styleConversion/buildMentionRulesCSS.js +33 -0
- package/lib/module/web/styleConversion/buildMentionRulesCSS.js.map +1 -0
- package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js +159 -0
- package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js.map +1 -0
- package/lib/module/web/styleConversion/enrichedInputThemingToCSSProperties.js +18 -0
- package/lib/module/web/styleConversion/enrichedInputThemingToCSSProperties.js.map +1 -0
- package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js +145 -0
- package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js.map +1 -0
- package/lib/module/web/styleConversion/mentionIndicatorCssKey.js +15 -0
- package/lib/module/web/styleConversion/mentionIndicatorCssKey.js.map +1 -0
- package/lib/module/web/styleConversion/toColor.js +18 -0
- package/lib/module/web/styleConversion/toColor.js.map +1 -0
- package/lib/module/web/tiptapHtmlNormalizer.js +30 -0
- package/lib/module/web/tiptapHtmlNormalizer.js.map +1 -0
- package/lib/module/web/useOnChangeHtml.js +8 -0
- package/lib/module/web/useOnChangeHtml.js.map +1 -0
- package/lib/module/web/useOnChangeState.js +88 -0
- package/lib/module/web/useOnChangeState.js.map +1 -0
- package/lib/module/web/useOnChangeText.js +11 -0
- package/lib/module/web/useOnChangeText.js.map +1 -0
- package/lib/module/web/useOnEditorChange.js +25 -0
- package/lib/module/web/useOnEditorChange.js.map +1 -0
- package/lib/module/web/useOnLinkDetected.js +68 -0
- package/lib/module/web/useOnLinkDetected.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/index.native.d.ts +5 -0
- package/lib/typescript/src/index.native.d.ts.map +1 -0
- package/lib/typescript/src/native/EnrichedText.d.ts +3 -0
- package/lib/typescript/src/native/EnrichedText.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 +405 -0
- package/lib/typescript/src/spec/EnrichedTextInputNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/spec/EnrichedTextNativeComponent.d.ts +88 -0
- package/lib/typescript/src/spec/EnrichedTextNativeComponent.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +721 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts +13 -0
- package/lib/typescript/src/utils/EnrichedTextInputDefaultProps.d.ts.map +1 -0
- package/lib/typescript/src/utils/defaultHtmlStyle.d.ts +4 -0
- package/lib/typescript/src/utils/defaultHtmlStyle.d.ts.map +1 -0
- package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts +3 -0
- package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts.map +1 -0
- package/lib/typescript/src/utils/isMentionStyleRecord.d.ts +3 -0
- package/lib/typescript/src/utils/isMentionStyleRecord.d.ts.map +1 -0
- package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts +6 -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/lib/typescript/src/web/EnrichedTextInput.d.ts +4 -0
- package/lib/typescript/src/web/EnrichedTextInput.d.ts.map +1 -0
- package/lib/typescript/src/web/adaptWebToNativeEvent.d.ts +3 -0
- package/lib/typescript/src/web/adaptWebToNativeEvent.d.ts.map +1 -0
- package/lib/typescript/src/web/checkboxHtmlNormalizer.d.ts +3 -0
- package/lib/typescript/src/web/checkboxHtmlNormalizer.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedBold.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedBold.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedCheckboxItem.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedCheckboxItem.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts +9 -0
- package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedCode.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedCode.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts +9 -0
- package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedHeading.d.ts +5 -0
- package/lib/typescript/src/web/formats/EnrichedHeading.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedImage.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedImage.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts +3 -0
- package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedItalic.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedItalic.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedLink.d.ts +5 -0
- package/lib/typescript/src/web/formats/EnrichedLink.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedListItem.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedListItem.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedMention.d.ts +4 -0
- package/lib/typescript/src/web/formats/EnrichedMention.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedStrike.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedStrike.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedUnderline.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedUnderline.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts +9 -0
- package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts +18 -0
- package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/formatRules.d.ts +10 -0
- package/lib/typescript/src/web/formats/formatRules.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/listKeyboard.d.ts +4 -0
- package/lib/typescript/src/web/formats/listKeyboard.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/wrappedBlockKeyboard.d.ts +12 -0
- package/lib/typescript/src/web/formats/wrappedBlockKeyboard.d.ts.map +1 -0
- package/lib/typescript/src/web/pasteImages.d.ts +10 -0
- package/lib/typescript/src/web/pasteImages.d.ts.map +1 -0
- package/lib/typescript/src/web/pastedImageDimensions.d.ts +9 -0
- package/lib/typescript/src/web/pastedImageDimensions.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts +9 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.d.ts +3 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts +4 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/mentionPluginKey.d.ts +4 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/mentionPluginKey.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.d.ts +9 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts +3 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/startMention.d.ts +3 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/startMention.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.d.ts +3 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts +4 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/types.d.ts +20 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/types.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.d.ts +3 -0
- package/lib/typescript/src/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts +7 -0
- package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts +3 -0
- package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts +10 -0
- package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/StripMarksInCodeBlockPlugin.d.ts +3 -0
- package/lib/typescript/src/web/pmPlugins/StripMarksInCodeBlockPlugin.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/StripMarksOnImagePlugin.d.ts +3 -0
- package/lib/typescript/src/web/pmPlugins/StripMarksOnImagePlugin.d.ts.map +1 -0
- package/lib/typescript/src/web/positionMapping.d.ts +39 -0
- package/lib/typescript/src/web/positionMapping.d.ts.map +1 -0
- package/lib/typescript/src/web/returnKeyTypeToEnterKeyHint.d.ts +4 -0
- package/lib/typescript/src/web/returnKeyTypeToEnterKeyHint.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts +3 -0
- package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts +8 -0
- package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/enrichedInputThemingToCSSProperties.d.ts +9 -0
- package/lib/typescript/src/web/styleConversion/enrichedInputThemingToCSSProperties.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts +10 -0
- package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/mentionIndicatorCssKey.d.ts +3 -0
- package/lib/typescript/src/web/styleConversion/mentionIndicatorCssKey.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/toColor.d.ts +3 -0
- package/lib/typescript/src/web/styleConversion/toColor.d.ts.map +1 -0
- package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts +3 -0
- package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts.map +1 -0
- package/lib/typescript/src/web/useOnChangeHtml.d.ts +5 -0
- package/lib/typescript/src/web/useOnChangeHtml.d.ts.map +1 -0
- package/lib/typescript/src/web/useOnChangeState.d.ts +6 -0
- package/lib/typescript/src/web/useOnChangeState.d.ts.map +1 -0
- package/lib/typescript/src/web/useOnChangeText.d.ts +5 -0
- package/lib/typescript/src/web/useOnChangeText.d.ts.map +1 -0
- package/lib/typescript/src/web/useOnEditorChange.d.ts +6 -0
- package/lib/typescript/src/web/useOnEditorChange.d.ts.map +1 -0
- package/lib/typescript/src/web/useOnLinkDetected.d.ts +4 -0
- package/lib/typescript/src/web/useOnLinkDetected.d.ts.map +1 -0
- package/package.json +232 -1
- package/react-native.config.js +16 -0
- package/src/index.native.tsx +34 -0
- package/src/index.tsx +24 -0
- package/src/native/EnrichedText.tsx +115 -0
- package/src/native/EnrichedTextInput.tsx +374 -0
- package/src/spec/EnrichedTextInputNativeComponent.ts +524 -0
- package/src/spec/EnrichedTextNativeComponent.ts +110 -0
- package/src/types.ts +864 -0
- package/src/utils/EnrichedTextInputDefaultProps.ts +16 -0
- package/src/utils/defaultHtmlStyle.ts +83 -0
- package/src/utils/expandMentionStylesForIndicators.ts +19 -0
- package/src/utils/isMentionStyleRecord.ts +22 -0
- package/src/utils/normalizeHtmlStyle.ts +181 -0
- package/src/utils/nullthrows.ts +7 -0
- package/src/utils/regexParser.ts +56 -0
- package/src/web/EnrichedTextInput.css +201 -0
- package/src/web/EnrichedTextInput.tsx +403 -0
- package/src/web/adaptWebToNativeEvent.ts +37 -0
- package/src/web/checkboxHtmlNormalizer.ts +62 -0
- package/src/web/formats/EnrichedBlockquote.ts +36 -0
- package/src/web/formats/EnrichedBold.ts +16 -0
- package/src/web/formats/EnrichedCheckboxItem.ts +35 -0
- package/src/web/formats/EnrichedCheckboxList.ts +47 -0
- package/src/web/formats/EnrichedCode.ts +13 -0
- package/src/web/formats/EnrichedCodeBlock.ts +53 -0
- package/src/web/formats/EnrichedHeading.ts +36 -0
- package/src/web/formats/EnrichedImage.ts +59 -0
- package/src/web/formats/EnrichedImageNodeView.tsx +89 -0
- package/src/web/formats/EnrichedItalic.ts +16 -0
- package/src/web/formats/EnrichedLink.ts +149 -0
- package/src/web/formats/EnrichedListItem.ts +17 -0
- package/src/web/formats/EnrichedMention.ts +55 -0
- package/src/web/formats/EnrichedOrderedList.ts +40 -0
- package/src/web/formats/EnrichedStrike.ts +16 -0
- package/src/web/formats/EnrichedUnderline.ts +16 -0
- package/src/web/formats/EnrichedUnorderedList.ts +50 -0
- package/src/web/formats/applyWrappingListToSelection.ts +76 -0
- package/src/web/formats/formatRules.ts +84 -0
- package/src/web/formats/listKeyboard.ts +58 -0
- package/src/web/formats/wrappedBlockKeyboard.ts +76 -0
- package/src/web/pasteImages.ts +96 -0
- package/src/web/pastedImageDimensions.ts +40 -0
- package/src/web/pmPlugins/MentionPlugin/index.ts +45 -0
- package/src/web/pmPlugins/MentionPlugin/isCaretInBlockedContext.ts +17 -0
- package/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.ts +75 -0
- package/src/web/pmPlugins/MentionPlugin/mentionPluginKey.ts +4 -0
- package/src/web/pmPlugins/MentionPlugin/removeMentionMarksIfSpansResized.ts +68 -0
- package/src/web/pmPlugins/MentionPlugin/setMention.ts +83 -0
- package/src/web/pmPlugins/MentionPlugin/startMention.ts +14 -0
- package/src/web/pmPlugins/MentionPlugin/stripPartialMentionMarks.ts +20 -0
- package/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.ts +105 -0
- package/src/web/pmPlugins/MentionPlugin/types.ts +22 -0
- package/src/web/pmPlugins/MergeAdjacentSameKindBlocksPlugin.ts +57 -0
- package/src/web/pmPlugins/ShortcutPlugin.ts +98 -0
- package/src/web/pmPlugins/StrictMarksPlugin.ts +131 -0
- package/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.ts +79 -0
- package/src/web/pmPlugins/StripMarksInCodeBlockPlugin.ts +34 -0
- package/src/web/pmPlugins/StripMarksOnImagePlugin.ts +33 -0
- package/src/web/positionMapping.ts +81 -0
- package/src/web/returnKeyTypeToEnterKeyHint.ts +29 -0
- package/src/web/styleConversion/buildMentionRulesCSS.ts +42 -0
- package/src/web/styleConversion/enrichedInputStyleToCSSProperties.ts +224 -0
- package/src/web/styleConversion/enrichedInputThemingToCSSProperties.ts +34 -0
- package/src/web/styleConversion/htmlStyleToCSSVariables.ts +226 -0
- package/src/web/styleConversion/mentionIndicatorCssKey.ts +14 -0
- package/src/web/styleConversion/toColor.ts +17 -0
- package/src/web/tiptapHtmlNormalizer.ts +33 -0
- package/src/web/useOnChangeHtml.ts +14 -0
- package/src/web/useOnChangeState.ts +125 -0
- package/src/web/useOnChangeText.ts +15 -0
- package/src/web/useOnEditorChange.ts +33 -0
- package/src/web/useOnLinkDetected.ts +82 -0
|
@@ -0,0 +1,2170 @@
|
|
|
1
|
+
#import "EnrichedTextInputView.h"
|
|
2
|
+
#import "AlignmentUtils.h"
|
|
3
|
+
#import "AttachmentLayoutUtils.h"
|
|
4
|
+
#import "CoreText/CoreText.h"
|
|
5
|
+
#import "DotReplacementUtils.h"
|
|
6
|
+
#import "HtmlParser.h"
|
|
7
|
+
#import "ImageAttachment.h"
|
|
8
|
+
#import "KeyboardUtils.h"
|
|
9
|
+
#import "LayoutManagerExtension.h"
|
|
10
|
+
#import "ParagraphAttributesUtils.h"
|
|
11
|
+
#import "RCTFabricComponentsPlugins.h"
|
|
12
|
+
#import "ShortcutsUtils.h"
|
|
13
|
+
#import "StringExtension.h"
|
|
14
|
+
#import "StyleHeaders.h"
|
|
15
|
+
#import "StyleUtils.h"
|
|
16
|
+
#import "TextBlockTapGestureRecognizer.h"
|
|
17
|
+
#import "TextInsertionUtils.h"
|
|
18
|
+
#import "UIView+React.h"
|
|
19
|
+
#import "WordsUtils.h"
|
|
20
|
+
#import "ZeroWidthSpaceUtils.h"
|
|
21
|
+
#import <React/RCTConversions.h>
|
|
22
|
+
#import <ReactNativeEnrichedHtml/EnrichedTextInputViewComponentDescriptor.h>
|
|
23
|
+
#import <ReactNativeEnrichedHtml/EventEmitters.h>
|
|
24
|
+
#import <ReactNativeEnrichedHtml/Props.h>
|
|
25
|
+
#import <ReactNativeEnrichedHtml/RCTComponentViewHelpers.h>
|
|
26
|
+
#import <folly/dynamic.h>
|
|
27
|
+
#import <react/utils/ManagedObjectWrapper.h>
|
|
28
|
+
|
|
29
|
+
#define GET_STYLE_STATE(TYPE_ENUM) \
|
|
30
|
+
{ \
|
|
31
|
+
.isActive = [self isStyleActive:TYPE_ENUM], \
|
|
32
|
+
.isBlocking = [self isStyle:TYPE_ENUM activeInMap:blockingStyles], \
|
|
33
|
+
.isConflicting = [self isStyle:TYPE_ENUM activeInMap:conflictingStyles] \
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
using namespace facebook::react;
|
|
37
|
+
|
|
38
|
+
@interface EnrichedTextInputView () <
|
|
39
|
+
RCTEnrichedTextInputViewViewProtocol, UITextViewDelegate,
|
|
40
|
+
UIGestureRecognizerDelegate, NSTextStorageDelegate, NSObject>
|
|
41
|
+
|
|
42
|
+
@end
|
|
43
|
+
|
|
44
|
+
@implementation EnrichedTextInputView {
|
|
45
|
+
EnrichedTextInputViewShadowNode::ConcreteState::Shared _state;
|
|
46
|
+
int _componentViewHeightUpdateCounter;
|
|
47
|
+
NSMutableSet<NSNumber *> *_activeStyles;
|
|
48
|
+
NSMutableSet<NSNumber *> *_blockedStyles;
|
|
49
|
+
LinkData *_recentlyActiveLinkData;
|
|
50
|
+
NSRange _recentlyActiveLinkRange;
|
|
51
|
+
NSString *_recentInputString;
|
|
52
|
+
MentionParams *_recentlyActiveMentionParams;
|
|
53
|
+
NSRange _recentlyActiveMentionRange;
|
|
54
|
+
NSString *_recentlyEmittedHtml;
|
|
55
|
+
BOOL _emitHtml;
|
|
56
|
+
UILabel *_placeholderLabel;
|
|
57
|
+
UIColor *_placeholderColor;
|
|
58
|
+
BOOL _emitFocusBlur;
|
|
59
|
+
BOOL _emitTextChange;
|
|
60
|
+
NSMutableDictionary<NSValue *, UIImageView *> *_attachmentViews;
|
|
61
|
+
NSArray<NSDictionary *> *_contextMenuItems;
|
|
62
|
+
NSString *_submitBehavior;
|
|
63
|
+
NSDictionary<NSAttributedStringKey, id> *_capturedAttributesBeforeChange;
|
|
64
|
+
NSString *_recentlyEmittedAlignment;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
@synthesize blockEmitting = blockEmitting;
|
|
68
|
+
|
|
69
|
+
// MARK: - Component utils
|
|
70
|
+
|
|
71
|
+
+ (ComponentDescriptorProvider)componentDescriptorProvider {
|
|
72
|
+
return concreteComponentDescriptorProvider<
|
|
73
|
+
EnrichedTextInputViewComponentDescriptor>();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
Class<RCTComponentViewProtocol> EnrichedTextInputViewCls(void) {
|
|
77
|
+
return EnrichedTextInputView.class;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
+ (BOOL)shouldBeRecycled {
|
|
81
|
+
return NO;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// MARK: - EnrichedViewHost protocol
|
|
85
|
+
|
|
86
|
+
- (UITextView *)textView {
|
|
87
|
+
return textView;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
- (EnrichedConfig *)config {
|
|
91
|
+
return config;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
- (NSDictionary<NSNumber *, id> *)stylesDict {
|
|
95
|
+
return stylesDict;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
- (InputAttributesManager *)attributesManager {
|
|
99
|
+
return attributesManager;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
- (NSMutableDictionary<NSNumber *, NSArray<NSNumber *> *> *)conflictingStyles {
|
|
103
|
+
return conflictingStyles;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
- (NSMutableDictionary<NSNumber *, NSArray<NSNumber *> *> *)blockingStyles {
|
|
107
|
+
return blockingStyles;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
- (NSMutableDictionary<NSAttributedStringKey, id> *)defaultTypingAttributes {
|
|
111
|
+
return defaultTypingAttributes;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// MARK: - Init
|
|
115
|
+
|
|
116
|
+
- (instancetype)initWithFrame:(CGRect)frame {
|
|
117
|
+
if (self = [super initWithFrame:frame]) {
|
|
118
|
+
static const auto defaultProps =
|
|
119
|
+
std::make_shared<const EnrichedTextInputViewProps>();
|
|
120
|
+
_props = defaultProps;
|
|
121
|
+
[self setDefaults];
|
|
122
|
+
[self setupTextView];
|
|
123
|
+
[self setupPlaceholderLabel];
|
|
124
|
+
self.contentView = textView;
|
|
125
|
+
}
|
|
126
|
+
return self;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
- (void)setDefaults {
|
|
130
|
+
_componentViewHeightUpdateCounter = 0;
|
|
131
|
+
_activeStyles = [[NSMutableSet alloc] init];
|
|
132
|
+
_blockedStyles = [[NSMutableSet alloc] init];
|
|
133
|
+
_recentlyActiveLinkRange = NSMakeRange(0, 0);
|
|
134
|
+
_recentlyActiveMentionRange = NSMakeRange(0, 0);
|
|
135
|
+
_recentlyEmittedAlignment = @"left";
|
|
136
|
+
_recentInputString = @"";
|
|
137
|
+
_recentlyEmittedHtml = @"<html>\n<p></p>\n</html>";
|
|
138
|
+
_emitHtml = NO;
|
|
139
|
+
blockEmitting = NO;
|
|
140
|
+
_emitFocusBlur = YES;
|
|
141
|
+
_emitTextChange = NO;
|
|
142
|
+
dotReplacementRange = nullptr;
|
|
143
|
+
|
|
144
|
+
defaultTypingAttributes =
|
|
145
|
+
[[NSMutableDictionary<NSAttributedStringKey, id> alloc] init];
|
|
146
|
+
|
|
147
|
+
stylesDict = [StyleUtils stylesDictForHost:self isInput:YES];
|
|
148
|
+
conflictingStyles = [[StyleUtils conflictMap] mutableCopy];
|
|
149
|
+
blockingStyles = [[StyleUtils blockingMap] mutableCopy];
|
|
150
|
+
|
|
151
|
+
parser = [[InputHtmlParser alloc] initWithInput:self];
|
|
152
|
+
_attachmentViews = [[NSMutableDictionary alloc] init];
|
|
153
|
+
attributesManager = [[InputAttributesManager alloc] initWithInput:self];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
- (void)setupTextView {
|
|
157
|
+
textView = [[EnrichedInputTextView alloc] init];
|
|
158
|
+
textView.backgroundColor = UIColor.clearColor;
|
|
159
|
+
textView.textContainerInset = UIEdgeInsetsMake(0, 0, 0, 0);
|
|
160
|
+
textView.textContainer.lineFragmentPadding = 0;
|
|
161
|
+
textView.delegate = self;
|
|
162
|
+
textView.input = self;
|
|
163
|
+
textView.layoutManager.input = self;
|
|
164
|
+
textView.textStorage.delegate = self;
|
|
165
|
+
|
|
166
|
+
textView.adjustsFontForContentSizeCategory = YES;
|
|
167
|
+
[textView addGestureRecognizer:[[TextBlockTapGestureRecognizer alloc]
|
|
168
|
+
initWithInput:self
|
|
169
|
+
action:@selector(onTextBlockTap:)]];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
- (void)setupPlaceholderLabel {
|
|
173
|
+
_placeholderLabel = [[UILabel alloc] initWithFrame:CGRectZero];
|
|
174
|
+
_placeholderLabel.translatesAutoresizingMaskIntoConstraints = NO;
|
|
175
|
+
[textView addSubview:_placeholderLabel];
|
|
176
|
+
[NSLayoutConstraint activateConstraints:@[
|
|
177
|
+
[_placeholderLabel.leadingAnchor
|
|
178
|
+
constraintEqualToAnchor:textView.leadingAnchor],
|
|
179
|
+
[_placeholderLabel.widthAnchor
|
|
180
|
+
constraintEqualToAnchor:textView.widthAnchor],
|
|
181
|
+
[_placeholderLabel.topAnchor constraintEqualToAnchor:textView.topAnchor],
|
|
182
|
+
[_placeholderLabel.bottomAnchor
|
|
183
|
+
constraintEqualToAnchor:textView.bottomAnchor]
|
|
184
|
+
]];
|
|
185
|
+
_placeholderLabel.lineBreakMode = NSLineBreakByTruncatingTail;
|
|
186
|
+
_placeholderLabel.text = @"";
|
|
187
|
+
_placeholderLabel.hidden = YES;
|
|
188
|
+
_placeholderLabel.adjustsFontForContentSizeCategory = YES;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// MARK: - Props
|
|
192
|
+
|
|
193
|
+
- (void)updateProps:(Props::Shared const &)props
|
|
194
|
+
oldProps:(Props::Shared const &)oldProps {
|
|
195
|
+
const auto &oldViewProps =
|
|
196
|
+
*std::static_pointer_cast<EnrichedTextInputViewProps const>(_props);
|
|
197
|
+
const auto &newViewProps =
|
|
198
|
+
*std::static_pointer_cast<EnrichedTextInputViewProps const>(props);
|
|
199
|
+
BOOL isFirstMount = NO;
|
|
200
|
+
BOOL stylePropChanged = NO;
|
|
201
|
+
|
|
202
|
+
// initial config
|
|
203
|
+
if (config == nullptr) {
|
|
204
|
+
isFirstMount = YES;
|
|
205
|
+
config = [[EnrichedConfig alloc] init];
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// any style prop changes:
|
|
209
|
+
// firstly we create the new config for the changes
|
|
210
|
+
|
|
211
|
+
EnrichedConfig *newConfig = [config copy];
|
|
212
|
+
|
|
213
|
+
if (newViewProps.color != oldViewProps.color) {
|
|
214
|
+
if (isColorMeaningful(newViewProps.color)) {
|
|
215
|
+
UIColor *uiColor = RCTUIColorFromSharedColor(newViewProps.color);
|
|
216
|
+
[newConfig setPrimaryColor:uiColor];
|
|
217
|
+
} else {
|
|
218
|
+
[newConfig setPrimaryColor:nullptr];
|
|
219
|
+
}
|
|
220
|
+
stylePropChanged = YES;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (newViewProps.fontSize != oldViewProps.fontSize) {
|
|
224
|
+
if (newViewProps.fontSize) {
|
|
225
|
+
NSNumber *fontSize = @(newViewProps.fontSize);
|
|
226
|
+
[newConfig setPrimaryFontSize:fontSize];
|
|
227
|
+
} else {
|
|
228
|
+
[newConfig setPrimaryFontSize:nullptr];
|
|
229
|
+
}
|
|
230
|
+
stylePropChanged = YES;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (newViewProps.lineHeight != oldViewProps.lineHeight) {
|
|
234
|
+
[newConfig setPrimaryLineHeight:newViewProps.lineHeight];
|
|
235
|
+
stylePropChanged = YES;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
if (newViewProps.fontWeight != oldViewProps.fontWeight) {
|
|
239
|
+
if (!newViewProps.fontWeight.empty()) {
|
|
240
|
+
[newConfig
|
|
241
|
+
setPrimaryFontWeight:[NSString
|
|
242
|
+
fromCppString:newViewProps.fontWeight]];
|
|
243
|
+
} else {
|
|
244
|
+
[newConfig setPrimaryFontWeight:nullptr];
|
|
245
|
+
}
|
|
246
|
+
stylePropChanged = YES;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (newViewProps.fontFamily != oldViewProps.fontFamily) {
|
|
250
|
+
if (!newViewProps.fontFamily.empty()) {
|
|
251
|
+
[newConfig
|
|
252
|
+
setPrimaryFontFamily:[NSString
|
|
253
|
+
fromCppString:newViewProps.fontFamily]];
|
|
254
|
+
} else {
|
|
255
|
+
[newConfig setPrimaryFontFamily:nullptr];
|
|
256
|
+
}
|
|
257
|
+
stylePropChanged = YES;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// rich text style
|
|
261
|
+
|
|
262
|
+
if (newViewProps.htmlStyle.h1.fontSize !=
|
|
263
|
+
oldViewProps.htmlStyle.h1.fontSize) {
|
|
264
|
+
[newConfig setH1FontSize:newViewProps.htmlStyle.h1.fontSize];
|
|
265
|
+
stylePropChanged = YES;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (newViewProps.htmlStyle.h1.bold != oldViewProps.htmlStyle.h1.bold) {
|
|
269
|
+
[newConfig setH1Bold:newViewProps.htmlStyle.h1.bold];
|
|
270
|
+
|
|
271
|
+
// Update style blocks and conflicts for bold
|
|
272
|
+
if (newViewProps.htmlStyle.h1.bold) {
|
|
273
|
+
[StyleUtils addStyleBlock:H1 to:Bold forHost:self];
|
|
274
|
+
[StyleUtils addStyleConflict:Bold to:H1 forHost:self];
|
|
275
|
+
} else {
|
|
276
|
+
[StyleUtils removeStyleBlock:H1 from:Bold forHost:self];
|
|
277
|
+
[StyleUtils removeStyleConflict:Bold from:H1 forHost:self];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
stylePropChanged = YES;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (newViewProps.htmlStyle.h2.fontSize !=
|
|
284
|
+
oldViewProps.htmlStyle.h2.fontSize) {
|
|
285
|
+
[newConfig setH2FontSize:newViewProps.htmlStyle.h2.fontSize];
|
|
286
|
+
stylePropChanged = YES;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (newViewProps.htmlStyle.h2.bold != oldViewProps.htmlStyle.h2.bold) {
|
|
290
|
+
[newConfig setH2Bold:newViewProps.htmlStyle.h2.bold];
|
|
291
|
+
|
|
292
|
+
// Update style blocks and conflicts for bold
|
|
293
|
+
if (newViewProps.htmlStyle.h2.bold) {
|
|
294
|
+
[StyleUtils addStyleBlock:H2 to:Bold forHost:self];
|
|
295
|
+
[StyleUtils addStyleConflict:Bold to:H2 forHost:self];
|
|
296
|
+
} else {
|
|
297
|
+
[StyleUtils removeStyleBlock:H2 from:Bold forHost:self];
|
|
298
|
+
[StyleUtils removeStyleConflict:Bold from:H2 forHost:self];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
stylePropChanged = YES;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
if (newViewProps.htmlStyle.h3.fontSize !=
|
|
305
|
+
oldViewProps.htmlStyle.h3.fontSize) {
|
|
306
|
+
[newConfig setH3FontSize:newViewProps.htmlStyle.h3.fontSize];
|
|
307
|
+
stylePropChanged = YES;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (newViewProps.htmlStyle.h3.bold != oldViewProps.htmlStyle.h3.bold) {
|
|
311
|
+
[newConfig setH3Bold:newViewProps.htmlStyle.h3.bold];
|
|
312
|
+
|
|
313
|
+
// Update style blocks and conflicts for bold
|
|
314
|
+
if (newViewProps.htmlStyle.h3.bold) {
|
|
315
|
+
[StyleUtils addStyleBlock:H3 to:Bold forHost:self];
|
|
316
|
+
[StyleUtils addStyleConflict:Bold to:H3 forHost:self];
|
|
317
|
+
} else {
|
|
318
|
+
[StyleUtils removeStyleBlock:H3 from:Bold forHost:self];
|
|
319
|
+
[StyleUtils removeStyleConflict:Bold from:H3 forHost:self];
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
stylePropChanged = YES;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (newViewProps.htmlStyle.h4.fontSize !=
|
|
326
|
+
oldViewProps.htmlStyle.h4.fontSize) {
|
|
327
|
+
[newConfig setH4FontSize:newViewProps.htmlStyle.h4.fontSize];
|
|
328
|
+
stylePropChanged = YES;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (newViewProps.htmlStyle.h4.bold != oldViewProps.htmlStyle.h4.bold) {
|
|
332
|
+
[newConfig setH4Bold:newViewProps.htmlStyle.h4.bold];
|
|
333
|
+
|
|
334
|
+
// Update style blocks and conflicts for bold
|
|
335
|
+
if (newViewProps.htmlStyle.h4.bold) {
|
|
336
|
+
[StyleUtils addStyleBlock:H4 to:Bold forHost:self];
|
|
337
|
+
[StyleUtils addStyleConflict:Bold to:H4 forHost:self];
|
|
338
|
+
} else {
|
|
339
|
+
[StyleUtils removeStyleBlock:H4 from:Bold forHost:self];
|
|
340
|
+
[StyleUtils removeStyleConflict:Bold from:H4 forHost:self];
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
stylePropChanged = YES;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
if (newViewProps.htmlStyle.h5.fontSize !=
|
|
347
|
+
oldViewProps.htmlStyle.h5.fontSize) {
|
|
348
|
+
[newConfig setH5FontSize:newViewProps.htmlStyle.h5.fontSize];
|
|
349
|
+
stylePropChanged = YES;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (newViewProps.htmlStyle.h5.bold != oldViewProps.htmlStyle.h5.bold) {
|
|
353
|
+
[newConfig setH5Bold:newViewProps.htmlStyle.h5.bold];
|
|
354
|
+
|
|
355
|
+
// Update style blocks and conflicts for bold
|
|
356
|
+
if (newViewProps.htmlStyle.h5.bold) {
|
|
357
|
+
[StyleUtils addStyleBlock:H5 to:Bold forHost:self];
|
|
358
|
+
[StyleUtils addStyleConflict:Bold to:H5 forHost:self];
|
|
359
|
+
} else {
|
|
360
|
+
[StyleUtils removeStyleBlock:H5 from:Bold forHost:self];
|
|
361
|
+
[StyleUtils removeStyleConflict:Bold from:H5 forHost:self];
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
stylePropChanged = YES;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (newViewProps.htmlStyle.h6.fontSize !=
|
|
368
|
+
oldViewProps.htmlStyle.h6.fontSize) {
|
|
369
|
+
[newConfig setH6FontSize:newViewProps.htmlStyle.h6.fontSize];
|
|
370
|
+
stylePropChanged = YES;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (newViewProps.htmlStyle.h6.bold != oldViewProps.htmlStyle.h6.bold) {
|
|
374
|
+
[newConfig setH6Bold:newViewProps.htmlStyle.h6.bold];
|
|
375
|
+
|
|
376
|
+
// Update style blocks and conflicts for bold
|
|
377
|
+
if (newViewProps.htmlStyle.h6.bold) {
|
|
378
|
+
[StyleUtils addStyleBlock:H6 to:Bold forHost:self];
|
|
379
|
+
[StyleUtils addStyleConflict:Bold to:H6 forHost:self];
|
|
380
|
+
} else {
|
|
381
|
+
[StyleUtils removeStyleBlock:H6 from:Bold forHost:self];
|
|
382
|
+
[StyleUtils removeStyleConflict:Bold from:H6 forHost:self];
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
stylePropChanged = YES;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (newViewProps.htmlStyle.blockquote.borderColor !=
|
|
389
|
+
oldViewProps.htmlStyle.blockquote.borderColor) {
|
|
390
|
+
if (isColorMeaningful(newViewProps.htmlStyle.blockquote.borderColor)) {
|
|
391
|
+
[newConfig setBlockquoteBorderColor:RCTUIColorFromSharedColor(
|
|
392
|
+
newViewProps.htmlStyle.blockquote
|
|
393
|
+
.borderColor)];
|
|
394
|
+
stylePropChanged = YES;
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (newViewProps.htmlStyle.blockquote.borderWidth !=
|
|
399
|
+
oldViewProps.htmlStyle.blockquote.borderWidth) {
|
|
400
|
+
[newConfig
|
|
401
|
+
setBlockquoteBorderWidth:newViewProps.htmlStyle.blockquote.borderWidth];
|
|
402
|
+
stylePropChanged = YES;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (newViewProps.htmlStyle.blockquote.gapWidth !=
|
|
406
|
+
oldViewProps.htmlStyle.blockquote.gapWidth) {
|
|
407
|
+
[newConfig
|
|
408
|
+
setBlockquoteGapWidth:newViewProps.htmlStyle.blockquote.gapWidth];
|
|
409
|
+
stylePropChanged = YES;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
// since this prop defaults to undefined on JS side, we need to force set the
|
|
413
|
+
// value on first mount
|
|
414
|
+
if (newViewProps.htmlStyle.blockquote.color !=
|
|
415
|
+
oldViewProps.htmlStyle.blockquote.color ||
|
|
416
|
+
isFirstMount) {
|
|
417
|
+
if (isColorMeaningful(newViewProps.htmlStyle.blockquote.color)) {
|
|
418
|
+
[newConfig
|
|
419
|
+
setBlockquoteColor:RCTUIColorFromSharedColor(
|
|
420
|
+
newViewProps.htmlStyle.blockquote.color)];
|
|
421
|
+
} else {
|
|
422
|
+
[newConfig setBlockquoteColor:[newConfig primaryColor]];
|
|
423
|
+
}
|
|
424
|
+
stylePropChanged = YES;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (newViewProps.htmlStyle.code.color != oldViewProps.htmlStyle.code.color) {
|
|
428
|
+
if (isColorMeaningful(newViewProps.htmlStyle.code.color)) {
|
|
429
|
+
[newConfig setInlineCodeFgColor:RCTUIColorFromSharedColor(
|
|
430
|
+
newViewProps.htmlStyle.code.color)];
|
|
431
|
+
stylePropChanged = YES;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (newViewProps.htmlStyle.code.backgroundColor !=
|
|
436
|
+
oldViewProps.htmlStyle.code.backgroundColor) {
|
|
437
|
+
if (isColorMeaningful(newViewProps.htmlStyle.code.backgroundColor)) {
|
|
438
|
+
[newConfig setInlineCodeBgColor:RCTUIColorFromSharedColor(
|
|
439
|
+
newViewProps.htmlStyle.code
|
|
440
|
+
.backgroundColor)];
|
|
441
|
+
stylePropChanged = YES;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (newViewProps.htmlStyle.ol.gapWidth !=
|
|
446
|
+
oldViewProps.htmlStyle.ol.gapWidth) {
|
|
447
|
+
[newConfig setOrderedListGapWidth:newViewProps.htmlStyle.ol.gapWidth];
|
|
448
|
+
stylePropChanged = YES;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (newViewProps.htmlStyle.ol.marginLeft !=
|
|
452
|
+
oldViewProps.htmlStyle.ol.marginLeft) {
|
|
453
|
+
[newConfig setOrderedListMarginLeft:newViewProps.htmlStyle.ol.marginLeft];
|
|
454
|
+
stylePropChanged = YES;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// since this prop defaults to undefined on JS side, we need to force set the
|
|
458
|
+
// value on first mount
|
|
459
|
+
if (newViewProps.htmlStyle.ol.markerFontWeight !=
|
|
460
|
+
oldViewProps.htmlStyle.ol.markerFontWeight ||
|
|
461
|
+
isFirstMount) {
|
|
462
|
+
if (!newViewProps.htmlStyle.ol.markerFontWeight.empty()) {
|
|
463
|
+
[newConfig
|
|
464
|
+
setOrderedListMarkerFontWeight:
|
|
465
|
+
[NSString
|
|
466
|
+
fromCppString:newViewProps.htmlStyle.ol.markerFontWeight]];
|
|
467
|
+
} else {
|
|
468
|
+
[newConfig setOrderedListMarkerFontWeight:[newConfig primaryFontWeight]];
|
|
469
|
+
}
|
|
470
|
+
stylePropChanged = YES;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// since this prop defaults to undefined on JS side, we need to force set the
|
|
474
|
+
// value on first mount
|
|
475
|
+
if (newViewProps.htmlStyle.ol.markerColor !=
|
|
476
|
+
oldViewProps.htmlStyle.ol.markerColor ||
|
|
477
|
+
isFirstMount) {
|
|
478
|
+
if (isColorMeaningful(newViewProps.htmlStyle.ol.markerColor)) {
|
|
479
|
+
[newConfig
|
|
480
|
+
setOrderedListMarkerColor:RCTUIColorFromSharedColor(
|
|
481
|
+
newViewProps.htmlStyle.ol.markerColor)];
|
|
482
|
+
} else {
|
|
483
|
+
[newConfig setOrderedListMarkerColor:[newConfig primaryColor]];
|
|
484
|
+
}
|
|
485
|
+
stylePropChanged = YES;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (newViewProps.htmlStyle.ul.bulletColor !=
|
|
489
|
+
oldViewProps.htmlStyle.ul.bulletColor) {
|
|
490
|
+
if (isColorMeaningful(newViewProps.htmlStyle.ul.bulletColor)) {
|
|
491
|
+
[newConfig setUnorderedListBulletColor:RCTUIColorFromSharedColor(
|
|
492
|
+
newViewProps.htmlStyle.ul
|
|
493
|
+
.bulletColor)];
|
|
494
|
+
stylePropChanged = YES;
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (newViewProps.htmlStyle.ul.bulletSize !=
|
|
499
|
+
oldViewProps.htmlStyle.ul.bulletSize) {
|
|
500
|
+
[newConfig setUnorderedListBulletSize:newViewProps.htmlStyle.ul.bulletSize];
|
|
501
|
+
stylePropChanged = YES;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (newViewProps.htmlStyle.ul.gapWidth !=
|
|
505
|
+
oldViewProps.htmlStyle.ul.gapWidth) {
|
|
506
|
+
[newConfig setUnorderedListGapWidth:newViewProps.htmlStyle.ul.gapWidth];
|
|
507
|
+
stylePropChanged = YES;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (newViewProps.htmlStyle.ul.marginLeft !=
|
|
511
|
+
oldViewProps.htmlStyle.ul.marginLeft) {
|
|
512
|
+
[newConfig setUnorderedListMarginLeft:newViewProps.htmlStyle.ul.marginLeft];
|
|
513
|
+
stylePropChanged = YES;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
if (newViewProps.htmlStyle.a.color != oldViewProps.htmlStyle.a.color) {
|
|
517
|
+
if (isColorMeaningful(newViewProps.htmlStyle.a.color)) {
|
|
518
|
+
[newConfig setLinkColor:RCTUIColorFromSharedColor(
|
|
519
|
+
newViewProps.htmlStyle.a.color)];
|
|
520
|
+
stylePropChanged = YES;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
if (newViewProps.htmlStyle.codeblock.color !=
|
|
525
|
+
oldViewProps.htmlStyle.codeblock.color) {
|
|
526
|
+
if (isColorMeaningful(newViewProps.htmlStyle.codeblock.color)) {
|
|
527
|
+
[newConfig
|
|
528
|
+
setCodeBlockFgColor:RCTUIColorFromSharedColor(
|
|
529
|
+
newViewProps.htmlStyle.codeblock.color)];
|
|
530
|
+
stylePropChanged = YES;
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (newViewProps.htmlStyle.codeblock.backgroundColor !=
|
|
535
|
+
oldViewProps.htmlStyle.codeblock.backgroundColor) {
|
|
536
|
+
if (isColorMeaningful(newViewProps.htmlStyle.codeblock.backgroundColor)) {
|
|
537
|
+
[newConfig setCodeBlockBgColor:RCTUIColorFromSharedColor(
|
|
538
|
+
newViewProps.htmlStyle.codeblock
|
|
539
|
+
.backgroundColor)];
|
|
540
|
+
stylePropChanged = YES;
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
if (newViewProps.htmlStyle.codeblock.borderRadius !=
|
|
545
|
+
oldViewProps.htmlStyle.codeblock.borderRadius) {
|
|
546
|
+
[newConfig
|
|
547
|
+
setCodeBlockBorderRadius:newViewProps.htmlStyle.codeblock.borderRadius];
|
|
548
|
+
stylePropChanged = YES;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if (newViewProps.htmlStyle.ulCheckbox.boxSize !=
|
|
552
|
+
oldViewProps.htmlStyle.ulCheckbox.boxSize) {
|
|
553
|
+
[newConfig
|
|
554
|
+
setCheckboxListBoxSize:newViewProps.htmlStyle.ulCheckbox.boxSize];
|
|
555
|
+
stylePropChanged = YES;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (newViewProps.htmlStyle.ulCheckbox.gapWidth !=
|
|
559
|
+
oldViewProps.htmlStyle.ulCheckbox.gapWidth) {
|
|
560
|
+
[newConfig
|
|
561
|
+
setCheckboxListGapWidth:newViewProps.htmlStyle.ulCheckbox.gapWidth];
|
|
562
|
+
stylePropChanged = YES;
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (newViewProps.htmlStyle.ulCheckbox.marginLeft !=
|
|
566
|
+
oldViewProps.htmlStyle.ulCheckbox.marginLeft) {
|
|
567
|
+
[newConfig
|
|
568
|
+
setCheckboxListMarginLeft:newViewProps.htmlStyle.ulCheckbox.marginLeft];
|
|
569
|
+
stylePropChanged = YES;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (newViewProps.htmlStyle.ulCheckbox.boxColor !=
|
|
573
|
+
oldViewProps.htmlStyle.ulCheckbox.boxColor) {
|
|
574
|
+
if (isColorMeaningful(newViewProps.htmlStyle.ulCheckbox.boxColor)) {
|
|
575
|
+
[newConfig setCheckboxListBoxColor:RCTUIColorFromSharedColor(
|
|
576
|
+
newViewProps.htmlStyle.ulCheckbox
|
|
577
|
+
.boxColor)];
|
|
578
|
+
stylePropChanged = YES;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
if (newViewProps.htmlStyle.a.textDecorationLine !=
|
|
583
|
+
oldViewProps.htmlStyle.a.textDecorationLine) {
|
|
584
|
+
NSString *objcString =
|
|
585
|
+
[NSString fromCppString:newViewProps.htmlStyle.a.textDecorationLine];
|
|
586
|
+
if ([objcString isEqualToString:DecorationUnderline]) {
|
|
587
|
+
[newConfig setLinkDecorationLine:DecorationUnderline];
|
|
588
|
+
} else {
|
|
589
|
+
// both DecorationNone and a different, wrong value gets a DecorationNone
|
|
590
|
+
// here
|
|
591
|
+
[newConfig setLinkDecorationLine:DecorationNone];
|
|
592
|
+
}
|
|
593
|
+
stylePropChanged = YES;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (newViewProps.scrollEnabled != oldViewProps.scrollEnabled ||
|
|
597
|
+
textView.scrollEnabled != newViewProps.scrollEnabled) {
|
|
598
|
+
[textView setScrollEnabled:newViewProps.scrollEnabled];
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
if (newViewProps.allowFontScaling != oldViewProps.allowFontScaling) {
|
|
602
|
+
[newConfig setAllowFontScaling:newViewProps.allowFontScaling];
|
|
603
|
+
stylePropChanged = YES;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
folly::dynamic oldMentionStyle = oldViewProps.htmlStyle.mention;
|
|
607
|
+
folly::dynamic newMentionStyle = newViewProps.htmlStyle.mention;
|
|
608
|
+
if (oldMentionStyle != newMentionStyle) {
|
|
609
|
+
bool newSingleProps = NO;
|
|
610
|
+
|
|
611
|
+
for (const auto &obj : newMentionStyle.items()) {
|
|
612
|
+
if (obj.second.isInt() || obj.second.isString()) {
|
|
613
|
+
// we are in just a single MentionStyleProps object
|
|
614
|
+
newSingleProps = YES;
|
|
615
|
+
break;
|
|
616
|
+
} else if (obj.second.isObject()) {
|
|
617
|
+
// we are in map of indicators to MentionStyleProps
|
|
618
|
+
newSingleProps = NO;
|
|
619
|
+
break;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
if (newSingleProps) {
|
|
624
|
+
[newConfig setMentionStyleProps:
|
|
625
|
+
[MentionStyleProps
|
|
626
|
+
getSinglePropsFromFollyDynamic:newMentionStyle]];
|
|
627
|
+
} else {
|
|
628
|
+
[newConfig setMentionStyleProps:
|
|
629
|
+
[MentionStyleProps
|
|
630
|
+
getComplexPropsFromFollyDynamic:newMentionStyle]];
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
stylePropChanged = YES;
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
if (stylePropChanged) {
|
|
637
|
+
// all the text needs to be rebuilt
|
|
638
|
+
// we get the current html using old config, then switch to new config and
|
|
639
|
+
// replace text using the html this way, the newest config attributes are
|
|
640
|
+
// being used!
|
|
641
|
+
|
|
642
|
+
// the html needs to be generated using the old config
|
|
643
|
+
NSString *currentHtml = [HtmlParser
|
|
644
|
+
parseToHtmlFromRange:NSMakeRange(0, textView.textStorage.string.length)
|
|
645
|
+
host:self];
|
|
646
|
+
// we want to preserve the selection between props changes
|
|
647
|
+
NSRange prevSelectedRange = textView.selectedRange;
|
|
648
|
+
|
|
649
|
+
// now set the new config
|
|
650
|
+
config = newConfig;
|
|
651
|
+
|
|
652
|
+
// fill the typing attributes with style props
|
|
653
|
+
defaultTypingAttributes[NSForegroundColorAttributeName] =
|
|
654
|
+
[config primaryColor];
|
|
655
|
+
defaultTypingAttributes[NSFontAttributeName] = [config primaryFont];
|
|
656
|
+
defaultTypingAttributes[NSUnderlineColorAttributeName] =
|
|
657
|
+
[config primaryColor];
|
|
658
|
+
defaultTypingAttributes[NSStrikethroughColorAttributeName] =
|
|
659
|
+
[config primaryColor];
|
|
660
|
+
NSMutableParagraphStyle *defaultPStyle =
|
|
661
|
+
[[NSMutableParagraphStyle alloc] init];
|
|
662
|
+
defaultPStyle.minimumLineHeight = [config scaledPrimaryLineHeight];
|
|
663
|
+
defaultTypingAttributes[NSParagraphStyleAttributeName] = defaultPStyle;
|
|
664
|
+
|
|
665
|
+
// no emitting during styles reload
|
|
666
|
+
blockEmitting = YES;
|
|
667
|
+
|
|
668
|
+
// make sure everything is sound in the html
|
|
669
|
+
NSString *initiallyProcessedHtml =
|
|
670
|
+
[parser initiallyProcessHtml:currentHtml];
|
|
671
|
+
if (initiallyProcessedHtml != nullptr) {
|
|
672
|
+
[parser replaceWholeFromHtml:initiallyProcessedHtml];
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
blockEmitting = NO;
|
|
676
|
+
|
|
677
|
+
textView.typingAttributes = defaultTypingAttributes;
|
|
678
|
+
textView.selectedRange = prevSelectedRange;
|
|
679
|
+
|
|
680
|
+
// make sure the newest lineHeight is applied
|
|
681
|
+
[self refreshLineHeight];
|
|
682
|
+
// update the placeholder as well
|
|
683
|
+
[self refreshPlaceholderLabelStyles];
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// editable
|
|
687
|
+
if (newViewProps.editable != textView.editable) {
|
|
688
|
+
textView.editable = newViewProps.editable;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
// useHtmlNormalizer
|
|
692
|
+
if (newViewProps.useHtmlNormalizer != oldViewProps.useHtmlNormalizer) {
|
|
693
|
+
useHtmlNormalizer = newViewProps.useHtmlNormalizer;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
// textShortcuts
|
|
697
|
+
bool textShortcutsChanged =
|
|
698
|
+
newViewProps.textShortcuts.size() != oldViewProps.textShortcuts.size();
|
|
699
|
+
if (!textShortcutsChanged) {
|
|
700
|
+
for (size_t i = 0; i < newViewProps.textShortcuts.size(); i++) {
|
|
701
|
+
const auto &newItem = newViewProps.textShortcuts[i];
|
|
702
|
+
const auto &oldItem = oldViewProps.textShortcuts[i];
|
|
703
|
+
if (newItem.trigger != oldItem.trigger ||
|
|
704
|
+
newItem.style != oldItem.style) {
|
|
705
|
+
textShortcutsChanged = true;
|
|
706
|
+
break;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
if (textShortcutsChanged) {
|
|
712
|
+
NSMutableArray *shortcuts = [NSMutableArray new];
|
|
713
|
+
for (const auto &item : newViewProps.textShortcuts) {
|
|
714
|
+
[shortcuts addObject:@{
|
|
715
|
+
@"trigger" : [NSString fromCppString:item.trigger],
|
|
716
|
+
@"style" : [NSString fromCppString:item.style],
|
|
717
|
+
}];
|
|
718
|
+
}
|
|
719
|
+
textShortcuts = shortcuts;
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
// default value - must be set before placeholder to make sure it correctly
|
|
723
|
+
// shows on first mount
|
|
724
|
+
if (newViewProps.defaultValue != oldViewProps.defaultValue) {
|
|
725
|
+
NSString *newDefaultValue =
|
|
726
|
+
[NSString fromCppString:newViewProps.defaultValue];
|
|
727
|
+
|
|
728
|
+
NSString *initiallyProcessedHtml =
|
|
729
|
+
[parser initiallyProcessHtml:newDefaultValue];
|
|
730
|
+
if (initiallyProcessedHtml == nullptr) {
|
|
731
|
+
// just plain text
|
|
732
|
+
textView.text = newDefaultValue;
|
|
733
|
+
} else {
|
|
734
|
+
// we've got some seemingly proper html
|
|
735
|
+
[parser replaceWholeFromHtml:initiallyProcessedHtml];
|
|
736
|
+
}
|
|
737
|
+
textView.selectedRange = NSRange(textView.textStorage.string.length, 0);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// placeholderTextColor
|
|
741
|
+
if (newViewProps.placeholderTextColor != oldViewProps.placeholderTextColor) {
|
|
742
|
+
// some real color
|
|
743
|
+
if (isColorMeaningful(newViewProps.placeholderTextColor)) {
|
|
744
|
+
_placeholderColor =
|
|
745
|
+
RCTUIColorFromSharedColor(newViewProps.placeholderTextColor);
|
|
746
|
+
} else {
|
|
747
|
+
_placeholderColor = nullptr;
|
|
748
|
+
}
|
|
749
|
+
[self refreshPlaceholderLabelStyles];
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// placeholder
|
|
753
|
+
if (newViewProps.placeholder != oldViewProps.placeholder) {
|
|
754
|
+
_placeholderLabel.text = [NSString fromCppString:newViewProps.placeholder];
|
|
755
|
+
[self refreshPlaceholderLabelStyles];
|
|
756
|
+
// additionally show placeholder on first mount if it should be there
|
|
757
|
+
if (isFirstMount && textView.text.length == 0) {
|
|
758
|
+
[self setPlaceholderLabelShown:YES];
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// mention indicators
|
|
763
|
+
auto mismatchPair = std::mismatch(newViewProps.mentionIndicators.begin(),
|
|
764
|
+
newViewProps.mentionIndicators.end(),
|
|
765
|
+
oldViewProps.mentionIndicators.begin(),
|
|
766
|
+
oldViewProps.mentionIndicators.end());
|
|
767
|
+
if (mismatchPair.first != newViewProps.mentionIndicators.end() ||
|
|
768
|
+
mismatchPair.second != oldViewProps.mentionIndicators.end()) {
|
|
769
|
+
NSMutableSet<NSNumber *> *newIndicators = [[NSMutableSet alloc] init];
|
|
770
|
+
for (const std::string &item : newViewProps.mentionIndicators) {
|
|
771
|
+
if (item.length() == 1) {
|
|
772
|
+
[newIndicators addObject:@(item[0])];
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
[config setMentionIndicators:newIndicators];
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// linkRegex
|
|
779
|
+
LinkRegexConfig *oldRegexConfig =
|
|
780
|
+
[[LinkRegexConfig alloc] initWithLinkRegexProp:oldViewProps.linkRegex];
|
|
781
|
+
LinkRegexConfig *newRegexConfig =
|
|
782
|
+
[[LinkRegexConfig alloc] initWithLinkRegexProp:newViewProps.linkRegex];
|
|
783
|
+
if (![newRegexConfig isEqualToConfig:oldRegexConfig]) {
|
|
784
|
+
[config setLinkRegexConfig:newRegexConfig];
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
// selection color sets both selection and cursor on iOS (just as in RN)
|
|
788
|
+
if (newViewProps.selectionColor != oldViewProps.selectionColor) {
|
|
789
|
+
if (isColorMeaningful(newViewProps.selectionColor)) {
|
|
790
|
+
textView.tintColor =
|
|
791
|
+
RCTUIColorFromSharedColor(newViewProps.selectionColor);
|
|
792
|
+
} else {
|
|
793
|
+
textView.tintColor = nullptr;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
if (newViewProps.returnKeyType != oldViewProps.returnKeyType) {
|
|
798
|
+
NSString *str = [NSString fromCppString:newViewProps.returnKeyType];
|
|
799
|
+
|
|
800
|
+
textView.returnKeyType =
|
|
801
|
+
[KeyboardUtils getUIReturnKeyTypeFromReturnKeyType:str];
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
if (newViewProps.submitBehavior != oldViewProps.submitBehavior) {
|
|
805
|
+
_submitBehavior = [NSString fromCppString:newViewProps.submitBehavior];
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// autoCapitalize
|
|
809
|
+
if (newViewProps.autoCapitalize != oldViewProps.autoCapitalize) {
|
|
810
|
+
NSString *str = [NSString fromCppString:newViewProps.autoCapitalize];
|
|
811
|
+
if ([str isEqualToString:@"none"]) {
|
|
812
|
+
textView.autocapitalizationType = UITextAutocapitalizationTypeNone;
|
|
813
|
+
} else if ([str isEqualToString:@"sentences"]) {
|
|
814
|
+
textView.autocapitalizationType = UITextAutocapitalizationTypeSentences;
|
|
815
|
+
} else if ([str isEqualToString:@"words"]) {
|
|
816
|
+
textView.autocapitalizationType = UITextAutocapitalizationTypeWords;
|
|
817
|
+
} else if ([str isEqualToString:@"characters"]) {
|
|
818
|
+
textView.autocapitalizationType =
|
|
819
|
+
UITextAutocapitalizationTypeAllCharacters;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
// textView needs to be refocused on autocapitalization type change and we
|
|
823
|
+
// don't want to emit these events
|
|
824
|
+
if ([textView isFirstResponder]) {
|
|
825
|
+
_emitFocusBlur = NO;
|
|
826
|
+
[textView reactBlur];
|
|
827
|
+
[textView reactFocus];
|
|
828
|
+
_emitFocusBlur = YES;
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
// isOnChangeHtmlSet
|
|
833
|
+
_emitHtml = newViewProps.isOnChangeHtmlSet;
|
|
834
|
+
|
|
835
|
+
// isOnChangeTextSet
|
|
836
|
+
_emitTextChange = newViewProps.isOnChangeTextSet;
|
|
837
|
+
|
|
838
|
+
// contextMenuItems
|
|
839
|
+
bool contextMenuChanged = newViewProps.contextMenuItems.size() !=
|
|
840
|
+
oldViewProps.contextMenuItems.size();
|
|
841
|
+
if (!contextMenuChanged) {
|
|
842
|
+
for (size_t i = 0; i < newViewProps.contextMenuItems.size(); i++) {
|
|
843
|
+
if (newViewProps.contextMenuItems[i].text !=
|
|
844
|
+
oldViewProps.contextMenuItems[i].text) {
|
|
845
|
+
contextMenuChanged = true;
|
|
846
|
+
break;
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
if (contextMenuChanged) {
|
|
851
|
+
NSMutableArray<NSString *> *items = [NSMutableArray new];
|
|
852
|
+
for (const auto &item : newViewProps.contextMenuItems) {
|
|
853
|
+
[items addObject:[NSString fromCppString:item.text]];
|
|
854
|
+
}
|
|
855
|
+
_contextMenuItems = [items copy];
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
[super updateProps:props oldProps:oldProps];
|
|
859
|
+
// run the changes callback
|
|
860
|
+
[self anyTextMayHaveBeenModified];
|
|
861
|
+
|
|
862
|
+
// autofocus - needs to be done at the very end
|
|
863
|
+
if (isFirstMount && newViewProps.autoFocus) {
|
|
864
|
+
[textView reactFocus];
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
- (void)setPlaceholderLabelShown:(BOOL)shown {
|
|
869
|
+
if (shown) {
|
|
870
|
+
[self refreshPlaceholderLabelStyles];
|
|
871
|
+
_placeholderLabel.hidden = NO;
|
|
872
|
+
} else {
|
|
873
|
+
_placeholderLabel.hidden = YES;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
- (void)refreshPlaceholderLabelStyles {
|
|
878
|
+
NSMutableDictionary *newAttrs = [defaultTypingAttributes mutableCopy];
|
|
879
|
+
if (_placeholderColor != nullptr) {
|
|
880
|
+
newAttrs[NSForegroundColorAttributeName] = _placeholderColor;
|
|
881
|
+
}
|
|
882
|
+
|
|
883
|
+
// Get the current active alignment in input
|
|
884
|
+
NSParagraphStyle *currentTypingPara =
|
|
885
|
+
textView.typingAttributes[NSParagraphStyleAttributeName];
|
|
886
|
+
NSTextAlignment activeAlignment =
|
|
887
|
+
currentTypingPara ? currentTypingPara.alignment : NSTextAlignmentNatural;
|
|
888
|
+
NSMutableParagraphStyle *placeholderPStyle =
|
|
889
|
+
[newAttrs[NSParagraphStyleAttributeName] mutableCopy];
|
|
890
|
+
if (!placeholderPStyle) {
|
|
891
|
+
placeholderPStyle = [[NSMutableParagraphStyle alloc] init];
|
|
892
|
+
}
|
|
893
|
+
placeholderPStyle.alignment = activeAlignment;
|
|
894
|
+
newAttrs[NSParagraphStyleAttributeName] = placeholderPStyle;
|
|
895
|
+
|
|
896
|
+
NSAttributedString *newAttrStr =
|
|
897
|
+
[[NSAttributedString alloc] initWithString:_placeholderLabel.text
|
|
898
|
+
attributes:newAttrs];
|
|
899
|
+
_placeholderLabel.attributedText = newAttrStr;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
- (void)refreshLineHeight {
|
|
903
|
+
[textView.textStorage
|
|
904
|
+
enumerateAttribute:NSParagraphStyleAttributeName
|
|
905
|
+
inRange:NSMakeRange(0, textView.textStorage.string.length)
|
|
906
|
+
options:0
|
|
907
|
+
usingBlock:^(id _Nullable value, NSRange range,
|
|
908
|
+
BOOL *_Nonnull stop) {
|
|
909
|
+
NSMutableParagraphStyle *pStyle =
|
|
910
|
+
[(NSParagraphStyle *)value mutableCopy];
|
|
911
|
+
if (pStyle == nil)
|
|
912
|
+
return;
|
|
913
|
+
pStyle.minimumLineHeight = [config scaledPrimaryLineHeight];
|
|
914
|
+
[textView.textStorage addAttribute:NSParagraphStyleAttributeName
|
|
915
|
+
value:pStyle
|
|
916
|
+
range:range];
|
|
917
|
+
}];
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
// MARK: - Measuring and states
|
|
921
|
+
|
|
922
|
+
- (CGSize)measureSize:(CGFloat)maxWidth {
|
|
923
|
+
// copy the the whole attributed string
|
|
924
|
+
NSMutableAttributedString *currentStr = [[NSMutableAttributedString alloc]
|
|
925
|
+
initWithAttributedString:textView.textStorage];
|
|
926
|
+
|
|
927
|
+
// edge case: empty input should still be of a height of a single line, so we
|
|
928
|
+
// add a mock "I" character
|
|
929
|
+
if ([currentStr length] == 0) {
|
|
930
|
+
[currentStr
|
|
931
|
+
appendAttributedString:[[NSAttributedString alloc]
|
|
932
|
+
initWithString:@"I"
|
|
933
|
+
attributes:textView.typingAttributes]];
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
// edge case: input with only a zero width space should still be of a height
|
|
937
|
+
// of a single line, so we add a mock "I" character
|
|
938
|
+
if ([currentStr length] == 1 &&
|
|
939
|
+
[[currentStr.string substringWithRange:NSMakeRange(0, 1)]
|
|
940
|
+
isEqualToString:@"\u200B"]) {
|
|
941
|
+
[currentStr
|
|
942
|
+
appendAttributedString:[[NSAttributedString alloc]
|
|
943
|
+
initWithString:@"I"
|
|
944
|
+
attributes:textView.typingAttributes]];
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
// edge case: trailing newlines aren't counted towards height calculations, so
|
|
948
|
+
// we add a mock "I" character
|
|
949
|
+
if (currentStr.length > 0) {
|
|
950
|
+
unichar lastChar =
|
|
951
|
+
[currentStr.string characterAtIndex:currentStr.length - 1];
|
|
952
|
+
if ([[NSCharacterSet newlineCharacterSet] characterIsMember:lastChar]) {
|
|
953
|
+
[currentStr
|
|
954
|
+
appendAttributedString:[[NSAttributedString alloc]
|
|
955
|
+
initWithString:@"I"
|
|
956
|
+
attributes:defaultTypingAttributes]];
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
CGRect boundingBox =
|
|
961
|
+
[currentStr boundingRectWithSize:CGSizeMake(maxWidth, CGFLOAT_MAX)
|
|
962
|
+
options:NSStringDrawingUsesLineFragmentOrigin |
|
|
963
|
+
NSStringDrawingUsesFontLeading
|
|
964
|
+
context:nullptr];
|
|
965
|
+
|
|
966
|
+
return CGSizeMake(maxWidth, ceil(boundingBox.size.height));
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
// make sure the newest state is kept in _state property
|
|
970
|
+
- (void)updateState:(State::Shared const &)state
|
|
971
|
+
oldState:(State::Shared const &)oldState {
|
|
972
|
+
_state = std::static_pointer_cast<
|
|
973
|
+
const EnrichedTextInputViewShadowNode::ConcreteState>(state);
|
|
974
|
+
|
|
975
|
+
// first render with all the needed stuff already defined (state and
|
|
976
|
+
// componentView) so we need to run a single height calculation for any
|
|
977
|
+
// initial values
|
|
978
|
+
if (oldState == nullptr) {
|
|
979
|
+
[self tryUpdatingHeight];
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
- (void)tryUpdatingHeight {
|
|
984
|
+
if (_state == nullptr) {
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
_componentViewHeightUpdateCounter++;
|
|
988
|
+
auto selfRef = wrapManagedObjectWeakly(self);
|
|
989
|
+
_state->updateState(
|
|
990
|
+
EnrichedTextInputViewState(_componentViewHeightUpdateCounter, selfRef));
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
// MARK: - Active styles
|
|
994
|
+
|
|
995
|
+
- (void)tryUpdatingActiveStyles {
|
|
996
|
+
// style updates are emitted only if something differs from the previously
|
|
997
|
+
// active styles
|
|
998
|
+
BOOL updateNeeded = NO;
|
|
999
|
+
|
|
1000
|
+
// active styles are kept in a separate set until we're sure they can be
|
|
1001
|
+
// emitted
|
|
1002
|
+
NSMutableSet *newActiveStyles = [_activeStyles mutableCopy];
|
|
1003
|
+
|
|
1004
|
+
// currently blocked styles are subject to change (e.g. bold being blocked by
|
|
1005
|
+
// headings might change in reaction to prop change) so they also are kept
|
|
1006
|
+
// separately
|
|
1007
|
+
NSMutableSet *newBlockedStyles = [_blockedStyles mutableCopy];
|
|
1008
|
+
|
|
1009
|
+
// data for onLinkDetected event
|
|
1010
|
+
LinkData *detectedLinkData;
|
|
1011
|
+
NSRange detectedLinkRange = NSMakeRange(0, 0);
|
|
1012
|
+
BOOL shouldClearLink = NO;
|
|
1013
|
+
|
|
1014
|
+
// data for onMentionDetected event
|
|
1015
|
+
MentionParams *detectedMentionParams = nullptr;
|
|
1016
|
+
NSRange detectedMentionRange = NSMakeRange(0, 0);
|
|
1017
|
+
BOOL shouldClearMention = NO;
|
|
1018
|
+
|
|
1019
|
+
for (NSNumber *type in stylesDict) {
|
|
1020
|
+
StyleBase *style = stylesDict[type];
|
|
1021
|
+
|
|
1022
|
+
BOOL wasActive = [newActiveStyles containsObject:type];
|
|
1023
|
+
BOOL isActive = [style detect:textView.selectedRange];
|
|
1024
|
+
|
|
1025
|
+
BOOL wasBlocked = [newBlockedStyles containsObject:type];
|
|
1026
|
+
BOOL isBlocked = [self isStyle:(StyleType)[type integerValue]
|
|
1027
|
+
activeInMap:blockingStyles];
|
|
1028
|
+
|
|
1029
|
+
if (wasActive != isActive) {
|
|
1030
|
+
updateNeeded = YES;
|
|
1031
|
+
if (isActive) {
|
|
1032
|
+
[newActiveStyles addObject:type];
|
|
1033
|
+
} else {
|
|
1034
|
+
[newActiveStyles removeObject:type];
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// blocked state change for a style also needs an update
|
|
1039
|
+
if (wasBlocked != isBlocked) {
|
|
1040
|
+
updateNeeded = YES;
|
|
1041
|
+
if (isBlocked) {
|
|
1042
|
+
[newBlockedStyles addObject:type];
|
|
1043
|
+
} else {
|
|
1044
|
+
[newBlockedStyles removeObject:type];
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// onLinkDetected event
|
|
1049
|
+
if ([type intValue] == [LinkStyle getType]) {
|
|
1050
|
+
if (isActive) {
|
|
1051
|
+
// get the link data
|
|
1052
|
+
LinkData *candidateLinkData;
|
|
1053
|
+
NSRange candidateLinkRange = NSMakeRange(0, 0);
|
|
1054
|
+
LinkStyle *linkStyleClass =
|
|
1055
|
+
(LinkStyle *)stylesDict[@([LinkStyle getType])];
|
|
1056
|
+
if (linkStyleClass != nullptr) {
|
|
1057
|
+
candidateLinkData =
|
|
1058
|
+
[linkStyleClass getLinkDataAt:textView.selectedRange.location];
|
|
1059
|
+
candidateLinkRange = [linkStyleClass
|
|
1060
|
+
getFullLinkRangeAt:textView.selectedRange.location];
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
if (wasActive == NO) {
|
|
1064
|
+
// we changed selection from non-link to a link
|
|
1065
|
+
detectedLinkData = candidateLinkData;
|
|
1066
|
+
detectedLinkRange = candidateLinkRange;
|
|
1067
|
+
} else if (![_recentlyActiveLinkData
|
|
1068
|
+
isEqualToLinkData:candidateLinkData] ||
|
|
1069
|
+
!NSEqualRanges(_recentlyActiveLinkRange,
|
|
1070
|
+
candidateLinkRange)) {
|
|
1071
|
+
// we changed selection from one link to the other or modified
|
|
1072
|
+
// current link's text
|
|
1073
|
+
detectedLinkData = candidateLinkData;
|
|
1074
|
+
detectedLinkRange = candidateLinkRange;
|
|
1075
|
+
}
|
|
1076
|
+
} else if (wasActive) {
|
|
1077
|
+
shouldClearLink = YES;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// onMentionDetected event
|
|
1082
|
+
if ([type intValue] == [MentionStyle getType]) {
|
|
1083
|
+
if (isActive) {
|
|
1084
|
+
// get mention data
|
|
1085
|
+
MentionParams *candidateMentionParams;
|
|
1086
|
+
NSRange candidateMentionRange = NSMakeRange(0, 0);
|
|
1087
|
+
MentionStyle *mentionStyleClass =
|
|
1088
|
+
(MentionStyle *)stylesDict[@([MentionStyle getType])];
|
|
1089
|
+
if (mentionStyleClass != nullptr) {
|
|
1090
|
+
candidateMentionParams = [mentionStyleClass
|
|
1091
|
+
getMentionParamsAt:textView.selectedRange.location];
|
|
1092
|
+
candidateMentionRange = [mentionStyleClass
|
|
1093
|
+
getFullMentionRangeAt:textView.selectedRange.location];
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
if (wasActive == NO) {
|
|
1097
|
+
// selection was changed from a non-mention to a mention
|
|
1098
|
+
detectedMentionParams = candidateMentionParams;
|
|
1099
|
+
detectedMentionRange = candidateMentionRange;
|
|
1100
|
+
} else if (![_recentlyActiveMentionParams.text
|
|
1101
|
+
isEqualToString:candidateMentionParams.text] ||
|
|
1102
|
+
![_recentlyActiveMentionParams.attributes
|
|
1103
|
+
isEqualToString:candidateMentionParams.attributes] ||
|
|
1104
|
+
!NSEqualRanges(_recentlyActiveMentionRange,
|
|
1105
|
+
candidateMentionRange)) {
|
|
1106
|
+
// selection changed from one mention to another
|
|
1107
|
+
detectedMentionParams = candidateMentionParams;
|
|
1108
|
+
detectedMentionRange = candidateMentionRange;
|
|
1109
|
+
}
|
|
1110
|
+
} else if (wasActive) {
|
|
1111
|
+
shouldClearMention = YES;
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1116
|
+
// detect alignment change
|
|
1117
|
+
AlignmentStyle *alignmentStyle = stylesDict[@([AlignmentStyle getType])];
|
|
1118
|
+
NSString *currentAlignment = [alignmentStyle getStyleState];
|
|
1119
|
+
if (![currentAlignment isEqualToString:_recentlyEmittedAlignment]) {
|
|
1120
|
+
updateNeeded = YES;
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
if (updateNeeded) {
|
|
1124
|
+
auto emitter = [self getEventEmitter];
|
|
1125
|
+
if (emitter != nullptr) {
|
|
1126
|
+
// update activeStyles and blockedStyles only if emitter is available
|
|
1127
|
+
_activeStyles = newActiveStyles;
|
|
1128
|
+
_blockedStyles = newBlockedStyles;
|
|
1129
|
+
_recentlyEmittedAlignment = currentAlignment;
|
|
1130
|
+
|
|
1131
|
+
emitter->onChangeState(
|
|
1132
|
+
{.bold = GET_STYLE_STATE([BoldStyle getType]),
|
|
1133
|
+
.italic = GET_STYLE_STATE([ItalicStyle getType]),
|
|
1134
|
+
.underline = GET_STYLE_STATE([UnderlineStyle getType]),
|
|
1135
|
+
.strikeThrough = GET_STYLE_STATE([StrikethroughStyle getType]),
|
|
1136
|
+
.inlineCode = GET_STYLE_STATE([InlineCodeStyle getType]),
|
|
1137
|
+
.link = GET_STYLE_STATE([LinkStyle getType]),
|
|
1138
|
+
.mention = GET_STYLE_STATE([MentionStyle getType]),
|
|
1139
|
+
.h1 = GET_STYLE_STATE([H1Style getType]),
|
|
1140
|
+
.h2 = GET_STYLE_STATE([H2Style getType]),
|
|
1141
|
+
.h3 = GET_STYLE_STATE([H3Style getType]),
|
|
1142
|
+
.h4 = GET_STYLE_STATE([H4Style getType]),
|
|
1143
|
+
.h5 = GET_STYLE_STATE([H5Style getType]),
|
|
1144
|
+
.h6 = GET_STYLE_STATE([H6Style getType]),
|
|
1145
|
+
.unorderedList = GET_STYLE_STATE([UnorderedListStyle getType]),
|
|
1146
|
+
.orderedList = GET_STYLE_STATE([OrderedListStyle getType]),
|
|
1147
|
+
.blockQuote = GET_STYLE_STATE([BlockQuoteStyle getType]),
|
|
1148
|
+
.codeBlock = GET_STYLE_STATE([CodeBlockStyle getType]),
|
|
1149
|
+
.image = GET_STYLE_STATE([ImageStyle getType]),
|
|
1150
|
+
.checkboxList = GET_STYLE_STATE([CheckboxListStyle getType]),
|
|
1151
|
+
.alignment = [currentAlignment UTF8String]});
|
|
1152
|
+
}
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
if (detectedLinkData != nullptr) {
|
|
1156
|
+
// emit onLinkeDetected event
|
|
1157
|
+
[self emitOnLinkDetectedEvent:detectedLinkData range:detectedLinkRange];
|
|
1158
|
+
} else if (shouldClearLink) {
|
|
1159
|
+
LinkData *emptyLinkData = [[LinkData alloc] init];
|
|
1160
|
+
emptyLinkData.text = @"";
|
|
1161
|
+
emptyLinkData.url = @"";
|
|
1162
|
+
[self emitOnLinkDetectedEvent:emptyLinkData range:NSMakeRange(0, 0)];
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
if (detectedMentionParams != nullptr) {
|
|
1166
|
+
// emit onMentionDetected event
|
|
1167
|
+
[self emitOnMentionDetectedEvent:detectedMentionParams.text
|
|
1168
|
+
indicator:detectedMentionParams.indicator
|
|
1169
|
+
attributes:detectedMentionParams.attributes];
|
|
1170
|
+
|
|
1171
|
+
_recentlyActiveMentionParams = detectedMentionParams;
|
|
1172
|
+
_recentlyActiveMentionRange = detectedMentionRange;
|
|
1173
|
+
} else if (shouldClearMention) {
|
|
1174
|
+
[self emitOnMentionDetectedEvent:@"" indicator:@"" attributes:@"{}"];
|
|
1175
|
+
_recentlyActiveMentionParams = nullptr;
|
|
1176
|
+
_recentlyActiveMentionRange = NSMakeRange(0, 0);
|
|
1177
|
+
}
|
|
1178
|
+
// emit onChangeHtml event if needed
|
|
1179
|
+
[self tryEmittingOnChangeHtmlEvent];
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
- (bool)isStyleActive:(StyleType)type {
|
|
1183
|
+
return [_activeStyles containsObject:@(type)];
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
- (bool)isStyle:(StyleType)type activeInMap:(NSDictionary *)styleMap {
|
|
1187
|
+
NSArray *relatedStyles = styleMap[@(type)];
|
|
1188
|
+
|
|
1189
|
+
if (!relatedStyles) {
|
|
1190
|
+
return false;
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
for (NSNumber *style in relatedStyles) {
|
|
1194
|
+
if ([_activeStyles containsObject:style]) {
|
|
1195
|
+
return true;
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
return false;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
- (bool)textInputShouldReturn {
|
|
1203
|
+
return [_submitBehavior isEqualToString:@"blurAndSubmit"];
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
- (bool)textInputShouldSubmitOnReturn {
|
|
1207
|
+
return [_submitBehavior isEqualToString:@"blurAndSubmit"] ||
|
|
1208
|
+
[_submitBehavior isEqualToString:@"submit"];
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// MARK: - Native commands and events
|
|
1212
|
+
|
|
1213
|
+
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args {
|
|
1214
|
+
if ([commandName isEqualToString:@"focus"]) {
|
|
1215
|
+
[self focus];
|
|
1216
|
+
} else if ([commandName isEqualToString:@"blur"]) {
|
|
1217
|
+
[self blur];
|
|
1218
|
+
} else if ([commandName isEqualToString:@"setValue"]) {
|
|
1219
|
+
NSString *value = (NSString *)args[0];
|
|
1220
|
+
[self setValue:value];
|
|
1221
|
+
} else if ([commandName isEqualToString:@"toggleBold"]) {
|
|
1222
|
+
[self toggleRegularStyle:[BoldStyle getType]];
|
|
1223
|
+
} else if ([commandName isEqualToString:@"toggleItalic"]) {
|
|
1224
|
+
[self toggleRegularStyle:[ItalicStyle getType]];
|
|
1225
|
+
} else if ([commandName isEqualToString:@"toggleUnderline"]) {
|
|
1226
|
+
[self toggleRegularStyle:[UnderlineStyle getType]];
|
|
1227
|
+
} else if ([commandName isEqualToString:@"toggleStrikeThrough"]) {
|
|
1228
|
+
[self toggleRegularStyle:[StrikethroughStyle getType]];
|
|
1229
|
+
} else if ([commandName isEqualToString:@"toggleInlineCode"]) {
|
|
1230
|
+
[self toggleRegularStyle:[InlineCodeStyle getType]];
|
|
1231
|
+
} else if ([commandName isEqualToString:@"addLink"]) {
|
|
1232
|
+
NSInteger start = [((NSNumber *)args[0]) integerValue];
|
|
1233
|
+
NSInteger end = [((NSNumber *)args[1]) integerValue];
|
|
1234
|
+
NSString *text = (NSString *)args[2];
|
|
1235
|
+
NSString *url = (NSString *)args[3];
|
|
1236
|
+
[self addLinkAt:start end:end text:text url:url];
|
|
1237
|
+
} else if ([commandName isEqualToString:@"removeLink"]) {
|
|
1238
|
+
NSInteger start = [((NSNumber *)args[0]) integerValue];
|
|
1239
|
+
NSInteger end = [((NSNumber *)args[1]) integerValue];
|
|
1240
|
+
[self removeLinkAt:start end:end];
|
|
1241
|
+
} else if ([commandName isEqualToString:@"addMention"]) {
|
|
1242
|
+
NSString *indicator = (NSString *)args[0];
|
|
1243
|
+
NSString *text = (NSString *)args[1];
|
|
1244
|
+
NSString *attributes = (NSString *)args[2];
|
|
1245
|
+
[self addMention:indicator text:text attributes:attributes];
|
|
1246
|
+
} else if ([commandName isEqualToString:@"startMention"]) {
|
|
1247
|
+
NSString *indicator = (NSString *)args[0];
|
|
1248
|
+
[self startMentionWithIndicator:indicator];
|
|
1249
|
+
} else if ([commandName isEqualToString:@"toggleH1"]) {
|
|
1250
|
+
[self toggleRegularStyle:[H1Style getType]];
|
|
1251
|
+
} else if ([commandName isEqualToString:@"toggleH2"]) {
|
|
1252
|
+
[self toggleRegularStyle:[H2Style getType]];
|
|
1253
|
+
} else if ([commandName isEqualToString:@"toggleH3"]) {
|
|
1254
|
+
[self toggleRegularStyle:[H3Style getType]];
|
|
1255
|
+
} else if ([commandName isEqualToString:@"toggleH4"]) {
|
|
1256
|
+
[self toggleRegularStyle:[H4Style getType]];
|
|
1257
|
+
} else if ([commandName isEqualToString:@"toggleH5"]) {
|
|
1258
|
+
[self toggleRegularStyle:[H5Style getType]];
|
|
1259
|
+
} else if ([commandName isEqualToString:@"toggleH6"]) {
|
|
1260
|
+
[self toggleRegularStyle:[H6Style getType]];
|
|
1261
|
+
} else if ([commandName isEqualToString:@"toggleUnorderedList"]) {
|
|
1262
|
+
[self toggleRegularStyle:[UnorderedListStyle getType]];
|
|
1263
|
+
} else if ([commandName isEqualToString:@"toggleOrderedList"]) {
|
|
1264
|
+
[self toggleRegularStyle:[OrderedListStyle getType]];
|
|
1265
|
+
} else if ([commandName isEqualToString:@"toggleCheckboxList"]) {
|
|
1266
|
+
BOOL checked = [args[0] boolValue];
|
|
1267
|
+
[self toggleCheckboxList:checked];
|
|
1268
|
+
} else if ([commandName isEqualToString:@"toggleBlockQuote"]) {
|
|
1269
|
+
[self toggleRegularStyle:[BlockQuoteStyle getType]];
|
|
1270
|
+
} else if ([commandName isEqualToString:@"toggleCodeBlock"]) {
|
|
1271
|
+
[self toggleRegularStyle:[CodeBlockStyle getType]];
|
|
1272
|
+
} else if ([commandName isEqualToString:@"addImage"]) {
|
|
1273
|
+
NSString *uri = (NSString *)args[0];
|
|
1274
|
+
CGFloat imgWidth = [(NSNumber *)args[1] floatValue];
|
|
1275
|
+
CGFloat imgHeight = [(NSNumber *)args[2] floatValue];
|
|
1276
|
+
|
|
1277
|
+
[self addImage:uri width:imgWidth height:imgHeight];
|
|
1278
|
+
} else if ([commandName isEqualToString:@"setSelection"]) {
|
|
1279
|
+
NSInteger start = [((NSNumber *)args[0]) integerValue];
|
|
1280
|
+
NSInteger end = [((NSNumber *)args[1]) integerValue];
|
|
1281
|
+
[self setCustomSelection:start end:end];
|
|
1282
|
+
} else if ([commandName isEqualToString:@"requestHTML"]) {
|
|
1283
|
+
NSInteger requestId = [((NSNumber *)args[0]) integerValue];
|
|
1284
|
+
[self requestHTML:requestId];
|
|
1285
|
+
} else if ([commandName isEqualToString:@"setTextAlignment"]) {
|
|
1286
|
+
NSString *alignmentString = (NSString *)args[0];
|
|
1287
|
+
|
|
1288
|
+
AlignmentStyle *alignmentStyle = stylesDict[@([AlignmentStyle getType])];
|
|
1289
|
+
[alignmentStyle
|
|
1290
|
+
addAlignment:[AlignmentUtils stringToAlignment:alignmentString]
|
|
1291
|
+
range:textView.selectedRange
|
|
1292
|
+
withTyping:YES
|
|
1293
|
+
withDirtyRange:YES];
|
|
1294
|
+
|
|
1295
|
+
[self anyTextMayHaveBeenModified];
|
|
1296
|
+
if (!_placeholderLabel.isHidden) {
|
|
1297
|
+
[self refreshPlaceholderLabelStyles];
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
- (std::shared_ptr<EnrichedTextInputViewEventEmitter>)getEventEmitter {
|
|
1303
|
+
if (_eventEmitter != nullptr && !blockEmitting) {
|
|
1304
|
+
auto emitter =
|
|
1305
|
+
static_cast<const EnrichedTextInputViewEventEmitter &>(*_eventEmitter);
|
|
1306
|
+
return std::make_shared<EnrichedTextInputViewEventEmitter>(emitter);
|
|
1307
|
+
} else {
|
|
1308
|
+
return nullptr;
|
|
1309
|
+
}
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
- (void)blur {
|
|
1313
|
+
[textView reactBlur];
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
- (void)focus {
|
|
1317
|
+
[textView reactFocus];
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
- (void)setValue:(NSString *)value {
|
|
1321
|
+
NSString *initiallyProcessedHtml = [parser initiallyProcessHtml:value];
|
|
1322
|
+
if (initiallyProcessedHtml == nullptr) {
|
|
1323
|
+
// reset the text first and reset typing attributes
|
|
1324
|
+
textView.text = @"";
|
|
1325
|
+
textView.typingAttributes = defaultTypingAttributes;
|
|
1326
|
+
// set new text
|
|
1327
|
+
textView.text = value;
|
|
1328
|
+
} else {
|
|
1329
|
+
// we've got some seemingly proper html
|
|
1330
|
+
[parser replaceWholeFromHtml:initiallyProcessedHtml];
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
// set selectedRange and check for changes
|
|
1334
|
+
textView.selectedRange = NSRange(textView.textStorage.string.length, 0);
|
|
1335
|
+
[self anyTextMayHaveBeenModified];
|
|
1336
|
+
}
|
|
1337
|
+
|
|
1338
|
+
- (void)setCustomSelection:(NSInteger)visibleStart end:(NSInteger)visibleEnd {
|
|
1339
|
+
NSString *text = textView.textStorage.string;
|
|
1340
|
+
|
|
1341
|
+
NSUInteger actualStart = [self getActualIndex:visibleStart text:text];
|
|
1342
|
+
NSUInteger actualEnd = [self getActualIndex:visibleEnd text:text];
|
|
1343
|
+
|
|
1344
|
+
textView.selectedRange = NSMakeRange(actualStart, actualEnd - actualStart);
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
// Helper: Walks through the string skipping ZWSPs to find the Nth visible
|
|
1348
|
+
// character
|
|
1349
|
+
- (NSUInteger)getActualIndex:(NSInteger)visibleIndex text:(NSString *)text {
|
|
1350
|
+
NSUInteger currentVisibleCount = 0;
|
|
1351
|
+
NSUInteger actualIndex = 0;
|
|
1352
|
+
|
|
1353
|
+
while (actualIndex < text.length) {
|
|
1354
|
+
if (currentVisibleCount == visibleIndex) {
|
|
1355
|
+
return actualIndex;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
// If the current char is not a hidden space, it counts towards our visible
|
|
1359
|
+
// index.
|
|
1360
|
+
if ([text characterAtIndex:actualIndex] != 0x200B) {
|
|
1361
|
+
currentVisibleCount++;
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
actualIndex++;
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
return actualIndex;
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
- (void)emitOnSubmitEdittingEvent {
|
|
1371
|
+
auto emitter = [self getEventEmitter];
|
|
1372
|
+
if (emitter != nullptr) {
|
|
1373
|
+
NSString *stringToBeEmitted = [[textView.textStorage.string
|
|
1374
|
+
stringByReplacingOccurrencesOfString:@"\u200B"
|
|
1375
|
+
withString:@""] copy];
|
|
1376
|
+
|
|
1377
|
+
emitter->onSubmitEditing({
|
|
1378
|
+
.text = [stringToBeEmitted toCppString],
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
- (void)emitOnLinkDetectedEvent:(LinkData *)linkData range:(NSRange)range {
|
|
1384
|
+
auto emitter = [self getEventEmitter];
|
|
1385
|
+
if (emitter != nullptr) {
|
|
1386
|
+
// update recently active link info
|
|
1387
|
+
_recentlyActiveLinkData = linkData;
|
|
1388
|
+
_recentlyActiveLinkRange = range;
|
|
1389
|
+
|
|
1390
|
+
emitter->onLinkDetected({
|
|
1391
|
+
.text = [linkData.text toCppString],
|
|
1392
|
+
.url = [linkData.url toCppString],
|
|
1393
|
+
.start = static_cast<int>(range.location),
|
|
1394
|
+
.end = static_cast<int>(range.location + range.length),
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
- (void)emitOnPasteImagesEvent:(NSArray<NSDictionary *> *)images {
|
|
1400
|
+
auto emitter = [self getEventEmitter];
|
|
1401
|
+
if (emitter != nullptr) {
|
|
1402
|
+
std::vector<EnrichedTextInputViewEventEmitter::OnPasteImagesImages>
|
|
1403
|
+
imagesVector;
|
|
1404
|
+
imagesVector.reserve(images.count);
|
|
1405
|
+
|
|
1406
|
+
for (NSDictionary *img in images) {
|
|
1407
|
+
NSString *uri = img[@"uri"];
|
|
1408
|
+
NSString *type = img[@"type"];
|
|
1409
|
+
double width = [img[@"width"] doubleValue];
|
|
1410
|
+
double height = [img[@"height"] doubleValue];
|
|
1411
|
+
|
|
1412
|
+
EnrichedTextInputViewEventEmitter::OnPasteImagesImages imageStruct = {
|
|
1413
|
+
.uri = [uri toCppString],
|
|
1414
|
+
.type = [type toCppString],
|
|
1415
|
+
.width = width,
|
|
1416
|
+
.height = height};
|
|
1417
|
+
|
|
1418
|
+
imagesVector.push_back(imageStruct);
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
emitter->onPasteImages({.images = imagesVector});
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
|
|
1425
|
+
- (void)emitOnMentionDetectedEvent:(NSString *)text
|
|
1426
|
+
indicator:(NSString *)indicator
|
|
1427
|
+
attributes:(NSString *)attributes {
|
|
1428
|
+
auto emitter = [self getEventEmitter];
|
|
1429
|
+
if (emitter != nullptr) {
|
|
1430
|
+
emitter->onMentionDetected({.text = [text toCppString],
|
|
1431
|
+
.indicator = [indicator toCppString],
|
|
1432
|
+
.payload = [attributes toCppString]});
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1436
|
+
- (void)emitOnMentionEvent:(NSString *)indicator text:(NSString *)text {
|
|
1437
|
+
auto emitter = [self getEventEmitter];
|
|
1438
|
+
if (emitter != nullptr) {
|
|
1439
|
+
if (text != nullptr) {
|
|
1440
|
+
folly::dynamic fdStr = [text toCppString];
|
|
1441
|
+
emitter->onMention({.indicator = [indicator toCppString], .text = fdStr});
|
|
1442
|
+
} else {
|
|
1443
|
+
folly::dynamic nul = nullptr;
|
|
1444
|
+
emitter->onMention({.indicator = [indicator toCppString], .text = nul});
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
- (void)tryEmittingOnChangeHtmlEvent {
|
|
1450
|
+
if (!_emitHtml || textView.markedTextRange != nullptr) {
|
|
1451
|
+
return;
|
|
1452
|
+
}
|
|
1453
|
+
auto emitter = [self getEventEmitter];
|
|
1454
|
+
if (emitter != nullptr) {
|
|
1455
|
+
NSString *htmlOutput = [HtmlParser
|
|
1456
|
+
parseToHtmlFromRange:NSMakeRange(0, textView.textStorage.string.length)
|
|
1457
|
+
host:self];
|
|
1458
|
+
// make sure html really changed
|
|
1459
|
+
if (![htmlOutput isEqualToString:_recentlyEmittedHtml]) {
|
|
1460
|
+
_recentlyEmittedHtml = htmlOutput;
|
|
1461
|
+
emitter->onChangeHtml({.value = [htmlOutput toCppString]});
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
- (void)requestHTML:(NSInteger)requestId {
|
|
1467
|
+
auto emitter = [self getEventEmitter];
|
|
1468
|
+
if (emitter != nullptr) {
|
|
1469
|
+
@try {
|
|
1470
|
+
NSString *htmlOutput = [HtmlParser
|
|
1471
|
+
parseToHtmlFromRange:NSMakeRange(0,
|
|
1472
|
+
textView.textStorage.string.length)
|
|
1473
|
+
host:self];
|
|
1474
|
+
emitter->onRequestHtmlResult({.requestId = static_cast<int>(requestId),
|
|
1475
|
+
.html = [htmlOutput toCppString]});
|
|
1476
|
+
} @catch (NSException *exception) {
|
|
1477
|
+
emitter->onRequestHtmlResult({.requestId = static_cast<int>(requestId),
|
|
1478
|
+
.html = folly::dynamic(nullptr)});
|
|
1479
|
+
}
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
- (void)emitOnKeyPressEvent:(NSString *)key {
|
|
1484
|
+
auto emitter = [self getEventEmitter];
|
|
1485
|
+
if (emitter != nullptr) {
|
|
1486
|
+
emitter->onInputKeyPress({.key = [key toCppString]});
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// MARK: - Styles manipulation
|
|
1491
|
+
|
|
1492
|
+
- (void)toggleRegularStyle:(StyleType)type {
|
|
1493
|
+
StyleBase *style = stylesDict[@(type)];
|
|
1494
|
+
NSRange range = textView.selectedRange;
|
|
1495
|
+
if ([style isParagraph]) {
|
|
1496
|
+
range = [textView.textStorage.string paragraphRangeForRange:range];
|
|
1497
|
+
}
|
|
1498
|
+
if ([StyleUtils handleStyleBlocksAndConflicts:type
|
|
1499
|
+
range:range
|
|
1500
|
+
forHost:self]) {
|
|
1501
|
+
[style toggle:range];
|
|
1502
|
+
[self anyTextMayHaveBeenModified];
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
|
|
1506
|
+
- (void)toggleCheckboxList:(BOOL)checked {
|
|
1507
|
+
CheckboxListStyle *style =
|
|
1508
|
+
(CheckboxListStyle *)stylesDict[@([CheckboxListStyle getType])];
|
|
1509
|
+
if (style == nullptr) {
|
|
1510
|
+
return;
|
|
1511
|
+
}
|
|
1512
|
+
NSRange range = [textView.textStorage.string
|
|
1513
|
+
paragraphRangeForRange:textView.selectedRange];
|
|
1514
|
+
if ([StyleUtils handleStyleBlocksAndConflicts:[CheckboxListStyle getType]
|
|
1515
|
+
range:range
|
|
1516
|
+
forHost:self]) {
|
|
1517
|
+
[style toggleWithChecked:checked range:range];
|
|
1518
|
+
[self anyTextMayHaveBeenModified];
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
- (void)addLinkAt:(NSInteger)start
|
|
1523
|
+
end:(NSInteger)end
|
|
1524
|
+
text:(NSString *)text
|
|
1525
|
+
url:(NSString *)url {
|
|
1526
|
+
LinkStyle *linkStyleClass = (LinkStyle *)stylesDict[@([LinkStyle getType])];
|
|
1527
|
+
if (linkStyleClass == nullptr) {
|
|
1528
|
+
return;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
// translate the output start-end notation to range
|
|
1532
|
+
NSRange linkRange = NSMakeRange(start, end - start);
|
|
1533
|
+
if ([StyleUtils handleStyleBlocksAndConflicts:[LinkStyle getType]
|
|
1534
|
+
range:linkRange
|
|
1535
|
+
forHost:self]) {
|
|
1536
|
+
LinkData *linkData = [[LinkData alloc] init];
|
|
1537
|
+
linkData.text = text;
|
|
1538
|
+
linkData.url = url;
|
|
1539
|
+
linkData.isManual = YES;
|
|
1540
|
+
[linkStyleClass addLink:linkData range:linkRange withSelection:YES];
|
|
1541
|
+
[self anyTextMayHaveBeenModified];
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
- (void)removeLinkAt:(NSInteger)start end:(NSInteger)end {
|
|
1546
|
+
LinkStyle *linkStyleClass = (LinkStyle *)stylesDict[@([LinkStyle getType])];
|
|
1547
|
+
if (linkStyleClass == nullptr) {
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
NSInteger textLength = (NSInteger)textView.textStorage.length;
|
|
1552
|
+
if (start < 0) {
|
|
1553
|
+
start = 0;
|
|
1554
|
+
}
|
|
1555
|
+
if (end > textLength) {
|
|
1556
|
+
end = textLength;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
NSInteger rangeStart = MIN(start, end);
|
|
1560
|
+
NSInteger rangeLength = MAX(start, end) - rangeStart;
|
|
1561
|
+
NSRange linkRange = NSMakeRange(rangeStart, rangeLength);
|
|
1562
|
+
|
|
1563
|
+
[linkStyleClass remove:linkRange withDirtyRange:YES];
|
|
1564
|
+
[self anyTextMayHaveBeenModified];
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
- (void)addMention:(NSString *)indicator
|
|
1568
|
+
text:(NSString *)text
|
|
1569
|
+
attributes:(NSString *)attributes {
|
|
1570
|
+
MentionStyle *mentionStyleClass =
|
|
1571
|
+
(MentionStyle *)stylesDict[@([MentionStyle getType])];
|
|
1572
|
+
if (mentionStyleClass == nullptr) {
|
|
1573
|
+
return;
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1576
|
+
NSValue *activeMentionRange = [mentionStyleClass getActiveMentionRange];
|
|
1577
|
+
NSRange rangeToUse = activeMentionRange != nullptr
|
|
1578
|
+
? [activeMentionRange rangeValue]
|
|
1579
|
+
: self.textView.selectedRange;
|
|
1580
|
+
|
|
1581
|
+
if ([StyleUtils handleStyleBlocksAndConflicts:[MentionStyle getType]
|
|
1582
|
+
range:rangeToUse
|
|
1583
|
+
forHost:self]) {
|
|
1584
|
+
[mentionStyleClass addMention:indicator text:text attributes:attributes];
|
|
1585
|
+
[self anyTextMayHaveBeenModified];
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
|
|
1589
|
+
- (void)addImage:(NSString *)uri width:(float)width height:(float)height {
|
|
1590
|
+
ImageStyle *imageStyleClass =
|
|
1591
|
+
(ImageStyle *)stylesDict[@([ImageStyle getType])];
|
|
1592
|
+
if (imageStyleClass == nullptr) {
|
|
1593
|
+
return;
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
if ([StyleUtils handleStyleBlocksAndConflicts:[ImageStyle getType]
|
|
1597
|
+
range:textView.selectedRange
|
|
1598
|
+
forHost:self]) {
|
|
1599
|
+
[imageStyleClass addImage:uri width:width height:height];
|
|
1600
|
+
[self anyTextMayHaveBeenModified];
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
|
|
1604
|
+
- (void)startMentionWithIndicator:(NSString *)indicator {
|
|
1605
|
+
MentionStyle *mentionStyleClass =
|
|
1606
|
+
(MentionStyle *)stylesDict[@([MentionStyle getType])];
|
|
1607
|
+
if (mentionStyleClass == nullptr) {
|
|
1608
|
+
return;
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
if ([StyleUtils handleStyleBlocksAndConflicts:[MentionStyle getType]
|
|
1612
|
+
range:textView.selectedRange
|
|
1613
|
+
forHost:self]) {
|
|
1614
|
+
[mentionStyleClass startMentionWithIndicator:indicator];
|
|
1615
|
+
[self anyTextMayHaveBeenModified];
|
|
1616
|
+
}
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
- (void)manageSelectionBasedChanges {
|
|
1620
|
+
NSString *currentString = [textView.textStorage.string copy];
|
|
1621
|
+
|
|
1622
|
+
MentionStyle *mentionStyleClass =
|
|
1623
|
+
(MentionStyle *)stylesDict[@([MentionStyle getType])];
|
|
1624
|
+
if (mentionStyleClass != nullptr) {
|
|
1625
|
+
// mention editing runs if only a selection was done (no text change)
|
|
1626
|
+
// otherwise we would double-emit with a second call in the
|
|
1627
|
+
// anyTextMayHaveBeenModified method
|
|
1628
|
+
if ([_recentInputString isEqualToString:currentString]) {
|
|
1629
|
+
[mentionStyleClass manageMentionEditing];
|
|
1630
|
+
}
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
// attributes manager handles proper typingAttributes at all times to properly
|
|
1634
|
+
// extend meta-attributes
|
|
1635
|
+
BOOL onlySelectionChanged =
|
|
1636
|
+
textView.selectedRange.length == 0 &&
|
|
1637
|
+
[_recentInputString isEqualToString:currentString];
|
|
1638
|
+
// removedTypingAttributes aren't normally removed during the regular flow
|
|
1639
|
+
// and we do remove them only here - so when we are sure that selection
|
|
1640
|
+
// changed. We want to remember which attributes were removed as long as we
|
|
1641
|
+
// stay at the same position. This prevents a removed attribute from being
|
|
1642
|
+
// re-applied from the preceding character right after we toggled it off.
|
|
1643
|
+
[attributesManager clearRemovedTypingAttributes];
|
|
1644
|
+
[attributesManager
|
|
1645
|
+
manageTypingAttributesWithOnlySelection:onlySelectionChanged];
|
|
1646
|
+
|
|
1647
|
+
// always update active styles
|
|
1648
|
+
[self tryUpdatingActiveStyles];
|
|
1649
|
+
}
|
|
1650
|
+
|
|
1651
|
+
- (void)handleWordModificationBasedChanges:(NSString *)word
|
|
1652
|
+
inRange:(NSRange)range {
|
|
1653
|
+
// manual links refreshing and automatic links detection handling
|
|
1654
|
+
LinkStyle *linkStyle = [stylesDict objectForKey:@([LinkStyle getType])];
|
|
1655
|
+
|
|
1656
|
+
if (linkStyle != nullptr) {
|
|
1657
|
+
// manual links need to be handled first because they can block automatic
|
|
1658
|
+
// links after being refreshed
|
|
1659
|
+
[linkStyle handleManualLinks:word inRange:range];
|
|
1660
|
+
[linkStyle handleAutomaticLinks:word inRange:range];
|
|
1661
|
+
}
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
- (void)anyTextMayHaveBeenModified {
|
|
1665
|
+
// we don't do no text changes when working with iOS marked text
|
|
1666
|
+
if (textView.markedTextRange != nullptr) {
|
|
1667
|
+
return;
|
|
1668
|
+
}
|
|
1669
|
+
|
|
1670
|
+
// zero width space adding or removal
|
|
1671
|
+
[ZeroWidthSpaceUtils handleZeroWidthSpacesInHost:self];
|
|
1672
|
+
|
|
1673
|
+
// emptying input typing attributes management
|
|
1674
|
+
if (textView.textStorage.string.length == 0 &&
|
|
1675
|
+
_recentInputString.length > 0) {
|
|
1676
|
+
// reset typing attribtues
|
|
1677
|
+
textView.typingAttributes = defaultTypingAttributes;
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
// mentions management: removal and editing
|
|
1681
|
+
MentionStyle *mentionStyleClass =
|
|
1682
|
+
(MentionStyle *)stylesDict[@([MentionStyle getType])];
|
|
1683
|
+
if (mentionStyleClass != nullptr) {
|
|
1684
|
+
[mentionStyleClass handleExistingMentions];
|
|
1685
|
+
[mentionStyleClass manageMentionEditing];
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
// placholder management
|
|
1689
|
+
if (!_placeholderLabel.hidden && textView.textStorage.string.length > 0) {
|
|
1690
|
+
[self setPlaceholderLabelShown:NO];
|
|
1691
|
+
} else if (textView.textStorage.string.length == 0 &&
|
|
1692
|
+
_placeholderLabel.hidden) {
|
|
1693
|
+
[self setPlaceholderLabelShown:YES];
|
|
1694
|
+
}
|
|
1695
|
+
|
|
1696
|
+
// modified words handling
|
|
1697
|
+
NSArray *currentDirtyRanges = [attributesManager getDirtyRanges];
|
|
1698
|
+
if (currentDirtyRanges.count > 0) {
|
|
1699
|
+
NSMutableArray *modifiedWords = [[NSMutableArray alloc] init];
|
|
1700
|
+
|
|
1701
|
+
for (NSValue *dirtyRangeValue in currentDirtyRanges) {
|
|
1702
|
+
NSRange dirtyRange = [dirtyRangeValue rangeValue];
|
|
1703
|
+
NSArray *words =
|
|
1704
|
+
[WordsUtils getAffectedWordsFromText:textView.textStorage.string
|
|
1705
|
+
modificationRange:dirtyRange];
|
|
1706
|
+
if (words != nullptr) {
|
|
1707
|
+
[modifiedWords addObjectsFromArray:words];
|
|
1708
|
+
}
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
for (NSDictionary *wordDict in modifiedWords) {
|
|
1712
|
+
NSString *wordText = (NSString *)[wordDict objectForKey:@"word"];
|
|
1713
|
+
NSValue *wordRange = (NSValue *)[wordDict objectForKey:@"range"];
|
|
1714
|
+
|
|
1715
|
+
if (wordText == nullptr || wordRange == nullptr) {
|
|
1716
|
+
continue;
|
|
1717
|
+
}
|
|
1718
|
+
|
|
1719
|
+
[self handleWordModificationBasedChanges:wordText
|
|
1720
|
+
inRange:[wordRange rangeValue]];
|
|
1721
|
+
}
|
|
1722
|
+
}
|
|
1723
|
+
|
|
1724
|
+
if (![textView.textStorage.string isEqualToString:_recentInputString]) {
|
|
1725
|
+
// emit onChangeText event
|
|
1726
|
+
auto emitter = [self getEventEmitter];
|
|
1727
|
+
if (emitter != nullptr && _emitTextChange) {
|
|
1728
|
+
// set the recent input string only if the emitter is defined
|
|
1729
|
+
_recentInputString = [textView.textStorage.string copy];
|
|
1730
|
+
|
|
1731
|
+
// emit string without zero width spaces
|
|
1732
|
+
NSString *stringToBeEmitted = [[textView.textStorage.string
|
|
1733
|
+
stringByReplacingOccurrencesOfString:@"\u200B"
|
|
1734
|
+
withString:@""] copy];
|
|
1735
|
+
|
|
1736
|
+
emitter->onChangeText({.value = [stringToBeEmitted toCppString]});
|
|
1737
|
+
}
|
|
1738
|
+
}
|
|
1739
|
+
// all the visible (not meta) attributes handling in the ranges that could
|
|
1740
|
+
// have changed
|
|
1741
|
+
[attributesManager handleDirtyRangesStyling];
|
|
1742
|
+
// update height on each character change
|
|
1743
|
+
[self tryUpdatingHeight];
|
|
1744
|
+
// update active styles as well
|
|
1745
|
+
[self tryUpdatingActiveStyles];
|
|
1746
|
+
[self layoutAttachments];
|
|
1747
|
+
// update drawing - schedule debounced relayout
|
|
1748
|
+
[self scheduleRelayoutIfNeeded];
|
|
1749
|
+
}
|
|
1750
|
+
|
|
1751
|
+
// Debounced relayout helper - coalesces multiple requests into one per runloop
|
|
1752
|
+
// tick
|
|
1753
|
+
- (void)scheduleRelayoutIfNeeded {
|
|
1754
|
+
// Cancel any previously scheduled invocation to debounce
|
|
1755
|
+
[NSObject cancelPreviousPerformRequestsWithTarget:self
|
|
1756
|
+
selector:@selector(_performRelayout)
|
|
1757
|
+
object:nil];
|
|
1758
|
+
// Schedule on next runloop cycle
|
|
1759
|
+
[self performSelector:@selector(_performRelayout)
|
|
1760
|
+
withObject:nil
|
|
1761
|
+
afterDelay:0];
|
|
1762
|
+
}
|
|
1763
|
+
|
|
1764
|
+
- (void)_performRelayout {
|
|
1765
|
+
if (!textView) {
|
|
1766
|
+
return;
|
|
1767
|
+
}
|
|
1768
|
+
|
|
1769
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1770
|
+
NSRange wholeRange =
|
|
1771
|
+
NSMakeRange(0, self->textView.textStorage.string.length);
|
|
1772
|
+
NSRange actualRange = NSMakeRange(0, 0);
|
|
1773
|
+
[self->textView.layoutManager
|
|
1774
|
+
invalidateLayoutForCharacterRange:wholeRange
|
|
1775
|
+
actualCharacterRange:&actualRange];
|
|
1776
|
+
[self->textView.layoutManager ensureLayoutForCharacterRange:actualRange];
|
|
1777
|
+
[self->textView.layoutManager
|
|
1778
|
+
invalidateDisplayForCharacterRange:wholeRange];
|
|
1779
|
+
|
|
1780
|
+
// We have to explicitly set contentSize
|
|
1781
|
+
// That way textView knows if content overflows and if should be scrollable
|
|
1782
|
+
// We recall measureSize here because value returned from previous
|
|
1783
|
+
// measureSize may not be up-to date at that point
|
|
1784
|
+
CGSize measuredSize = [self measureSize:self->textView.frame.size.width];
|
|
1785
|
+
self->textView.contentSize = measuredSize;
|
|
1786
|
+
});
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
- (void)didMoveToWindow {
|
|
1790
|
+
[super didMoveToWindow];
|
|
1791
|
+
// used to run all lifecycle callbacks
|
|
1792
|
+
[self anyTextMayHaveBeenModified];
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
// MARK: - Delegate methods
|
|
1796
|
+
|
|
1797
|
+
- (void)textViewDidBeginEditing:(UITextView *)textView {
|
|
1798
|
+
auto emitter = [self getEventEmitter];
|
|
1799
|
+
if (emitter != nullptr) {
|
|
1800
|
+
// send onFocus event if allowed
|
|
1801
|
+
if (_emitFocusBlur) {
|
|
1802
|
+
emitter->onInputFocus({});
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
// manage selection changes since textViewDidChangeSelection sometimes doesn't
|
|
1806
|
+
// run on focus
|
|
1807
|
+
[self manageSelectionBasedChanges];
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
- (void)textViewDidEndEditing:(UITextView *)textView {
|
|
1811
|
+
auto emitter = [self getEventEmitter];
|
|
1812
|
+
if (emitter != nullptr && _emitFocusBlur) {
|
|
1813
|
+
// send onBlur event
|
|
1814
|
+
emitter->onInputBlur({});
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
|
|
1818
|
+
- (UIMenu *)textView:(UITextView *)tv
|
|
1819
|
+
editMenuForTextInRange:(NSRange)range
|
|
1820
|
+
suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions
|
|
1821
|
+
API_AVAILABLE(ios(16.0)) {
|
|
1822
|
+
if (_contextMenuItems == nil || _contextMenuItems.count == 0) {
|
|
1823
|
+
return [UIMenu menuWithChildren:suggestedActions];
|
|
1824
|
+
}
|
|
1825
|
+
|
|
1826
|
+
NSMutableArray<UIMenuElement *> *customActions = [NSMutableArray new];
|
|
1827
|
+
|
|
1828
|
+
for (NSString *title in _contextMenuItems) {
|
|
1829
|
+
__weak EnrichedTextInputView *weakSelf = self;
|
|
1830
|
+
|
|
1831
|
+
UIAction *action =
|
|
1832
|
+
[UIAction actionWithTitle:title
|
|
1833
|
+
image:nil
|
|
1834
|
+
identifier:nil
|
|
1835
|
+
handler:^(__kindof UIAction *_Nonnull action) {
|
|
1836
|
+
[weakSelf emitOnContextMenuItemPressEvent:title];
|
|
1837
|
+
}];
|
|
1838
|
+
[customActions addObject:action];
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
[customActions addObjectsFromArray:suggestedActions];
|
|
1842
|
+
return [UIMenu menuWithChildren:customActions];
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
- (void)emitOnContextMenuItemPressEvent:(NSString *)itemText {
|
|
1846
|
+
auto emitter = [self getEventEmitter];
|
|
1847
|
+
if (emitter != nullptr) {
|
|
1848
|
+
NSRange selectedRange = textView.selectedRange;
|
|
1849
|
+
NSString *selectedText = @"";
|
|
1850
|
+
if (selectedRange.length > 0) {
|
|
1851
|
+
selectedText =
|
|
1852
|
+
[textView.textStorage.string substringWithRange:selectedRange];
|
|
1853
|
+
}
|
|
1854
|
+
|
|
1855
|
+
AlignmentStyle *alignmentStyle = stylesDict[@([AlignmentStyle getType])];
|
|
1856
|
+
NSString *currentAlignment = [alignmentStyle getStyleState];
|
|
1857
|
+
|
|
1858
|
+
emitter->onContextMenuItemPress(
|
|
1859
|
+
{.itemText = [itemText toCppString],
|
|
1860
|
+
.selectedText = [selectedText toCppString],
|
|
1861
|
+
.selectionStart = static_cast<int>(selectedRange.location),
|
|
1862
|
+
.selectionEnd =
|
|
1863
|
+
static_cast<int>(selectedRange.location + selectedRange.length),
|
|
1864
|
+
.styleState = {
|
|
1865
|
+
.bold = GET_STYLE_STATE([BoldStyle getType]),
|
|
1866
|
+
.italic = GET_STYLE_STATE([ItalicStyle getType]),
|
|
1867
|
+
.underline = GET_STYLE_STATE([UnderlineStyle getType]),
|
|
1868
|
+
.strikeThrough = GET_STYLE_STATE([StrikethroughStyle getType]),
|
|
1869
|
+
.inlineCode = GET_STYLE_STATE([InlineCodeStyle getType]),
|
|
1870
|
+
.h1 = GET_STYLE_STATE([H1Style getType]),
|
|
1871
|
+
.h2 = GET_STYLE_STATE([H2Style getType]),
|
|
1872
|
+
.h3 = GET_STYLE_STATE([H3Style getType]),
|
|
1873
|
+
.h4 = GET_STYLE_STATE([H4Style getType]),
|
|
1874
|
+
.h5 = GET_STYLE_STATE([H5Style getType]),
|
|
1875
|
+
.h6 = GET_STYLE_STATE([H6Style getType]),
|
|
1876
|
+
.codeBlock = GET_STYLE_STATE([CodeBlockStyle getType]),
|
|
1877
|
+
.blockQuote = GET_STYLE_STATE([BlockQuoteStyle getType]),
|
|
1878
|
+
.orderedList = GET_STYLE_STATE([OrderedListStyle getType]),
|
|
1879
|
+
.unorderedList = GET_STYLE_STATE([UnorderedListStyle getType]),
|
|
1880
|
+
.link = GET_STYLE_STATE([LinkStyle getType]),
|
|
1881
|
+
.image = GET_STYLE_STATE([ImageStyle getType]),
|
|
1882
|
+
.mention = GET_STYLE_STATE([MentionStyle getType]),
|
|
1883
|
+
.checkboxList = GET_STYLE_STATE([CheckboxListStyle getType]),
|
|
1884
|
+
.alignment = [currentAlignment UTF8String]}});
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1888
|
+
- (void)handleKeyPressInRange:(NSString *)text range:(NSRange)range {
|
|
1889
|
+
NSString *key = nil;
|
|
1890
|
+
|
|
1891
|
+
if (text.length == 0) {
|
|
1892
|
+
key = @"Backspace";
|
|
1893
|
+
} else if ([text isEqualToString:@"\n"]) {
|
|
1894
|
+
key = @"Enter";
|
|
1895
|
+
} else if ([text isEqualToString:@"\t"]) {
|
|
1896
|
+
key = @"Tab";
|
|
1897
|
+
} else if (text.length == 1) {
|
|
1898
|
+
key = text;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
if (key != nil) {
|
|
1902
|
+
[self emitOnKeyPressEvent:key];
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
- (bool)textView:(UITextView *)textView
|
|
1907
|
+
shouldChangeTextInRange:(NSRange)range
|
|
1908
|
+
replacementText:(NSString *)text {
|
|
1909
|
+
// Capture the attributes at range.location that are being replaced
|
|
1910
|
+
// (autocorrect / predictive) so didProcessEditing: can re-stamp them onto the
|
|
1911
|
+
// replacement.
|
|
1912
|
+
if (range.length > 0) {
|
|
1913
|
+
_capturedAttributesBeforeChange =
|
|
1914
|
+
[textView.textStorage attributesAtIndex:range.location
|
|
1915
|
+
effectiveRange:NULL];
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
// Check if the user pressed "Enter"
|
|
1919
|
+
if ([text isEqualToString:@"\n"]) {
|
|
1920
|
+
const bool shouldSubmit = [self textInputShouldSubmitOnReturn];
|
|
1921
|
+
const bool shouldReturn = [self textInputShouldReturn];
|
|
1922
|
+
|
|
1923
|
+
if (shouldSubmit) {
|
|
1924
|
+
[self emitOnSubmitEdittingEvent];
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
if (shouldReturn) {
|
|
1928
|
+
[textView endEditing:NO];
|
|
1929
|
+
}
|
|
1930
|
+
|
|
1931
|
+
if (shouldSubmit || shouldReturn) {
|
|
1932
|
+
return NO;
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
[self handleKeyPressInRange:text range:range];
|
|
1937
|
+
|
|
1938
|
+
CheckboxListStyle *cbLStyle =
|
|
1939
|
+
(CheckboxListStyle *)stylesDict[@([CheckboxListStyle getType])];
|
|
1940
|
+
H1Style *h1Style = stylesDict[@([H1Style getType])];
|
|
1941
|
+
H2Style *h2Style = stylesDict[@([H2Style getType])];
|
|
1942
|
+
H3Style *h3Style = stylesDict[@([H3Style getType])];
|
|
1943
|
+
H4Style *h4Style = stylesDict[@([H4Style getType])];
|
|
1944
|
+
H5Style *h5Style = stylesDict[@([H5Style getType])];
|
|
1945
|
+
H6Style *h6Style = stylesDict[@([H6Style getType])];
|
|
1946
|
+
|
|
1947
|
+
// some of the changes these checks do could interfere with later checks and
|
|
1948
|
+
// cause a crash so here we rely on short circuiting evaluation of the logical
|
|
1949
|
+
// expression. Either way it's not possible to have two of them come off at
|
|
1950
|
+
// the same time
|
|
1951
|
+
if (
|
|
1952
|
+
// ZWS backspace handling for paragraph styles
|
|
1953
|
+
[ZeroWidthSpaceUtils handleBackspaceInRange:range
|
|
1954
|
+
replacementText:text
|
|
1955
|
+
host:self] ||
|
|
1956
|
+
[cbLStyle handleNewlinesInRange:range replacementText:text] ||
|
|
1957
|
+
[h1Style handleNewlinesInRange:range replacementText:text] ||
|
|
1958
|
+
[h2Style handleNewlinesInRange:range replacementText:text] ||
|
|
1959
|
+
[h3Style handleNewlinesInRange:range replacementText:text] ||
|
|
1960
|
+
[h4Style handleNewlinesInRange:range replacementText:text] ||
|
|
1961
|
+
[h5Style handleNewlinesInRange:range replacementText:text] ||
|
|
1962
|
+
[h6Style handleNewlinesInRange:range replacementText:text] ||
|
|
1963
|
+
[ParagraphAttributesUtils handleBackspaceInRange:range
|
|
1964
|
+
replacementText:text
|
|
1965
|
+
input:self] ||
|
|
1966
|
+
[ParagraphAttributesUtils handleResetTypingAttributesOnBackspace:range
|
|
1967
|
+
replacementText:text
|
|
1968
|
+
input:self]
|
|
1969
|
+
// Check configurable text shortcuts (block: "# " → h1, inline: `code` →
|
|
1970
|
+
// inline_code)
|
|
1971
|
+
|| [ShortcutsUtils tryHandlingParagraphShortcutsInRange:range
|
|
1972
|
+
replacementText:text
|
|
1973
|
+
input:self] ||
|
|
1974
|
+
[ShortcutsUtils tryHandlingInlineShortcutsInRange:range
|
|
1975
|
+
replacementText:text
|
|
1976
|
+
input:self]
|
|
1977
|
+
// CRITICAL: This callback HAS TO be always evaluated last.
|
|
1978
|
+
//
|
|
1979
|
+
// This function is the "Generic Fallback": if no specific style
|
|
1980
|
+
// claims the backspace action to change its state, only then do we
|
|
1981
|
+
// proceed to physically delete the newline and merge paragraphs.
|
|
1982
|
+
||
|
|
1983
|
+
[ParagraphAttributesUtils handleParagraphStylesMergeOnBackspace:range
|
|
1984
|
+
replacementText:text
|
|
1985
|
+
input:self]) {
|
|
1986
|
+
[self anyTextMayHaveBeenModified];
|
|
1987
|
+
return NO;
|
|
1988
|
+
}
|
|
1989
|
+
|
|
1990
|
+
return YES;
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
- (void)textViewDidChangeSelection:(UITextView *)textView {
|
|
1994
|
+
// emit the event
|
|
1995
|
+
NSString *textAtSelection =
|
|
1996
|
+
[[[NSMutableString alloc] initWithString:textView.textStorage.string]
|
|
1997
|
+
substringWithRange:textView.selectedRange];
|
|
1998
|
+
|
|
1999
|
+
auto emitter = [self getEventEmitter];
|
|
2000
|
+
if (emitter != nullptr) {
|
|
2001
|
+
// iOS range works differently because it specifies location and length
|
|
2002
|
+
// here, start is the location, but end is the first index BEHIND the end.
|
|
2003
|
+
// So a 0 length range will have equal start and end
|
|
2004
|
+
emitter->onChangeSelection(
|
|
2005
|
+
{.start = static_cast<int>(textView.selectedRange.location),
|
|
2006
|
+
.end = static_cast<int>(textView.selectedRange.location +
|
|
2007
|
+
textView.selectedRange.length),
|
|
2008
|
+
.text = [textAtSelection toCppString]});
|
|
2009
|
+
}
|
|
2010
|
+
|
|
2011
|
+
// manage selection changes
|
|
2012
|
+
[self manageSelectionBasedChanges];
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
// this function isn't called always when some text changes (for example setting
|
|
2016
|
+
// link or starting mention with indicator doesn't fire it) so all the logic is
|
|
2017
|
+
// in anyTextMayHaveBeenModified
|
|
2018
|
+
- (void)textViewDidChange:(UITextView *)textView {
|
|
2019
|
+
[self anyTextMayHaveBeenModified];
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
/**
|
|
2023
|
+
* Handles iOS Dynamic Type changes (User changing font size in System
|
|
2024
|
+
* Settings).
|
|
2025
|
+
*
|
|
2026
|
+
* Unlike Android, iOS Views do not automatically rescale existing
|
|
2027
|
+
* NSAttributedStrings when the system font size changes. The text attributes
|
|
2028
|
+
* are static once drawn.
|
|
2029
|
+
*
|
|
2030
|
+
* This method detects the change and performs a "Hard Refresh" of the content.
|
|
2031
|
+
*/
|
|
2032
|
+
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
|
|
2033
|
+
[super traitCollectionDidChange:previousTraitCollection];
|
|
2034
|
+
|
|
2035
|
+
if (!config.allowFontScaling) {
|
|
2036
|
+
return;
|
|
2037
|
+
}
|
|
2038
|
+
|
|
2039
|
+
if (previousTraitCollection.preferredContentSizeCategory ==
|
|
2040
|
+
self.traitCollection.preferredContentSizeCategory) {
|
|
2041
|
+
return;
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
[config invalidateFonts];
|
|
2045
|
+
|
|
2046
|
+
NSMutableDictionary *newTypingAttrs = [defaultTypingAttributes mutableCopy];
|
|
2047
|
+
newTypingAttrs[NSFontAttributeName] = [config primaryFont];
|
|
2048
|
+
|
|
2049
|
+
defaultTypingAttributes = newTypingAttrs;
|
|
2050
|
+
textView.typingAttributes = defaultTypingAttributes;
|
|
2051
|
+
|
|
2052
|
+
[self refreshPlaceholderLabelStyles];
|
|
2053
|
+
|
|
2054
|
+
NSRange prevSelectedRange = textView.selectedRange;
|
|
2055
|
+
|
|
2056
|
+
NSString *currentHtml = [HtmlParser
|
|
2057
|
+
parseToHtmlFromRange:NSMakeRange(0, textView.textStorage.string.length)
|
|
2058
|
+
host:self];
|
|
2059
|
+
NSString *initiallyProcessedHtml = [parser initiallyProcessHtml:currentHtml];
|
|
2060
|
+
[parser replaceWholeFromHtml:initiallyProcessedHtml];
|
|
2061
|
+
|
|
2062
|
+
textView.selectedRange = prevSelectedRange;
|
|
2063
|
+
[self anyTextMayHaveBeenModified];
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
- (void)onTextBlockTap:(TextBlockTapGestureRecognizer *)gr {
|
|
2067
|
+
if (gr.state != UIGestureRecognizerStateEnded)
|
|
2068
|
+
return;
|
|
2069
|
+
if (![self->textView isFirstResponder]) {
|
|
2070
|
+
[self->textView becomeFirstResponder];
|
|
2071
|
+
}
|
|
2072
|
+
|
|
2073
|
+
switch (gr.tapKind) {
|
|
2074
|
+
|
|
2075
|
+
case TextBlockTapKindCheckbox: {
|
|
2076
|
+
CheckboxListStyle *checkboxStyle =
|
|
2077
|
+
(CheckboxListStyle *)stylesDict[@([CheckboxListStyle getType])];
|
|
2078
|
+
|
|
2079
|
+
if (checkboxStyle) {
|
|
2080
|
+
NSUInteger charIndex = (NSUInteger)gr.characterIndex;
|
|
2081
|
+
[checkboxStyle toggleCheckedAt:charIndex withDirtyRange:YES];
|
|
2082
|
+
[self anyTextMayHaveBeenModified];
|
|
2083
|
+
|
|
2084
|
+
NSString *fullText = textView.textStorage.string;
|
|
2085
|
+
NSRange paragraphRange =
|
|
2086
|
+
[fullText paragraphRangeForRange:NSMakeRange(charIndex, 0)];
|
|
2087
|
+
NSUInteger endOfLineIndex = NSMaxRange(paragraphRange);
|
|
2088
|
+
|
|
2089
|
+
// If the paragraph ends with a newline, step back by 1 so the cursor
|
|
2090
|
+
// stays on the current line instead of jumping to the next one.
|
|
2091
|
+
if (endOfLineIndex > 0 && endOfLineIndex <= fullText.length) {
|
|
2092
|
+
unichar lastChar = [fullText characterAtIndex:endOfLineIndex - 1];
|
|
2093
|
+
if ([[NSCharacterSet newlineCharacterSet] characterIsMember:lastChar]) {
|
|
2094
|
+
endOfLineIndex--;
|
|
2095
|
+
}
|
|
2096
|
+
}
|
|
2097
|
+
|
|
2098
|
+
// Move the cursor to the end of the currently tapped checkbox line.
|
|
2099
|
+
// Without this, the cursor may remain at its previous position,
|
|
2100
|
+
// potentially inside a different checkbox line.
|
|
2101
|
+
textView.selectedRange = NSMakeRange(endOfLineIndex, 0);
|
|
2102
|
+
}
|
|
2103
|
+
break;
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2106
|
+
default:
|
|
2107
|
+
break;
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
- (void)textStorage:(NSTextStorage *)textStorage
|
|
2112
|
+
didProcessEditing:(NSTextStorageEditActions)editedMask
|
|
2113
|
+
range:(NSRange)editedRange
|
|
2114
|
+
changeInLength:(NSInteger)delta {
|
|
2115
|
+
// iOS replacing quick double space with ". " attributes fix.
|
|
2116
|
+
[DotReplacementUtils handleDotReplacement:self
|
|
2117
|
+
textStorage:textStorage
|
|
2118
|
+
editedMask:editedMask
|
|
2119
|
+
editedRange:editedRange
|
|
2120
|
+
delta:delta];
|
|
2121
|
+
|
|
2122
|
+
// Needed dirty ranges adjustments happen on every character edition.
|
|
2123
|
+
if ((editedMask & NSTextStorageEditedCharacters) != 0) {
|
|
2124
|
+
// Re-stamp custom meta-attributes captured in shouldChangeTextInRange: onto
|
|
2125
|
+
// the new range so autocorrect/predictive replacements keep their styling.
|
|
2126
|
+
if (_capturedAttributesBeforeChange != nil) {
|
|
2127
|
+
// Skip while an IME composition is in progress; restamp on commit.
|
|
2128
|
+
if (textView.markedTextRange == nil) {
|
|
2129
|
+
NSSet *customKeys = [attributesManager customAttributesKeys];
|
|
2130
|
+
for (NSString *key in _capturedAttributesBeforeChange) {
|
|
2131
|
+
if ([customKeys containsObject:key]) {
|
|
2132
|
+
[textStorage addAttribute:key
|
|
2133
|
+
value:_capturedAttributesBeforeChange[key]
|
|
2134
|
+
range:editedRange];
|
|
2135
|
+
}
|
|
2136
|
+
}
|
|
2137
|
+
}
|
|
2138
|
+
|
|
2139
|
+
// Clear after consuming
|
|
2140
|
+
_capturedAttributesBeforeChange = nil;
|
|
2141
|
+
}
|
|
2142
|
+
// Always try shifting dirty ranges (happens only with delta != 0).
|
|
2143
|
+
[attributesManager shiftDirtyRangesWithEditedRange:editedRange
|
|
2144
|
+
changeInLength:delta];
|
|
2145
|
+
|
|
2146
|
+
// Add dirty ranges. We also add zero-length ranges because they are useful
|
|
2147
|
+
// for word modification-based changes.
|
|
2148
|
+
[attributesManager addDirtyRange:editedRange];
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
// MARK: - Media attachments delegate
|
|
2153
|
+
|
|
2154
|
+
- (void)mediaAttachmentDidUpdate:(MediaAttachment *)attachment {
|
|
2155
|
+
[AttachmentLayoutUtils handleAttachmentUpdate:attachment
|
|
2156
|
+
textView:textView
|
|
2157
|
+
onLayoutBlock:^{
|
|
2158
|
+
[self layoutAttachments];
|
|
2159
|
+
}];
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
// MARK: - Image/GIF Overlay Management
|
|
2163
|
+
|
|
2164
|
+
- (void)layoutAttachments {
|
|
2165
|
+
_attachmentViews =
|
|
2166
|
+
[AttachmentLayoutUtils layoutAttachmentsInTextView:textView
|
|
2167
|
+
config:config
|
|
2168
|
+
existingViews:_attachmentViews];
|
|
2169
|
+
}
|
|
2170
|
+
@end
|