@mathers101/react-native-render-rich-text 0.0.0-stage → 0.1.10
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 +140 -3
- package/ReactNativeRenderRichText.podspec +23 -0
- package/android/build.gradle +86 -0
- package/android/consumer-rules.pro +3 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/cpp/jni-adapter.cpp +191 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapText.kt +673 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextLayoutManager.kt +24 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextManager.kt +405 -0
- package/android/src/main/java/com/renderrichtext/EnrichedTiptapTextPackage.kt +13 -0
- package/android/src/main/java/com/renderrichtext/MeasurementStore.kt +644 -0
- package/android/src/main/java/com/renderrichtext/accessibility/AccessibilityLabels.kt +17 -0
- package/android/src/main/java/com/renderrichtext/accessibility/AccessibleTiptapTextView.kt +112 -0
- package/android/src/main/java/com/renderrichtext/accessibility/TiptapAccessibilityHelper.kt +576 -0
- package/android/src/main/java/com/renderrichtext/events/CodeBlockPressEvent.kt +24 -0
- package/android/src/main/java/com/renderrichtext/events/ContextMenuItemPressEvent.kt +28 -0
- package/android/src/main/java/com/renderrichtext/events/CopyPressEvent.kt +24 -0
- package/android/src/main/java/com/renderrichtext/events/FactoidPressEvent.kt +23 -0
- package/android/src/main/java/com/renderrichtext/events/ImagePressEvent.kt +25 -0
- package/android/src/main/java/com/renderrichtext/events/LatexErrorEvent.kt +29 -0
- package/android/src/main/java/com/renderrichtext/events/LinkLongPressEvent.kt +23 -0
- package/android/src/main/java/com/renderrichtext/events/LinkPressEvent.kt +23 -0
- package/android/src/main/java/com/renderrichtext/events/MentionLongPressEvent.kt +25 -0
- package/android/src/main/java/com/renderrichtext/events/MentionPressEvent.kt +25 -0
- package/android/src/main/java/com/renderrichtext/events/TaskListItemPressEvent.kt +26 -0
- package/android/src/main/java/com/renderrichtext/math/LatexErrorReporter.kt +9 -0
- package/android/src/main/java/com/renderrichtext/parser/Parser.kt +59 -0
- package/android/src/main/java/com/renderrichtext/parser/TextLinkMatching.kt +35 -0
- package/android/src/main/java/com/renderrichtext/parser/TiptapASTNode.kt +69 -0
- package/android/src/main/java/com/renderrichtext/renderer/BaselineShiftRenderer.kt +45 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlankLineRenderer.kt +39 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlockStyleContext.kt +181 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlockquoteRenderer.kt +243 -0
- package/android/src/main/java/com/renderrichtext/renderer/BlockquoteTextRenderer.kt +56 -0
- package/android/src/main/java/com/renderrichtext/renderer/CodeBlockRenderer.kt +135 -0
- package/android/src/main/java/com/renderrichtext/renderer/CodeRenderer.kt +37 -0
- package/android/src/main/java/com/renderrichtext/renderer/EmphasisRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/renderer/FactoidRenderer.kt +97 -0
- package/android/src/main/java/com/renderrichtext/renderer/HeadingRenderer.kt +81 -0
- package/android/src/main/java/com/renderrichtext/renderer/HighlightRenderer.kt +26 -0
- package/android/src/main/java/com/renderrichtext/renderer/LineBreakRenderer.kt +17 -0
- package/android/src/main/java/com/renderrichtext/renderer/LinkRenderer.kt +90 -0
- package/android/src/main/java/com/renderrichtext/renderer/ListContextManager.kt +109 -0
- package/android/src/main/java/com/renderrichtext/renderer/ListItemRenderer.kt +211 -0
- package/android/src/main/java/com/renderrichtext/renderer/ListRenderer.kt +133 -0
- package/android/src/main/java/com/renderrichtext/renderer/MentionRenderer.kt +85 -0
- package/android/src/main/java/com/renderrichtext/renderer/NodeRenderer.kt +207 -0
- package/android/src/main/java/com/renderrichtext/renderer/ParagraphRenderer.kt +91 -0
- package/android/src/main/java/com/renderrichtext/renderer/Renderer.kt +148 -0
- package/android/src/main/java/com/renderrichtext/renderer/SoftBreakRenderer.kt +17 -0
- package/android/src/main/java/com/renderrichtext/renderer/SpanStyleCache.kt +143 -0
- package/android/src/main/java/com/renderrichtext/renderer/StrikethroughRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/renderer/StrongRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/renderer/TextRenderer.kt +31 -0
- package/android/src/main/java/com/renderrichtext/renderer/ThematicBreakRenderer.kt +46 -0
- package/android/src/main/java/com/renderrichtext/renderer/UnderlineRenderer.kt +28 -0
- package/android/src/main/java/com/renderrichtext/spans/BaseListSpan.kt +164 -0
- package/android/src/main/java/com/renderrichtext/spans/BaselineShiftSpan.kt +27 -0
- package/android/src/main/java/com/renderrichtext/spans/BlockquoteSpan.kt +302 -0
- package/android/src/main/java/com/renderrichtext/spans/CodeBackgroundSpan.kt +205 -0
- package/android/src/main/java/com/renderrichtext/spans/CodeBlockSpan.kt +216 -0
- package/android/src/main/java/com/renderrichtext/spans/CodeSpan.kt +32 -0
- package/android/src/main/java/com/renderrichtext/spans/EmphasisSpan.kt +42 -0
- package/android/src/main/java/com/renderrichtext/spans/FactoidBackgroundSpan.kt +265 -0
- package/android/src/main/java/com/renderrichtext/spans/FactoidPaddingSpan.kt +258 -0
- package/android/src/main/java/com/renderrichtext/spans/FactoidSpan.kt +32 -0
- package/android/src/main/java/com/renderrichtext/spans/FadeInSpan.kt +29 -0
- package/android/src/main/java/com/renderrichtext/spans/HeadingSpan.kt +38 -0
- package/android/src/main/java/com/renderrichtext/spans/HighlightSpan.kt +80 -0
- package/android/src/main/java/com/renderrichtext/spans/ImageSpan.kt +481 -0
- package/android/src/main/java/com/renderrichtext/spans/InlineBackgroundGeometry.kt +42 -0
- package/android/src/main/java/com/renderrichtext/spans/LineHeightSpan.kt +39 -0
- package/android/src/main/java/com/renderrichtext/spans/LinkPillIconCache.kt +154 -0
- package/android/src/main/java/com/renderrichtext/spans/LinkPillSpan.kt +381 -0
- package/android/src/main/java/com/renderrichtext/spans/LinkSpan.kt +68 -0
- package/android/src/main/java/com/renderrichtext/spans/ListItemSpacingSpan.kt +33 -0
- package/android/src/main/java/com/renderrichtext/spans/ListMarkerAnchorSpan.kt +38 -0
- package/android/src/main/java/com/renderrichtext/spans/MarginBottomSpan.kt +76 -0
- package/android/src/main/java/com/renderrichtext/spans/MentionChipSpan.kt +132 -0
- package/android/src/main/java/com/renderrichtext/spans/MentionSpan.kt +46 -0
- package/android/src/main/java/com/renderrichtext/spans/OrderedListSpan.kt +127 -0
- package/android/src/main/java/com/renderrichtext/spans/SpoilerSpan.kt +33 -0
- package/android/src/main/java/com/renderrichtext/spans/StrikethroughSpan.kt +12 -0
- package/android/src/main/java/com/renderrichtext/spans/StrongSpan.kt +39 -0
- package/android/src/main/java/com/renderrichtext/spans/TaskListSpan.kt +143 -0
- package/android/src/main/java/com/renderrichtext/spans/TextSpan.kt +26 -0
- package/android/src/main/java/com/renderrichtext/spans/ThematicBreakSpan.kt +69 -0
- package/android/src/main/java/com/renderrichtext/spans/UnorderedListSpan.kt +96 -0
- package/android/src/main/java/com/renderrichtext/spoiler/SpoilerStubs.kt +46 -0
- package/android/src/main/java/com/renderrichtext/styles/BaseBlockStyle.kt +11 -0
- package/android/src/main/java/com/renderrichtext/styles/BlockquoteStyle.kt +83 -0
- package/android/src/main/java/com/renderrichtext/styles/CodeBlockStyle.kt +80 -0
- package/android/src/main/java/com/renderrichtext/styles/CodeStyle.kt +26 -0
- package/android/src/main/java/com/renderrichtext/styles/EmphasisStyle.kt +21 -0
- package/android/src/main/java/com/renderrichtext/styles/FactoidStyle.kt +21 -0
- package/android/src/main/java/com/renderrichtext/styles/HeadingStyle.kt +33 -0
- package/android/src/main/java/com/renderrichtext/styles/HighlightStyle.kt +19 -0
- package/android/src/main/java/com/renderrichtext/styles/ImageStyle.kt +29 -0
- package/android/src/main/java/com/renderrichtext/styles/InlineImageStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/InlineMathStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkPillContent.kt +36 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkPillStyle.kt +44 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkStyle.kt +23 -0
- package/android/src/main/java/com/renderrichtext/styles/LinkVariantEntry.kt +34 -0
- package/android/src/main/java/com/renderrichtext/styles/ListStyle.kt +65 -0
- package/android/src/main/java/com/renderrichtext/styles/MathStyle.kt +29 -0
- package/android/src/main/java/com/renderrichtext/styles/MentionStyle.kt +19 -0
- package/android/src/main/java/com/renderrichtext/styles/ParagraphStyle.kt +33 -0
- package/android/src/main/java/com/renderrichtext/styles/SpoilerStyle.kt +27 -0
- package/android/src/main/java/com/renderrichtext/styles/StrikethroughStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/StrongStyle.kt +21 -0
- package/android/src/main/java/com/renderrichtext/styles/StyleConfig.kt +411 -0
- package/android/src/main/java/com/renderrichtext/styles/StyleParser.kt +92 -0
- package/android/src/main/java/com/renderrichtext/styles/SubscriptStyle.kt +16 -0
- package/android/src/main/java/com/renderrichtext/styles/SuperscriptStyle.kt +16 -0
- package/android/src/main/java/com/renderrichtext/styles/TableStyle.kt +75 -0
- package/android/src/main/java/com/renderrichtext/styles/TaskListStyle.kt +29 -0
- package/android/src/main/java/com/renderrichtext/styles/TextAlignment.kt +32 -0
- package/android/src/main/java/com/renderrichtext/styles/ThematicBreakStyle.kt +23 -0
- package/android/src/main/java/com/renderrichtext/styles/UnderlineStyle.kt +17 -0
- package/android/src/main/java/com/renderrichtext/styles/VideoStyle.kt +31 -0
- package/android/src/main/java/com/renderrichtext/utils/common/BorderStyleUtils.kt +86 -0
- package/android/src/main/java/com/renderrichtext/utils/common/BreakStrategyUtils.kt +32 -0
- package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockHighlighter.kt +36 -0
- package/android/src/main/java/com/renderrichtext/utils/common/CodeBlockNode.kt +42 -0
- package/android/src/main/java/com/renderrichtext/utils/common/EllipsizeUtils.kt +27 -0
- package/android/src/main/java/com/renderrichtext/utils/common/EnrichedTiptapAncestorUtils.kt +10 -0
- package/android/src/main/java/com/renderrichtext/utils/common/FeatureFlags.kt +9 -0
- package/android/src/main/java/com/renderrichtext/utils/common/LinkRecognitionProps.kt +31 -0
- package/android/src/main/java/com/renderrichtext/utils/common/LinkRegexConfig.kt +56 -0
- package/android/src/main/java/com/renderrichtext/utils/common/MotionPreferences.kt +18 -0
- package/android/src/main/java/com/renderrichtext/utils/common/ReadableMapExtensions.kt +28 -0
- package/android/src/main/java/com/renderrichtext/utils/common/TiptapViewManagerUtils.kt +242 -0
- package/android/src/main/java/com/renderrichtext/utils/common/layout/RTLUtils.kt +6 -0
- package/android/src/main/java/com/renderrichtext/utils/common/serialization/TiptapASTSerializer.kt +368 -0
- package/android/src/main/java/com/renderrichtext/utils/text/ImageCache.kt +88 -0
- package/android/src/main/java/com/renderrichtext/utils/text/ImageDownloader.kt +203 -0
- package/android/src/main/java/com/renderrichtext/utils/text/LocalImageLoader.kt +167 -0
- package/android/src/main/java/com/renderrichtext/utils/text/TailFadeInAnimator.kt +84 -0
- package/android/src/main/java/com/renderrichtext/utils/text/conversion/HTMLGenerator.kt +1105 -0
- package/android/src/main/java/com/renderrichtext/utils/text/conversion/TiptapExtractor.kt +423 -0
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/ParagraphUtils.kt +9 -0
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/SpannableExtensions.kt +9 -0
- package/android/src/main/java/com/renderrichtext/utils/text/extensions/TextPaintExtensions.kt +76 -0
- package/android/src/main/java/com/renderrichtext/utils/text/interaction/CheckboxTouchHelper.kt +61 -0
- package/android/src/main/java/com/renderrichtext/utils/text/interaction/TaskListTapUtils.kt +286 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/ApplyLineHeight.kt +39 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/BlockSpacing.kt +142 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/SpanFlags.kt +43 -0
- package/android/src/main/java/com/renderrichtext/utils/text/span/WidthAwareSpans.kt +30 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/CodeBlockPressHost.kt +15 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/ImagePressHost.kt +19 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/LinkEvents.kt +88 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/LinkLongPressMovementMethod.kt +449 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/SelectionActionMode.kt +235 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/TextSelectionColors.kt +48 -0
- package/android/src/main/java/com/renderrichtext/utils/text/view/TextViewSetup.kt +119 -0
- package/android/src/main/java/com/renderrichtext/views/ContextMenuPopup.kt +217 -0
- package/android/src/main/java/com/renderrichtext/views/CopyGlyph.kt +101 -0
- package/android/src/main/java/com/renderrichtext/views/WrapGlyph.kt +40 -0
- package/android/src/main/jni/CMakeLists.txt +52 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/ComponentDescriptors.h +30 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextComponentDescriptor.h +29 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.cpp +45 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextMeasurementManager.h +21 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.cpp +38 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextShadowNode.h +53 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.cpp +9 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/TiptapTextState.h +24 -0
- package/android/src/main/jni/react/renderer/components/RichTextSpec/conversions.h +40 -0
- package/cpp/parser/TiptapASTNode.hpp +68 -0
- package/cpp/parser/TiptapParser.cpp +233 -0
- package/cpp/parser/TiptapParser.hpp +13 -0
- package/cpp/vendor/nlohmann/json.hpp +24765 -0
- package/ios/EnrichedTiptapText.h +27 -0
- package/ios/EnrichedTiptapText.mm +1222 -0
- package/ios/attachments/ENRMImageAttachment.h +51 -0
- package/ios/attachments/ENRMImageAttachment.m +398 -0
- package/ios/attachments/ENRMLinkPillAttachment.h +47 -0
- package/ios/attachments/ENRMLinkPillAttachment.m +324 -0
- package/ios/attachments/ENRMLinkPillIconCache.h +28 -0
- package/ios/attachments/ENRMLinkPillIconCache.m +181 -0
- package/ios/attachments/ENRMLinkPillText.h +27 -0
- package/ios/attachments/ENRMLinkPillText.m +118 -0
- package/ios/attachments/ENRMMathInlineAttachment.h +7 -0
- package/ios/attachments/ENRMMathInlineAttachment.m +4 -0
- package/ios/attachments/ThematicBreakAttachment.h +15 -0
- package/ios/attachments/ThematicBreakAttachment.m +33 -0
- package/ios/internals/ENRMViewFreeMeasurement.h +208 -0
- package/ios/internals/EnrichedTiptapTextComponentDescriptor.h +19 -0
- package/ios/internals/EnrichedTiptapTextShadowNode.h +43 -0
- package/ios/internals/EnrichedTiptapTextShadowNode.mm +69 -0
- package/ios/internals/EnrichedTiptapTextState.h +24 -0
- package/ios/internals/MeasurementCache.h +308 -0
- package/ios/internals/ShadowMeasurementUtils.h +145 -0
- package/ios/parser/ENRMTextLinkAttributes.h +7 -0
- package/ios/parser/ENRMTiptapParser.h +33 -0
- package/ios/parser/ENRMTiptapParser.mm +73 -0
- package/ios/parser/TiptapASTNode.h +57 -0
- package/ios/parser/TiptapASTNode.m +52 -0
- package/ios/parser/TiptapParserBridge.mm +179 -0
- package/ios/renderer/AttributedRenderer.h +23 -0
- package/ios/renderer/AttributedRenderer.m +229 -0
- package/ios/renderer/BaseRenderer.h +33 -0
- package/ios/renderer/BaseRenderer.m +46 -0
- package/ios/renderer/BaselineShiftTextAttributes.h +10 -0
- package/ios/renderer/BaselineShiftTextAttributes.m +26 -0
- package/ios/renderer/BlankLineRenderer.h +4 -0
- package/ios/renderer/BlankLineRenderer.m +51 -0
- package/ios/renderer/BlockquoteRenderer.h +6 -0
- package/ios/renderer/BlockquoteRenderer.m +258 -0
- package/ios/renderer/CodeBlockRenderer.h +9 -0
- package/ios/renderer/CodeBlockRenderer.m +99 -0
- package/ios/renderer/CodeRenderer.h +9 -0
- package/ios/renderer/CodeRenderer.m +69 -0
- package/ios/renderer/ENRMBlockquoteTextRenderer.h +25 -0
- package/ios/renderer/ENRMBlockquoteTextRenderer.m +48 -0
- package/ios/renderer/EmphasisRenderer.h +5 -0
- package/ios/renderer/EmphasisRenderer.m +100 -0
- package/ios/renderer/FactoidRenderer.h +9 -0
- package/ios/renderer/FactoidRenderer.m +130 -0
- package/ios/renderer/HeadingRenderer.h +6 -0
- package/ios/renderer/HeadingRenderer.m +103 -0
- package/ios/renderer/HighlightRenderer.h +7 -0
- package/ios/renderer/HighlightRenderer.m +33 -0
- package/ios/renderer/LinkRenderer.h +6 -0
- package/ios/renderer/LinkRenderer.m +158 -0
- package/ios/renderer/ListItemRenderer.h +24 -0
- package/ios/renderer/ListItemRenderer.m +283 -0
- package/ios/renderer/ListRenderer.h +13 -0
- package/ios/renderer/ListRenderer.m +126 -0
- package/ios/renderer/MentionRenderer.h +9 -0
- package/ios/renderer/MentionRenderer.m +96 -0
- package/ios/renderer/NodeRenderer.h +8 -0
- package/ios/renderer/ParagraphRenderer.h +6 -0
- package/ios/renderer/ParagraphRenderer.m +85 -0
- package/ios/renderer/RenderContext.h +145 -0
- package/ios/renderer/RenderContext.m +403 -0
- package/ios/renderer/RendererFactory.h +12 -0
- package/ios/renderer/RendererFactory.m +158 -0
- package/ios/renderer/StrikethroughRenderer.h +5 -0
- package/ios/renderer/StrikethroughRenderer.m +30 -0
- package/ios/renderer/StrongRenderer.h +5 -0
- package/ios/renderer/StrongRenderer.m +88 -0
- package/ios/renderer/TextRenderer.h +6 -0
- package/ios/renderer/TextRenderer.m +18 -0
- package/ios/renderer/ThematicBreakRenderer.h +4 -0
- package/ios/renderer/ThematicBreakRenderer.m +42 -0
- package/ios/renderer/UnderlineRenderer.h +4 -0
- package/ios/renderer/UnderlineRenderer.m +41 -0
- package/ios/styles/ENRMFontSlot.h +12 -0
- package/ios/styles/ENRMFontSlot.m +24 -0
- package/ios/styles/StyleConfig.h +488 -0
- package/ios/styles/StyleConfig.mm +2820 -0
- package/ios/utils/AccessibilityInfo.h +35 -0
- package/ios/utils/AccessibilityInfo.m +24 -0
- package/ios/utils/BlockquoteBorder.h +26 -0
- package/ios/utils/BlockquoteBorder.m +294 -0
- package/ios/utils/CodeBackground.h +19 -0
- package/ios/utils/CodeBackground.m +197 -0
- package/ios/utils/CodeBlockBackground.h +17 -0
- package/ios/utils/CodeBlockBackground.m +103 -0
- package/ios/utils/ContextMenuUtils.h +76 -0
- package/ios/utils/ContextMenuUtils.mm +75 -0
- package/ios/utils/ENRMAccessibilityLabels.h +40 -0
- package/ios/utils/ENRMAccessibilityLabels.m +5 -0
- package/ios/utils/ENRMAsyncRenderCoordinator.h +24 -0
- package/ios/utils/ENRMAsyncRenderCoordinator.m +37 -0
- package/ios/utils/ENRMAtomicSize.h +65 -0
- package/ios/utils/ENRMCodeBlockContent.h +37 -0
- package/ios/utils/ENRMCodeBlockContent.mm +89 -0
- package/ios/utils/ENRMFeatureFlags.h +25 -0
- package/ios/utils/ENRMImageDownloader.h +30 -0
- package/ios/utils/ENRMImageDownloader.m +168 -0
- package/ios/utils/ENRMLatexErrorCoordinator.h +27 -0
- package/ios/utils/ENRMLatexErrorCoordinator.mm +75 -0
- package/ios/utils/ENRMLatexErrorReporting.h +13 -0
- package/ios/utils/ENRMLinkContextMenus.h +93 -0
- package/ios/utils/ENRMLinkContextMenus.m +178 -0
- package/ios/utils/ENRMLinkRegexConfig.h +30 -0
- package/ios/utils/ENRMLinkRegexConfig.mm +104 -0
- package/ios/utils/ENRMLinkRegexProps.h +23 -0
- package/ios/utils/ENRMLocalImageLoader.h +35 -0
- package/ios/utils/ENRMLocalImageLoader.m +111 -0
- package/ios/utils/ENRMMenuAction.h +27 -0
- package/ios/utils/ENRMMenuAction.m +33 -0
- package/ios/utils/ENRMSpoilerOverlayManager.h +15 -0
- package/ios/utils/ENRMSpoilerOverlayManager.m +19 -0
- package/ios/utils/ENRMSpoilerTapUtils.h +6 -0
- package/ios/utils/ENRMSpoilerTapUtils.m +11 -0
- package/ios/utils/ENRMTailFadeInAnimator.h +16 -0
- package/ios/utils/ENRMTailFadeInAnimator.m +161 -0
- package/ios/utils/ENRMTextHitTest.h +23 -0
- package/ios/utils/ENRMTextHitTest.m +20 -0
- package/ios/utils/ENRMTextInteractionUtils.h +20 -0
- package/ios/utils/ENRMTextInteractionUtils.m +15 -0
- package/ios/utils/ENRMTextRenderer.h +38 -0
- package/ios/utils/ENRMTextRenderer.m +54 -0
- package/ios/utils/ENRMTextViewSetup.h +132 -0
- package/ios/utils/ENRMUIKit.h +429 -0
- package/ios/utils/EditMenuUtils.h +105 -0
- package/ios/utils/EditMenuUtils.m +143 -0
- package/ios/utils/FactoidBackground.h +20 -0
- package/ios/utils/FactoidBackground.m +161 -0
- package/ios/utils/FontScaleObserver.h +10 -0
- package/ios/utils/FontScaleObserver.m +55 -0
- package/ios/utils/FontUtils.h +31 -0
- package/ios/utils/FontUtils.m +63 -0
- package/ios/utils/HTMLGenerator.h +25 -0
- package/ios/utils/HTMLGenerator.m +1041 -0
- package/ios/utils/HeightUpdateUtils.h +25 -0
- package/ios/utils/ImageRequestHeaderUtils.h +35 -0
- package/ios/utils/LastElementUtils.h +97 -0
- package/ios/utils/LinkContextMenuUtils.h +55 -0
- package/ios/utils/LinkPillContentUtils.h +46 -0
- package/ios/utils/LinkTapUtils.h +56 -0
- package/ios/utils/LinkTapUtils.m +139 -0
- package/ios/utils/ListMarkerDrawer.h +15 -0
- package/ios/utils/ListMarkerDrawer.m +239 -0
- package/ios/utils/MentionBackground.h +26 -0
- package/ios/utils/MentionBackground.m +86 -0
- package/ios/utils/ParagraphStyleUtils.h +87 -0
- package/ios/utils/ParagraphStyleUtils.m +484 -0
- package/ios/utils/PasteboardUtils.h +48 -0
- package/ios/utils/PasteboardUtils.m +166 -0
- package/ios/utils/RTFExportUtils.h +24 -0
- package/ios/utils/RTFExportUtils.m +299 -0
- package/ios/utils/RuntimeKeys.h +44 -0
- package/ios/utils/RuntimeKeys.m +13 -0
- package/ios/utils/SelectionColorUtils.h +14 -0
- package/ios/utils/StylePropsUtils.h +1340 -0
- package/ios/utils/TaskListTapUtils.h +6 -0
- package/ios/utils/TaskListTapUtils.m +14 -0
- package/ios/utils/TextViewLayoutManager.h +14 -0
- package/ios/utils/TextViewLayoutManager.mm +198 -0
- package/ios/utils/TiptapAccessibilityElementBuilder.h +43 -0
- package/ios/utils/TiptapAccessibilityElementBuilder.m +432 -0
- package/ios/utils/TiptapExtractor.h +17 -0
- package/ios/utils/TiptapExtractor.m +367 -0
- package/ios/views/ENRMTiptapTextView.h +25 -0
- package/ios/views/ENRMTiptapTextView.m +247 -0
- package/package.json +70 -6
- package/react-native.config.js +10 -0
- package/src/EnrichedTiptapTextNativeComponent.ts +739 -0
- package/src/RichText.tsx +164 -0
- package/src/admonitionDefaults.ts +58 -0
- package/src/darkModeTextStyle.ts +99 -0
- package/src/headingDefaults.ts +21 -0
- package/src/index.tsx +4 -0
- package/src/linkVariantUtils.ts +120 -0
- package/src/normalizeTiptapStyle.ts +397 -0
- package/src/rn-globals.d.ts +1 -0
- package/src/styleUtils.ts +107 -0
- package/src/types/TiptapStyle.ts +532 -0
- package/src/types/TiptapStyleInternal.ts +299 -0
- package/src/types/linkPill.ts +9 -0
- package/src/utils/isDev.ts +4 -0
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
package com.renderrichtext.utils.text.interaction
|
|
2
|
+
|
|
3
|
+
import android.text.Spannable
|
|
4
|
+
import android.text.SpannableStringBuilder
|
|
5
|
+
import android.text.Spanned
|
|
6
|
+
import android.text.style.ForegroundColorSpan
|
|
7
|
+
import android.text.style.StrikethroughSpan
|
|
8
|
+
import android.widget.TextView
|
|
9
|
+
import com.renderrichtext.renderer.SpanStyleCache
|
|
10
|
+
import com.renderrichtext.spans.BaseListSpan
|
|
11
|
+
import com.renderrichtext.spans.CodeBlockSpan
|
|
12
|
+
import com.renderrichtext.spans.ListMarkerAnchorSpan
|
|
13
|
+
import com.renderrichtext.spans.TaskListSpan
|
|
14
|
+
import com.renderrichtext.styles.StyleConfig
|
|
15
|
+
import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
|
|
16
|
+
|
|
17
|
+
data class TaskListHitTestResult(
|
|
18
|
+
val taskIndex: Int,
|
|
19
|
+
val checked: Boolean,
|
|
20
|
+
val itemText: String,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
object TaskListToggleUtils {
|
|
24
|
+
private val TASK_PATTERN = Regex("""^([ \t]*[-*+][ \t]+)\[[ xX]]""", RegexOption.MULTILINE)
|
|
25
|
+
|
|
26
|
+
fun toggleAtIndex(
|
|
27
|
+
tiptap: String,
|
|
28
|
+
index: Int,
|
|
29
|
+
checked: Boolean,
|
|
30
|
+
): String {
|
|
31
|
+
val matches = TASK_PATTERN.findAll(tiptap).toList()
|
|
32
|
+
if (index < 0 || index >= matches.size) return tiptap
|
|
33
|
+
|
|
34
|
+
val match = matches[index]
|
|
35
|
+
val prefix = match.groupValues[1]
|
|
36
|
+
|
|
37
|
+
val replacement = "$prefix[${if (checked) "x" else " "}]"
|
|
38
|
+
|
|
39
|
+
return tiptap.replaceRange(match.range, replacement)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
object TaskListTapUtils {
|
|
44
|
+
fun hitTest(
|
|
45
|
+
textView: TextView,
|
|
46
|
+
rawX: Float,
|
|
47
|
+
rawY: Float,
|
|
48
|
+
): TaskListHitTestResult? =
|
|
49
|
+
with(textView) {
|
|
50
|
+
val layout = layout ?: return null
|
|
51
|
+
val spannable = text as? Spanned ?: return null
|
|
52
|
+
|
|
53
|
+
val x = rawX.toInt() - totalPaddingLeft + scrollX
|
|
54
|
+
val y = rawY.toInt() - totalPaddingTop + scrollY
|
|
55
|
+
|
|
56
|
+
val line = layout.getLineForVertical(y)
|
|
57
|
+
|
|
58
|
+
val taskSpan =
|
|
59
|
+
spannable
|
|
60
|
+
.getSpans(
|
|
61
|
+
layout.getLineStart(line),
|
|
62
|
+
layout.getLineEnd(line),
|
|
63
|
+
TaskListSpan::class.java,
|
|
64
|
+
).maxByOrNull { it.depth } ?: return null
|
|
65
|
+
|
|
66
|
+
val isRtl = layout.getParagraphDirection(line) == android.text.Layout.DIR_RIGHT_TO_LEFT
|
|
67
|
+
if (isRtl) {
|
|
68
|
+
val lineRight = layout.getLineRight(line).toInt()
|
|
69
|
+
val indentWidth = lineRight - layout.getParagraphRight(line).toInt()
|
|
70
|
+
if (x <= layout.width - indentWidth) return null
|
|
71
|
+
} else {
|
|
72
|
+
val lineLeft = layout.getLineLeft(line).toInt()
|
|
73
|
+
val indentWidth = layout.getParagraphLeft(line).toInt() - lineLeft
|
|
74
|
+
if (x >= indentWidth) return null
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
val spanStart = spannable.getSpanStart(taskSpan)
|
|
78
|
+
val spanEnd = spannable.getSpanEnd(taskSpan)
|
|
79
|
+
|
|
80
|
+
val itemText =
|
|
81
|
+
spannable
|
|
82
|
+
.subSequence(spanStart, spanEnd)
|
|
83
|
+
.toString()
|
|
84
|
+
.substringBefore('\n')
|
|
85
|
+
.trim()
|
|
86
|
+
|
|
87
|
+
return TaskListHitTestResult(
|
|
88
|
+
taskIndex = taskSpan.taskIndex,
|
|
89
|
+
checked = taskSpan.isChecked,
|
|
90
|
+
itemText = itemText,
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
fun updateTaskListItemCheckedState(
|
|
95
|
+
textView: TextView,
|
|
96
|
+
targetIndex: Int,
|
|
97
|
+
newChecked: Boolean,
|
|
98
|
+
styleConfig: StyleConfig,
|
|
99
|
+
): Boolean {
|
|
100
|
+
val text = textView.text
|
|
101
|
+
if (text !is Spannable) {
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
val spannable = SpannableStringBuilder(text)
|
|
106
|
+
val targetSpans =
|
|
107
|
+
spannable
|
|
108
|
+
.getSpans(0, spannable.length, TaskListSpan::class.java)
|
|
109
|
+
.filter { it.taskIndex == targetIndex }
|
|
110
|
+
if (targetSpans.isEmpty()) {
|
|
111
|
+
return false
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (targetSpans.all { it.isChecked == newChecked }) {
|
|
115
|
+
return true
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
val itemDepth = targetSpans.first().depth
|
|
119
|
+
val spanStart = targetSpans.minOf { spannable.getSpanStart(it) }
|
|
120
|
+
val spanEnd = targetSpans.maxOf { spannable.getSpanEnd(it) }
|
|
121
|
+
|
|
122
|
+
val styleCache = SpanStyleCache(styleConfig)
|
|
123
|
+
|
|
124
|
+
fun toggledCopy(old: TaskListSpan): TaskListSpan =
|
|
125
|
+
TaskListSpan(
|
|
126
|
+
taskStyle = styleConfig.taskListStyle,
|
|
127
|
+
listStyle = styleConfig.listStyle,
|
|
128
|
+
depth = old.depth,
|
|
129
|
+
context = textView.context,
|
|
130
|
+
styleCache = styleCache,
|
|
131
|
+
taskIndex = targetIndex,
|
|
132
|
+
isChecked = newChecked,
|
|
133
|
+
drawsMarker = old.drawsMarker,
|
|
134
|
+
ancestorMarkerOffset = old.ancestorMarkerOffset,
|
|
135
|
+
markerExtraWidth = old.markerExtraWidth,
|
|
136
|
+
parentMarkerWidth = old.parentMarkerWidth,
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
// Layout draws leading margins in insertion order, and a re-added span moves to the end of it.
|
|
140
|
+
// Re-add the task's spans and everything attached after them (its ancestors) in their original
|
|
141
|
+
// order; the task's own nested items were attached first and stay where they are.
|
|
142
|
+
data class ListSpanState(
|
|
143
|
+
val span: BaseListSpan,
|
|
144
|
+
val start: Int,
|
|
145
|
+
val end: Int,
|
|
146
|
+
val flags: Int,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
val coveringSpans = spannable.getSpans(spanStart, spanEnd, BaseListSpan::class.java)
|
|
150
|
+
val firstTarget = coveringSpans.indexOfFirst { it is TaskListSpan && it.taskIndex == targetIndex }
|
|
151
|
+
val listSpans =
|
|
152
|
+
coveringSpans.drop(firstTarget).map {
|
|
153
|
+
ListSpanState(it, spannable.getSpanStart(it), spannable.getSpanEnd(it), spannable.getSpanFlags(it))
|
|
154
|
+
}
|
|
155
|
+
listSpans.forEach { spannable.removeSpan(it.span) }
|
|
156
|
+
for ((old, start, end, flags) in listSpans) {
|
|
157
|
+
val replacement = if (old is TaskListSpan && old.taskIndex == targetIndex) toggledCopy(old) else old
|
|
158
|
+
spannable.setSpan(replacement, start, end, flags)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// The marker may be drawn by an anchor span wrapping an unattached copy
|
|
162
|
+
spannable
|
|
163
|
+
.getSpans(0, spannable.length, ListMarkerAnchorSpan::class.java)
|
|
164
|
+
.filter { (it.marker as? TaskListSpan)?.taskIndex == targetIndex }
|
|
165
|
+
.forEach { old ->
|
|
166
|
+
val start = spannable.getSpanStart(old)
|
|
167
|
+
val end = spannable.getSpanEnd(old)
|
|
168
|
+
spannable.removeSpan(old)
|
|
169
|
+
spannable.setSpan(
|
|
170
|
+
ListMarkerAnchorSpan(toggledCopy(old.marker as TaskListSpan)),
|
|
171
|
+
start,
|
|
172
|
+
end,
|
|
173
|
+
SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
val taskStyle = styleConfig.taskListStyle
|
|
178
|
+
val checkedTextColor = taskStyle.checkedTextColor
|
|
179
|
+
val strikethrough = taskStyle.checkedStrikethrough
|
|
180
|
+
|
|
181
|
+
val excludedRanges =
|
|
182
|
+
(
|
|
183
|
+
spannable.getSpans(spanStart, spanEnd, BaseListSpan::class.java).filter { it.depth > itemDepth } +
|
|
184
|
+
spannable.getSpans(spanStart, spanEnd, CodeBlockSpan::class.java).toList()
|
|
185
|
+
).map { spannable.getSpanStart(it) to spannable.getSpanEnd(it) }
|
|
186
|
+
.sortedBy { it.first }
|
|
187
|
+
|
|
188
|
+
applyDecorationsToRanges(
|
|
189
|
+
spannable = spannable,
|
|
190
|
+
spanStart = spanStart,
|
|
191
|
+
spanEnd = spanEnd,
|
|
192
|
+
excludedRanges = excludedRanges,
|
|
193
|
+
isChecked = newChecked,
|
|
194
|
+
checkedTextColor = checkedTextColor,
|
|
195
|
+
strikethrough = strikethrough,
|
|
196
|
+
styleConfig = styleConfig,
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
val imageSpans = text.getSpans(0, text.length, com.renderrichtext.spans.ImageSpan::class.java).toList()
|
|
200
|
+
val originalSpanStarts = imageSpans.associateWith { text.getSpanStart(it) }
|
|
201
|
+
|
|
202
|
+
textView.text = spannable
|
|
203
|
+
|
|
204
|
+
val newImageSpans = spannable.getSpans(0, spannable.length, com.renderrichtext.spans.ImageSpan::class.java)
|
|
205
|
+
imageSpans.forEach { originalSpan ->
|
|
206
|
+
val matchingSpan =
|
|
207
|
+
newImageSpans.firstOrNull {
|
|
208
|
+
it.imageUrl == originalSpan.imageUrl &&
|
|
209
|
+
spannable.getSpanStart(it) == originalSpanStarts[originalSpan]
|
|
210
|
+
}
|
|
211
|
+
(matchingSpan ?: originalSpan).registerTextView(textView)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
textView.invalidate()
|
|
215
|
+
|
|
216
|
+
return true
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private fun applyDecorationsToRanges(
|
|
220
|
+
spannable: Spannable,
|
|
221
|
+
spanStart: Int,
|
|
222
|
+
spanEnd: Int,
|
|
223
|
+
excludedRanges: List<Pair<Int, Int>>,
|
|
224
|
+
isChecked: Boolean,
|
|
225
|
+
checkedTextColor: Int,
|
|
226
|
+
strikethrough: Boolean,
|
|
227
|
+
styleConfig: StyleConfig,
|
|
228
|
+
) {
|
|
229
|
+
var currentPos = spanStart
|
|
230
|
+
for ((start, end) in excludedRanges) {
|
|
231
|
+
if (start > currentPos) {
|
|
232
|
+
if (isChecked) {
|
|
233
|
+
applyCheckedSpans(spannable, currentPos, start, checkedTextColor, strikethrough)
|
|
234
|
+
} else {
|
|
235
|
+
removeCheckedSpans(spannable, currentPos, start, styleConfig)
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
currentPos = maxOf(currentPos, end)
|
|
239
|
+
}
|
|
240
|
+
if (currentPos < spanEnd) {
|
|
241
|
+
if (isChecked) {
|
|
242
|
+
applyCheckedSpans(spannable, currentPos, spanEnd, checkedTextColor, strikethrough)
|
|
243
|
+
} else {
|
|
244
|
+
removeCheckedSpans(spannable, currentPos, spanEnd, styleConfig)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
private fun applyCheckedSpans(
|
|
250
|
+
spannable: Spannable,
|
|
251
|
+
start: Int,
|
|
252
|
+
end: Int,
|
|
253
|
+
color: Int,
|
|
254
|
+
strikethrough: Boolean,
|
|
255
|
+
) {
|
|
256
|
+
if (color != 0) {
|
|
257
|
+
spannable.setSpan(ForegroundColorSpan(color), start, end, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
|
|
258
|
+
}
|
|
259
|
+
if (strikethrough) {
|
|
260
|
+
spannable.setSpan(StrikethroughSpan(), start, end, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
private fun removeCheckedSpans(
|
|
265
|
+
spannable: Spannable,
|
|
266
|
+
start: Int,
|
|
267
|
+
end: Int,
|
|
268
|
+
styleConfig: StyleConfig,
|
|
269
|
+
) {
|
|
270
|
+
val strikethroughSpans = spannable.getSpans(start, end, StrikethroughSpan::class.java)
|
|
271
|
+
strikethroughSpans.forEach { spannable.removeSpan(it) }
|
|
272
|
+
|
|
273
|
+
val colorSpans = spannable.getSpans(start, end, ForegroundColorSpan::class.java)
|
|
274
|
+
colorSpans.forEach { spannable.removeSpan(it) }
|
|
275
|
+
|
|
276
|
+
val listStyleColor = styleConfig.listStyle.color
|
|
277
|
+
if (listStyleColor != 0) {
|
|
278
|
+
spannable.setSpan(
|
|
279
|
+
ForegroundColorSpan(listStyleColor),
|
|
280
|
+
start,
|
|
281
|
+
end,
|
|
282
|
+
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE,
|
|
283
|
+
)
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package com.renderrichtext.utils.text.span
|
|
2
|
+
|
|
3
|
+
import android.graphics.Paint.FontMetricsInt
|
|
4
|
+
import kotlin.math.ceil
|
|
5
|
+
import kotlin.math.floor
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Splits extra leading above and below the line, copied from RN
|
|
9
|
+
* CustomLineHeightSpan so we do not depend on React Native internal span types.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors CustomLineHeightSpan.chooseHeight:
|
|
12
|
+
* https://github.com/react/react-native/blob/v0.86.2/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/views/text/internal/span/CustomLineHeightSpan.kt#L44-L57
|
|
13
|
+
*
|
|
14
|
+
* Unlike RN, [lineHeight] is a floor, not a clamp: taller content grows the
|
|
15
|
+
* line instead of being clipped (for example, inline code taller than the paragraph).
|
|
16
|
+
*/
|
|
17
|
+
internal fun applyLineHeight(
|
|
18
|
+
fm: FontMetricsInt,
|
|
19
|
+
lineHeight: Int,
|
|
20
|
+
start: Int,
|
|
21
|
+
end: Int,
|
|
22
|
+
textLength: Int,
|
|
23
|
+
) {
|
|
24
|
+
val leading = lineHeight - ((-fm.ascent) + fm.descent)
|
|
25
|
+
|
|
26
|
+
// Use lineHeight as a floor: if the default line-height is greater, don't
|
|
27
|
+
// override it
|
|
28
|
+
if (leading > 0) {
|
|
29
|
+
fm.ascent -= ceil(leading / 2.0f).toInt()
|
|
30
|
+
fm.descent += floor(leading / 2.0f).toInt()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (start == 0) {
|
|
34
|
+
fm.top = fm.ascent
|
|
35
|
+
}
|
|
36
|
+
if (end == textLength) {
|
|
37
|
+
fm.bottom = fm.descent
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
package com.renderrichtext.utils.text.span
|
|
2
|
+
|
|
3
|
+
import android.text.SpannableStringBuilder
|
|
4
|
+
import com.renderrichtext.spans.CodeBlockSpan
|
|
5
|
+
import com.renderrichtext.spans.ImageSpan
|
|
6
|
+
import com.renderrichtext.spans.LineHeightSpan
|
|
7
|
+
import com.renderrichtext.spans.LinkPillSpan
|
|
8
|
+
import com.renderrichtext.spans.MarginBottomSpan
|
|
9
|
+
import com.renderrichtext.utils.text.span.SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE
|
|
10
|
+
import android.text.style.LineHeightSpan as AndroidLineHeightSpan
|
|
11
|
+
|
|
12
|
+
fun createLineHeightSpan(lineHeight: Float): AndroidLineHeightSpan = LineHeightSpan(lineHeight)
|
|
13
|
+
|
|
14
|
+
/** The line height floor that link pills ask of their paragraph. */
|
|
15
|
+
private class LinkPillLineHeightSpan(
|
|
16
|
+
height: Float,
|
|
17
|
+
) : AndroidLineHeightSpan by LineHeightSpan(height)
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Raises the line height of every paragraph in [start]..[end] to the largest
|
|
21
|
+
* `pill.lineHeight` among its link pills. A line grows to fit a pill on its own, but only
|
|
22
|
+
* that line and only to the pill's box, so pills on consecutive lines touch. A style that
|
|
23
|
+
* sets `pill.lineHeight` gets a floor for the whole paragraph instead, which keeps its
|
|
24
|
+
* lines even and the pills apart.
|
|
25
|
+
*
|
|
26
|
+
* Nested blocks set line height over ranges that overlap, so a paragraph that already
|
|
27
|
+
* has its floor is left alone. [spanFlags] are those of the block's own line height span:
|
|
28
|
+
* line height spans apply in order, and the floor has to come before the spans that add a
|
|
29
|
+
* margin to a line, or it would swallow that margin.
|
|
30
|
+
*/
|
|
31
|
+
fun applyLinkPillLineHeight(
|
|
32
|
+
builder: SpannableStringBuilder,
|
|
33
|
+
start: Int,
|
|
34
|
+
end: Int,
|
|
35
|
+
spanFlags: Int = SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
36
|
+
) {
|
|
37
|
+
val heights = HashMap<Int, Float>()
|
|
38
|
+
for (pill in builder.getSpans(start, end, LinkPillSpan::class.java)) {
|
|
39
|
+
val pillStart = builder.getSpanStart(pill)
|
|
40
|
+
// getSpans also returns a pill that only touches the range.
|
|
41
|
+
if (pill.lineHeight <= 0 || pillStart < start || pillStart >= end) continue
|
|
42
|
+
val paragraphStart = builder.lastIndexOf('\n', pillStart - 1).let { if (it < start) start else it + 1 }
|
|
43
|
+
heights[paragraphStart] = maxOf(heights[paragraphStart] ?: 0f, pill.lineHeight)
|
|
44
|
+
}
|
|
45
|
+
for ((paragraphStart, height) in heights) {
|
|
46
|
+
val paragraphEnd = builder.indexOf('\n', paragraphStart).let { if (it < 0 || it >= end) end else it + 1 }
|
|
47
|
+
// getSpans also returns the floor of a neighbouring paragraph, which only touches this one.
|
|
48
|
+
val hasFloor =
|
|
49
|
+
builder
|
|
50
|
+
.getSpans(paragraphStart, paragraphEnd, LinkPillLineHeightSpan::class.java)
|
|
51
|
+
.any { builder.getSpanStart(it) <= paragraphStart && builder.getSpanEnd(it) > paragraphStart }
|
|
52
|
+
if (hasFloor) continue
|
|
53
|
+
builder.setSpan(LinkPillLineHeightSpan(height), paragraphStart, paragraphEnd, spanFlags)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Applies [LineHeightSpan] to [start]..[end] but skips ranges occupied by
|
|
59
|
+
* block [ImageSpan]s (and their trailing '\n') and by [CodeBlockSpan]s —
|
|
60
|
+
* both of these apply their own font-metric adjustments that a fixed
|
|
61
|
+
* line height would override.
|
|
62
|
+
*/
|
|
63
|
+
fun applyLineHeightSkippingImages(
|
|
64
|
+
builder: SpannableStringBuilder,
|
|
65
|
+
start: Int,
|
|
66
|
+
end: Int,
|
|
67
|
+
lineHeight: Float,
|
|
68
|
+
spanFlags: Int = SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
69
|
+
) {
|
|
70
|
+
val blockImageRanges =
|
|
71
|
+
builder
|
|
72
|
+
.getSpans(start, end, ImageSpan::class.java)
|
|
73
|
+
.filter { !it.isInline }
|
|
74
|
+
.map { builder.getSpanStart(it) to builder.getSpanEnd(it) }
|
|
75
|
+
|
|
76
|
+
val codeBlockRanges =
|
|
77
|
+
builder
|
|
78
|
+
.getSpans(start, end, CodeBlockSpan::class.java)
|
|
79
|
+
.map { builder.getSpanStart(it) to builder.getSpanEnd(it) }
|
|
80
|
+
|
|
81
|
+
val excludedRanges = (blockImageRanges + codeBlockRanges).sortedBy { it.first }
|
|
82
|
+
|
|
83
|
+
var pos = start
|
|
84
|
+
for ((exStart, exEnd) in excludedRanges) {
|
|
85
|
+
if (pos < exStart) {
|
|
86
|
+
builder.setSpan(
|
|
87
|
+
createLineHeightSpan(lineHeight),
|
|
88
|
+
pos,
|
|
89
|
+
exStart,
|
|
90
|
+
spanFlags,
|
|
91
|
+
)
|
|
92
|
+
}
|
|
93
|
+
val skipEnd = if (exEnd < end && builder[exEnd] == '\n') exEnd + 1 else exEnd
|
|
94
|
+
pos = maxOf(pos, skipEnd)
|
|
95
|
+
}
|
|
96
|
+
if (pos < end) {
|
|
97
|
+
builder.setSpan(
|
|
98
|
+
createLineHeightSpan(lineHeight),
|
|
99
|
+
pos,
|
|
100
|
+
end,
|
|
101
|
+
spanFlags,
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
applyLinkPillLineHeight(builder, start, end, spanFlags)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
fun applyMarginTop(
|
|
108
|
+
builder: SpannableStringBuilder,
|
|
109
|
+
insertionPoint: Int,
|
|
110
|
+
marginTop: Float,
|
|
111
|
+
) {
|
|
112
|
+
if (marginTop <= 0) return
|
|
113
|
+
|
|
114
|
+
// Insert a newline character to act as a vertical spacer
|
|
115
|
+
builder.insert(insertionPoint, "\n")
|
|
116
|
+
|
|
117
|
+
// Apply MarginBottomSpan to the spacer character to create the gap before the content
|
|
118
|
+
builder.setSpan(
|
|
119
|
+
MarginBottomSpan(marginTop),
|
|
120
|
+
insertionPoint,
|
|
121
|
+
insertionPoint + 1,
|
|
122
|
+
SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
fun applyMarginBottom(
|
|
127
|
+
builder: SpannableStringBuilder,
|
|
128
|
+
marginBottom: Float,
|
|
129
|
+
) {
|
|
130
|
+
val spacerStart = builder.length
|
|
131
|
+
builder.append("\n")
|
|
132
|
+
// Always create a MarginBottomSpan, even when marginBottom = 0.
|
|
133
|
+
// This ensures removeTrailingMargin can correctly identify the LAST element's
|
|
134
|
+
// margin value. Without a span on the last element, it would pick up a previous
|
|
135
|
+
// element's span (e.g. blockquote with marginBottom: 16) and use that wrong value.
|
|
136
|
+
builder.setSpan(
|
|
137
|
+
MarginBottomSpan(marginBottom),
|
|
138
|
+
spacerStart,
|
|
139
|
+
builder.length,
|
|
140
|
+
SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE,
|
|
141
|
+
)
|
|
142
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
package com.renderrichtext.utils.text.span
|
|
2
|
+
|
|
3
|
+
import android.text.SpannableString
|
|
4
|
+
import android.text.Spanned
|
|
5
|
+
|
|
6
|
+
const val SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE = SpannableString.SPAN_EXCLUSIVE_EXCLUSIVE
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* `SPAN_EXCLUSIVE_EXCLUSIVE` with the maximum span priority.
|
|
10
|
+
*
|
|
11
|
+
* Higher-priority spans are iterated — and therefore applied and drawn — first,
|
|
12
|
+
* so lower-priority spans over the same range override them. Use this for a
|
|
13
|
+
* block span set after its children that must not override the inline spans
|
|
14
|
+
* inside it (e.g. [HeadingSpan]), or for a container background that must sit
|
|
15
|
+
* under inline pill/chip backgrounds (e.g. [BlockquoteSpan]).
|
|
16
|
+
*/
|
|
17
|
+
const val SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE_PRIORITY =
|
|
18
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE or Spanned.SPAN_PRIORITY
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* `SPAN_EXCLUSIVE_EXCLUSIVE` with the maximum span priority, for a block-wide
|
|
22
|
+
* LineHeightSpan (e.g. a blockquote's uniform line height) that must be iterated
|
|
23
|
+
* BEFORE the per-block [MarginBottomSpan]s it overlaps. LineHeightSpans are
|
|
24
|
+
* applied in iteration order and higher priority iterates first, so this span
|
|
25
|
+
* sets the base line height and the lower-priority margin span then adds its gap
|
|
26
|
+
* on top instead of being normalized away. Without it a line-height pass applied
|
|
27
|
+
* after the margins (e.g. in BlockquoteTextRenderer.postProcess) would erase them
|
|
28
|
+
* and clip the first line's ascent.
|
|
29
|
+
*/
|
|
30
|
+
const val SPAN_FLAGS_LINE_HEIGHT_PRIORITY =
|
|
31
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE or Spanned.SPAN_PRIORITY
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* `SPAN_EXCLUSIVE_EXCLUSIVE` with a span priority just above the default.
|
|
35
|
+
*
|
|
36
|
+
* List item spans are set after the item's children, so at the default
|
|
37
|
+
* priority they apply after inline spans such as `CodeSpan` and replace their
|
|
38
|
+
* font and size. This priority applies them first, while staying below
|
|
39
|
+
* [SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE_PRIORITY] so a blockquote still draws its
|
|
40
|
+
* leading margin before a list's.
|
|
41
|
+
*/
|
|
42
|
+
const val SPAN_FLAGS_LIST_ITEM =
|
|
43
|
+
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE or (1 shl Spanned.SPAN_PRIORITY_SHIFT)
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
package com.renderrichtext.utils.text.span
|
|
2
|
+
|
|
3
|
+
import android.text.Spanned
|
|
4
|
+
import com.renderrichtext.spans.ImageSpan
|
|
5
|
+
import com.renderrichtext.spans.LinkPillSpan
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Primes every span whose size depends on the layout width (pills, images). Call it
|
|
9
|
+
* before building any layout over [text]. [includeImages] is off for a visible text
|
|
10
|
+
* view, whose image spans size themselves from the view they are registered with.
|
|
11
|
+
*
|
|
12
|
+
* Returns true when a pill's width limit changed, i.e. an existing layout is stale.
|
|
13
|
+
*/
|
|
14
|
+
fun prepareWidthAwareSpans(
|
|
15
|
+
text: CharSequence?,
|
|
16
|
+
widthPx: Int,
|
|
17
|
+
includeImages: Boolean = true,
|
|
18
|
+
): Boolean {
|
|
19
|
+
val spanned = text as? Spanned ?: return false
|
|
20
|
+
// widthPx == 1 is the coerceAtLeast(1) fallback for a not-yet-measured view. Priming
|
|
21
|
+
// with it would squeeze pills on screen to a sliver until the next real measure.
|
|
22
|
+
if (widthPx <= 1) return false
|
|
23
|
+
val pillsChanged = LinkPillSpan.prepareForMeasurement(spanned, widthPx)
|
|
24
|
+
if (includeImages) {
|
|
25
|
+
spanned
|
|
26
|
+
.getSpans(0, spanned.length, ImageSpan::class.java)
|
|
27
|
+
.forEach { it.prepareForMeasurement(spanned, widthPx) }
|
|
28
|
+
}
|
|
29
|
+
return pillsChanged
|
|
30
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
package com.renderrichtext.utils.text.view
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Implemented by the commonmark host view so [LinkLongPressMovementMethod] can
|
|
5
|
+
* gate and dispatch taps landing on a code block span. GFM uses a separate
|
|
6
|
+
* CodeBlockContainerView, so its host walk resolves to none.
|
|
7
|
+
*/
|
|
8
|
+
interface CodeBlockPressHost {
|
|
9
|
+
val codeBlockPressEnabled: Boolean
|
|
10
|
+
|
|
11
|
+
fun emitOnCodeBlockPress(
|
|
12
|
+
code: String,
|
|
13
|
+
language: String,
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
package com.renderrichtext.utils.text.view
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Implemented by the React-tagged host views (EnrichedTiptapText and the GFM
|
|
5
|
+
* EnrichedTiptap container) so [LinkLongPressMovementMethod] can gate and
|
|
6
|
+
* dispatch image taps without knowing which flavor owns the tapped TextView.
|
|
7
|
+
*
|
|
8
|
+
* In GFM the tapped TextView is an internal segment child, so the movement
|
|
9
|
+
* method walks up to the nearest host - the same routing ImageSpan uses for its
|
|
10
|
+
* box-height notifications.
|
|
11
|
+
*/
|
|
12
|
+
interface ImagePressHost {
|
|
13
|
+
val imagePressEnabled: Boolean
|
|
14
|
+
|
|
15
|
+
fun emitOnImagePress(
|
|
16
|
+
url: String,
|
|
17
|
+
altText: String,
|
|
18
|
+
)
|
|
19
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
package com.renderrichtext.utils.text.view
|
|
2
|
+
|
|
3
|
+
import android.view.MotionEvent
|
|
4
|
+
import android.view.View
|
|
5
|
+
import android.widget.TextView
|
|
6
|
+
import com.facebook.react.bridge.ReactContext
|
|
7
|
+
import com.facebook.react.uimanager.UIManagerHelper
|
|
8
|
+
import com.facebook.react.uimanager.events.NativeGestureUtil
|
|
9
|
+
import com.renderrichtext.events.FactoidPressEvent
|
|
10
|
+
import com.renderrichtext.events.ImagePressEvent
|
|
11
|
+
import com.renderrichtext.events.LinkLongPressEvent
|
|
12
|
+
import com.renderrichtext.events.LinkPressEvent
|
|
13
|
+
import com.renderrichtext.events.MentionLongPressEvent
|
|
14
|
+
import com.renderrichtext.events.MentionPressEvent
|
|
15
|
+
|
|
16
|
+
fun View.emitMentionPressEvent(
|
|
17
|
+
mentionId: String,
|
|
18
|
+
label: String,
|
|
19
|
+
) {
|
|
20
|
+
val reactContext = context as? ReactContext ?: return
|
|
21
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
22
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
23
|
+
dispatcher?.dispatchEvent(MentionPressEvent(surfaceId, id, mentionId, label))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
fun View.emitMentionLongPressEvent(
|
|
27
|
+
mentionId: String,
|
|
28
|
+
label: String,
|
|
29
|
+
) {
|
|
30
|
+
val reactContext = context as? ReactContext ?: return
|
|
31
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
32
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
33
|
+
dispatcher?.dispatchEvent(MentionLongPressEvent(surfaceId, id, mentionId, label))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fun View.emitFactoidPressEvent(factoidId: String) {
|
|
37
|
+
val reactContext = context as? ReactContext ?: return
|
|
38
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
39
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
40
|
+
dispatcher?.dispatchEvent(FactoidPressEvent(surfaceId, id, factoidId))
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
fun View.emitLinkPressEvent(url: String) {
|
|
44
|
+
val reactContext = context as? ReactContext ?: return
|
|
45
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
46
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
47
|
+
dispatcher?.dispatchEvent(LinkPressEvent(surfaceId, id, url))
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
fun View.emitImagePressEvent(
|
|
51
|
+
url: String,
|
|
52
|
+
altText: String,
|
|
53
|
+
) {
|
|
54
|
+
val reactContext = context as? ReactContext ?: return
|
|
55
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
56
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
57
|
+
dispatcher?.dispatchEvent(ImagePressEvent(surfaceId, id, url, altText))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
fun View.emitLinkLongPressEvent(url: String) {
|
|
61
|
+
val reactContext = context as? ReactContext ?: return
|
|
62
|
+
val surfaceId = UIManagerHelper.getSurfaceId(reactContext)
|
|
63
|
+
val dispatcher = UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
64
|
+
dispatcher?.dispatchEvent(LinkLongPressEvent(surfaceId, id, url))
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Cancels the JS touch for an active link tap, preventing parent
|
|
69
|
+
* Pressable/TouchableOpacity from firing onPress for the same tap.
|
|
70
|
+
*
|
|
71
|
+
* Parent intercept is handled by [LinkLongPressMovementMethod], which
|
|
72
|
+
* disallows it for the press and releases it when the press ends.
|
|
73
|
+
*/
|
|
74
|
+
fun TextView.cancelJSTouchForLinkTap(event: MotionEvent) {
|
|
75
|
+
val currentMovementMethod = movementMethod
|
|
76
|
+
if (currentMovementMethod is LinkLongPressMovementMethod && currentMovementMethod.isLinkTouchActive) {
|
|
77
|
+
NativeGestureUtil.notifyNativeGestureStarted(this, event)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Cancels the JS touch unconditionally, preventing parent
|
|
83
|
+
* Pressable/TouchableOpacity from firing onPress for the same gesture.
|
|
84
|
+
*/
|
|
85
|
+
fun View.cancelJSTouchForCheckboxTap(event: MotionEvent) {
|
|
86
|
+
parent?.requestDisallowInterceptTouchEvent(true)
|
|
87
|
+
NativeGestureUtil.notifyNativeGestureStarted(this, event)
|
|
88
|
+
}
|