react-native-enriched-markdown-scaffold 0.7.4-scaffold.1 → 0.7.4-scaffold.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/android/build.gradle +5 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerDelegate.java +3 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +1 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +5 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +49 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +6 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +20 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +55 -9
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/BlockSyntaxSentinel.kt +69 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +71 -12
- package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputLayoutManager.kt +30 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +7 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/HeadingRenderer.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/HeadingSpan.kt +8 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LineHeightSpan.kt +27 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineMathStyle.kt +5 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/MathFontWeight.kt +25 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/MathStyle.kt +12 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +7 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/extensions/SpannableExtensions.kt +12 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +1 -1
- package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +2 -0
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathChipSpan.kt +1 -1
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathInlinePlaceholderSpan.kt +7 -3
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathInlineSpan.kt +8 -5
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathTypeface.kt +30 -0
- package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +3 -1
- package/docs/API_REFERENCE.md +9 -0
- package/docs/INPUT.md +48 -0
- package/docs/LATEX_MATH.md +62 -0
- package/docs/STYLES.md +3 -0
- package/ios/EnrichedMarkdown.mm +44 -12
- package/ios/EnrichedMarkdownText.mm +47 -11
- package/ios/attachments/ENRMMathInlineAttachment+macOS.m +5 -1
- package/ios/attachments/ENRMMathInlineAttachment.h +3 -0
- package/ios/attachments/ENRMMathInlineAttachment.m +2 -1
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +5 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +49 -0
- package/ios/input/ENRMBlockSyntaxSentinel.h +25 -0
- package/ios/input/ENRMBlockSyntaxSentinel.mm +80 -0
- package/ios/input/ENRMInputFormatter.h +4 -0
- package/ios/input/ENRMInputFormatter.mm +2 -0
- package/ios/input/ENRMInputParser.h +5 -0
- package/ios/input/ENRMInputParser.mm +79 -6
- package/ios/input/EnrichedMarkdownTextInput.mm +55 -22
- package/ios/input/InputStylePropsUtils.h +13 -0
- package/ios/internals/MeasurementCache.h +1 -0
- package/ios/math/ENRMRaTeXBridge.swift +52 -3
- package/ios/parser/ENRMMarkdownParser.h +8 -0
- package/ios/parser/ENRMMarkdownParser.mm +11 -0
- package/ios/renderer/ENRMMathInlineRenderer.m +3 -0
- package/ios/renderer/ListItemRenderer.m +8 -5
- package/ios/styles/StyleConfig.h +6 -0
- package/ios/styles/StyleConfig.mm +36 -0
- package/ios/utils/ENRMMathFontWeight.h +18 -0
- package/ios/utils/StylePropsUtils.h +21 -0
- package/ios/views/ENRMMathContainerView.m +3 -1
- package/lib/module/EnrichedMarkdownNativeComponent.ts +3 -0
- package/lib/module/EnrichedMarkdownTextInput.js +2 -0
- package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
- package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +12 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +3 -0
- package/lib/module/normalizeMarkdownStyle.js +5 -2
- package/lib/module/normalizeMarkdownStyle.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.web.js +5 -2
- package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
- package/lib/module/normalizeMarkdownTextInputStyle.js +7 -3
- package/lib/module/normalizeMarkdownTextInputStyle.js.map +1 -1
- package/lib/module/web/globalStyles.js +8 -1
- package/lib/module/web/globalStyles.js.map +1 -1
- package/lib/module/web/styles.js +3 -1
- package/lib/module/web/styles.js.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +3 -0
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +3 -1
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +12 -0
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +3 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts +1 -0
- package/lib/typescript/src/normalizeMarkdownTextInputStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyle.d.ts +32 -0
- package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +3 -0
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
- package/lib/typescript/src/web/styles.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/EnrichedMarkdownNativeComponent.ts +3 -0
- package/src/EnrichedMarkdownTextInput.tsx +4 -0
- package/src/EnrichedMarkdownTextInputNativeComponent.ts +12 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +3 -0
- package/src/normalizeMarkdownStyle.ts +6 -1
- package/src/normalizeMarkdownStyle.web.ts +6 -1
- package/src/normalizeMarkdownTextInputStyle.ts +5 -0
- package/src/types/MarkdownStyle.ts +32 -0
- package/src/types/MarkdownStyleInternal.ts +3 -0
- package/src/web/globalStyles.ts +7 -0
- package/src/web/styles.ts +5 -1
package/android/build.gradle
CHANGED
|
@@ -67,6 +67,10 @@ android {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
|
+
|
|
71
|
+
testOptions {
|
|
72
|
+
unitTests.includeAndroidResources = true
|
|
73
|
+
}
|
|
70
74
|
}
|
|
71
75
|
|
|
72
76
|
repositories {
|
|
@@ -85,6 +89,7 @@ dependencies {
|
|
|
85
89
|
if (enableMath) {
|
|
86
90
|
implementation("io.github.erweixin:ratex-android:0.1.10")
|
|
87
91
|
}
|
|
92
|
+
testImplementation "junit:junit:4.13.2"
|
|
88
93
|
}
|
|
89
94
|
|
|
90
95
|
react {
|
|
@@ -44,6 +44,9 @@ public class EnrichedMarkdownTextInputManagerDelegate<T extends View, U extends
|
|
|
44
44
|
case "scrollEnabled":
|
|
45
45
|
mViewManager.setScrollEnabled(view, value == null ? false : (boolean) value);
|
|
46
46
|
break;
|
|
47
|
+
case "blockSyntax":
|
|
48
|
+
mViewManager.setBlockSyntax(view, value == null ? false : (boolean) value);
|
|
49
|
+
break;
|
|
47
50
|
case "autoCapitalize":
|
|
48
51
|
mViewManager.setAutoCapitalize(view, value == null ? null : (String) value);
|
|
49
52
|
break;
|
|
@@ -22,6 +22,7 @@ public interface EnrichedMarkdownTextInputManagerInterface<T extends View> exten
|
|
|
22
22
|
void setEditable(T view, boolean value);
|
|
23
23
|
void setAutoFocus(T view, boolean value);
|
|
24
24
|
void setScrollEnabled(T view, boolean value);
|
|
25
|
+
void setBlockSyntax(T view, boolean value);
|
|
25
26
|
void setAutoCapitalize(T view, @Nullable String value);
|
|
26
27
|
void setMultiline(T view, boolean value);
|
|
27
28
|
void setCursorColor(T view, @Nullable Integer value);
|
|
@@ -144,6 +144,7 @@ EnrichedMarkdownTextInputProps::EnrichedMarkdownTextInputProps(
|
|
|
144
144
|
editable(convertRawProp(context, rawProps, "editable", sourceProps.editable, {false})),
|
|
145
145
|
autoFocus(convertRawProp(context, rawProps, "autoFocus", sourceProps.autoFocus, {false})),
|
|
146
146
|
scrollEnabled(convertRawProp(context, rawProps, "scrollEnabled", sourceProps.scrollEnabled, {false})),
|
|
147
|
+
blockSyntax(convertRawProp(context, rawProps, "blockSyntax", sourceProps.blockSyntax, {false})),
|
|
147
148
|
autoCapitalize(convertRawProp(context, rawProps, "autoCapitalize", sourceProps.autoCapitalize, {})),
|
|
148
149
|
multiline(convertRawProp(context, rawProps, "multiline", sourceProps.multiline, {false})),
|
|
149
150
|
cursorColor(convertRawProp(context, rawProps, "cursorColor", sourceProps.cursorColor, {})),
|
|
@@ -202,6 +203,10 @@ folly::dynamic EnrichedMarkdownTextInputProps::getDiffProps(
|
|
|
202
203
|
result["scrollEnabled"] = scrollEnabled;
|
|
203
204
|
}
|
|
204
205
|
|
|
206
|
+
if (blockSyntax != oldProps->blockSyntax) {
|
|
207
|
+
result["blockSyntax"] = blockSyntax;
|
|
208
|
+
}
|
|
209
|
+
|
|
205
210
|
if (autoCapitalize != oldProps->autoCapitalize) {
|
|
206
211
|
result["autoCapitalize"] = autoCapitalize;
|
|
207
212
|
}
|
|
@@ -1534,6 +1534,7 @@ struct EnrichedMarkdownMarkdownStyleMathStruct {
|
|
|
1534
1534
|
Float marginTop{0.0};
|
|
1535
1535
|
Float marginBottom{0.0};
|
|
1536
1536
|
std::string textAlign{};
|
|
1537
|
+
std::string fontWeight{};
|
|
1537
1538
|
|
|
1538
1539
|
|
|
1539
1540
|
#ifdef RN_SERIALIZABLE_STATE
|
|
@@ -1548,6 +1549,7 @@ struct EnrichedMarkdownMarkdownStyleMathStruct {
|
|
|
1548
1549
|
result["marginTop"] = marginTop;
|
|
1549
1550
|
result["marginBottom"] = marginBottom;
|
|
1550
1551
|
result["textAlign"] = textAlign;
|
|
1552
|
+
result["fontWeight"] = fontWeight;
|
|
1551
1553
|
return result;
|
|
1552
1554
|
}
|
|
1553
1555
|
#endif
|
|
@@ -1584,6 +1586,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
1584
1586
|
if (tmp_textAlign != map.end()) {
|
|
1585
1587
|
fromRawValue(context, tmp_textAlign->second, result.textAlign);
|
|
1586
1588
|
}
|
|
1589
|
+
auto tmp_fontWeight = map.find("fontWeight");
|
|
1590
|
+
if (tmp_fontWeight != map.end()) {
|
|
1591
|
+
fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
|
|
1592
|
+
}
|
|
1587
1593
|
}
|
|
1588
1594
|
|
|
1589
1595
|
static inline std::string toString(const EnrichedMarkdownMarkdownStyleMathStruct &value) {
|
|
@@ -1598,6 +1604,8 @@ static inline folly::dynamic toDynamic(const EnrichedMarkdownMarkdownStyleMathSt
|
|
|
1598
1604
|
|
|
1599
1605
|
struct EnrichedMarkdownMarkdownStyleInlineMathStruct {
|
|
1600
1606
|
SharedColor color{};
|
|
1607
|
+
Float paddingVertical{0.0};
|
|
1608
|
+
std::string fontWeight{};
|
|
1601
1609
|
|
|
1602
1610
|
|
|
1603
1611
|
#ifdef RN_SERIALIZABLE_STATE
|
|
@@ -1606,6 +1614,8 @@ struct EnrichedMarkdownMarkdownStyleInlineMathStruct {
|
|
|
1606
1614
|
folly::dynamic toDynamic() const {
|
|
1607
1615
|
folly::dynamic result = folly::dynamic::object();
|
|
1608
1616
|
result["color"] = ::facebook::react::toDynamic(color);
|
|
1617
|
+
result["paddingVertical"] = paddingVertical;
|
|
1618
|
+
result["fontWeight"] = fontWeight;
|
|
1609
1619
|
return result;
|
|
1610
1620
|
}
|
|
1611
1621
|
#endif
|
|
@@ -1618,6 +1628,14 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
1618
1628
|
if (tmp_color != map.end()) {
|
|
1619
1629
|
fromRawValue(context, tmp_color->second, result.color);
|
|
1620
1630
|
}
|
|
1631
|
+
auto tmp_paddingVertical = map.find("paddingVertical");
|
|
1632
|
+
if (tmp_paddingVertical != map.end()) {
|
|
1633
|
+
fromRawValue(context, tmp_paddingVertical->second, result.paddingVertical);
|
|
1634
|
+
}
|
|
1635
|
+
auto tmp_fontWeight = map.find("fontWeight");
|
|
1636
|
+
if (tmp_fontWeight != map.end()) {
|
|
1637
|
+
fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
|
|
1638
|
+
}
|
|
1621
1639
|
}
|
|
1622
1640
|
|
|
1623
1641
|
static inline std::string toString(const EnrichedMarkdownMarkdownStyleInlineMathStruct &value) {
|
|
@@ -2804,6 +2822,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathStruct {
|
|
|
2804
2822
|
Float paddingVertical{0.0};
|
|
2805
2823
|
SharedColor selectedBackgroundColor{};
|
|
2806
2824
|
SharedColor selectedBorderColor{};
|
|
2825
|
+
std::string fontWeight{};
|
|
2807
2826
|
|
|
2808
2827
|
|
|
2809
2828
|
#ifdef RN_SERIALIZABLE_STATE
|
|
@@ -2821,6 +2840,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathStruct {
|
|
|
2821
2840
|
result["paddingVertical"] = paddingVertical;
|
|
2822
2841
|
result["selectedBackgroundColor"] = ::facebook::react::toDynamic(selectedBackgroundColor);
|
|
2823
2842
|
result["selectedBorderColor"] = ::facebook::react::toDynamic(selectedBorderColor);
|
|
2843
|
+
result["fontWeight"] = fontWeight;
|
|
2824
2844
|
return result;
|
|
2825
2845
|
}
|
|
2826
2846
|
#endif
|
|
@@ -2869,6 +2889,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
2869
2889
|
if (tmp_selectedBorderColor != map.end()) {
|
|
2870
2890
|
fromRawValue(context, tmp_selectedBorderColor->second, result.selectedBorderColor);
|
|
2871
2891
|
}
|
|
2892
|
+
auto tmp_fontWeight = map.find("fontWeight");
|
|
2893
|
+
if (tmp_fontWeight != map.end()) {
|
|
2894
|
+
fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
|
|
2895
|
+
}
|
|
2872
2896
|
}
|
|
2873
2897
|
|
|
2874
2898
|
static inline std::string toString(const EnrichedMarkdownTextInputMarkdownStyleMathStruct &value) {
|
|
@@ -2892,6 +2916,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathBlockStruct {
|
|
|
2892
2916
|
Float paddingVertical{0.0};
|
|
2893
2917
|
SharedColor selectedBackgroundColor{};
|
|
2894
2918
|
SharedColor selectedBorderColor{};
|
|
2919
|
+
std::string fontWeight{};
|
|
2895
2920
|
Float marginTop{0.0};
|
|
2896
2921
|
Float marginBottom{0.0};
|
|
2897
2922
|
std::string alignment{};
|
|
@@ -2912,6 +2937,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathBlockStruct {
|
|
|
2912
2937
|
result["paddingVertical"] = paddingVertical;
|
|
2913
2938
|
result["selectedBackgroundColor"] = ::facebook::react::toDynamic(selectedBackgroundColor);
|
|
2914
2939
|
result["selectedBorderColor"] = ::facebook::react::toDynamic(selectedBorderColor);
|
|
2940
|
+
result["fontWeight"] = fontWeight;
|
|
2915
2941
|
result["marginTop"] = marginTop;
|
|
2916
2942
|
result["marginBottom"] = marginBottom;
|
|
2917
2943
|
result["alignment"] = alignment;
|
|
@@ -2963,6 +2989,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
2963
2989
|
if (tmp_selectedBorderColor != map.end()) {
|
|
2964
2990
|
fromRawValue(context, tmp_selectedBorderColor->second, result.selectedBorderColor);
|
|
2965
2991
|
}
|
|
2992
|
+
auto tmp_fontWeight = map.find("fontWeight");
|
|
2993
|
+
if (tmp_fontWeight != map.end()) {
|
|
2994
|
+
fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
|
|
2995
|
+
}
|
|
2966
2996
|
auto tmp_marginTop = map.find("marginTop");
|
|
2967
2997
|
if (tmp_marginTop != map.end()) {
|
|
2968
2998
|
fromRawValue(context, tmp_marginTop->second, result.marginTop);
|
|
@@ -3329,6 +3359,7 @@ class EnrichedMarkdownTextInputProps final : public ViewProps {
|
|
|
3329
3359
|
bool editable{false};
|
|
3330
3360
|
bool autoFocus{false};
|
|
3331
3361
|
bool scrollEnabled{false};
|
|
3362
|
+
bool blockSyntax{false};
|
|
3332
3363
|
std::string autoCapitalize{};
|
|
3333
3364
|
bool multiline{false};
|
|
3334
3365
|
SharedColor cursorColor{};
|
|
@@ -4872,6 +4903,7 @@ struct EnrichedMarkdownTextMarkdownStyleMathStruct {
|
|
|
4872
4903
|
Float marginTop{0.0};
|
|
4873
4904
|
Float marginBottom{0.0};
|
|
4874
4905
|
std::string textAlign{};
|
|
4906
|
+
std::string fontWeight{};
|
|
4875
4907
|
|
|
4876
4908
|
|
|
4877
4909
|
#ifdef RN_SERIALIZABLE_STATE
|
|
@@ -4886,6 +4918,7 @@ struct EnrichedMarkdownTextMarkdownStyleMathStruct {
|
|
|
4886
4918
|
result["marginTop"] = marginTop;
|
|
4887
4919
|
result["marginBottom"] = marginBottom;
|
|
4888
4920
|
result["textAlign"] = textAlign;
|
|
4921
|
+
result["fontWeight"] = fontWeight;
|
|
4889
4922
|
return result;
|
|
4890
4923
|
}
|
|
4891
4924
|
#endif
|
|
@@ -4922,6 +4955,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
4922
4955
|
if (tmp_textAlign != map.end()) {
|
|
4923
4956
|
fromRawValue(context, tmp_textAlign->second, result.textAlign);
|
|
4924
4957
|
}
|
|
4958
|
+
auto tmp_fontWeight = map.find("fontWeight");
|
|
4959
|
+
if (tmp_fontWeight != map.end()) {
|
|
4960
|
+
fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
|
|
4961
|
+
}
|
|
4925
4962
|
}
|
|
4926
4963
|
|
|
4927
4964
|
static inline std::string toString(const EnrichedMarkdownTextMarkdownStyleMathStruct &value) {
|
|
@@ -4936,6 +4973,8 @@ static inline folly::dynamic toDynamic(const EnrichedMarkdownTextMarkdownStyleMa
|
|
|
4936
4973
|
|
|
4937
4974
|
struct EnrichedMarkdownTextMarkdownStyleInlineMathStruct {
|
|
4938
4975
|
SharedColor color{};
|
|
4976
|
+
Float paddingVertical{0.0};
|
|
4977
|
+
std::string fontWeight{};
|
|
4939
4978
|
|
|
4940
4979
|
|
|
4941
4980
|
#ifdef RN_SERIALIZABLE_STATE
|
|
@@ -4944,6 +4983,8 @@ struct EnrichedMarkdownTextMarkdownStyleInlineMathStruct {
|
|
|
4944
4983
|
folly::dynamic toDynamic() const {
|
|
4945
4984
|
folly::dynamic result = folly::dynamic::object();
|
|
4946
4985
|
result["color"] = ::facebook::react::toDynamic(color);
|
|
4986
|
+
result["paddingVertical"] = paddingVertical;
|
|
4987
|
+
result["fontWeight"] = fontWeight;
|
|
4947
4988
|
return result;
|
|
4948
4989
|
}
|
|
4949
4990
|
#endif
|
|
@@ -4956,6 +4997,14 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
4956
4997
|
if (tmp_color != map.end()) {
|
|
4957
4998
|
fromRawValue(context, tmp_color->second, result.color);
|
|
4958
4999
|
}
|
|
5000
|
+
auto tmp_paddingVertical = map.find("paddingVertical");
|
|
5001
|
+
if (tmp_paddingVertical != map.end()) {
|
|
5002
|
+
fromRawValue(context, tmp_paddingVertical->second, result.paddingVertical);
|
|
5003
|
+
}
|
|
5004
|
+
auto tmp_fontWeight = map.find("fontWeight");
|
|
5005
|
+
if (tmp_fontWeight != map.end()) {
|
|
5006
|
+
fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
|
|
5007
|
+
}
|
|
4959
5008
|
}
|
|
4960
5009
|
|
|
4961
5010
|
static inline std::string toString(const EnrichedMarkdownTextMarkdownStyleInlineMathStruct &value) {
|
|
@@ -25,6 +25,7 @@ import com.swmansion.enriched.markdown.utils.common.RenderedSegment
|
|
|
25
25
|
import com.swmansion.enriched.markdown.utils.common.StreamingMarkdownFilter
|
|
26
26
|
import com.swmansion.enriched.markdown.utils.common.TableStreamingMode
|
|
27
27
|
import com.swmansion.enriched.markdown.utils.common.getBooleanOrDefault
|
|
28
|
+
import com.swmansion.enriched.markdown.utils.common.getFloatOrDefault
|
|
28
29
|
import com.swmansion.enriched.markdown.utils.common.getMapOrNull
|
|
29
30
|
import com.swmansion.enriched.markdown.utils.common.getStringOrDefault
|
|
30
31
|
import com.swmansion.enriched.markdown.utils.common.splitASTIntoSegments
|
|
@@ -299,7 +300,6 @@ object MeasurementStore {
|
|
|
299
300
|
|
|
300
301
|
// 2. Render & Measure
|
|
301
302
|
val spannable = tryRenderMarkdown(markdown, styleMap, context, md4cFlags, allowFontScaling, maxFontSizeMultiplier)
|
|
302
|
-
spannable?.replaceMathSpansWithPlaceholders(context)
|
|
303
303
|
val textToMeasure = spannable ?: markdown
|
|
304
304
|
val (size, _) = measureWithLayout(width, textToMeasure, measurePaint, id)
|
|
305
305
|
|
|
@@ -420,7 +420,7 @@ object MeasurementStore {
|
|
|
420
420
|
|
|
421
421
|
when (segment) {
|
|
422
422
|
is RenderedSegment.Text -> {
|
|
423
|
-
segment.styledText.replaceMathSpansWithPlaceholders(context)
|
|
423
|
+
segment.styledText.replaceMathSpansWithPlaceholders(context, style.inlineMathStyle.paddingVertical)
|
|
424
424
|
|
|
425
425
|
val layout = createStaticLayout(segment.styledText, fontSize, widthPx, id)
|
|
426
426
|
totalHeightPx += layout.height
|
|
@@ -503,7 +503,9 @@ object MeasurementStore {
|
|
|
503
503
|
val ast = Parser.shared.parseMarkdown(markdown, md4cFlags) ?: return null
|
|
504
504
|
val style = StyleConfig(styleMap, context, allowFontScaling, maxFontSizeMultiplier)
|
|
505
505
|
measureRenderer.configure(style, context)
|
|
506
|
-
measureRenderer.renderDocument(ast, null)
|
|
506
|
+
measureRenderer.renderDocument(ast, null).apply {
|
|
507
|
+
replaceMathSpansWithPlaceholders(context, style.inlineMathStyle.paddingVertical)
|
|
508
|
+
}
|
|
507
509
|
} catch (e: Exception) {
|
|
508
510
|
Log.w(TAG, "Failed to render markdown for measurement, falling back to raw text", e)
|
|
509
511
|
null
|
|
@@ -517,7 +519,7 @@ object MeasurementStore {
|
|
|
517
519
|
fontScale: Float,
|
|
518
520
|
maxFontSizeMultiplier: Float,
|
|
519
521
|
): Float {
|
|
520
|
-
val fontSizeSp = styleMap
|
|
522
|
+
val fontSizeSp = styleMap.getMapOrNull("paragraph").getFloatOrDefault("fontSize", 16f)
|
|
521
523
|
val density = context.resources.displayMetrics.density
|
|
522
524
|
|
|
523
525
|
if (!allowFontScaling) {
|
|
@@ -57,6 +57,13 @@ class EnrichedMarkdownTextInputManager :
|
|
|
57
57
|
stateWrapper: StateWrapper?,
|
|
58
58
|
): Any? {
|
|
59
59
|
view.stateWrapper = stateWrapper
|
|
60
|
+
// Fabric applies props before it hands over the state wrapper, so every layout
|
|
61
|
+
// invalidation raised during the mount transaction was dropped. Replay it now that we can
|
|
62
|
+
// actually push state — otherwise the shadow node keeps the initialMeasure() estimate,
|
|
63
|
+
// which ignores math chips, until the first keystroke.
|
|
64
|
+
if (stateWrapper != null) {
|
|
65
|
+
view.layoutManager.flushPendingInvalidation()
|
|
66
|
+
}
|
|
60
67
|
return super.updateState(view, props, stateWrapper)
|
|
61
68
|
}
|
|
62
69
|
|
|
@@ -69,6 +76,11 @@ class EnrichedMarkdownTextInputManager :
|
|
|
69
76
|
view.dismissActiveMention()
|
|
70
77
|
super.onDropViewInstance(view)
|
|
71
78
|
view.layoutManager.release()
|
|
79
|
+
// Unreachable while view recycling is off — it needs a `setupViewRecycling()` call from this
|
|
80
|
+
// constructor *and* ReactNativeFeatureFlags.enableViewRecycling(). Kept because the flush
|
|
81
|
+
// above turns a recycled view into a view that pushes state at mount: without this, it would
|
|
82
|
+
// push into the shadow node it was dropped from.
|
|
83
|
+
view.stateWrapper = null
|
|
72
84
|
}
|
|
73
85
|
|
|
74
86
|
override fun measure(
|
|
@@ -158,6 +170,14 @@ class EnrichedMarkdownTextInputManager :
|
|
|
158
170
|
view?.isVerticalScrollBarEnabled = value
|
|
159
171
|
}
|
|
160
172
|
|
|
173
|
+
@ReactProp(name = "blockSyntax", defaultBoolean = true)
|
|
174
|
+
override fun setBlockSyntax(
|
|
175
|
+
view: EnrichedMarkdownTextInputView?,
|
|
176
|
+
value: Boolean,
|
|
177
|
+
) {
|
|
178
|
+
view?.blockSyntax = value
|
|
179
|
+
}
|
|
180
|
+
|
|
161
181
|
@ReactProp(name = "autoCapitalize")
|
|
162
182
|
override fun setAutoCapitalize(
|
|
163
183
|
view: EnrichedMarkdownTextInputView?,
|
package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt
CHANGED
|
@@ -97,6 +97,17 @@ class EnrichedMarkdownTextInputView(
|
|
|
97
97
|
private var detectScrollMovement = false
|
|
98
98
|
var scrollEnabled: Boolean = true
|
|
99
99
|
|
|
100
|
+
// The markdown last handed over by JS, kept so `blockSyntax` can re-parse it. @ReactProp
|
|
101
|
+
// setters fire in undefined order, so `defaultValue` regularly lands before the flag does.
|
|
102
|
+
private var lastMarkdownFromJS: String? = null
|
|
103
|
+
|
|
104
|
+
var blockSyntax: Boolean = true
|
|
105
|
+
set(value) {
|
|
106
|
+
if (field == value) return
|
|
107
|
+
field = value
|
|
108
|
+
lastMarkdownFromJS?.let { setValueFromJS(it) }
|
|
109
|
+
}
|
|
110
|
+
|
|
100
111
|
private var mentionIndicators: LinkedHashSet<String> = linkedSetOf()
|
|
101
112
|
private var activeMentionIndicator: String? = null
|
|
102
113
|
private var activeMentionStart = -1
|
|
@@ -634,7 +645,8 @@ class EnrichedMarkdownTextInputView(
|
|
|
634
645
|
}
|
|
635
646
|
|
|
636
647
|
fun setValueFromJS(markdown: String) {
|
|
637
|
-
|
|
648
|
+
lastMarkdownFromJS = markdown
|
|
649
|
+
val parsed = InputParser.parseToPlainTextAndRanges(markdown, blockSyntax)
|
|
638
650
|
blockEmitting = true
|
|
639
651
|
try {
|
|
640
652
|
runAsATransaction {
|
|
@@ -708,8 +720,16 @@ class EnrichedMarkdownTextInputView(
|
|
|
708
720
|
}
|
|
709
721
|
invalidate()
|
|
710
722
|
requestLayout()
|
|
723
|
+
// Chips only reach the text here when `defaultValue` landed before `markdownStyle`
|
|
724
|
+
// (@ReactProp order is undefined). requestLayout() cannot change a height Fabric owns —
|
|
725
|
+
// the shadow tree has to re-measure text that now carries the spans.
|
|
726
|
+
layoutManager.invalidateLayout()
|
|
711
727
|
} else if (mathStyleChanged) {
|
|
712
728
|
refreshAllMathChips()
|
|
729
|
+
// A math font size / padding change resizes every chip, so the measured height moves.
|
|
730
|
+
// Colour-only changes end up here too; store() then measures the same size and skips
|
|
731
|
+
// the state update.
|
|
732
|
+
layoutManager.invalidateLayout()
|
|
713
733
|
}
|
|
714
734
|
return changed
|
|
715
735
|
}
|
|
@@ -848,18 +868,36 @@ class EnrichedMarkdownTextInputView(
|
|
|
848
868
|
return true
|
|
849
869
|
}
|
|
850
870
|
|
|
851
|
-
|
|
871
|
+
/** Chip glyph rect in [Layout] coordinates — no padding, no scroll offset. */
|
|
872
|
+
private fun chipBoundsInLayout(
|
|
852
873
|
layout: Layout,
|
|
853
874
|
start: Int,
|
|
854
875
|
): Rect {
|
|
855
876
|
val line = layout.getLineForOffset(start)
|
|
856
|
-
val xStart =
|
|
857
|
-
val xEnd =
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
877
|
+
val xStart = layout.getPrimaryHorizontal(start).toInt()
|
|
878
|
+
val xEnd = layout.getPrimaryHorizontal(start + 1).toInt()
|
|
879
|
+
// Ordered, not raw start/end: in an RTL run xStart > xEnd, which would make an
|
|
880
|
+
// inverted Rect that contains() always rejects.
|
|
881
|
+
val left = xStart.coerceAtMost(xEnd)
|
|
882
|
+
var right = xStart.coerceAtLeast(xEnd)
|
|
883
|
+
if (right <= left) {
|
|
884
|
+
// A chip that is the last character has start + 1 == length, where some layouts
|
|
885
|
+
// report the same horizontal for both offsets. Fall back to the line's content edge
|
|
886
|
+
// so the rect keeps a real width instead of collapsing.
|
|
887
|
+
right = layout.getLineRight(line).toInt()
|
|
888
|
+
}
|
|
889
|
+
return Rect(left, layout.getLineTop(line), right, layout.getLineBottom(line))
|
|
861
890
|
}
|
|
862
891
|
|
|
892
|
+
/** Chip glyph rect in view coordinates, for reporting to JS. */
|
|
893
|
+
private fun computeChipBounds(
|
|
894
|
+
layout: Layout,
|
|
895
|
+
start: Int,
|
|
896
|
+
): Rect =
|
|
897
|
+
chipBoundsInLayout(layout, start).apply {
|
|
898
|
+
offset(totalPaddingLeft - scrollX, totalPaddingTop - scrollY)
|
|
899
|
+
}
|
|
900
|
+
|
|
863
901
|
fun mathChipPositionAtTouch(
|
|
864
902
|
x: Float,
|
|
865
903
|
y: Float,
|
|
@@ -869,13 +907,21 @@ class EnrichedMarkdownTextInputView(
|
|
|
869
907
|
val layoutNow = layout ?: return -1
|
|
870
908
|
val adjustedX = (x - totalPaddingLeft + scrollX).toInt()
|
|
871
909
|
val adjustedY = (y - totalPaddingTop + scrollY).toInt()
|
|
872
|
-
|
|
910
|
+
// getLineForVertical and getOffsetForHorizontal both clamp into the laid-out text, so
|
|
911
|
+
// a touch in the padding or in the blank space below the last line resolves onto the
|
|
912
|
+
// nearest character. Bound the touch to the text box, then require it to land inside
|
|
913
|
+
// the chip's own rect — otherwise a tap beside a chip reads as a tap on it.
|
|
914
|
+
if (adjustedY < 0 || adjustedY > layoutNow.height) return -1
|
|
873
915
|
val line = layoutNow.getLineForVertical(adjustedY)
|
|
874
916
|
val offset = layoutNow.getOffsetForHorizontal(line, adjustedX.toFloat())
|
|
875
|
-
if (offset < 0
|
|
917
|
+
if (offset < 0) return -1
|
|
876
918
|
val candidates = intArrayOf(offset, offset - 1).filter { it in 0 until editable.length }
|
|
877
919
|
for (candidate in candidates) {
|
|
878
920
|
val range = controller.chipRangeAt(editable, candidate) ?: continue
|
|
921
|
+
// An empty rect means the layout could not give us the chip's extent; accept the
|
|
922
|
+
// candidate then, rather than making the chip untappable.
|
|
923
|
+
val bounds = chipBoundsInLayout(layoutNow, range.first)
|
|
924
|
+
if (!bounds.isEmpty && !bounds.contains(adjustedX, adjustedY)) continue
|
|
879
925
|
return range.first
|
|
880
926
|
}
|
|
881
927
|
return -1
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.input.formatting
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hides block-level markdown structure from md4c so a document parses as a single
|
|
5
|
+
* `Paragraph`. Used by `blockSyntax = false` on `EnrichedMarkdownTextInput`.
|
|
6
|
+
*
|
|
7
|
+
* Every newline is swapped for a vertical tab (`0x0B`) and one more is prepended.
|
|
8
|
+
* md4c treats VT as ordinary whitespace, never as a line break, so no line ever
|
|
9
|
+
* starts at a position where a list marker, `#`, `>` or an indented code block can
|
|
10
|
+
* be recognised — including line 1, which is what the leading sentinel is for.
|
|
11
|
+
* Inline syntax (`$...$`, `**...**`, links) still parses normally.
|
|
12
|
+
*
|
|
13
|
+
* The transform is 1:1 in characters *and* in UTF-8 bytes (VT and `\n` are both one
|
|
14
|
+
* of each), so offsets coming back out of the parser only need a flat -1 shift for
|
|
15
|
+
* the leading sentinel. iOS relies on the same property to keep its byte-to-UTF16
|
|
16
|
+
* map valid.
|
|
17
|
+
*/
|
|
18
|
+
internal object BlockSyntaxSentinel {
|
|
19
|
+
const val SENTINEL = '\u000B'
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* `\r\n` / `\r` collapse to `\n` (md4c normalizes line endings this way regardless,
|
|
23
|
+
* so this is pre-existing lossiness, not a new one), and any `0x0B` already present
|
|
24
|
+
* is dropped — leaving it in would make [decode] silently turn it into a newline.
|
|
25
|
+
*/
|
|
26
|
+
fun canonicalize(markdown: String): String {
|
|
27
|
+
val out = StringBuilder(markdown.length)
|
|
28
|
+
var i = 0
|
|
29
|
+
while (i < markdown.length) {
|
|
30
|
+
when (markdown[i]) {
|
|
31
|
+
'\r' -> {
|
|
32
|
+
out.append('\n')
|
|
33
|
+
if (i + 1 < markdown.length && markdown[i + 1] == '\n') i++
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
SENTINEL -> {}
|
|
37
|
+
|
|
38
|
+
else -> {
|
|
39
|
+
out.append(markdown[i])
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
i++
|
|
43
|
+
}
|
|
44
|
+
return out.toString()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** Prepends one sentinel and replaces every `\n` with one. Length grows by exactly 1. */
|
|
48
|
+
fun encode(canonical: String): String {
|
|
49
|
+
val out = StringBuilder(canonical.length + 1)
|
|
50
|
+
out.append(SENTINEL)
|
|
51
|
+
for (c in canonical) out.append(if (c == '\n') SENTINEL else c)
|
|
52
|
+
return out.toString()
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Reverses [encode] on the parser's plain text. Returns `null` when the leading
|
|
57
|
+
* sentinel is missing — the caller then falls back to the canonical source with no
|
|
58
|
+
* ranges rather than emitting text shifted by an offset that no longer holds.
|
|
59
|
+
*/
|
|
60
|
+
fun decode(plainText: String): String? {
|
|
61
|
+
if (plainText.isEmpty() || plainText[0] != SENTINEL) return null
|
|
62
|
+
val out = StringBuilder(plainText.length - 1)
|
|
63
|
+
for (i in 1 until plainText.length) {
|
|
64
|
+
val c = plainText[i]
|
|
65
|
+
out.append(if (c == SENTINEL) '\n' else c)
|
|
66
|
+
}
|
|
67
|
+
return out.toString()
|
|
68
|
+
}
|
|
69
|
+
}
|
package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt
CHANGED
|
@@ -22,19 +22,73 @@ object InputParser {
|
|
|
22
22
|
// A source paragraph break: a newline followed by one or more blank lines.
|
|
23
23
|
private val PARAGRAPH_BREAK_RE = Regex("\\n[ \\t]*(?:\\n[ \\t]*)+")
|
|
24
24
|
|
|
25
|
-
fun parseToPlainTextAndRanges(
|
|
25
|
+
fun parseToPlainTextAndRanges(
|
|
26
|
+
markdown: String,
|
|
27
|
+
blockSyntax: Boolean = true,
|
|
28
|
+
): ParseResult {
|
|
26
29
|
if (markdown.isEmpty()) {
|
|
27
30
|
return ParseResult("", emptyList())
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
if (!blockSyntax) {
|
|
34
|
+
return parseWithoutBlockSyntax(markdown)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return parseCompleted(InputRemend.complete(markdown), markdown, blockSeparators = true)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Parses with every block construct hidden behind [BlockSyntaxSentinel], so the whole
|
|
42
|
+
* document lands in one `Paragraph` and list markers, `#`, `>` and friends stay in the
|
|
43
|
+
* text verbatim. `InputRemend` is skipped as well: its speculative closing delimiters
|
|
44
|
+
* are the other source of characters the serializer cannot replay.
|
|
45
|
+
*/
|
|
46
|
+
private fun parseWithoutBlockSyntax(markdown: String): ParseResult {
|
|
47
|
+
val canonical = BlockSyntaxSentinel.canonicalize(markdown)
|
|
48
|
+
if (canonical.isEmpty()) {
|
|
49
|
+
return ParseResult("", emptyList())
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// No block separators: the encoded text has no newline for md4c to drop, so every
|
|
53
|
+
// newline the replay would add is spurious. `$$...$$` is the case that proves it —
|
|
54
|
+
// md4c reports display math as a top-level block even mid-line under the sentinel.
|
|
55
|
+
val parsed = parseCompleted(BlockSyntaxSentinel.encode(canonical), canonical, blockSeparators = false)
|
|
56
|
+
val decoded =
|
|
57
|
+
BlockSyntaxSentinel.decode(parsed.plainText)
|
|
58
|
+
?: return ParseResult(canonical, emptyList())
|
|
59
|
+
|
|
60
|
+
// Only the leading sentinel is removed, so every offset shifts by exactly one.
|
|
61
|
+
val ranges =
|
|
62
|
+
parsed.formattingRanges.mapNotNull { range ->
|
|
63
|
+
val start = range.start - 1
|
|
64
|
+
val end = range.end - 1
|
|
65
|
+
if (start < 0 || end > decoded.length || end <= start) {
|
|
66
|
+
null
|
|
67
|
+
} else {
|
|
68
|
+
FormattingRange(range.type, start, end, range.url)
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
val atoms =
|
|
72
|
+
parsed.mathAtoms.mapNotNull { atom ->
|
|
73
|
+
val position = atom.position - 1
|
|
74
|
+
if (position < 0 || position >= decoded.length) null else atom.copy(position = position)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return ParseResult(decoded, ranges, atoms)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
private fun parseCompleted(
|
|
81
|
+
completed: String,
|
|
82
|
+
fallback: String,
|
|
83
|
+
blockSeparators: Boolean,
|
|
84
|
+
): ParseResult {
|
|
31
85
|
val flags =
|
|
32
86
|
Md4cFlags(
|
|
33
87
|
underline = true,
|
|
34
88
|
latexMath = FeatureFlags.IS_MATH_ENABLED,
|
|
35
89
|
permissiveAutolinks = false,
|
|
36
90
|
)
|
|
37
|
-
val ast = Parser.shared.parseMarkdown(completed, flags) ?: return ParseResult(
|
|
91
|
+
val ast = Parser.shared.parseMarkdown(completed, flags) ?: return ParseResult(fallback, emptyList())
|
|
38
92
|
|
|
39
93
|
val plainText = StringBuilder()
|
|
40
94
|
val ranges = mutableListOf<FormattingRange>()
|
|
@@ -43,14 +97,18 @@ object InputParser {
|
|
|
43
97
|
// md4c collapses any blank-line run into a single break, unlike iOS which keeps them.
|
|
44
98
|
// Re-read the real runs so each break replays its original number of newlines.
|
|
45
99
|
val blankRuns =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
100
|
+
if (blockSeparators) {
|
|
101
|
+
ArrayDeque(
|
|
102
|
+
PARAGRAPH_BREAK_RE
|
|
103
|
+
.findAll(completed.trim())
|
|
104
|
+
.map { match -> match.value.count { it == '\n' } }
|
|
105
|
+
.toList(),
|
|
106
|
+
)
|
|
107
|
+
} else {
|
|
108
|
+
ArrayDeque()
|
|
109
|
+
}
|
|
52
110
|
|
|
53
|
-
walkNode(ast, plainText, ranges, mathAtoms, ArrayDeque(), blankRuns)
|
|
111
|
+
walkNode(ast, plainText, ranges, mathAtoms, ArrayDeque(), blankRuns, blockSeparators)
|
|
54
112
|
|
|
55
113
|
return ParseResult(plainText.toString(), ranges, mathAtoms)
|
|
56
114
|
}
|
|
@@ -62,6 +120,7 @@ object InputParser {
|
|
|
62
120
|
mathAtoms: MutableList<MathAtom>,
|
|
63
121
|
activeStyles: ArrayDeque<ActiveStyle>,
|
|
64
122
|
blankRuns: ArrayDeque<Int>,
|
|
123
|
+
blockSeparators: Boolean,
|
|
65
124
|
) {
|
|
66
125
|
if (node.type == NodeType.LatexMathInline || node.type == NodeType.LatexMathDisplay) {
|
|
67
126
|
val latex = extractLatex(node)
|
|
@@ -92,10 +151,10 @@ object InputParser {
|
|
|
92
151
|
|
|
93
152
|
for ((index, child) in node.children.withIndex()) {
|
|
94
153
|
// Keep the source's blank lines between top-level blocks (md4c drops them, iOS keeps them).
|
|
95
|
-
if (index > 0 && child.type.isTopLevelBlock() && plainText.isNotEmpty()) {
|
|
154
|
+
if (blockSeparators && index > 0 && child.type.isTopLevelBlock() && plainText.isNotEmpty()) {
|
|
96
155
|
plainText.append("\n".repeat(blankRuns.removeFirstOrNull() ?: 2))
|
|
97
156
|
}
|
|
98
|
-
walkNode(child, plainText, ranges, mathAtoms, activeStyles, blankRuns)
|
|
157
|
+
walkNode(child, plainText, ranges, mathAtoms, activeStyles, blankRuns, blockSeparators)
|
|
99
158
|
}
|
|
100
159
|
|
|
101
160
|
if (styleType != null) {
|
package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputLayoutManager.kt
CHANGED
|
@@ -8,21 +8,49 @@ class InputLayoutManager(
|
|
|
8
8
|
) {
|
|
9
9
|
private var forceHeightRecalculationCounter = 0
|
|
10
10
|
|
|
11
|
+
/**
|
|
12
|
+
* An invalidation that arrived before `stateWrapper` existed. Fabric applies every prop
|
|
13
|
+
* (and runs `onAfterUpdateTransaction`) inside `ViewManager.updateProperties`, which runs
|
|
14
|
+
* *before* `updateState` assigns the wrapper — so the whole mount transaction invalidates
|
|
15
|
+
* into the void. Without this flag the measurement store stays empty and Yoga keeps the
|
|
16
|
+
* `initialMeasure()` approximation until the next text/prop change.
|
|
17
|
+
*/
|
|
18
|
+
private var pending = false
|
|
19
|
+
|
|
11
20
|
fun invalidateLayout() {
|
|
12
|
-
|
|
21
|
+
val stateWrapper = view.stateWrapper
|
|
22
|
+
if (stateWrapper == null) {
|
|
23
|
+
pending = true
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
pending = false
|
|
13
27
|
|
|
14
28
|
val text = view.text
|
|
15
29
|
val paint = view.paint
|
|
16
30
|
|
|
31
|
+
// First call for this view measures against cachedWidth = 0 and stores a throwaway size;
|
|
32
|
+
// Yoga immediately re-measures through getMeasureById() with the real width and overwrites
|
|
33
|
+
// it. The state update below is what makes that second pass happen.
|
|
17
34
|
val needUpdate = InputMeasurementStore.store(view.id, text, paint)
|
|
18
35
|
if (!needUpdate) return
|
|
19
36
|
|
|
20
37
|
val state = Arguments.createMap()
|
|
21
38
|
state.putInt("forceHeightRecalculationCounter", forceHeightRecalculationCounter++)
|
|
22
|
-
|
|
39
|
+
stateWrapper.updateState(state)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Replays an invalidation that was dropped while `stateWrapper` was null. Called once the
|
|
44
|
+
* wrapper is assigned, i.e. after every prop of the mount transaction has been applied — so
|
|
45
|
+
* the text carries its math chips and the paint carries the resolved typeface/size.
|
|
46
|
+
*/
|
|
47
|
+
fun flushPendingInvalidation() {
|
|
48
|
+
if (!pending) return
|
|
49
|
+
invalidateLayout()
|
|
23
50
|
}
|
|
24
51
|
|
|
25
52
|
fun release() {
|
|
53
|
+
pending = false
|
|
26
54
|
InputMeasurementStore.release(view.id)
|
|
27
55
|
}
|
|
28
56
|
}
|