react-native-marcus 0.1.0 → 0.2.0
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/README.md +220 -63
- package/RNMarcus.podspec +6 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusPackage.kt +2 -1
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorView.kt +168 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManager.kt +38 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextDecoratorViewManagerSpec.kt +19 -0
- package/android/src/main/java/app/getbullet/marcus/MarcusTextInputDecoratorView.kt +23 -1
- package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPainter.kt +147 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownBackgroundPass.kt +19 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownCopy.kt +87 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownFormatter.kt +652 -113
- package/android/src/main/java/app/getbullet/marcus/MarkdownRanges.kt +47 -0
- package/android/src/main/java/app/getbullet/marcus/MarkdownStyle.kt +140 -61
- package/android/src/main/java/app/getbullet/marcus/MarkdownUtils.kt +37 -7
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownAbsoluteLineHeightSpan.kt +48 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundSpan.kt +102 -48
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBlockIndentSpan.kt +22 -4
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBulletSpan.kt +78 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpan.kt +177 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontStyleSpan.kt +32 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownFontWeightSpan.kt +34 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownGapSpan.kt +24 -11
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownLetterSpacingSpan.kt +32 -0
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownTextShadowSpan.kt +32 -0
- package/android/src/main/new_arch/CMakeLists.txt +17 -0
- package/android/src/main/new_arch/RNMarcusSpec.h +1 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.cpp +235 -0
- package/android/src/main/new_arch/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorShadowNode.h +62 -0
- package/android/src/reactNativeVersionPatch/CustomMountingManager/81/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
- package/android/src/reactNativeVersionPatch/CustomMountingManager/latest/app/getbullet/marcus/CustomFabricUIManager.java +22 -3
- package/android/src/test/java/app/getbullet/marcus/MarkdownCopyTest.kt +69 -0
- package/android/src/test/java/app/getbullet/marcus/MarkdownFormatterTest.kt +170 -25
- package/android/src/test/java/app/getbullet/marcus/RenderModel.kt +22 -5
- package/android/src/test/java/app/getbullet/marcus/spans/MarkdownCodeBlockSpanTest.kt +91 -0
- package/android/src/test/resources/render-model.txt +221 -16
- package/apple/MarcusSwizzle.h +25 -0
- package/apple/MarcusTextDecoratorComponentView.h +22 -0
- package/apple/MarcusTextDecoratorComponentView.mm +72 -0
- package/apple/MarcusTextDecoratorShadowNode.h +62 -0
- package/apple/MarcusTextDecoratorShadowNode.mm +192 -0
- package/apple/MarcusTextDecoratorViewManager.h +4 -0
- package/apple/MarcusTextDecoratorViewManager.mm +11 -0
- package/apple/MarcusTextInputDecoratorComponentView.mm +1 -1
- package/apple/MarcusTextInputDecoratorShadowNode.mm +1 -1
- package/apple/MarcusTextLayoutManager.h +40 -0
- package/apple/MarcusTextLayoutManager.mm +173 -0
- package/apple/MarkdownAttributes.h +23 -2
- package/apple/MarkdownAttributes.m +6 -0
- package/apple/MarkdownCopy.swift +63 -0
- package/apple/MarkdownDecorator.swift +2 -2
- package/apple/MarkdownFormatter+React.swift +23 -0
- package/apple/MarkdownFormatter.swift +869 -148
- package/apple/MarkdownParagraphLayoutManager.swift +455 -0
- package/apple/MarkdownSwiftInterop.h +0 -1
- package/apple/MarkdownTextBackground.swift +59 -1
- package/apple/MarkdownTextFieldObserver.swift +2 -2
- package/apple/MarkdownTextLayoutFragment.swift +159 -33
- package/apple/MarkdownTextLayoutManagerDelegate.swift +67 -3
- package/apple/MarkdownTextStorageDelegate.swift +2 -2
- package/apple/MarkdownTextViewObserver.swift +2 -2
- package/apple/MarkdownUtils.swift +38 -0
- package/apple/MarkdownWritingDirection.swift +132 -0
- package/apple/RCTMarcusStyle+Codegen.h +115 -9
- package/apple/RCTMarcusStyle.h +128 -31
- package/apple/RCTMarcusStyle.m +48 -0
- package/apple/RCTParagraphComponentView+Marcus.h +17 -0
- package/apple/RCTParagraphComponentView+Marcus.mm +38 -0
- package/apple/RCTTextInputComponentView+Marcus.mm +1 -25
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorState.h +37 -0
- package/cpp/react/renderer/components/RNMarcusSpec/MarcusTextDecoratorViewComponentDescriptor.h +17 -0
- package/lib/commonjs/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/lib/commonjs/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/lib/commonjs/MarkdownText.js +330 -0
- package/lib/commonjs/MarkdownText.js.map +1 -0
- package/lib/commonjs/MarkdownText.web.js +529 -0
- package/lib/commonjs/MarkdownText.web.js.map +1 -0
- package/lib/commonjs/MarkdownTextInput.js +22 -27
- package/lib/commonjs/MarkdownTextInput.js.map +1 -1
- package/lib/commonjs/MarkdownTextInput.web.js +12 -7
- package/lib/commonjs/MarkdownTextInput.web.js.map +1 -1
- package/lib/commonjs/atomUtils.js +112 -0
- package/lib/commonjs/atomUtils.js.map +1 -0
- package/lib/commonjs/childrenUtils.js +36 -0
- package/lib/commonjs/childrenUtils.js.map +1 -0
- package/lib/commonjs/index.js +8 -7
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/math/environment.js +69 -0
- package/lib/commonjs/math/environment.js.map +1 -0
- package/lib/commonjs/math/index.js +61 -0
- package/lib/commonjs/math/index.js.map +1 -0
- package/lib/commonjs/math/mathjax.js +548 -0
- package/lib/commonjs/math/mathjax.js.map +1 -0
- package/lib/commonjs/metro.js +172 -0
- package/lib/commonjs/metro.js.map +1 -0
- package/lib/commonjs/parser/index.d.ts +3 -0
- package/lib/commonjs/parser/index.js +450 -42
- package/lib/commonjs/parser/index.js.map +4 -4
- package/lib/commonjs/stripUtils.js +743 -0
- package/lib/commonjs/stripUtils.js.map +1 -0
- package/lib/commonjs/styleUtils.js +194 -23
- package/lib/commonjs/styleUtils.js.map +1 -1
- package/lib/commonjs/web/MarkdownElements.css +67 -0
- package/lib/commonjs/web/MarkdownTextInput.css +9 -7
- package/lib/commonjs/web/utils/blockLayout.js +95 -7
- package/lib/commonjs/web/utils/blockLayout.js.map +1 -1
- package/lib/commonjs/web/utils/blockUtils.js +327 -54
- package/lib/commonjs/web/utils/blockUtils.js.map +1 -1
- package/lib/commonjs/web/utils/elementUtils.js +80 -0
- package/lib/commonjs/web/utils/elementUtils.js.map +1 -0
- package/lib/commonjs/web/utils/parserUtils.js +84 -28
- package/lib/commonjs/web/utils/parserUtils.js.map +1 -1
- package/lib/commonjs/web/utils/treeUtils.js +6 -0
- package/lib/commonjs/web/utils/treeUtils.js.map +1 -1
- package/lib/commonjs/web/utils/webStyleUtils.js +25 -5
- package/lib/commonjs/web/utils/webStyleUtils.js.map +1 -1
- package/lib/module/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/lib/module/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/lib/module/MarkdownText.js +325 -0
- package/lib/module/MarkdownText.js.map +1 -0
- package/lib/module/MarkdownText.web.js +524 -0
- package/lib/module/MarkdownText.web.js.map +1 -0
- package/lib/module/MarkdownTextInput.js +24 -29
- package/lib/module/MarkdownTextInput.js.map +1 -1
- package/lib/module/MarkdownTextInput.web.js +12 -7
- package/lib/module/MarkdownTextInput.web.js.map +1 -1
- package/lib/module/atomUtils.js +106 -0
- package/lib/module/atomUtils.js.map +1 -0
- package/lib/module/childrenUtils.js +33 -0
- package/lib/module/childrenUtils.js.map +1 -0
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/math/environment.js +67 -0
- package/lib/module/math/environment.js.map +1 -0
- package/lib/module/math/index.js +56 -0
- package/lib/module/math/index.js.map +1 -0
- package/lib/module/math/mathjax.js +541 -0
- package/lib/module/math/mathjax.js.map +1 -0
- package/lib/module/metro.js +169 -0
- package/lib/module/metro.js.map +1 -0
- package/lib/module/parser/index.d.ts +3 -0
- package/lib/module/parser/index.js +450 -42
- package/lib/module/parser/index.js.map +4 -4
- package/lib/module/stripUtils.js +739 -0
- package/lib/module/stripUtils.js.map +1 -0
- package/lib/module/styleUtils.js +196 -25
- package/lib/module/styleUtils.js.map +1 -1
- package/lib/module/web/MarkdownElements.css +67 -0
- package/lib/module/web/MarkdownTextInput.css +9 -7
- package/lib/module/web/utils/blockLayout.js +94 -8
- package/lib/module/web/utils/blockLayout.js.map +1 -1
- package/lib/module/web/utils/blockUtils.js +328 -56
- package/lib/module/web/utils/blockUtils.js.map +1 -1
- package/lib/module/web/utils/elementUtils.js +75 -0
- package/lib/module/web/utils/elementUtils.js.map +1 -0
- package/lib/module/web/utils/parserUtils.js +86 -30
- package/lib/module/web/utils/parserUtils.js.map +1 -1
- package/lib/module/web/utils/treeUtils.js +6 -0
- package/lib/module/web/utils/treeUtils.js.map +1 -1
- package/lib/module/web/utils/webStyleUtils.js +25 -5
- package/lib/module/web/utils/webStyleUtils.js.map +1 -1
- package/lib/typescript/commonjs/e2e/links.test.d.ts +2 -0
- package/lib/typescript/commonjs/e2e/links.test.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
- package/lib/typescript/commonjs/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
- package/lib/typescript/commonjs/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/MarkdownText.d.ts +127 -0
- package/lib/typescript/commonjs/src/MarkdownText.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownText.web.d.ts +126 -0
- package/lib/typescript/commonjs/src/MarkdownText.web.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts +23 -1
- package/lib/typescript/commonjs/src/MarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts +26 -4
- package/lib/typescript/commonjs/src/MarkdownTextInput.web.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/atomUtils.d.ts +58 -0
- package/lib/typescript/commonjs/src/atomUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/childrenUtils.d.ts +12 -0
- package/lib/typescript/commonjs/src/childrenUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/commonTypes.d.ts +39 -3
- package/lib/typescript/commonjs/src/commonTypes.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +2 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/math/environment.d.ts +7 -0
- package/lib/typescript/commonjs/src/math/environment.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/math/index.d.ts +64 -0
- package/lib/typescript/commonjs/src/math/index.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/math/mathjax.d.ts +63 -0
- package/lib/typescript/commonjs/src/math/mathjax.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/metro.d.ts +59 -0
- package/lib/typescript/commonjs/src/metro.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/parser/index.d.ts +3 -3
- package/lib/typescript/commonjs/src/parser/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/parser/micromark-extension-mentions.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/rangeUtils.d.ts +1 -1
- package/lib/typescript/commonjs/src/stripUtils.d.ts +108 -0
- package/lib/typescript/commonjs/src/stripUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/styleUtils.d.ts +62 -4
- package/lib/typescript/commonjs/src/styleUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts +32 -4
- package/lib/typescript/commonjs/src/web/utils/blockLayout.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts +20 -8
- package/lib/typescript/commonjs/src/web/utils/blockUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts +37 -0
- package/lib/typescript/commonjs/src/web/utils/elementUtils.d.ts.map +1 -0
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts +5 -5
- package/lib/typescript/commonjs/src/web/utils/parserUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts +6 -1
- package/lib/typescript/commonjs/src/web/utils/treeUtils.d.ts.map +1 -1
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts +2 -2
- package/lib/typescript/commonjs/src/web/utils/webStyleUtils.d.ts.map +1 -1
- package/lib/typescript/module/e2e/links.test.d.ts +2 -0
- package/lib/typescript/module/e2e/links.test.d.ts.map +1 -0
- package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts +93 -0
- package/lib/typescript/module/src/MarcusTextDecoratorViewNativeComponent.d.ts.map +1 -0
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts +64 -51
- package/lib/typescript/module/src/MarcusTextInputDecoratorViewNativeComponent.d.ts.map +1 -1
- package/lib/typescript/module/src/MarkdownText.d.ts +127 -0
- package/lib/typescript/module/src/MarkdownText.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownText.web.d.ts +126 -0
- package/lib/typescript/module/src/MarkdownText.web.d.ts.map +1 -0
- package/lib/typescript/module/src/MarkdownTextInput.d.ts +23 -1
- package/lib/typescript/module/src/MarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts +26 -4
- package/lib/typescript/module/src/MarkdownTextInput.web.d.ts.map +1 -1
- package/lib/typescript/module/src/atomUtils.d.ts +58 -0
- package/lib/typescript/module/src/atomUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/childrenUtils.d.ts +12 -0
- package/lib/typescript/module/src/childrenUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/commonTypes.d.ts +39 -3
- package/lib/typescript/module/src/commonTypes.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +2 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/math/environment.d.ts +7 -0
- package/lib/typescript/module/src/math/environment.d.ts.map +1 -0
- package/lib/typescript/module/src/math/index.d.ts +64 -0
- package/lib/typescript/module/src/math/index.d.ts.map +1 -0
- package/lib/typescript/module/src/math/mathjax.d.ts +63 -0
- package/lib/typescript/module/src/math/mathjax.d.ts.map +1 -0
- package/lib/typescript/module/src/metro.d.ts +59 -0
- package/lib/typescript/module/src/metro.d.ts.map +1 -0
- package/lib/typescript/module/src/parser/index.d.ts +3 -3
- package/lib/typescript/module/src/parser/index.d.ts.map +1 -1
- package/lib/typescript/module/src/parser/micromark-extension-mentions.d.ts.map +1 -1
- package/lib/typescript/module/src/rangeUtils.d.ts +1 -1
- package/lib/typescript/module/src/stripUtils.d.ts +108 -0
- package/lib/typescript/module/src/stripUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/styleUtils.d.ts +62 -4
- package/lib/typescript/module/src/styleUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts +32 -4
- package/lib/typescript/module/src/web/utils/blockLayout.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts +20 -8
- package/lib/typescript/module/src/web/utils/blockUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/elementUtils.d.ts +37 -0
- package/lib/typescript/module/src/web/utils/elementUtils.d.ts.map +1 -0
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts +5 -5
- package/lib/typescript/module/src/web/utils/parserUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts +6 -1
- package/lib/typescript/module/src/web/utils/treeUtils.d.ts.map +1 -1
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts +2 -2
- package/lib/typescript/module/src/web/utils/webStyleUtils.d.ts.map +1 -1
- package/package.json +29 -2
- package/react-native.config.js +8 -1
- package/src/MarcusTextDecoratorViewNativeComponent.ts +199 -0
- package/src/MarcusTextInputDecoratorViewNativeComponent.ts +145 -58
- package/src/MarkdownText.tsx +472 -0
- package/src/MarkdownText.web.tsx +668 -0
- package/src/MarkdownTextInput.tsx +42 -38
- package/src/MarkdownTextInput.web.tsx +36 -13
- package/src/atomUtils.ts +112 -0
- package/src/childrenUtils.ts +39 -0
- package/src/commonTypes.ts +69 -9
- package/src/index.tsx +2 -1
- package/src/math/environment.ts +68 -0
- package/src/math/index.ts +78 -0
- package/src/math/mathjax.ts +604 -0
- package/src/metro.ts +238 -0
- package/src/parser/index.ts +175 -10
- package/src/parser/micromark-extension-mentions.ts +104 -11
- package/src/stripUtils.ts +919 -0
- package/src/styleUtils.ts +290 -26
- package/src/web/MarkdownElements.css +67 -0
- package/src/web/MarkdownTextInput.css +9 -7
- package/src/web/utils/blockLayout.ts +124 -14
- package/src/web/utils/blockUtils.ts +365 -59
- package/src/web/utils/elementUtils.ts +87 -0
- package/src/web/utils/parserUtils.ts +119 -31
- package/src/web/utils/treeUtils.ts +6 -1
- package/src/web/utils/webStyleUtils.ts +32 -8
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBackgroundColorSpan.kt +0 -6
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownBoldSpan.kt +0 -6
- package/android/src/main/java/app/getbullet/marcus/spans/MarkdownItalicSpan.kt +0 -6
- package/apple/RCTMarcusStyle+Codegen.mm +0 -64
- package/apple/RCTMarcusUtils.h +0 -26
- package/apple/RCTMarcusUtils.mm +0 -26
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import type {
|
|
2
|
+
MarkdownBoxStyle,
|
|
3
|
+
MarkdownTextStyle,
|
|
4
|
+
} from "../../MarcusTextInputDecoratorViewNativeComponent"
|
|
5
|
+
import type { ResolvedMarkdownStyle } from "../../styleUtils"
|
|
6
|
+
import { SHOWN_MARKERS, toNumber } from "./blockLayout"
|
|
7
|
+
import type { LineLayout, MarkerRendering } from "./blockLayout"
|
|
4
8
|
import type { NodeType } from "./treeUtils"
|
|
5
9
|
|
|
6
10
|
/**
|
|
@@ -22,17 +26,109 @@ function applyStyle(node: HTMLElement, styles: Record<string, string | number |
|
|
|
22
26
|
})
|
|
23
27
|
}
|
|
24
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Everything a `MarkdownTextStyle` says, in CSS.
|
|
31
|
+
*
|
|
32
|
+
* One place rather than a line per entry in the switch below, because every styled type now carries
|
|
33
|
+
* the same set of text attributes: the difference between a mention and a heading is the box around
|
|
34
|
+
* it and the size it is given, not which properties it understands. `MarkdownFormatter` reads the
|
|
35
|
+
* same struct into attributed-string attributes on iOS and into spans on Android.
|
|
36
|
+
*
|
|
37
|
+
* Undefined properties are dropped by `applyStyle`, so a run inherits whatever the prose around it
|
|
38
|
+
* is drawn in -- which is what an unset text attribute means on all three platforms.
|
|
39
|
+
*/
|
|
40
|
+
function textStyle(style: Partial<MarkdownTextStyle> | undefined) {
|
|
41
|
+
if (!style) {
|
|
42
|
+
return {}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
color: style.color as string | undefined,
|
|
47
|
+
fontFamily: style.fontFamily,
|
|
48
|
+
fontSize: style.fontSize,
|
|
49
|
+
fontStyle: style.fontStyle,
|
|
50
|
+
fontWeight: style.fontWeight,
|
|
51
|
+
letterSpacing: style.letterSpacing,
|
|
52
|
+
lineHeight: style.lineHeight,
|
|
53
|
+
textDecorationLine: style.textDecorationLine,
|
|
54
|
+
textDecorationColor: style.textDecorationColor as string | undefined,
|
|
55
|
+
textDecorationStyle: style.textDecorationStyle,
|
|
56
|
+
textShadow: textShadow(style),
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* React Native's three shadow properties as the one CSS declaration they add up to.
|
|
62
|
+
*
|
|
63
|
+
* The offset and the radius stay raw numbers through `processMarkdownStyle` -- an offset is an
|
|
64
|
+
* object rather than a length, and half a shadow carrying units would not compose -- so the units
|
|
65
|
+
* are put on here.
|
|
66
|
+
*/
|
|
67
|
+
function textShadow(style: Partial<MarkdownTextStyle>): string | undefined {
|
|
68
|
+
const color = style.textShadowColor as string | undefined
|
|
69
|
+
const radius = toNumber(style.textShadowRadius)
|
|
70
|
+
const x = toNumber(style.textShadowOffset?.width)
|
|
71
|
+
const y = toNumber(style.textShadowOffset?.height)
|
|
72
|
+
|
|
73
|
+
if (!color && radius <= 0 && x === 0 && y === 0) {
|
|
74
|
+
return undefined
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return `${x}px ${y}px ${radius}px ${color || "currentColor"}`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The room a boxed run holds open on one axis: the axis value where it is set, and the value for
|
|
82
|
+
* both axes where it is not.
|
|
83
|
+
*
|
|
84
|
+
* A fenced block usually wants more above and below than beside it, and the single `padding` a
|
|
85
|
+
* style used to carry could not say that. The native formatters resolve the pair the same way.
|
|
86
|
+
*/
|
|
87
|
+
function axis(both: unknown, axisValue: unknown): number {
|
|
88
|
+
return axisValue === undefined || axisValue === null || axisValue === ""
|
|
89
|
+
? toNumber(both as string | number | undefined)
|
|
90
|
+
: toNumber(axisValue as string | number | undefined)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The box a run of code or a mention sits in: its fill, its border, and the room inside and around
|
|
95
|
+
* it, written as the two-value shorthands so the horizontal halves stay direction-agnostic.
|
|
96
|
+
*/
|
|
97
|
+
function boxStyle(style: Partial<MarkdownBoxStyle> | undefined) {
|
|
98
|
+
if (!style) {
|
|
99
|
+
return {}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const borderWidth = toNumber(style.borderWidth)
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
backgroundColor: style.backgroundColor as string | undefined,
|
|
106
|
+
borderRadius: style.borderRadius,
|
|
107
|
+
borderWidth: borderWidth > 0 ? `${borderWidth}px` : undefined,
|
|
108
|
+
borderStyle: borderWidth > 0 ? "solid" : undefined,
|
|
109
|
+
borderColor: borderWidth > 0 ? (style.borderColor as string) || "currentColor" : undefined,
|
|
110
|
+
padding: `${axis(style.padding, style.paddingVertical)}px ${axis(
|
|
111
|
+
style.padding,
|
|
112
|
+
style.paddingHorizontal,
|
|
113
|
+
)}px`,
|
|
114
|
+
margin: `${axis(style.margin, style.marginVertical)}px ${axis(
|
|
115
|
+
style.margin,
|
|
116
|
+
style.marginHorizontal,
|
|
117
|
+
)}px`,
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
25
121
|
/**
|
|
26
122
|
* Applies the layout the block walk computed for one line.
|
|
27
123
|
*
|
|
28
|
-
* Everything a container contributes lands here rather than on the container's own
|
|
124
|
+
* Everything a container contributes lands here rather than on the container's own element: its gutter
|
|
29
125
|
* is part of the paragraph's indent, and its ribbon is painted behind the whole paragraph. That is
|
|
30
126
|
* how the native formatters do it too, and on the web it is the only placement that works -- a
|
|
31
|
-
* container's
|
|
127
|
+
* container's element begins before the markers of the containers outside it, so a border drawn on it
|
|
32
128
|
* would sit in front of a bullet that is meant to precede it, and a gutter set on it would open
|
|
33
129
|
* before that bullet rather than after.
|
|
34
130
|
*
|
|
35
|
-
* `padding-
|
|
131
|
+
* `padding-inline-start` holds every line of the paragraph at the indent, and the negative `text-indent`
|
|
36
132
|
* pulls the first one back to where its own marker starts -- `firstLineHeadIndent` and `headIndent`,
|
|
37
133
|
* spelled in CSS. A line that wraps, or a line continuing the block with no marker of its own, then
|
|
38
134
|
* lines up with the text above it instead of with the marker.
|
|
@@ -40,7 +136,7 @@ function applyStyle(node: HTMLElement, styles: Record<string, string | number |
|
|
|
40
136
|
function addLineLayout(
|
|
41
137
|
node: HTMLElement,
|
|
42
138
|
layout: LineLayout | undefined,
|
|
43
|
-
markdownStyle:
|
|
139
|
+
markdownStyle: ResolvedMarkdownStyle,
|
|
44
140
|
) {
|
|
45
141
|
applyStyle(node, {
|
|
46
142
|
margin: "0",
|
|
@@ -53,7 +149,10 @@ function addLineLayout(
|
|
|
53
149
|
|
|
54
150
|
if (layout.indent > 0) {
|
|
55
151
|
applyStyle(node, {
|
|
56
|
-
|
|
152
|
+
// Logical rather than `padding-left`, because a container's gutter opens on the line's
|
|
153
|
+
// leading edge -- which is what `marginStart` and `paddingStart` name, and what both native
|
|
154
|
+
// platforms already indent from.
|
|
155
|
+
paddingInlineStart: `${layout.indent}px`,
|
|
57
156
|
textIndent: `${layout.firstLineIndent - layout.indent}px`,
|
|
58
157
|
})
|
|
59
158
|
}
|
|
@@ -64,17 +163,87 @@ function addLineLayout(
|
|
|
64
163
|
}
|
|
65
164
|
|
|
66
165
|
// One background layer per ribbon, each a solid bar of its own. They are painted on the paragraph
|
|
67
|
-
// rather than on
|
|
68
|
-
// unbroken bar, exactly as `MarkdownTextLayoutFragment` draws it over the whole fragment.
|
|
166
|
+
// rather than on anything inside it so that they run its full height -- a quote that wraps gets
|
|
167
|
+
// one unbroken bar, exactly as `MarkdownTextLayoutFragment` draws it over the whole fragment.
|
|
168
|
+
//
|
|
169
|
+
// The offsets are distances from the line's leading edge -- the same numbers `getLeadingMargin`
|
|
170
|
+
// is handed on Android -- so the edge they are measured from has to move with the line's own
|
|
171
|
+
// direction. `RIBBON_EDGE` leaves that edge to be named later, by `setRibbonEdge`.
|
|
69
172
|
const color = (markdownStyle.blockquote?.borderColor as string) || "transparent"
|
|
173
|
+
const radius = toNumber(markdownStyle.blockquote?.borderRadius)
|
|
174
|
+
|
|
70
175
|
applyStyle(node, {
|
|
71
|
-
backgroundImage: layout.ribbons.map(() =>
|
|
72
|
-
|
|
176
|
+
backgroundImage: layout.ribbons.map(() => bar(color, radius)).join(", "),
|
|
177
|
+
backgroundPositionX: layout.ribbons.map((x) => `${RIBBON_EDGE} ${x}px`).join(", "),
|
|
178
|
+
// One value for every layer: a shorter list is repeated to cover them all.
|
|
179
|
+
backgroundPositionY: "0",
|
|
73
180
|
backgroundSize: layout.ribbons.map(() => `${width}px 100%`).join(", "),
|
|
74
181
|
backgroundRepeat: "no-repeat",
|
|
75
182
|
})
|
|
76
183
|
}
|
|
77
184
|
|
|
185
|
+
/**
|
|
186
|
+
* The custom property naming the edge a ribbon's offset is measured from, and the reference to it
|
|
187
|
+
* the offsets are written against.
|
|
188
|
+
*
|
|
189
|
+
* `background-position` is the one part of a line still spelled physically: it has no logical form.
|
|
190
|
+
* So the edge is named indirectly and `setRibbonEdge` fills it in once the line has its text --
|
|
191
|
+
* which is also why it is a custom property rather than a value written straight into the offsets:
|
|
192
|
+
* the offsets are known while building the line, the direction only after.
|
|
193
|
+
*
|
|
194
|
+
* The fallback in the reference is what a line gets before then, and what one gets in a browser
|
|
195
|
+
* that cannot answer the question at all.
|
|
196
|
+
*/
|
|
197
|
+
const RIBBON_EDGE_PROPERTY = "--marcus-ribbon-edge"
|
|
198
|
+
const RIBBON_EDGE = `var(${RIBBON_EDGE_PROPERTY}, left)`
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Turns a line's ribbons round when the line reads right to left.
|
|
202
|
+
*
|
|
203
|
+
* Asked of the browser rather than worked out from the text, because the browser is what lays the
|
|
204
|
+
* line out: whatever it resolved `dir="auto"` to is where it put the gutter, and the bars belong on
|
|
205
|
+
* that side whether or not a bidi rule written here would have agreed. `:dir()` is unsupported in
|
|
206
|
+
* browsers older than Safari 16.4, where `matches` throws and the fallback edge stands.
|
|
207
|
+
*
|
|
208
|
+
* Called once the line holds its text, since that is what a line's own direction is read from, and
|
|
209
|
+
* only for a line that has ribbons: nothing else on a line is measured from an edge.
|
|
210
|
+
*/
|
|
211
|
+
function setRibbonEdge(node: HTMLElement, layout: LineLayout | undefined) {
|
|
212
|
+
if (!layout || layout.ribbons.length === 0) {
|
|
213
|
+
return
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
if (node.matches(":dir(rtl)")) {
|
|
218
|
+
node.style.setProperty(RIBBON_EDGE_PROPERTY, "right")
|
|
219
|
+
}
|
|
220
|
+
} catch {
|
|
221
|
+
// A browser without `:dir()`. Left is the fallback the offsets already carry.
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* One quote bar, as a background layer.
|
|
227
|
+
*
|
|
228
|
+
* A gradient has no corners, so a bar that is asked to round its ends is drawn as an SVG rectangle
|
|
229
|
+
* instead. The SVG carries no `viewBox`, which means its user units are the pixels the layer is
|
|
230
|
+
* sized to and `rx` is the radius that was asked for rather than a radius stretched by the height
|
|
231
|
+
* of the line. `MarkdownTextLayoutFragment` and `MarkdownBlockIndentSpan` round the same bar with a
|
|
232
|
+
* rounded-rect path.
|
|
233
|
+
*/
|
|
234
|
+
function bar(color: string, radius: number): string {
|
|
235
|
+
if (radius <= 0) {
|
|
236
|
+
return `linear-gradient(${color}, ${color})`
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const svg =
|
|
240
|
+
`<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%">` +
|
|
241
|
+
`<rect width="100%" height="100%" rx="${radius}" ry="${radius}" fill="${color}"/>` +
|
|
242
|
+
`</svg>`
|
|
243
|
+
|
|
244
|
+
return `url("data:image/svg+xml,${encodeURIComponent(svg)}")`
|
|
245
|
+
}
|
|
246
|
+
|
|
78
247
|
/**
|
|
79
248
|
* Opens the space a marker holds for the container that follows it.
|
|
80
249
|
*
|
|
@@ -83,30 +252,49 @@ function addLineLayout(
|
|
|
83
252
|
*/
|
|
84
253
|
function addBlockPrefixGap(node: HTMLElement, gap: number | undefined) {
|
|
85
254
|
if (gap) {
|
|
86
|
-
|
|
255
|
+
// Logical: the room belongs after the marker in reading order, which is its left in a
|
|
256
|
+
// right-to-left line. `MarkdownFormatter` kerns the marker's last character, and kerning
|
|
257
|
+
// follows the run's own direction for the same reason.
|
|
258
|
+
node.style.marginInlineEnd = `${gap}px`
|
|
87
259
|
}
|
|
88
260
|
}
|
|
89
261
|
|
|
90
262
|
function addStyleToBlock(
|
|
91
263
|
targetElement: HTMLElement,
|
|
92
264
|
type: NodeType,
|
|
93
|
-
markdownStyle:
|
|
265
|
+
markdownStyle: ResolvedMarkdownStyle,
|
|
94
266
|
isMultiline = true,
|
|
267
|
+
rendering: MarkerRendering = SHOWN_MARKERS,
|
|
95
268
|
) {
|
|
96
269
|
const node = targetElement
|
|
97
270
|
|
|
98
271
|
switch (type) {
|
|
272
|
+
// A fenced block's language is its own type so that something can read it,
|
|
273
|
+
// but in an input it is part of the fence you are typing and reads as
|
|
274
|
+
// markup, so it is coloured like the rest of it.
|
|
99
275
|
case "syntax":
|
|
100
|
-
|
|
276
|
+
case "language": {
|
|
277
|
+
// A list's marker is the one syntax a display keeps, and it is drawn there
|
|
278
|
+
// rather than shown -- everything else the colour is for has been stripped
|
|
279
|
+
// out by the time a display is built, so this is the only case where the
|
|
280
|
+
// two components part company.
|
|
281
|
+
const list = rendering.display && type === "syntax" ? markerList(node) : null
|
|
282
|
+
|
|
283
|
+
if (list) {
|
|
284
|
+
addMarkerStyle(node, list, markdownStyle, rendering)
|
|
285
|
+
} else {
|
|
286
|
+
applyStyle(node, textStyle(markdownStyle.syntax))
|
|
287
|
+
}
|
|
101
288
|
break
|
|
102
|
-
|
|
103
|
-
|
|
289
|
+
}
|
|
290
|
+
case "strong":
|
|
291
|
+
applyStyle(node, textStyle(markdownStyle.strong))
|
|
104
292
|
break
|
|
105
|
-
case "
|
|
106
|
-
node.
|
|
293
|
+
case "emphasis":
|
|
294
|
+
applyStyle(node, textStyle(markdownStyle.emphasis))
|
|
107
295
|
break
|
|
108
296
|
case "strikethrough":
|
|
109
|
-
node.
|
|
297
|
+
applyStyle(node, textStyle(markdownStyle.strikethrough))
|
|
110
298
|
break
|
|
111
299
|
case "emoji":
|
|
112
300
|
applyStyle(node, {
|
|
@@ -117,43 +305,42 @@ function addStyleToBlock(
|
|
|
117
305
|
verticalAlign: "middle",
|
|
118
306
|
})
|
|
119
307
|
break
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
addMentionStyle(node, markdownStyle.mentionReport)
|
|
308
|
+
// A pill: the same inline box an inline run of code sits in, and drawn the same way. The
|
|
309
|
+
// padding and margin push the words either side of it apart, the padding grows into the
|
|
310
|
+
// line's own spacing above and below rather than pushing the lines apart, and a mention that
|
|
311
|
+
// wraps is rounded only where it begins and ends.
|
|
312
|
+
case "mention":
|
|
313
|
+
applyStyle(node, textStyle(markdownStyle.mention))
|
|
314
|
+
applyStyle(node, boxStyle(markdownStyle.mention))
|
|
128
315
|
break
|
|
129
316
|
case "link":
|
|
130
|
-
applyStyle(node,
|
|
131
|
-
color: markdownStyle.link?.color as string,
|
|
132
|
-
textDecoration: "underline",
|
|
133
|
-
})
|
|
317
|
+
applyStyle(node, textStyle(markdownStyle.link))
|
|
134
318
|
break
|
|
135
|
-
//
|
|
136
|
-
//
|
|
319
|
+
// An inline box, which is what the platforms draw by hand: the padding and margin push the
|
|
320
|
+
// words either side of it apart, the padding grows into the line's own spacing above and
|
|
321
|
+
// below rather than pushing the lines apart, and a run that wraps is rounded only where it
|
|
322
|
+
// begins and ends. All three are what CSS does with an inline element by default.
|
|
137
323
|
case "code":
|
|
138
|
-
applyStyle(node,
|
|
139
|
-
|
|
140
|
-
fontSize: markdownStyle.code?.fontSize,
|
|
141
|
-
color: markdownStyle.code?.color as string,
|
|
142
|
-
backgroundColor: markdownStyle.code?.backgroundColor as string,
|
|
143
|
-
})
|
|
324
|
+
applyStyle(node, textStyle(markdownStyle.code))
|
|
325
|
+
applyStyle(node, boxStyle(markdownStyle.code))
|
|
144
326
|
break
|
|
327
|
+
// Only the text attributes of a block; the box it sits in is `codeblock`, which is the range
|
|
328
|
+
// that covers the whole thing rather than only the code inside it.
|
|
145
329
|
case "pre":
|
|
146
|
-
applyStyle(node,
|
|
147
|
-
fontFamily: markdownStyle.pre?.fontFamily,
|
|
148
|
-
fontSize: markdownStyle.pre?.fontSize,
|
|
149
|
-
color: markdownStyle.pre?.color as string,
|
|
150
|
-
backgroundColor: markdownStyle.pre?.backgroundColor as string,
|
|
151
|
-
})
|
|
330
|
+
applyStyle(node, textStyle(markdownStyle.pre))
|
|
152
331
|
break
|
|
153
|
-
|
|
154
|
-
|
|
332
|
+
case "codeblock":
|
|
333
|
+
addCodeBlockStyle(node, markdownStyle, isMultiline)
|
|
334
|
+
break
|
|
335
|
+
// A quoted block holds no box of its own: its gutter and its ribbons belong to the paragraph,
|
|
336
|
+
// in `addLineLayout`. What is left is the text inside it, and letting a long unbroken word
|
|
155
337
|
// break rather than push the line past the input's edge.
|
|
156
338
|
case "blockquote":
|
|
339
|
+
applyStyle(node, textStyle(markdownStyle.blockquote))
|
|
340
|
+
applyStyle(node, { overflowWrap: "anywhere" })
|
|
341
|
+
break
|
|
342
|
+
// Neither do the list containers, and their markers are styled from the `syntax` range inside
|
|
343
|
+
// their prefix rather than from here.
|
|
157
344
|
case "list-ordered":
|
|
158
345
|
case "list-unordered":
|
|
159
346
|
applyStyle(node, { overflowWrap: "anywhere" })
|
|
@@ -174,32 +361,150 @@ function addStyleToBlock(
|
|
|
174
361
|
}
|
|
175
362
|
}
|
|
176
363
|
|
|
177
|
-
|
|
364
|
+
/**
|
|
365
|
+
* The box behind a fenced or indented block: one fill for the whole thing, from the first line to
|
|
366
|
+
* the last, which is what the native formatters draw.
|
|
367
|
+
*
|
|
368
|
+
* `codeblock` covers the block including its fences, and the builder merges all of its lines into
|
|
369
|
+
* one element, so here the box is a real box rather than the several line rects each platform has
|
|
370
|
+
* to fill by hand. What that costs is the one thing an element cannot do: a block opened after a
|
|
371
|
+
* bullet or a quote's `>` shares that line in the buffer but not on screen, because a block-level
|
|
372
|
+
* box drops below the marker in front of it.
|
|
373
|
+
*
|
|
374
|
+
* A singleline input has no room for a block at all -- every line of it is drawn on one -- so there
|
|
375
|
+
* the background stays an inline one, painted behind the text.
|
|
376
|
+
*/
|
|
377
|
+
function addCodeBlockStyle(
|
|
178
378
|
node: HTMLElement,
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
| undefined,
|
|
379
|
+
markdownStyle: ResolvedMarkdownStyle,
|
|
380
|
+
isMultiline: boolean,
|
|
182
381
|
) {
|
|
382
|
+
const style = markdownStyle.pre
|
|
383
|
+
const box = boxStyle(style)
|
|
384
|
+
|
|
385
|
+
applyStyle(node, {
|
|
386
|
+
backgroundColor: box.backgroundColor,
|
|
387
|
+
borderRadius: box.borderRadius,
|
|
388
|
+
borderWidth: box.borderWidth,
|
|
389
|
+
borderStyle: box.borderStyle,
|
|
390
|
+
borderColor: box.borderColor,
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
if (!isMultiline) {
|
|
394
|
+
return
|
|
395
|
+
}
|
|
396
|
+
|
|
183
397
|
applyStyle(node, {
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
398
|
+
display: "block",
|
|
399
|
+
// The padding is inside the box, so the box still spans the line rather than overflowing it.
|
|
400
|
+
boxSizing: "border-box",
|
|
401
|
+
padding: box.padding,
|
|
402
|
+
margin: box.margin,
|
|
403
|
+
})
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* The list whose marker `node` is, or null if this syntax is something else.
|
|
408
|
+
*
|
|
409
|
+
* A marker is a `syntax` inside the `block-prefix` of a list container, which is a shape only a
|
|
410
|
+
* list has: a quote's `>` is removed with the rest of the markup before a display is built.
|
|
411
|
+
*/
|
|
412
|
+
function markerList(node: HTMLElement): "list-ordered" | "list-unordered" | null {
|
|
413
|
+
let current = node.parentNode as HTMLElement | null
|
|
414
|
+
let inPrefix = false
|
|
415
|
+
|
|
416
|
+
while (current && current.contentEditable !== "true") {
|
|
417
|
+
const type = current.getAttribute?.("data-type")
|
|
418
|
+
|
|
419
|
+
if (type === "block-prefix") {
|
|
420
|
+
inPrefix = true
|
|
421
|
+
} else if (type === "list-ordered" || type === "list-unordered") {
|
|
422
|
+
return inPrefix ? type : null
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
current = current.parentNode as HTMLElement | null
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return null
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
/**
|
|
432
|
+
* Draws a list's marker: a bullet for an unordered item, the number at its own scale for an
|
|
433
|
+
* ordered one.
|
|
434
|
+
*
|
|
435
|
+
* Both are sized from the base font rather than from a length of their own, so a list marks itself
|
|
436
|
+
* in proportion to the prose it marks, and both are drawn from the list's own `marker` style over
|
|
437
|
+
* `syntax` -- which, in a display where every other piece of syntax has been removed, is where a
|
|
438
|
+
* marker's colour came from before lists could name one. `MarkdownFormatter` draws the same two
|
|
439
|
+
* shapes from the same numbers on iOS and Android.
|
|
440
|
+
*
|
|
441
|
+
* The bullet keeps the marker character inside it, boxed and invisible: it is what a reader copies
|
|
442
|
+
* and what a screen reader announces, and the box is exactly the width `layoutBlocks` indented the
|
|
443
|
+
* text past.
|
|
444
|
+
*/
|
|
445
|
+
function addMarkerStyle(
|
|
446
|
+
node: HTMLElement,
|
|
447
|
+
type: "list-ordered" | "list-unordered",
|
|
448
|
+
markdownStyle: ResolvedMarkdownStyle,
|
|
449
|
+
rendering: MarkerRendering,
|
|
450
|
+
) {
|
|
451
|
+
const list = type === "list-ordered" ? markdownStyle.orderedList : markdownStyle.unorderedList
|
|
452
|
+
// What the list says about its marker, over what `syntax` says about markup in general. Before
|
|
453
|
+
// lists had a `marker` entry this was `syntax.color` and nothing else, which is what an unset
|
|
454
|
+
// `marker` still resolves to.
|
|
455
|
+
const style = { ...markdownStyle.syntax, ...list?.marker }
|
|
456
|
+
const color = (style.color as string) || ""
|
|
457
|
+
const { fontSize } = rendering
|
|
458
|
+
const scale = toNumber(list?.markerScale)
|
|
459
|
+
|
|
460
|
+
if (scale <= 0 || fontSize <= 0) {
|
|
461
|
+
applyStyle(node, textStyle(style))
|
|
462
|
+
return
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (type === "list-ordered") {
|
|
466
|
+
applyStyle(node, textStyle(style))
|
|
467
|
+
applyStyle(node, { fontSize: fontSize * scale })
|
|
468
|
+
return
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
const diameter = fontSize * scale
|
|
472
|
+
|
|
473
|
+
// A box one line high, aligned to the top of the line, with the circle painted in the middle of
|
|
474
|
+
// it: that is what centres the bullet on the line rather than on the text, which is where both
|
|
475
|
+
// native platforms draw it. The height is the marker character's own line box -- an inline block
|
|
476
|
+
// holding one line of text is exactly one line high -- so it follows the line height without
|
|
477
|
+
// having to be told it.
|
|
478
|
+
applyStyle(node, textStyle(style))
|
|
479
|
+
applyStyle(node, {
|
|
480
|
+
display: "inline-block",
|
|
481
|
+
width: diameter,
|
|
482
|
+
verticalAlign: "top",
|
|
483
|
+
backgroundImage: `radial-gradient(circle closest-side, ${
|
|
484
|
+
color || "currentColor"
|
|
485
|
+
} 100%, transparent 100%)`,
|
|
486
|
+
backgroundSize: `${diameter}px ${diameter}px`,
|
|
487
|
+
backgroundPosition: "center",
|
|
488
|
+
backgroundRepeat: "no-repeat",
|
|
489
|
+
// The character is still in the box, and stays out of sight of everything but a selection.
|
|
490
|
+
color: "transparent",
|
|
491
|
+
overflow: "hidden",
|
|
187
492
|
})
|
|
188
493
|
}
|
|
189
494
|
|
|
190
495
|
/**
|
|
191
496
|
* Sizes a heading the way native does: level N is the base size scaled N-1 times.
|
|
192
497
|
*
|
|
193
|
-
* The level does not reach here -- `ungroupRanges` turns it into one nested
|
|
498
|
+
* The level does not reach here -- `ungroupRanges` turns it into one nested element per level -- so
|
|
194
499
|
* the scaling is expressed in `em` and compounds down the nesting to the same number. This runs
|
|
195
|
-
* once the
|
|
500
|
+
* once the element is in the tree, which is what lets it tell an inner level from the outermost one.
|
|
196
501
|
*/
|
|
197
|
-
function addHeadingStyle(node: HTMLElement, markdownStyle:
|
|
502
|
+
function addHeadingStyle(node: HTMLElement, markdownStyle: ResolvedMarkdownStyle) {
|
|
198
503
|
const nested = isChildOfMarkdownElement(node, "heading")
|
|
199
504
|
const scale = markdownStyle.heading?.scale
|
|
200
505
|
|
|
506
|
+
applyStyle(node, textStyle(markdownStyle.heading))
|
|
201
507
|
applyStyle(node, {
|
|
202
|
-
fontWeight: "bold",
|
|
203
508
|
fontSize: nested ? `${scale === undefined ? 1 : scale}em` : markdownStyle.heading?.fontSize,
|
|
204
509
|
})
|
|
205
510
|
}
|
|
@@ -237,6 +542,7 @@ function isChildOfMultilineMarkdownElement(node: HTMLElement): boolean {
|
|
|
237
542
|
export {
|
|
238
543
|
addStyleToBlock,
|
|
239
544
|
addLineLayout,
|
|
545
|
+
setRibbonEdge,
|
|
240
546
|
addBlockPrefixGap,
|
|
241
547
|
isMultilineMarkdownType,
|
|
242
548
|
isChildOfMarkdownElement,
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { HTMLMarkdownElement } from "../../MarkdownTextInput.web"
|
|
2
|
+
import type { NodeType } from "./treeUtils"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The tag each markdown type is built as.
|
|
6
|
+
*
|
|
7
|
+
* A range says what a run of text *is*; the element it becomes should say the same thing to
|
|
8
|
+
* everything that reads the document rather than looking at it. A screen reader announces a
|
|
9
|
+
* `<strong>` and a `<blockquote>` and says nothing about a span; text copied out of the input
|
|
10
|
+
* arrives in another editor still emphasised; and the page `expo export` prerenders carries the
|
|
11
|
+
* structure rather than a wall of undifferentiated spans.
|
|
12
|
+
*
|
|
13
|
+
* `data-type` stays on every one of them regardless -- it is what the styling, the CSS and the
|
|
14
|
+
* render-model baselines are keyed on, and it distinguishes the several types that share a tag.
|
|
15
|
+
*
|
|
16
|
+
* Two pairings are worth spelling out. A fenced or indented block is `<pre>` from `codeblock`, the
|
|
17
|
+
* range covering the whole thing including its fences, with `<code>` inside it from `pre`, the
|
|
18
|
+
* range covering only the code -- which is the nesting HTML asks for, in the opposite naming to
|
|
19
|
+
* ours. And `link` is an anchor even in the input, where it has no `href` yet: an anchor without
|
|
20
|
+
* one is exactly what HTML has for a link whose destination is not resolved.
|
|
21
|
+
*
|
|
22
|
+
* Everything absent from here is a span, because HTML has nothing that means it: markdown's own
|
|
23
|
+
* punctuation, the parts of a link's markup, a mention, an emoji run.
|
|
24
|
+
*/
|
|
25
|
+
/** Marks an element as one of ours, for the rules that undo the tag's browser defaults. */
|
|
26
|
+
const ELEMENT_CLASS = "react-native-marcus"
|
|
27
|
+
|
|
28
|
+
const TAGS: Partial<Record<NodeType, string>> = {
|
|
29
|
+
"strong": "strong",
|
|
30
|
+
"emphasis": "em",
|
|
31
|
+
// GFM renders `~~x~~` as `<del>`, and so does this. `<s>` is for text that is no longer accurate
|
|
32
|
+
// rather than text that was taken out.
|
|
33
|
+
"strikethrough": "del",
|
|
34
|
+
"code": "code",
|
|
35
|
+
"pre": "code",
|
|
36
|
+
"codeblock": "pre",
|
|
37
|
+
"blockquote": "blockquote",
|
|
38
|
+
"list-ordered": "ol",
|
|
39
|
+
"list-unordered": "ul",
|
|
40
|
+
"list-item": "li",
|
|
41
|
+
"link": "a",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The types that become a block-level element, and so cannot sit in a `<p>`.
|
|
46
|
+
*
|
|
47
|
+
* `pre` is not among them: it is built as `<code>`, which is phrasing content whatever it is
|
|
48
|
+
* displayed as. Its enclosing `codeblock` is the `<pre>`, and that is.
|
|
49
|
+
*/
|
|
50
|
+
const BLOCK_LEVEL_TYPES = new Set<NodeType>([
|
|
51
|
+
"blockquote",
|
|
52
|
+
"codeblock",
|
|
53
|
+
"heading",
|
|
54
|
+
"list-ordered",
|
|
55
|
+
"list-unordered",
|
|
56
|
+
])
|
|
57
|
+
|
|
58
|
+
function isBlockLevelType(type: NodeType) {
|
|
59
|
+
return BLOCK_LEVEL_TYPES.has(type)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Builds the element for a range, tagged and typed.
|
|
64
|
+
*
|
|
65
|
+
* `level` applies to headings only, and is the ATX level: `1` through `6` produce that heading,
|
|
66
|
+
* and `0` produces a span. A level arrives as one nested range per level -- `ungroupRanges` is what
|
|
67
|
+
* turns `depth` into the nesting the size scaling compounds down -- so only the outermost of them
|
|
68
|
+
* can be the heading itself, and the copies inside it are spans. `<h2><h2>` is not a thing.
|
|
69
|
+
*/
|
|
70
|
+
function createMarkdownElement(type: NodeType, level = 0): HTMLMarkdownElement {
|
|
71
|
+
const tag =
|
|
72
|
+
type === "heading" ? (level > 0 ? `h${Math.min(level, 6)}` : "span") : TAGS[type] ?? "span"
|
|
73
|
+
const element = document.createElement(tag) as HTMLMarkdownElement
|
|
74
|
+
|
|
75
|
+
element.setAttribute("data-type", type)
|
|
76
|
+
|
|
77
|
+
// Every tag but the span brings the browser's own idea of how it looks, and `MarkdownElements.css`
|
|
78
|
+
// hands that back. The class is what scopes those rules to elements this library built, rather
|
|
79
|
+
// than to every `<blockquote data-type>` on the page.
|
|
80
|
+
if (tag !== "span") {
|
|
81
|
+
element.className = ELEMENT_CLASS
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return element
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export { createMarkdownElement, isBlockLevelType, ELEMENT_CLASS }
|