react-native-enriched-markdown-scaffold 0.7.4-scaffold.7 → 0.7.4-scaffold.9
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 +37 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +8 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +13 -1
- 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/InputMeasurementStore.kt +4 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/input/model/MathChipStyle.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/InlineMathStyle.kt +3 -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/input/MarkdownStyleParser.kt +10 -0
- package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +1 -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/MathInlineSpan.kt +2 -2
- 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 +49 -0
- package/docs/STYLES.md +2 -0
- 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 +37 -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 +19 -2
- package/ios/input/InputStylePropsUtils.h +13 -0
- package/ios/math/ENRMRaTeXBridge.swift +52 -3
- package/ios/renderer/ENRMMathInlineRenderer.m +2 -0
- package/ios/styles/StyleConfig.h +4 -0
- package/ios/styles/StyleConfig.mm +24 -0
- package/ios/utils/ENRMMathFontWeight.h +18 -0
- package/ios/utils/StylePropsUtils.h +16 -0
- package/ios/views/ENRMMathContainerView.m +3 -1
- package/lib/module/EnrichedMarkdownNativeComponent.ts +2 -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 +2 -0
- package/lib/module/normalizeMarkdownStyle.js +4 -2
- package/lib/module/normalizeMarkdownStyle.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.web.js +4 -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 +2 -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 +2 -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 +24 -0
- package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +2 -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 +2 -0
- package/src/EnrichedMarkdownTextInput.tsx +4 -0
- package/src/EnrichedMarkdownTextInputNativeComponent.ts +12 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +2 -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 +24 -0
- package/src/types/MarkdownStyleInternal.ts +2 -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) {
|
|
@@ -1599,6 +1605,7 @@ static inline folly::dynamic toDynamic(const EnrichedMarkdownMarkdownStyleMathSt
|
|
|
1599
1605
|
struct EnrichedMarkdownMarkdownStyleInlineMathStruct {
|
|
1600
1606
|
SharedColor color{};
|
|
1601
1607
|
Float paddingVertical{0.0};
|
|
1608
|
+
std::string fontWeight{};
|
|
1602
1609
|
|
|
1603
1610
|
|
|
1604
1611
|
#ifdef RN_SERIALIZABLE_STATE
|
|
@@ -1608,6 +1615,7 @@ struct EnrichedMarkdownMarkdownStyleInlineMathStruct {
|
|
|
1608
1615
|
folly::dynamic result = folly::dynamic::object();
|
|
1609
1616
|
result["color"] = ::facebook::react::toDynamic(color);
|
|
1610
1617
|
result["paddingVertical"] = paddingVertical;
|
|
1618
|
+
result["fontWeight"] = fontWeight;
|
|
1611
1619
|
return result;
|
|
1612
1620
|
}
|
|
1613
1621
|
#endif
|
|
@@ -1624,6 +1632,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
1624
1632
|
if (tmp_paddingVertical != map.end()) {
|
|
1625
1633
|
fromRawValue(context, tmp_paddingVertical->second, result.paddingVertical);
|
|
1626
1634
|
}
|
|
1635
|
+
auto tmp_fontWeight = map.find("fontWeight");
|
|
1636
|
+
if (tmp_fontWeight != map.end()) {
|
|
1637
|
+
fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
|
|
1638
|
+
}
|
|
1627
1639
|
}
|
|
1628
1640
|
|
|
1629
1641
|
static inline std::string toString(const EnrichedMarkdownMarkdownStyleInlineMathStruct &value) {
|
|
@@ -2810,6 +2822,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathStruct {
|
|
|
2810
2822
|
Float paddingVertical{0.0};
|
|
2811
2823
|
SharedColor selectedBackgroundColor{};
|
|
2812
2824
|
SharedColor selectedBorderColor{};
|
|
2825
|
+
std::string fontWeight{};
|
|
2813
2826
|
|
|
2814
2827
|
|
|
2815
2828
|
#ifdef RN_SERIALIZABLE_STATE
|
|
@@ -2827,6 +2840,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathStruct {
|
|
|
2827
2840
|
result["paddingVertical"] = paddingVertical;
|
|
2828
2841
|
result["selectedBackgroundColor"] = ::facebook::react::toDynamic(selectedBackgroundColor);
|
|
2829
2842
|
result["selectedBorderColor"] = ::facebook::react::toDynamic(selectedBorderColor);
|
|
2843
|
+
result["fontWeight"] = fontWeight;
|
|
2830
2844
|
return result;
|
|
2831
2845
|
}
|
|
2832
2846
|
#endif
|
|
@@ -2875,6 +2889,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
2875
2889
|
if (tmp_selectedBorderColor != map.end()) {
|
|
2876
2890
|
fromRawValue(context, tmp_selectedBorderColor->second, result.selectedBorderColor);
|
|
2877
2891
|
}
|
|
2892
|
+
auto tmp_fontWeight = map.find("fontWeight");
|
|
2893
|
+
if (tmp_fontWeight != map.end()) {
|
|
2894
|
+
fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
|
|
2895
|
+
}
|
|
2878
2896
|
}
|
|
2879
2897
|
|
|
2880
2898
|
static inline std::string toString(const EnrichedMarkdownTextInputMarkdownStyleMathStruct &value) {
|
|
@@ -2898,6 +2916,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathBlockStruct {
|
|
|
2898
2916
|
Float paddingVertical{0.0};
|
|
2899
2917
|
SharedColor selectedBackgroundColor{};
|
|
2900
2918
|
SharedColor selectedBorderColor{};
|
|
2919
|
+
std::string fontWeight{};
|
|
2901
2920
|
Float marginTop{0.0};
|
|
2902
2921
|
Float marginBottom{0.0};
|
|
2903
2922
|
std::string alignment{};
|
|
@@ -2918,6 +2937,7 @@ struct EnrichedMarkdownTextInputMarkdownStyleMathBlockStruct {
|
|
|
2918
2937
|
result["paddingVertical"] = paddingVertical;
|
|
2919
2938
|
result["selectedBackgroundColor"] = ::facebook::react::toDynamic(selectedBackgroundColor);
|
|
2920
2939
|
result["selectedBorderColor"] = ::facebook::react::toDynamic(selectedBorderColor);
|
|
2940
|
+
result["fontWeight"] = fontWeight;
|
|
2921
2941
|
result["marginTop"] = marginTop;
|
|
2922
2942
|
result["marginBottom"] = marginBottom;
|
|
2923
2943
|
result["alignment"] = alignment;
|
|
@@ -2969,6 +2989,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
2969
2989
|
if (tmp_selectedBorderColor != map.end()) {
|
|
2970
2990
|
fromRawValue(context, tmp_selectedBorderColor->second, result.selectedBorderColor);
|
|
2971
2991
|
}
|
|
2992
|
+
auto tmp_fontWeight = map.find("fontWeight");
|
|
2993
|
+
if (tmp_fontWeight != map.end()) {
|
|
2994
|
+
fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
|
|
2995
|
+
}
|
|
2972
2996
|
auto tmp_marginTop = map.find("marginTop");
|
|
2973
2997
|
if (tmp_marginTop != map.end()) {
|
|
2974
2998
|
fromRawValue(context, tmp_marginTop->second, result.marginTop);
|
|
@@ -3335,6 +3359,7 @@ class EnrichedMarkdownTextInputProps final : public ViewProps {
|
|
|
3335
3359
|
bool editable{false};
|
|
3336
3360
|
bool autoFocus{false};
|
|
3337
3361
|
bool scrollEnabled{false};
|
|
3362
|
+
bool blockSyntax{false};
|
|
3338
3363
|
std::string autoCapitalize{};
|
|
3339
3364
|
bool multiline{false};
|
|
3340
3365
|
SharedColor cursorColor{};
|
|
@@ -4878,6 +4903,7 @@ struct EnrichedMarkdownTextMarkdownStyleMathStruct {
|
|
|
4878
4903
|
Float marginTop{0.0};
|
|
4879
4904
|
Float marginBottom{0.0};
|
|
4880
4905
|
std::string textAlign{};
|
|
4906
|
+
std::string fontWeight{};
|
|
4881
4907
|
|
|
4882
4908
|
|
|
4883
4909
|
#ifdef RN_SERIALIZABLE_STATE
|
|
@@ -4892,6 +4918,7 @@ struct EnrichedMarkdownTextMarkdownStyleMathStruct {
|
|
|
4892
4918
|
result["marginTop"] = marginTop;
|
|
4893
4919
|
result["marginBottom"] = marginBottom;
|
|
4894
4920
|
result["textAlign"] = textAlign;
|
|
4921
|
+
result["fontWeight"] = fontWeight;
|
|
4895
4922
|
return result;
|
|
4896
4923
|
}
|
|
4897
4924
|
#endif
|
|
@@ -4928,6 +4955,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
4928
4955
|
if (tmp_textAlign != map.end()) {
|
|
4929
4956
|
fromRawValue(context, tmp_textAlign->second, result.textAlign);
|
|
4930
4957
|
}
|
|
4958
|
+
auto tmp_fontWeight = map.find("fontWeight");
|
|
4959
|
+
if (tmp_fontWeight != map.end()) {
|
|
4960
|
+
fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
|
|
4961
|
+
}
|
|
4931
4962
|
}
|
|
4932
4963
|
|
|
4933
4964
|
static inline std::string toString(const EnrichedMarkdownTextMarkdownStyleMathStruct &value) {
|
|
@@ -4943,6 +4974,7 @@ static inline folly::dynamic toDynamic(const EnrichedMarkdownTextMarkdownStyleMa
|
|
|
4943
4974
|
struct EnrichedMarkdownTextMarkdownStyleInlineMathStruct {
|
|
4944
4975
|
SharedColor color{};
|
|
4945
4976
|
Float paddingVertical{0.0};
|
|
4977
|
+
std::string fontWeight{};
|
|
4946
4978
|
|
|
4947
4979
|
|
|
4948
4980
|
#ifdef RN_SERIALIZABLE_STATE
|
|
@@ -4952,6 +4984,7 @@ struct EnrichedMarkdownTextMarkdownStyleInlineMathStruct {
|
|
|
4952
4984
|
folly::dynamic result = folly::dynamic::object();
|
|
4953
4985
|
result["color"] = ::facebook::react::toDynamic(color);
|
|
4954
4986
|
result["paddingVertical"] = paddingVertical;
|
|
4987
|
+
result["fontWeight"] = fontWeight;
|
|
4955
4988
|
return result;
|
|
4956
4989
|
}
|
|
4957
4990
|
#endif
|
|
@@ -4968,6 +5001,10 @@ static inline void fromRawValue(const PropsParserContext& context, const RawValu
|
|
|
4968
5001
|
if (tmp_paddingVertical != map.end()) {
|
|
4969
5002
|
fromRawValue(context, tmp_paddingVertical->second, result.paddingVertical);
|
|
4970
5003
|
}
|
|
5004
|
+
auto tmp_fontWeight = map.find("fontWeight");
|
|
5005
|
+
if (tmp_fontWeight != map.end()) {
|
|
5006
|
+
fromRawValue(context, tmp_fontWeight->second, result.fontWeight);
|
|
5007
|
+
}
|
|
4971
5008
|
}
|
|
4972
5009
|
|
|
4973
5010
|
static inline std::string toString(const EnrichedMarkdownTextMarkdownStyleInlineMathStruct &value) {
|
|
@@ -170,6 +170,14 @@ class EnrichedMarkdownTextInputManager :
|
|
|
170
170
|
view?.isVerticalScrollBarEnabled = value
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
@ReactProp(name = "blockSyntax", defaultBoolean = true)
|
|
174
|
+
override fun setBlockSyntax(
|
|
175
|
+
view: EnrichedMarkdownTextInputView?,
|
|
176
|
+
value: Boolean,
|
|
177
|
+
) {
|
|
178
|
+
view?.blockSyntax = value
|
|
179
|
+
}
|
|
180
|
+
|
|
173
181
|
@ReactProp(name = "autoCapitalize")
|
|
174
182
|
override fun setAutoCapitalize(
|
|
175
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 {
|
|
@@ -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/InputMeasurementStore.kt
CHANGED
|
@@ -99,7 +99,10 @@ object InputMeasurementStore {
|
|
|
99
99
|
// invisible characters and over-estimates the height into extra lines, until the next text
|
|
100
100
|
// change forces a re-measure. Parsing first matches what the editor actually renders.
|
|
101
101
|
val markdown = props?.getString("defaultValue") ?: props?.getString("placeholder") ?: "I"
|
|
102
|
-
|
|
102
|
+
// Must read the same flag the view parses with, or the measured height and the
|
|
103
|
+
// rendered height disagree: `blockSyntax = false` keeps list markers and `#` as text.
|
|
104
|
+
val blockSyntax = if (props?.hasKey("blockSyntax") == true) props.getBoolean("blockSyntax") else true
|
|
105
|
+
val text = InputParser.parseToPlainTextAndRanges(markdown, blockSyntax).plainText
|
|
103
106
|
val fontSize = props?.getDouble("fontSize")?.toFloat() ?: 16f
|
|
104
107
|
val spSize = kotlin.math.ceil(PixelUtil.toPixelFromSP(fontSize).toDouble()).toFloat()
|
|
105
108
|
|
|
@@ -13,6 +13,7 @@ open class MathChipStyle(
|
|
|
13
13
|
val paddingVertical: Float,
|
|
14
14
|
val selectedBackgroundColor: Int?,
|
|
15
15
|
val selectedBorderColor: Int?,
|
|
16
|
+
val fontWeight: String = "normal",
|
|
16
17
|
) {
|
|
17
18
|
open fun isBlock(): Boolean = false
|
|
18
19
|
}
|
|
@@ -28,6 +29,7 @@ class MathBlockStyle(
|
|
|
28
29
|
paddingVertical: Float,
|
|
29
30
|
selectedBackgroundColor: Int?,
|
|
30
31
|
selectedBorderColor: Int?,
|
|
32
|
+
fontWeight: String = "normal",
|
|
31
33
|
val marginTop: Float,
|
|
32
34
|
val marginBottom: Float,
|
|
33
35
|
val alignment: Layout.Alignment,
|
|
@@ -42,6 +44,7 @@ class MathBlockStyle(
|
|
|
42
44
|
paddingVertical,
|
|
43
45
|
selectedBackgroundColor,
|
|
44
46
|
selectedBorderColor,
|
|
47
|
+
fontWeight,
|
|
45
48
|
) {
|
|
46
49
|
override fun isBlock(): Boolean = true
|
|
47
50
|
}
|
|
@@ -5,6 +5,7 @@ import com.facebook.react.bridge.ReadableMap
|
|
|
5
5
|
data class InlineMathStyle(
|
|
6
6
|
val color: Int,
|
|
7
7
|
val paddingVertical: Float,
|
|
8
|
+
val fontWeight: String,
|
|
8
9
|
) {
|
|
9
10
|
companion object {
|
|
10
11
|
fun fromReadableMap(
|
|
@@ -13,7 +14,8 @@ data class InlineMathStyle(
|
|
|
13
14
|
): InlineMathStyle {
|
|
14
15
|
val color = parser.parseColor(map, "color")
|
|
15
16
|
val paddingVertical = parser.toPixelFromDIP(map.getDouble("paddingVertical").toFloat())
|
|
16
|
-
|
|
17
|
+
val fontWeight = parser.parseString(map, "fontWeight", "normal")
|
|
18
|
+
return InlineMathStyle(color, paddingVertical, fontWeight)
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
21
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
package com.swmansion.enriched.markdown.styles
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Weight a formula can actually be typeset at.
|
|
5
|
+
*
|
|
6
|
+
* The style model keeps `fontWeight` as a [String], the way every other
|
|
7
|
+
* `fontWeight` prop in the codebase does, so a value like `"600"` survives as
|
|
8
|
+
* far as the renderer. This enum is the narrowing step at that last hop — see
|
|
9
|
+
* `ratexTypefaceLoader`, the only caller — so adding a third weight is a
|
|
10
|
+
* compile error at the draw site rather than a value that silently falls back
|
|
11
|
+
* to regular. Mirrors ENRMMathFontWeight.h on iOS.
|
|
12
|
+
*/
|
|
13
|
+
enum class MathFontWeight {
|
|
14
|
+
NORMAL,
|
|
15
|
+
BOLD,
|
|
16
|
+
;
|
|
17
|
+
|
|
18
|
+
companion object {
|
|
19
|
+
fun fromString(value: String): MathFontWeight =
|
|
20
|
+
when (value.lowercase()) {
|
|
21
|
+
"bold" -> BOLD
|
|
22
|
+
else -> NORMAL
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -10,6 +10,7 @@ data class MathStyle(
|
|
|
10
10
|
val marginTop: Float,
|
|
11
11
|
val marginBottom: Float,
|
|
12
12
|
val textAlign: String,
|
|
13
|
+
val fontWeight: String,
|
|
13
14
|
) {
|
|
14
15
|
companion object {
|
|
15
16
|
fun fromReadableMap(
|
|
@@ -23,7 +24,17 @@ data class MathStyle(
|
|
|
23
24
|
val marginTop = parser.toPixelFromDIP(map.getDouble("marginTop").toFloat())
|
|
24
25
|
val marginBottom = parser.toPixelFromDIP(map.getDouble("marginBottom").toFloat())
|
|
25
26
|
val textAlign = parser.parseString(map, "textAlign", "center")
|
|
26
|
-
|
|
27
|
+
val fontWeight = parser.parseString(map, "fontWeight", "normal")
|
|
28
|
+
return MathStyle(
|
|
29
|
+
fontSize,
|
|
30
|
+
color,
|
|
31
|
+
backgroundColor,
|
|
32
|
+
padding,
|
|
33
|
+
marginTop,
|
|
34
|
+
marginBottom,
|
|
35
|
+
textAlign,
|
|
36
|
+
fontWeight,
|
|
37
|
+
)
|
|
27
38
|
}
|
|
28
39
|
}
|
|
29
40
|
}
|
package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/MarkdownStyleParser.kt
CHANGED
|
@@ -76,6 +76,8 @@ object MarkdownStyleParser {
|
|
|
76
76
|
if (mathMap?.hasKey("selectedBackgroundColor") == true) mathMap.getInt("selectedBackgroundColor") else null
|
|
77
77
|
val selectedBorderColor =
|
|
78
78
|
if (mathMap?.hasKey("selectedBorderColor") == true) mathMap.getInt("selectedBorderColor") else null
|
|
79
|
+
val fontWeight =
|
|
80
|
+
(if (mathMap?.hasKey("fontWeight") == true) mathMap.getString("fontWeight") else null) ?: "normal"
|
|
79
81
|
|
|
80
82
|
return MathChipStyle(
|
|
81
83
|
color = color,
|
|
@@ -88,6 +90,7 @@ object MarkdownStyleParser {
|
|
|
88
90
|
paddingVertical = paddingVertical,
|
|
89
91
|
selectedBackgroundColor = nullIfTransparent(selectedBackgroundColor),
|
|
90
92
|
selectedBorderColor = nullIfTransparent(selectedBorderColor),
|
|
93
|
+
fontWeight = fontWeight,
|
|
91
94
|
)
|
|
92
95
|
}
|
|
93
96
|
|
|
@@ -143,6 +146,12 @@ object MarkdownStyleParser {
|
|
|
143
146
|
} else {
|
|
144
147
|
inlineFallback.selectedBorderColor
|
|
145
148
|
}
|
|
149
|
+
val fontWeight =
|
|
150
|
+
if (mathBlockMap?.hasKey("fontWeight") == true) {
|
|
151
|
+
mathBlockMap.getString("fontWeight") ?: "normal"
|
|
152
|
+
} else {
|
|
153
|
+
inlineFallback.fontWeight
|
|
154
|
+
}
|
|
146
155
|
val marginTop =
|
|
147
156
|
PixelUtil.toPixelFromDIP(
|
|
148
157
|
if (mathBlockMap?.hasKey("marginTop") == true) mathBlockMap.getDouble("marginTop").toFloat() else 8f,
|
|
@@ -170,6 +179,7 @@ object MarkdownStyleParser {
|
|
|
170
179
|
paddingVertical = paddingVertical,
|
|
171
180
|
selectedBackgroundColor = nullIfTransparent(selectedBackgroundColor),
|
|
172
181
|
selectedBorderColor = nullIfTransparent(selectedBorderColor),
|
|
182
|
+
fontWeight = fontWeight,
|
|
173
183
|
marginTop = marginTop,
|
|
174
184
|
marginBottom = marginBottom,
|
|
175
185
|
alignment = alignment,
|
package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt
CHANGED
|
@@ -36,6 +36,7 @@ class MathInlineRenderer(
|
|
|
36
36
|
fontSize = blockStyle.fontSize,
|
|
37
37
|
textColor = config.style.inlineMathStyle.color,
|
|
38
38
|
paddingVertical = config.style.inlineMathStyle.paddingVertical,
|
|
39
|
+
fontWeight = config.style.inlineMathStyle.fontWeight,
|
|
39
40
|
)
|
|
40
41
|
|
|
41
42
|
builder.setSpan(span, start, end, SPAN_FLAGS_EXCLUSIVE_EXCLUSIVE)
|
|
@@ -66,7 +66,7 @@ class MathChipSpan(
|
|
|
66
66
|
displayMode = displayMode,
|
|
67
67
|
color = style.color,
|
|
68
68
|
)
|
|
69
|
-
val renderer = RaTeXRenderer(displayList, style.fontSize
|
|
69
|
+
val renderer = RaTeXRenderer(displayList, style.fontSize, ratexTypefaceLoader(style.fontWeight))
|
|
70
70
|
formulaWidth = renderer.widthPx
|
|
71
71
|
formulaAscent = renderer.heightPx
|
|
72
72
|
formulaDescent = renderer.depthPx
|
|
@@ -6,7 +6,6 @@ import android.graphics.Canvas
|
|
|
6
6
|
import android.graphics.Paint
|
|
7
7
|
import android.text.style.ReplacementSpan
|
|
8
8
|
import io.ratex.RaTeXEngine
|
|
9
|
-
import io.ratex.RaTeXFontLoader
|
|
10
9
|
import io.ratex.RaTeXRenderer
|
|
11
10
|
import kotlin.math.ceil
|
|
12
11
|
|
|
@@ -16,6 +15,7 @@ class MathInlineSpan(
|
|
|
16
15
|
internal val fontSize: Float,
|
|
17
16
|
private val textColor: Int,
|
|
18
17
|
private val paddingVertical: Float,
|
|
18
|
+
private val fontWeight: String = "normal",
|
|
19
19
|
) : ReplacementSpan() {
|
|
20
20
|
private var cachedBitmap: Bitmap? = null
|
|
21
21
|
private var cachedWidth = 0
|
|
@@ -29,7 +29,7 @@ class MathInlineSpan(
|
|
|
29
29
|
|
|
30
30
|
try {
|
|
31
31
|
val displayList = RaTeXEngine.parseBlocking(latex, displayMode = false, color = textColor)
|
|
32
|
-
val renderer = RaTeXRenderer(displayList, fontSize
|
|
32
|
+
val renderer = RaTeXRenderer(displayList, fontSize, ratexTypefaceLoader(fontWeight))
|
|
33
33
|
|
|
34
34
|
cachedWidth = renderer.widthPx.toInt().coerceAtLeast(1)
|
|
35
35
|
mathAscent = renderer.heightPx
|