react-native-enriched-html 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/java/com/swmansion/enriched/common/EnrichedSpanFlags.kt +39 -0
- package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedParser.java +43 -20
- package/android/src/main/java/com/swmansion/enriched/common/parser/EnrichedSpanFactory.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextSpanFactory.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/text/EnrichedTextView.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputView.kt +8 -5
- package/android/src/main/java/com/swmansion/enriched/textinput/events/MentionHandler.kt +12 -2
- package/android/src/main/java/com/swmansion/enriched/textinput/events/OnMentionEvent.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/AlignmentStyles.kt +6 -7
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/InlineStyles.kt +13 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ListStyles.kt +4 -3
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParagraphStyles.kt +4 -3
- package/android/src/main/java/com/swmansion/enriched/textinput/styles/ParametrizedStyles.kt +38 -11
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSelection.kt +2 -0
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/EnrichedSpannable.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/textinput/utils/Utils.kt +2 -1
- package/cpp/parser/GumboNormalizer.c +61 -3
- package/cpp/tests/GumboParserTest.cpp +54 -0
- package/ios/EnrichedTextInputView.mm +37 -25
- package/ios/extensions/LayoutManagerExtension.mm +73 -62
- package/ios/extensions/StringExtension.mm +75 -0
- package/ios/htmlParser/HtmlParser.h +4 -1
- package/ios/htmlParser/HtmlParser.mm +23 -5
- package/ios/inputHtmlParser/InputHtmlParser.mm +6 -3
- package/ios/interfaces/StyleHeaders.h +4 -0
- package/ios/styles/LinkStyle.mm +20 -34
- package/ios/styles/MentionStyle.mm +55 -4
- package/ios/textHtmlParser/TextHtmlParser.mm +2 -1
- package/ios/utils/ParagraphAttributesUtils.mm +7 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/utils/expandMentionStylesForIndicators.js +2 -3
- package/lib/module/utils/expandMentionStylesForIndicators.js.map +1 -1
- package/lib/module/utils/normalizeHtmlStyle.js +1 -1
- package/lib/module/utils/normalizeHtmlStyle.js.map +1 -1
- package/lib/module/web/EnrichedText.css +343 -0
- package/lib/module/web/EnrichedText.js +55 -0
- package/lib/module/web/EnrichedText.js.map +1 -0
- package/lib/module/web/EnrichedTextInput.js +54 -52
- package/lib/module/web/EnrichedTextInput.js.map +1 -1
- package/lib/module/web/constants/brokenImageGlyph.js +9 -0
- package/lib/module/web/constants/brokenImageGlyph.js.map +1 -0
- package/lib/module/web/constants/classNames.js +5 -0
- package/lib/module/web/constants/classNames.js.map +1 -0
- package/lib/module/web/emitLinkDetected.js +12 -0
- package/lib/module/web/emitLinkDetected.js.map +1 -0
- package/lib/module/web/formats/EnrichedBlockquote.js +1 -1
- package/lib/module/web/formats/EnrichedBlockquote.js.map +1 -1
- package/lib/module/web/formats/EnrichedCheckboxList.js +2 -2
- package/lib/module/web/formats/EnrichedCheckboxList.js.map +1 -1
- package/lib/module/web/formats/EnrichedCodeBlock.js +1 -1
- package/lib/module/web/formats/EnrichedCodeBlock.js.map +1 -1
- package/lib/module/web/formats/EnrichedHeading.js +1 -1
- package/lib/module/web/formats/EnrichedHeading.js.map +1 -1
- package/lib/module/web/formats/EnrichedImageNodeView.js +1 -1
- package/lib/module/web/formats/EnrichedImageNodeView.js.map +1 -1
- package/lib/module/web/formats/EnrichedLink.js +43 -3
- package/lib/module/web/formats/EnrichedLink.js.map +1 -1
- package/lib/module/web/formats/EnrichedOrderedList.js +2 -2
- package/lib/module/web/formats/EnrichedOrderedList.js.map +1 -1
- package/lib/module/web/formats/EnrichedTextAlign.js +82 -0
- package/lib/module/web/formats/EnrichedTextAlign.js.map +1 -0
- package/lib/module/web/formats/EnrichedUnorderedList.js +2 -2
- package/lib/module/web/formats/EnrichedUnorderedList.js.map +1 -1
- package/lib/module/web/formats/applyWrappingListToSelection.js +3 -2
- package/lib/module/web/formats/applyWrappingListToSelection.js.map +1 -1
- package/lib/module/web/formats/formatRules.js +39 -3
- package/lib/module/web/formats/formatRules.js.map +1 -1
- package/lib/module/web/formats/listKeyboard.js +4 -1
- package/lib/module/web/formats/listKeyboard.js.map +1 -1
- package/lib/module/web/normalization/checkboxHtmlNormalizer.js.map +1 -0
- package/lib/module/web/normalization/htmlNormalizer.js +514 -0
- package/lib/module/web/normalization/htmlNormalizer.js.map +1 -0
- package/lib/module/web/normalization/prepareHtmlForWeb.js +78 -0
- package/lib/module/web/normalization/prepareHtmlForWeb.js.map +1 -0
- package/lib/module/web/{tiptapHtmlNormalizer.js → normalization/tiptapHtmlNormalizer.js} +6 -2
- package/lib/module/web/normalization/tiptapHtmlNormalizer.js.map +1 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js +27 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/autolinkRegex.js.map +1 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/index.js +178 -0
- package/lib/module/web/pmPlugins/AutolinkPlugin/index.js.map +1 -0
- package/lib/module/web/pmPlugins/MentionPlugin/index.js +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/index.js.map +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js +23 -4
- package/lib/module/web/pmPlugins/MentionPlugin/makeMentionPluginState.js.map +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/setMention.js +17 -18
- package/lib/module/web/pmPlugins/MentionPlugin/setMention.js.map +1 -1
- package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js +105 -0
- package/lib/module/web/pmPlugins/MentionPlugin/useMentionEvents.js.map +1 -0
- package/lib/module/web/pmPlugins/ShortcutPlugin.js +11 -0
- package/lib/module/web/pmPlugins/ShortcutPlugin.js.map +1 -1
- package/lib/module/web/pmPlugins/StrictMarksPlugin.js +2 -0
- package/lib/module/web/pmPlugins/StrictMarksPlugin.js.map +1 -1
- package/lib/module/web/pmPlugins/StripBoldInStyledHeadingsPlugin.js.map +1 -1
- package/lib/module/web/sanitization/htmlSanitizer.js +10 -0
- package/lib/module/web/sanitization/htmlSanitizer.js.map +1 -0
- package/lib/module/web/styleConversion/buildMentionRulesCSS.js +12 -6
- package/lib/module/web/styleConversion/buildMentionRulesCSS.js.map +1 -1
- package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js +159 -0
- package/lib/module/web/styleConversion/enrichedBaseStyleToCSSProperties.js.map +1 -0
- package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js +3 -155
- package/lib/module/web/styleConversion/enrichedInputStyleToCSSProperties.js.map +1 -1
- package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js +44 -0
- package/lib/module/web/styleConversion/enrichedTextStyleToCSSProperties.js.map +1 -0
- package/lib/module/web/styleConversion/{enrichedInputThemingToCSSProperties.js → enrichedThemingToCSSProperties.js} +3 -3
- package/lib/module/web/styleConversion/enrichedThemingToCSSProperties.js.map +1 -0
- package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js +87 -64
- package/lib/module/web/styleConversion/htmlStyleToCSSVariables.js.map +1 -1
- package/lib/module/web/styleConversion/inlineImageCSSVariables.js +7 -0
- package/lib/module/web/styleConversion/inlineImageCSSVariables.js.map +1 -0
- package/lib/module/web/useImageErrorFallback.js +33 -0
- package/lib/module/web/useImageErrorFallback.js.map +1 -0
- package/lib/module/web/useOnChangeHtml.js +1 -1
- package/lib/module/web/useOnChangeHtml.js.map +1 -1
- package/lib/module/web/useOnChangeState.js +6 -3
- package/lib/module/web/useOnChangeState.js.map +1 -1
- package/lib/module/web/useOnLinkDetected.js +32 -45
- package/lib/module/web/useOnLinkDetected.js.map +1 -1
- package/lib/module/web/usePressInteractions.js +19 -0
- package/lib/module/web/usePressInteractions.js.map +1 -0
- package/lib/module/web/useStableRef.js +13 -0
- package/lib/module/web/useStableRef.js.map +1 -0
- package/lib/typescript/src/index.d.ts +1 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/EnrichedText.d.ts +1 -1
- package/lib/typescript/src/native/EnrichedText.d.ts.map +1 -1
- package/lib/typescript/src/native/EnrichedTextInput.d.ts +1 -1
- package/lib/typescript/src/native/EnrichedTextInput.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +1 -1
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts +1 -1
- package/lib/typescript/src/utils/expandMentionStylesForIndicators.d.ts.map +1 -1
- package/lib/typescript/src/utils/normalizeHtmlStyle.d.ts.map +1 -1
- package/lib/typescript/src/web/EnrichedText.d.ts +4 -0
- package/lib/typescript/src/web/EnrichedText.d.ts.map +1 -0
- package/lib/typescript/src/web/EnrichedTextInput.d.ts +2 -2
- package/lib/typescript/src/web/EnrichedTextInput.d.ts.map +1 -1
- package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts +3 -0
- package/lib/typescript/src/web/constants/brokenImageGlyph.d.ts.map +1 -0
- package/lib/typescript/src/web/constants/classNames.d.ts +3 -0
- package/lib/typescript/src/web/constants/classNames.d.ts.map +1 -0
- package/lib/typescript/src/web/emitLinkDetected.d.ts +10 -0
- package/lib/typescript/src/web/emitLinkDetected.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedBlockquote.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedCheckboxList.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedCodeBlock.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedHeading.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts +1 -1
- package/lib/typescript/src/web/formats/EnrichedImageNodeView.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedLink.d.ts +4 -1
- package/lib/typescript/src/web/formats/EnrichedLink.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedOrderedList.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts +2 -0
- package/lib/typescript/src/web/formats/EnrichedTextAlign.d.ts.map +1 -0
- package/lib/typescript/src/web/formats/EnrichedUnorderedList.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/applyWrappingListToSelection.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/formatRules.d.ts +3 -1
- package/lib/typescript/src/web/formats/formatRules.d.ts.map +1 -1
- package/lib/typescript/src/web/formats/listKeyboard.d.ts.map +1 -1
- package/lib/typescript/src/web/normalization/checkboxHtmlNormalizer.d.ts.map +1 -0
- package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts +2 -0
- package/lib/typescript/src/web/normalization/htmlNormalizer.d.ts.map +1 -0
- package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts +2 -0
- package/lib/typescript/src/web/normalization/prepareHtmlForWeb.d.ts.map +1 -0
- package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts +3 -0
- package/lib/typescript/src/web/normalization/tiptapHtmlNormalizer.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts +7 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts +8 -0
- package/lib/typescript/src/web/pmPlugins/AutolinkPlugin/index.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/index.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/setMention.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts +4 -0
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/useMentionEvents.d.ts.map +1 -0
- package/lib/typescript/src/web/pmPlugins/ShortcutPlugin.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/StrictMarksPlugin.d.ts.map +1 -1
- package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts +6 -1
- package/lib/typescript/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.d.ts.map +1 -1
- package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts +2 -0
- package/lib/typescript/src/web/sanitization/htmlSanitizer.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts +2 -2
- package/lib/typescript/src/web/styleConversion/buildMentionRulesCSS.d.ts.map +1 -1
- package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts +8 -0
- package/lib/typescript/src/web/styleConversion/enrichedBaseStyleToCSSProperties.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts +2 -5
- package/lib/typescript/src/web/styleConversion/enrichedInputStyleToCSSProperties.d.ts.map +1 -1
- package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts +4 -0
- package/lib/typescript/src/web/styleConversion/enrichedTextStyleToCSSProperties.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/{enrichedInputThemingToCSSProperties.d.ts → enrichedThemingToCSSProperties.d.ts} +1 -1
- package/lib/typescript/src/web/styleConversion/enrichedThemingToCSSProperties.d.ts.map +1 -0
- package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts +5 -4
- package/lib/typescript/src/web/styleConversion/htmlStyleToCSSVariables.d.ts.map +1 -1
- package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts +3 -0
- package/lib/typescript/src/web/styleConversion/inlineImageCSSVariables.d.ts.map +1 -0
- package/lib/typescript/src/web/useImageErrorFallback.d.ts +3 -0
- package/lib/typescript/src/web/useImageErrorFallback.d.ts.map +1 -0
- package/lib/typescript/src/web/useOnChangeState.d.ts.map +1 -1
- package/lib/typescript/src/web/useOnLinkDetected.d.ts +2 -2
- package/lib/typescript/src/web/useOnLinkDetected.d.ts.map +1 -1
- package/lib/typescript/src/web/usePressInteractions.d.ts +2 -0
- package/lib/typescript/src/web/usePressInteractions.d.ts.map +1 -0
- package/lib/typescript/src/web/useStableRef.d.ts +3 -0
- package/lib/typescript/src/web/useStableRef.d.ts.map +1 -0
- package/package.json +54 -28
- package/src/index.tsx +1 -0
- package/src/types.ts +5 -3
- package/src/utils/expandMentionStylesForIndicators.ts +3 -3
- package/src/utils/normalizeHtmlStyle.ts +2 -1
- package/src/web/EnrichedText.css +343 -0
- package/src/web/EnrichedText.tsx +82 -0
- package/src/web/EnrichedTextInput.tsx +69 -55
- package/src/web/constants/brokenImageGlyph.ts +7 -0
- package/src/web/constants/classNames.ts +2 -0
- package/src/web/emitLinkDetected.ts +29 -0
- package/src/web/formats/EnrichedBlockquote.ts +2 -1
- package/src/web/formats/EnrichedCheckboxList.ts +5 -2
- package/src/web/formats/EnrichedCodeBlock.ts +2 -1
- package/src/web/formats/EnrichedHeading.ts +2 -1
- package/src/web/formats/EnrichedImageNodeView.tsx +1 -3
- package/src/web/formats/EnrichedLink.ts +59 -4
- package/src/web/formats/EnrichedOrderedList.ts +5 -2
- package/src/web/formats/EnrichedTextAlign.ts +88 -0
- package/src/web/formats/EnrichedUnorderedList.ts +5 -2
- package/src/web/formats/applyWrappingListToSelection.ts +4 -4
- package/src/web/formats/formatRules.ts +48 -4
- package/src/web/formats/listKeyboard.ts +6 -1
- package/src/web/normalization/htmlNormalizer.ts +656 -0
- package/src/web/normalization/prepareHtmlForWeb.ts +93 -0
- package/src/web/{tiptapHtmlNormalizer.ts → normalization/tiptapHtmlNormalizer.ts} +12 -2
- package/src/web/pmPlugins/AutolinkPlugin/autolinkRegex.ts +43 -0
- package/src/web/pmPlugins/AutolinkPlugin/index.ts +274 -0
- package/src/web/pmPlugins/MentionPlugin/index.ts +1 -1
- package/src/web/pmPlugins/MentionPlugin/makeMentionPluginState.ts +32 -5
- package/src/web/pmPlugins/MentionPlugin/setMention.ts +19 -29
- package/src/web/pmPlugins/MentionPlugin/useMentionEvents.ts +114 -0
- package/src/web/pmPlugins/ShortcutPlugin.ts +7 -0
- package/src/web/pmPlugins/StrictMarksPlugin.ts +3 -0
- package/src/web/pmPlugins/StripBoldInStyledHeadingsPlugin.ts +41 -36
- package/src/web/sanitization/htmlSanitizer.ts +8 -0
- package/src/web/styleConversion/buildMentionRulesCSS.ts +30 -11
- package/src/web/styleConversion/enrichedBaseStyleToCSSProperties.ts +228 -0
- package/src/web/styleConversion/enrichedInputStyleToCSSProperties.ts +6 -217
- package/src/web/styleConversion/enrichedTextStyleToCSSProperties.ts +53 -0
- package/src/web/styleConversion/{enrichedInputThemingToCSSProperties.ts → enrichedThemingToCSSProperties.ts} +4 -4
- package/src/web/styleConversion/htmlStyleToCSSVariables.ts +121 -70
- package/src/web/styleConversion/inlineImageCSSVariables.ts +6 -0
- package/src/web/useImageErrorFallback.ts +37 -0
- package/src/web/useOnChangeHtml.ts +1 -1
- package/src/web/useOnChangeState.ts +7 -5
- package/src/web/useOnLinkDetected.ts +38 -50
- package/src/web/usePressInteractions.tsx +22 -0
- package/src/web/useStableRef.ts +12 -0
- package/lib/module/web/EnrichedTextInput.css +0 -201
- package/lib/module/web/checkboxHtmlNormalizer.js.map +0 -1
- package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js +0 -100
- package/lib/module/web/pmPlugins/MentionPlugin/subscribeMentionEvents.js.map +0 -1
- package/lib/module/web/styleConversion/enrichedInputThemingToCSSProperties.js.map +0 -1
- package/lib/module/web/tiptapHtmlNormalizer.js.map +0 -1
- package/lib/typescript/src/web/checkboxHtmlNormalizer.d.ts.map +0 -1
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts +0 -4
- package/lib/typescript/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.d.ts.map +0 -1
- package/lib/typescript/src/web/styleConversion/enrichedInputThemingToCSSProperties.d.ts.map +0 -1
- package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts +0 -3
- package/lib/typescript/src/web/tiptapHtmlNormalizer.d.ts.map +0 -1
- package/src/web/EnrichedTextInput.css +0 -201
- package/src/web/pmPlugins/MentionPlugin/subscribeMentionEvents.ts +0 -105
- /package/lib/module/web/{checkboxHtmlNormalizer.js → normalization/checkboxHtmlNormalizer.js} +0 -0
- /package/lib/typescript/src/web/{checkboxHtmlNormalizer.d.ts → normalization/checkboxHtmlNormalizer.d.ts} +0 -0
- /package/src/web/{checkboxHtmlNormalizer.ts → normalization/checkboxHtmlNormalizer.ts} +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package com.swmansion.enriched.common
|
|
2
|
+
|
|
3
|
+
import android.text.Spannable
|
|
4
|
+
import com.swmansion.enriched.common.spans.EnrichedAlignmentSpan
|
|
5
|
+
import com.swmansion.enriched.common.spans.interfaces.EnrichedInlineSpan
|
|
6
|
+
|
|
7
|
+
// Higher priority spans are processed first, so styles with lower priorities are painted on top of previously applied styles.
|
|
8
|
+
// For example, inline styles are applied on top of paragraph styles, allowing them to override paragraph-level styling.
|
|
9
|
+
// Alignment styles are applied last, ensuring they position the final, fully styled text.
|
|
10
|
+
object EnrichedSpanFlags {
|
|
11
|
+
private const val ALIGNMENT_SPAN_PRIORITY = 0
|
|
12
|
+
private const val INLINE_SPAN_PRIORITY = 1
|
|
13
|
+
private const val PARAGRAPH_SPAN_PRIORITY = 2
|
|
14
|
+
|
|
15
|
+
@JvmStatic
|
|
16
|
+
@JvmOverloads
|
|
17
|
+
fun forSpan(
|
|
18
|
+
span: Any?,
|
|
19
|
+
baseFlags: Int = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
|
|
20
|
+
): Int {
|
|
21
|
+
val priority =
|
|
22
|
+
when (span) {
|
|
23
|
+
is EnrichedAlignmentSpan -> ALIGNMENT_SPAN_PRIORITY
|
|
24
|
+
is EnrichedInlineSpan -> INLINE_SPAN_PRIORITY
|
|
25
|
+
else -> PARAGRAPH_SPAN_PRIORITY
|
|
26
|
+
}
|
|
27
|
+
return applyPriority(baseFlags, priority)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
private fun applyPriority(
|
|
31
|
+
flags: Int,
|
|
32
|
+
priority: Int,
|
|
33
|
+
): Int {
|
|
34
|
+
// Cleaning up priority bits
|
|
35
|
+
val cleared = flags and Spannable.SPAN_PRIORITY.inv()
|
|
36
|
+
// Injecting priority bits
|
|
37
|
+
return cleared or ((priority shl Spannable.SPAN_PRIORITY_SHIFT) and Spannable.SPAN_PRIORITY)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -7,6 +7,7 @@ import android.text.Spanned;
|
|
|
7
7
|
import android.text.TextUtils;
|
|
8
8
|
import android.text.style.ParagraphStyle;
|
|
9
9
|
import com.swmansion.enriched.common.EnrichedConstants;
|
|
10
|
+
import com.swmansion.enriched.common.EnrichedSpanFlags;
|
|
10
11
|
import com.swmansion.enriched.common.spans.EnrichedAlignmentSpan;
|
|
11
12
|
import com.swmansion.enriched.common.spans.EnrichedBoldSpan;
|
|
12
13
|
import com.swmansion.enriched.common.spans.EnrichedCheckboxListSpan;
|
|
@@ -66,6 +67,11 @@ public class EnrichedParser {
|
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
public static <T> Spanned fromHtml(String source, T style, EnrichedSpanFactory<T> spanFactory) {
|
|
70
|
+
return fromHtml(source, style, spanFactory, null);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
public static <T> Spanned fromHtml(
|
|
74
|
+
String source, T style, EnrichedSpanFactory<T> spanFactory, Pattern linkRegex) {
|
|
69
75
|
Parser parser = new Parser();
|
|
70
76
|
try {
|
|
71
77
|
parser.setProperty(Parser.schemaProperty, HtmlParser.schema);
|
|
@@ -74,7 +80,7 @@ public class EnrichedParser {
|
|
|
74
80
|
throw new RuntimeException(e);
|
|
75
81
|
}
|
|
76
82
|
HtmlToSpannedConverter converter =
|
|
77
|
-
new HtmlToSpannedConverter(source, style, parser, spanFactory);
|
|
83
|
+
new HtmlToSpannedConverter(source, style, parser, spanFactory, linkRegex);
|
|
78
84
|
return converter.convert();
|
|
79
85
|
}
|
|
80
86
|
|
|
@@ -408,6 +414,7 @@ class HtmlToSpannedConverter<T> implements ContentHandler {
|
|
|
408
414
|
private final String mSource;
|
|
409
415
|
private final XMLReader mReader;
|
|
410
416
|
private final SpannableStringBuilder mSpannableStringBuilder;
|
|
417
|
+
private final Pattern mLinkRegex;
|
|
411
418
|
private static Integer currentOrderedListItemIndex = 0;
|
|
412
419
|
private static Boolean isInOrderedList = false;
|
|
413
420
|
private static Boolean isInCheckboxList = false;
|
|
@@ -432,12 +439,17 @@ class HtmlToSpannedConverter<T> implements ContentHandler {
|
|
|
432
439
|
}
|
|
433
440
|
|
|
434
441
|
public HtmlToSpannedConverter(
|
|
435
|
-
String source,
|
|
442
|
+
String source,
|
|
443
|
+
T style,
|
|
444
|
+
Parser parser,
|
|
445
|
+
EnrichedSpanFactory<T> spanFactory,
|
|
446
|
+
Pattern linkRegex) {
|
|
436
447
|
mStyle = style;
|
|
437
448
|
mSource = source;
|
|
438
449
|
mSpannableStringBuilder = new SpannableStringBuilder();
|
|
439
450
|
mReader = parser;
|
|
440
451
|
mSpanFactory = spanFactory;
|
|
452
|
+
mLinkRegex = linkRegex;
|
|
441
453
|
}
|
|
442
454
|
|
|
443
455
|
public Spanned convert() {
|
|
@@ -467,10 +479,7 @@ class HtmlToSpannedConverter<T> implements ContentHandler {
|
|
|
467
479
|
if (end == start) {
|
|
468
480
|
mSpannableStringBuilder.removeSpan(obj[i]);
|
|
469
481
|
} else {
|
|
470
|
-
|
|
471
|
-
// Previously it was SPAN_PARAGRAPH. I've changed that in order to fix ranges for list
|
|
472
|
-
// items.
|
|
473
|
-
mSpannableStringBuilder.setSpan(obj[i], start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
482
|
+
mSpannableStringBuilder.setSpan(obj[i], start, end, EnrichedSpanFlags.forSpan(obj[i]));
|
|
474
483
|
}
|
|
475
484
|
}
|
|
476
485
|
|
|
@@ -505,7 +514,7 @@ class HtmlToSpannedConverter<T> implements ContentHandler {
|
|
|
505
514
|
|
|
506
515
|
mSpannableStringBuilder.removeSpan(zeroWidthSpaceSpan);
|
|
507
516
|
mSpannableStringBuilder.setSpan(
|
|
508
|
-
zeroWidthSpaceSpan, start, end,
|
|
517
|
+
zeroWidthSpaceSpan, start, end, EnrichedSpanFlags.forSpan(zeroWidthSpaceSpan));
|
|
509
518
|
}
|
|
510
519
|
|
|
511
520
|
return mSpannableStringBuilder;
|
|
@@ -602,7 +611,7 @@ class HtmlToSpannedConverter<T> implements ContentHandler {
|
|
|
602
611
|
} else if (tag.equalsIgnoreCase("codeblock")) {
|
|
603
612
|
endCodeBlock(mSpannableStringBuilder, mStyle, mSpanFactory);
|
|
604
613
|
} else if (tag.equalsIgnoreCase("a")) {
|
|
605
|
-
endA(mSpannableStringBuilder, mStyle, mSpanFactory);
|
|
614
|
+
endA(mSpannableStringBuilder, mStyle, mSpanFactory, mLinkRegex);
|
|
606
615
|
} else if (tag.equalsIgnoreCase("u")) {
|
|
607
616
|
end(mSpannableStringBuilder, Underline.class, mSpanFactory.createUnderlineSpan(mStyle));
|
|
608
617
|
} else if (tag.equalsIgnoreCase("s")) {
|
|
@@ -802,7 +811,7 @@ class HtmlToSpannedConverter<T> implements ContentHandler {
|
|
|
802
811
|
int len = text.length();
|
|
803
812
|
if (where != len) {
|
|
804
813
|
for (Object span : spans) {
|
|
805
|
-
text.setSpan(span, where, len,
|
|
814
|
+
text.setSpan(span, where, len, EnrichedSpanFlags.forSpan(span));
|
|
806
815
|
}
|
|
807
816
|
}
|
|
808
817
|
}
|
|
@@ -825,7 +834,7 @@ class HtmlToSpannedConverter<T> implements ContentHandler {
|
|
|
825
834
|
|
|
826
835
|
if (where != len) {
|
|
827
836
|
for (Object span : spans) {
|
|
828
|
-
text.setSpan(span, where, len,
|
|
837
|
+
text.setSpan(span, where, len, EnrichedSpanFlags.forSpan(span));
|
|
829
838
|
}
|
|
830
839
|
}
|
|
831
840
|
}
|
|
@@ -850,11 +859,19 @@ class HtmlToSpannedConverter<T> implements ContentHandler {
|
|
|
850
859
|
|
|
851
860
|
int len = text.length();
|
|
852
861
|
text.append("");
|
|
853
|
-
|
|
854
|
-
spanFactory.createImageSpan(src,
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
862
|
+
Object imageSpan =
|
|
863
|
+
spanFactory.createImageSpan(src, parseDimension(width), parseDimension(height));
|
|
864
|
+
text.setSpan(imageSpan, len, text.length(), EnrichedSpanFlags.forSpan(imageSpan));
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
private static int parseDimension(String value) {
|
|
868
|
+
if (value == null) return 0;
|
|
869
|
+
try {
|
|
870
|
+
int parsed = (int) Math.floor(Float.parseFloat(value));
|
|
871
|
+
return Math.max(parsed, 0);
|
|
872
|
+
} catch (NumberFormatException e) {
|
|
873
|
+
return 0;
|
|
874
|
+
}
|
|
858
875
|
}
|
|
859
876
|
|
|
860
877
|
private static void startA(Editable text, Attributes attributes) {
|
|
@@ -862,12 +879,18 @@ class HtmlToSpannedConverter<T> implements ContentHandler {
|
|
|
862
879
|
start(text, new Href(href));
|
|
863
880
|
}
|
|
864
881
|
|
|
865
|
-
private static
|
|
882
|
+
private static boolean urlMatchesLinkRegex(String url, Pattern linkRegex) {
|
|
883
|
+
if (linkRegex == null) return false;
|
|
884
|
+
return linkRegex.matcher(url).matches();
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
private static <T> void endA(
|
|
888
|
+
Editable text, T style, EnrichedSpanFactory<T> spanFactory, Pattern linkRegex) {
|
|
866
889
|
Href h = getLast(text, Href.class);
|
|
867
|
-
if (h != null) {
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
890
|
+
if (h != null && h.mHref != null) {
|
|
891
|
+
String linkText = text.subSequence(text.getSpanStart(h), text.length()).toString();
|
|
892
|
+
boolean isManual = !linkText.equals(h.mHref) || !urlMatchesLinkRegex(h.mHref, linkRegex);
|
|
893
|
+
setSpanFromMark(text, h, spanFactory.createLinkSpan(h.mHref, style, isManual));
|
|
871
894
|
}
|
|
872
895
|
}
|
|
873
896
|
|
|
@@ -23,6 +23,7 @@ import com.facebook.react.views.text.ReactTypefaceUtils.applyStyles
|
|
|
23
23
|
import com.facebook.react.views.text.ReactTypefaceUtils.parseFontStyle
|
|
24
24
|
import com.facebook.react.views.text.ReactTypefaceUtils.parseFontWeight
|
|
25
25
|
import com.swmansion.enriched.common.EnrichedConstants
|
|
26
|
+
import com.swmansion.enriched.common.EnrichedSpanFlags
|
|
26
27
|
import com.swmansion.enriched.common.GumboNormalizer
|
|
27
28
|
import com.swmansion.enriched.common.parser.EnrichedParser
|
|
28
29
|
import com.swmansion.enriched.common.pixelFromSpOrDp
|
|
@@ -277,7 +278,7 @@ class EnrichedTextView : AppCompatTextView {
|
|
|
277
278
|
|
|
278
279
|
spannable.removeSpan(span)
|
|
279
280
|
val newSpan = span.rebuildWithStyle(enrichedStyle)
|
|
280
|
-
spannable.setSpan(newSpan, start, end, flags)
|
|
281
|
+
spannable.setSpan(newSpan, start, end, EnrichedSpanFlags.forSpan(newSpan, flags))
|
|
281
282
|
modified = true
|
|
282
283
|
}
|
|
283
284
|
|
package/android/src/main/java/com/swmansion/enriched/textinput/EnrichedTextInputSpannableFactory.kt
CHANGED
|
@@ -40,7 +40,8 @@ class EnrichedTextInputSpannableFactory : EnrichedSpanFactory<HtmlStyle> {
|
|
|
40
40
|
override fun createLinkSpan(
|
|
41
41
|
url: String,
|
|
42
42
|
style: HtmlStyle,
|
|
43
|
-
|
|
43
|
+
isManual: Boolean,
|
|
44
|
+
) = EnrichedInputLinkSpan(url, style, isManual)
|
|
44
45
|
|
|
45
46
|
override fun createMentionSpan(
|
|
46
47
|
text: String,
|
|
@@ -9,6 +9,7 @@ import android.graphics.Color
|
|
|
9
9
|
import android.graphics.Rect
|
|
10
10
|
import android.graphics.text.LineBreaker
|
|
11
11
|
import android.os.Build
|
|
12
|
+
import android.text.Editable
|
|
12
13
|
import android.text.InputType
|
|
13
14
|
import android.text.Spannable
|
|
14
15
|
import android.text.SpannableString
|
|
@@ -38,6 +39,7 @@ import com.facebook.react.views.text.ReactTypefaceUtils.applyStyles
|
|
|
38
39
|
import com.facebook.react.views.text.ReactTypefaceUtils.parseFontStyle
|
|
39
40
|
import com.facebook.react.views.text.ReactTypefaceUtils.parseFontWeight
|
|
40
41
|
import com.swmansion.enriched.common.EnrichedConstants
|
|
42
|
+
import com.swmansion.enriched.common.EnrichedSpanFlags
|
|
41
43
|
import com.swmansion.enriched.common.GumboNormalizer
|
|
42
44
|
import com.swmansion.enriched.common.parser.EnrichedParser
|
|
43
45
|
import com.swmansion.enriched.common.pixelFromSpOrDp
|
|
@@ -398,8 +400,9 @@ class EnrichedTextInputView :
|
|
|
398
400
|
val pasteEnd = (start + insertedLength).coerceIn(0, finalText.length)
|
|
399
401
|
setSelection(pasteEnd)
|
|
400
402
|
|
|
401
|
-
//
|
|
402
|
-
|
|
403
|
+
// Update links and mentions in the newly pasted range
|
|
404
|
+
val editable = text as? Editable ?: return
|
|
405
|
+
parametrizedStyles?.afterTextChanged(editable, start.coerceAtMost(pasteEnd), pasteEnd)
|
|
403
406
|
}
|
|
404
407
|
|
|
405
408
|
fun requestFocusProgrammatically() {
|
|
@@ -413,7 +416,7 @@ class EnrichedTextInputView :
|
|
|
413
416
|
val normalized = GumboNormalizer.normalizeHtml(text.toString()) ?: return text
|
|
414
417
|
|
|
415
418
|
return try {
|
|
416
|
-
val parsed = EnrichedParser.fromHtml(normalized, htmlStyle, spannableFactory)
|
|
419
|
+
val parsed = EnrichedParser.fromHtml(normalized, htmlStyle, spannableFactory, linkRegex)
|
|
417
420
|
parsed.trimEnd('\n')
|
|
418
421
|
} catch (e: Exception) {
|
|
419
422
|
Log.e(TAG, "Error parsing normalized HTML: ${e.message}")
|
|
@@ -426,7 +429,7 @@ class EnrichedTextInputView :
|
|
|
426
429
|
|
|
427
430
|
if (isInternalHtml) {
|
|
428
431
|
try {
|
|
429
|
-
val parsed = EnrichedParser.fromHtml(text.toString(), htmlStyle, spannableFactory)
|
|
432
|
+
val parsed = EnrichedParser.fromHtml(text.toString(), htmlStyle, spannableFactory, linkRegex)
|
|
430
433
|
return parsed.trimEnd('\n')
|
|
431
434
|
} catch (e: Exception) {
|
|
432
435
|
Log.e(TAG, "Error parsing HTML: ${e.message}")
|
|
@@ -1108,7 +1111,7 @@ class EnrichedTextInputView :
|
|
|
1108
1111
|
|
|
1109
1112
|
spannable.removeSpan(span)
|
|
1110
1113
|
val newSpan = span.rebuildWithStyle(htmlStyle)
|
|
1111
|
-
spannable.setSpan(newSpan, start, end, flags)
|
|
1114
|
+
spannable.setSpan(newSpan, start, end, EnrichedSpanFlags.forSpan(newSpan, flags))
|
|
1112
1115
|
}
|
|
1113
1116
|
|
|
1114
1117
|
if (shouldEmitStateChange) {
|
|
@@ -27,8 +27,17 @@ class MentionHandler(
|
|
|
27
27
|
indicator: String,
|
|
28
28
|
text: String?,
|
|
29
29
|
) {
|
|
30
|
+
// switching directly to an active mention
|
|
31
|
+
if (previousIndicator != indicator) {
|
|
32
|
+
endMention()
|
|
33
|
+
|
|
34
|
+
// explicit startMention event before changeMention event
|
|
35
|
+
if (!text.isNullOrEmpty()) {
|
|
36
|
+
emitEvent(indicator, "")
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
emitEvent(indicator, text)
|
|
31
|
-
previousIndicator = indicator
|
|
32
41
|
}
|
|
33
42
|
|
|
34
43
|
private fun emitEvent(
|
|
@@ -36,8 +45,9 @@ class MentionHandler(
|
|
|
36
45
|
text: String?,
|
|
37
46
|
) {
|
|
38
47
|
// Do not emit events too often
|
|
39
|
-
if (previousText == text) return
|
|
48
|
+
if (previousIndicator == indicator && previousText == text) return
|
|
40
49
|
|
|
50
|
+
previousIndicator = indicator
|
|
41
51
|
previousText = text
|
|
42
52
|
val context = view.context as ReactContext
|
|
43
53
|
val surfaceId = UIManagerHelper.getSurfaceId(context)
|
|
@@ -13,6 +13,12 @@ class OnMentionEvent(
|
|
|
13
13
|
) : Event<OnMentionEvent>(surfaceId, viewId) {
|
|
14
14
|
override fun getEventName(): String = EVENT_NAME
|
|
15
15
|
|
|
16
|
+
// start/change/end can be emitted as a burst within a single frame
|
|
17
|
+
// (e.g. when switching mentions: end -> start -> change).
|
|
18
|
+
// The default coalescing would merge them in the batch and drop the
|
|
19
|
+
// intermediate ones, so it must be disabled to deliver every event in order.
|
|
20
|
+
override fun canCoalesce(): Boolean = false
|
|
21
|
+
|
|
16
22
|
override fun getEventData(): WritableMap? {
|
|
17
23
|
val eventData: WritableMap = Arguments.createMap()
|
|
18
24
|
eventData.putString("indicator", indicator)
|
|
@@ -4,6 +4,7 @@ import android.text.Editable
|
|
|
4
4
|
import android.text.Spannable
|
|
5
5
|
import android.text.SpannableStringBuilder
|
|
6
6
|
import com.swmansion.enriched.common.EnrichedConstants
|
|
7
|
+
import com.swmansion.enriched.common.EnrichedSpanFlags
|
|
7
8
|
import com.swmansion.enriched.textinput.EnrichedTextInputView
|
|
8
9
|
import com.swmansion.enriched.textinput.spans.EnrichedInputAlignmentSpan
|
|
9
10
|
import com.swmansion.enriched.textinput.spans.EnrichedInputCheckboxListSpan
|
|
@@ -24,12 +25,8 @@ class AlignmentStyles(
|
|
|
24
25
|
flags: Int = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
|
|
25
26
|
) {
|
|
26
27
|
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, end)
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
safeStart,
|
|
30
|
-
safeEnd,
|
|
31
|
-
flags,
|
|
32
|
-
)
|
|
28
|
+
val span = EnrichedInputAlignmentSpan(cssValue)
|
|
29
|
+
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span, flags))
|
|
33
30
|
}
|
|
34
31
|
|
|
35
32
|
private fun toCssValue(alignment: String): String =
|
|
@@ -302,7 +299,9 @@ class AlignmentStyles(
|
|
|
302
299
|
// INCLUSIVE_EXCLUSIVE is intentional here: autoStretchAlignmentSpan will convert
|
|
303
300
|
// it to EXCLUSIVE_EXCLUSIVE once the merge is complete.
|
|
304
301
|
val (safeStart, safeEnd) = s.getSafeSpanBoundaries(paraStart, paraEnd)
|
|
305
|
-
dominantTopSpan?.let {
|
|
302
|
+
dominantTopSpan?.let {
|
|
303
|
+
s.setSpan(it, safeStart, safeEnd, EnrichedSpanFlags.forSpan(it, Spannable.SPAN_INCLUSIVE_EXCLUSIVE))
|
|
304
|
+
}
|
|
306
305
|
return cursorPosition
|
|
307
306
|
}
|
|
308
307
|
|
|
@@ -2,6 +2,7 @@ package com.swmansion.enriched.textinput.styles
|
|
|
2
2
|
|
|
3
3
|
import android.text.Editable
|
|
4
4
|
import android.text.Spannable
|
|
5
|
+
import com.swmansion.enriched.common.EnrichedSpanFlags
|
|
5
6
|
import com.swmansion.enriched.textinput.EnrichedTextInputView
|
|
6
7
|
import com.swmansion.enriched.textinput.spans.EnrichedSpans
|
|
7
8
|
import com.swmansion.enriched.textinput.utils.getSafeSpanBoundaries
|
|
@@ -41,7 +42,7 @@ class InlineStyles(
|
|
|
41
42
|
|
|
42
43
|
val span = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)
|
|
43
44
|
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(minimum, maximum)
|
|
44
|
-
spannable.setSpan(span, safeStart, safeEnd,
|
|
45
|
+
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
private fun <T> setAndMergeSpans(
|
|
@@ -222,7 +223,18 @@ class InlineStyles(
|
|
|
222
223
|
if (spans.isEmpty()) return false
|
|
223
224
|
|
|
224
225
|
for (span in spans) {
|
|
226
|
+
val spanStart = spannable.getSpanStart(span)
|
|
227
|
+
val spanEnd = spannable.getSpanEnd(span)
|
|
228
|
+
|
|
225
229
|
spannable.removeSpan(span)
|
|
230
|
+
|
|
231
|
+
if (spanStart < start) {
|
|
232
|
+
setSpan(spannable, config.clazz, spanStart, start - 1)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (spanEnd > end) {
|
|
236
|
+
setSpan(spannable, config.clazz, end, spanEnd)
|
|
237
|
+
}
|
|
226
238
|
}
|
|
227
239
|
|
|
228
240
|
return true
|
|
@@ -5,6 +5,7 @@ import android.text.Spannable
|
|
|
5
5
|
import android.text.SpannableStringBuilder
|
|
6
6
|
import android.text.Spanned
|
|
7
7
|
import com.swmansion.enriched.common.EnrichedConstants
|
|
8
|
+
import com.swmansion.enriched.common.EnrichedSpanFlags
|
|
8
9
|
import com.swmansion.enriched.textinput.EnrichedTextInputView
|
|
9
10
|
import com.swmansion.enriched.textinput.spans.EnrichedInputCheckboxListSpan
|
|
10
11
|
import com.swmansion.enriched.textinput.spans.EnrichedInputOrderedListSpan
|
|
@@ -66,18 +67,18 @@ class ListStyles(
|
|
|
66
67
|
when (name) {
|
|
67
68
|
EnrichedSpans.UNORDERED_LIST -> {
|
|
68
69
|
val span = EnrichedInputUnorderedListSpan(view.htmlStyle)
|
|
69
|
-
spannable.setSpan(span, safeStart, safeEnd,
|
|
70
|
+
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
EnrichedSpans.ORDERED_LIST -> {
|
|
73
74
|
val index = getOrderedListIndex(spannable, safeStart)
|
|
74
75
|
val span = EnrichedInputOrderedListSpan(index, view.htmlStyle)
|
|
75
|
-
spannable.setSpan(span, safeStart, safeEnd,
|
|
76
|
+
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
EnrichedSpans.CHECKBOX_LIST -> {
|
|
79
80
|
val span = EnrichedInputCheckboxListSpan(isChecked ?: false, view.htmlStyle)
|
|
80
|
-
spannable.setSpan(span, safeStart, safeEnd,
|
|
81
|
+
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
|
|
81
82
|
|
|
82
83
|
// Invalidate layout to update checkbox drawing in case checkbox is bigger than line height
|
|
83
84
|
view.layoutManager.invalidateLayout()
|
|
@@ -4,6 +4,7 @@ import android.text.Editable
|
|
|
4
4
|
import android.text.Spannable
|
|
5
5
|
import android.text.SpannableStringBuilder
|
|
6
6
|
import android.util.Log
|
|
7
|
+
import com.swmansion.enriched.common.EnrichedSpanFlags
|
|
7
8
|
import com.swmansion.enriched.textinput.EnrichedTextInputView
|
|
8
9
|
import com.swmansion.enriched.textinput.spans.EnrichedSpans
|
|
9
10
|
import com.swmansion.enriched.textinput.spans.interfaces.EnrichedInputSpan
|
|
@@ -89,7 +90,7 @@ class ParagraphStyles(
|
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(newStart, newEnd)
|
|
92
|
-
spannable.setSpan(span, safeStart, safeEnd,
|
|
93
|
+
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
private fun <T> setSpan(
|
|
@@ -105,7 +106,7 @@ class ParagraphStyles(
|
|
|
105
106
|
|
|
106
107
|
val span = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)
|
|
107
108
|
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, end)
|
|
108
|
-
spannable.setSpan(span, safeStart, safeEnd,
|
|
109
|
+
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
// Removes spans of the given type in the specified range.
|
|
@@ -232,7 +233,7 @@ class ParagraphStyles(
|
|
|
232
233
|
val (safeStart, safeEnd) = s.getSafeSpanBoundaries(newStart, newEnd)
|
|
233
234
|
val span = type.getDeclaredConstructor(HtmlStyle::class.java).newInstance(view.htmlStyle)
|
|
234
235
|
|
|
235
|
-
s.setSpan(span, safeStart, safeEnd,
|
|
236
|
+
s.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
|
|
236
237
|
}
|
|
237
238
|
|
|
238
239
|
private fun handleConflictsDuringNewlineDeletion(
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
package com.swmansion.enriched.textinput.styles
|
|
2
2
|
|
|
3
3
|
import android.text.Editable
|
|
4
|
+
import android.text.Selection
|
|
4
5
|
import android.text.Spannable
|
|
5
6
|
import android.text.SpannableStringBuilder
|
|
6
7
|
import android.text.Spanned
|
|
7
8
|
import com.swmansion.enriched.common.EnrichedConstants
|
|
9
|
+
import com.swmansion.enriched.common.EnrichedSpanFlags
|
|
8
10
|
import com.swmansion.enriched.textinput.EnrichedTextInputView
|
|
9
11
|
import com.swmansion.enriched.textinput.spans.EnrichedInputImageSpan
|
|
10
12
|
import com.swmansion.enriched.textinput.spans.EnrichedInputLinkSpan
|
|
@@ -17,6 +19,7 @@ class ParametrizedStyles(
|
|
|
17
19
|
private val view: EnrichedTextInputView,
|
|
18
20
|
) {
|
|
19
21
|
private var mentionStart: Int? = null
|
|
22
|
+
private var mentionEnd: Int? = null
|
|
20
23
|
private var isSettingLinkSpan = false
|
|
21
24
|
|
|
22
25
|
var mentionIndicators: Array<String> = emptyArray<String>()
|
|
@@ -63,7 +66,7 @@ class ParametrizedStyles(
|
|
|
63
66
|
val spanEnd = start + text.length
|
|
64
67
|
val span = EnrichedInputLinkSpan(url, view.htmlStyle, true)
|
|
65
68
|
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, spanEnd)
|
|
66
|
-
spannable.setSpan(span, safeStart, safeEnd,
|
|
69
|
+
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
|
|
67
70
|
|
|
68
71
|
view.selection?.validateStyles()
|
|
69
72
|
isSettingLinkSpan = false
|
|
@@ -91,7 +94,23 @@ class ParametrizedStyles(
|
|
|
91
94
|
endCursorPosition: Int,
|
|
92
95
|
) {
|
|
93
96
|
afterTextChangedLinks(startCursorPosition, endCursorPosition)
|
|
94
|
-
|
|
97
|
+
detectActiveMention(s, startCursorPosition)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Re-runs in-progress mention detection on a pure caret move (no text change),
|
|
101
|
+
fun afterSelectionChangedMentions(
|
|
102
|
+
start: Int,
|
|
103
|
+
end: Int,
|
|
104
|
+
) {
|
|
105
|
+
val s = view.text ?: return
|
|
106
|
+
|
|
107
|
+
// A non-collapsed selection can't be editing a single mention.
|
|
108
|
+
if (start != end) {
|
|
109
|
+
view.mentionHandler?.endMention()
|
|
110
|
+
return
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
detectActiveMention(s, end)
|
|
95
114
|
}
|
|
96
115
|
|
|
97
116
|
fun onStyleToggled(
|
|
@@ -160,7 +179,7 @@ class ParametrizedStyles(
|
|
|
160
179
|
span,
|
|
161
180
|
safeStart,
|
|
162
181
|
safeEnd,
|
|
163
|
-
|
|
182
|
+
EnrichedSpanFlags.forSpan(span),
|
|
164
183
|
)
|
|
165
184
|
}
|
|
166
185
|
}
|
|
@@ -239,7 +258,7 @@ class ParametrizedStyles(
|
|
|
239
258
|
detectLinksInRange(spannable, affectedRange.first, affectedRange.last)
|
|
240
259
|
}
|
|
241
260
|
|
|
242
|
-
private fun
|
|
261
|
+
private fun detectActiveMention(
|
|
243
262
|
s: CharSequence,
|
|
244
263
|
endCursorPosition: Int,
|
|
245
264
|
) {
|
|
@@ -261,12 +280,16 @@ class ParametrizedStyles(
|
|
|
261
280
|
|
|
262
281
|
// No previous word -> no mention to be detected
|
|
263
282
|
if (previousWord == null) {
|
|
283
|
+
mentionStart = null
|
|
284
|
+
mentionEnd = null
|
|
264
285
|
mentionHandler.endMention()
|
|
265
286
|
return
|
|
266
287
|
}
|
|
267
288
|
|
|
268
289
|
// Previous word is not a mention -> end mention
|
|
269
290
|
if (!mentionRegex.matches(previousWord.text)) {
|
|
291
|
+
mentionStart = null
|
|
292
|
+
mentionEnd = null
|
|
270
293
|
mentionHandler.endMention()
|
|
271
294
|
return
|
|
272
295
|
}
|
|
@@ -280,6 +303,9 @@ class ParametrizedStyles(
|
|
|
280
303
|
indicator = mentionIndicatorRegex.find(currentWord.text)?.value ?: ""
|
|
281
304
|
}
|
|
282
305
|
|
|
306
|
+
mentionStart = finalStart
|
|
307
|
+
mentionEnd = finalEnd
|
|
308
|
+
|
|
283
309
|
// Mirror iOS conflicting-styles behaviour: check the full candidate range for
|
|
284
310
|
// a finalized mention span. If the span's stored text still matches what is in
|
|
285
311
|
// the buffer the mention is intact — block the event (covers HTML-loaded
|
|
@@ -292,21 +318,19 @@ class ParametrizedStyles(
|
|
|
292
318
|
val spanEnd = spannable.getSpanEnd(span)
|
|
293
319
|
val currentSpanText = spannable.subSequence(spanStart, spanEnd).toString()
|
|
294
320
|
if (currentSpanText == span.getText()) {
|
|
321
|
+
mentionStart = null
|
|
322
|
+
mentionEnd = null
|
|
295
323
|
mentionHandler.endMention()
|
|
296
324
|
return
|
|
297
325
|
}
|
|
298
326
|
spannable.removeSpan(span)
|
|
299
327
|
mentionStart = spanStart
|
|
328
|
+
mentionEnd = spanEnd
|
|
300
329
|
}
|
|
301
330
|
|
|
302
331
|
// Extract text without indicator
|
|
303
332
|
val text = spannable.subSequence(finalStart, finalEnd).toString().replaceFirst(indicator, "")
|
|
304
333
|
|
|
305
|
-
// Means we are starting mention
|
|
306
|
-
if (text.isEmpty()) {
|
|
307
|
-
mentionStart = finalStart
|
|
308
|
-
}
|
|
309
|
-
|
|
310
334
|
mentionHandler.onMention(indicator, text)
|
|
311
335
|
}
|
|
312
336
|
|
|
@@ -366,24 +390,27 @@ class ParametrizedStyles(
|
|
|
366
390
|
}
|
|
367
391
|
|
|
368
392
|
val start = mentionStart ?: selectionStart
|
|
393
|
+
val end = mentionEnd ?: selectionEnd
|
|
369
394
|
|
|
370
395
|
view.runAsATransaction {
|
|
371
|
-
spannable.replace(start,
|
|
396
|
+
spannable.replace(start, end, text)
|
|
372
397
|
|
|
373
398
|
val span = EnrichedInputMentionSpan(text, indicator, attributes, view.htmlStyle)
|
|
374
399
|
val spanEnd = start + text.length
|
|
375
400
|
val (safeStart, safeEnd) = spannable.getSafeSpanBoundaries(start, spanEnd)
|
|
376
|
-
spannable.setSpan(span, safeStart, safeEnd,
|
|
401
|
+
spannable.setSpan(span, safeStart, safeEnd, EnrichedSpanFlags.forSpan(span))
|
|
377
402
|
|
|
378
403
|
val hasSpaceAtTheEnd = spannable.length > safeEnd && spannable[safeEnd] == ' '
|
|
379
404
|
if (!hasSpaceAtTheEnd) {
|
|
380
405
|
spannable.insert(safeEnd, " ")
|
|
381
406
|
}
|
|
407
|
+
Selection.setSelection(spannable, (safeEnd + 1).coerceAtMost(spannable.length))
|
|
382
408
|
}
|
|
383
409
|
|
|
384
410
|
view.mentionHandler?.reset()
|
|
385
411
|
view.selection.validateStyles()
|
|
386
412
|
mentionStart = null
|
|
413
|
+
mentionEnd = null
|
|
387
414
|
}
|
|
388
415
|
|
|
389
416
|
fun getStyleRange(): Pair<Int, Int> = view.selection?.getInlineSelection() ?: Pair(0, 0)
|
|
@@ -3,6 +3,7 @@ package com.swmansion.enriched.textinput.utils
|
|
|
3
3
|
import android.text.Spannable
|
|
4
4
|
import android.text.SpannableString
|
|
5
5
|
import android.text.SpannableStringBuilder
|
|
6
|
+
import com.swmansion.enriched.common.EnrichedSpanFlags
|
|
6
7
|
import com.swmansion.enriched.common.spans.interfaces.EnrichedBlockSpan
|
|
7
8
|
import com.swmansion.enriched.common.spans.interfaces.EnrichedParagraphSpan
|
|
8
9
|
import com.swmansion.enriched.common.spans.interfaces.EnrichedSpan
|
|
@@ -144,7 +145,7 @@ fun Spannable.mergeSpannables(
|
|
|
144
145
|
val (_, newParagraphEnd) = builder.getParagraphBounds(spanStart, pasteEnd)
|
|
145
146
|
val flags = builder.getSpanFlags(span)
|
|
146
147
|
builder.removeSpan(span)
|
|
147
|
-
builder.setSpan(span, spanStart, newParagraphEnd, flags)
|
|
148
|
+
builder.setSpan(span, spanStart, newParagraphEnd, EnrichedSpanFlags.forSpan(span, flags))
|
|
148
149
|
}
|
|
149
150
|
}
|
|
150
151
|
|
|
@@ -7,6 +7,7 @@ import android.text.Spanned
|
|
|
7
7
|
import android.util.Log
|
|
8
8
|
import android.view.MotionEvent
|
|
9
9
|
import android.widget.TextView
|
|
10
|
+
import com.swmansion.enriched.common.EnrichedSpanFlags
|
|
10
11
|
import com.swmansion.enriched.textinput.spans.EnrichedInputCheckboxListSpan
|
|
11
12
|
import org.json.JSONObject
|
|
12
13
|
|
|
@@ -78,7 +79,7 @@ fun TextView.setCheckboxClickListener() {
|
|
|
78
79
|
|
|
79
80
|
// Reapply span so changes are visible without need to redraw entire TextView
|
|
80
81
|
spannable.removeSpan(span)
|
|
81
|
-
spannable.setSpan(span, start, end, flags)
|
|
82
|
+
spannable.setSpan(span, start, end, EnrichedSpanFlags.forSpan(span, flags))
|
|
82
83
|
|
|
83
84
|
// For focused input, ensure cursor is active for affected paragraph
|
|
84
85
|
if (tv.isFocused) {
|