@apollohg/react-native-rich-text-editor 2.0.4 → 2.0.6
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 +11 -0
- package/android/src/main/java/com/apollohg/editor/EditorCheckboxSpan.kt +8 -3
- package/android/src/main/java/com/apollohg/editor/EditorEditTextConfiguration.kt +5 -1
- package/android/src/main/java/com/apollohg/editor/EditorEditTextToolbar.kt +1 -1
- package/android/src/main/java/com/apollohg/editor/EditorMentionStyle.kt +3 -2
- package/android/src/main/java/com/apollohg/editor/EditorStyleSheet.kt +196 -8
- package/android/src/main/java/com/apollohg/editor/EditorV2Adapter.kt +7 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2Driver.kt +1 -0
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +48 -9
- package/android/src/main/java/com/apollohg/editor/RenderBridgeBlockStyles.kt +17 -4
- package/android/src/main/java/com/apollohg/editor/RenderBridgeElements.kt +83 -25
- package/android/src/main/java/com/apollohg/editor/RenderBridgeInlineAndAtoms.kt +28 -14
- package/android/src/main/java/com/apollohg/editor/RenderParagraphSpans.kt +5 -1
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +5 -1
- package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +141 -40
- package/dist/EditorStyleSheetNormalization.js +37 -0
- package/dist/EditorStyleSheetTypes.d.ts +22 -1
- package/dist/EditorTheme.js +4 -1
- package/dist/index.d.ts +1 -1
- package/dist/useRichTextEditorCommands.js +1 -6
- package/ios/EditorCore.xcframework/Info.plist +5 -5
- package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +2 -2
- package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +2 -2
- package/ios/EditorLayoutManager.swift +2 -4
- package/ios/EditorStyleSheet.swift +77 -11
- package/ios/EditorTextView+Commands.swift +1 -1
- package/ios/EditorTextView.swift +5 -2
- package/ios/EditorTheme.swift +3 -1
- package/ios/EditorV2Adapter+Commands.swift +10 -0
- package/ios/EditorV2Shadow.swift +4 -0
- package/ios/RenderBridge+Atoms.swift +15 -6
- package/ios/RenderBridge+Style.swift +59 -17
- package/ios/RenderBridge.swift +19 -16
- package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +31 -13
- package/ios/Viewer/CoreTextProseLayoutEngine+Models.swift +17 -2
- package/ios/Viewer/CoreTextProseLayoutEngine.swift +39 -21
- package/ios/Viewer/Fabric/PREPPreparedProseViewerComponentView.mm +1 -0
- package/package.json +3 -3
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- package/rust/android/x86/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/src/EditorStyleSheetNormalization.ts +50 -0
- package/src/EditorStyleSheetTypes.ts +51 -6
- package/src/EditorTheme.ts +5 -1
- package/src/index.ts +1 -0
- package/src/useRichTextEditorCommands.tsx +1 -7
|
@@ -90,7 +90,9 @@ private data class PreparedMarker(
|
|
|
90
90
|
val ascentPx: Int,
|
|
91
91
|
val baselinePx: Int,
|
|
92
92
|
val checked: Boolean,
|
|
93
|
-
val checkbox: com.apollohg.editor.EditorElementStyle? = null
|
|
93
|
+
val checkbox: com.apollohg.editor.EditorElementStyle? = null,
|
|
94
|
+
val color: Int? = null,
|
|
95
|
+
val gapPx: Int? = null
|
|
94
96
|
)
|
|
95
97
|
|
|
96
98
|
internal const val PREPARED_LIST_MARKER_GAP_DP = 6f
|
|
@@ -251,7 +253,8 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
251
253
|
ancestor.context,
|
|
252
254
|
nestingDepth,
|
|
253
255
|
theme.paintFor(block),
|
|
254
|
-
theme
|
|
256
|
+
theme,
|
|
257
|
+
markerAncestors(block, nestingDepth)
|
|
255
258
|
)
|
|
256
259
|
}
|
|
257
260
|
}
|
|
@@ -259,7 +262,9 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
259
262
|
val containerBounds = mutableMapOf<Int, Rect>()
|
|
260
263
|
val sheet = theme.sourceTheme?.styleSheet
|
|
261
264
|
val leafBoxes = visibleBlocks.mapIndexed { index, block ->
|
|
262
|
-
val box =
|
|
265
|
+
val box =
|
|
266
|
+
sheet?.box(block.nodeType, block.containers.map { it.nodeType })?.scaled(density)
|
|
267
|
+
?: EditorBoxStyle()
|
|
263
268
|
val parent = block.containers.lastOrNull()
|
|
264
269
|
if (block.nodeType == "paragraph" && parent?.nodeType == "blockquote" &&
|
|
265
270
|
parent.lastLeaf == index
|
|
@@ -288,13 +293,23 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
288
293
|
}
|
|
289
294
|
val bottom = (
|
|
290
295
|
previous.getOrNull(common)?.let {
|
|
291
|
-
sheet.box(
|
|
296
|
+
sheet.box(
|
|
297
|
+
it.nodeType,
|
|
298
|
+
previous.take(common).map {
|
|
299
|
+
it.nodeType
|
|
300
|
+
}
|
|
301
|
+
).scaled(density).margin.bottom
|
|
292
302
|
}
|
|
293
303
|
?: leafBoxes[index - 1].margin.bottom
|
|
294
304
|
).toInt()
|
|
295
305
|
val top = (
|
|
296
306
|
containers.getOrNull(common)?.let {
|
|
297
|
-
sheet.box(
|
|
307
|
+
sheet.box(
|
|
308
|
+
it.nodeType,
|
|
309
|
+
containers.take(common).map {
|
|
310
|
+
it.nodeType
|
|
311
|
+
}
|
|
312
|
+
).scaled(density).margin.top
|
|
298
313
|
}
|
|
299
314
|
?: leafBoxes[index].margin.top
|
|
300
315
|
).toInt()
|
|
@@ -302,7 +317,13 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
302
317
|
}
|
|
303
318
|
var containerInset = EditorEdges()
|
|
304
319
|
containers.forEach { ancestor ->
|
|
305
|
-
val box = sheet!!.box(
|
|
320
|
+
val box = sheet!!.box(
|
|
321
|
+
ancestor.nodeType,
|
|
322
|
+
containers.takeWhile {
|
|
323
|
+
it.identity !=
|
|
324
|
+
ancestor.identity
|
|
325
|
+
}.map { it.nodeType }
|
|
326
|
+
).scaled(density)
|
|
306
327
|
if (ancestor.firstLeaf == index) {
|
|
307
328
|
cursorY += box.margin.top.toInt()
|
|
308
329
|
containerBounds[ancestor.identity] =
|
|
@@ -379,7 +400,13 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
379
400
|
cursorY = prepared.nextY
|
|
380
401
|
containers.asReversed().forEach { ancestor ->
|
|
381
402
|
if (ancestor.lastLeaf == index) {
|
|
382
|
-
val box = sheet!!.box(
|
|
403
|
+
val box = sheet!!.box(
|
|
404
|
+
ancestor.nodeType,
|
|
405
|
+
containers.takeWhile {
|
|
406
|
+
it.identity !=
|
|
407
|
+
ancestor.identity
|
|
408
|
+
}.map { it.nodeType }
|
|
409
|
+
).scaled(density)
|
|
383
410
|
cursorY += box.inset.bottom.toInt()
|
|
384
411
|
containerBounds[ancestor.identity]?.bottom = cursorY
|
|
385
412
|
cursorY += box.margin.bottom.toInt()
|
|
@@ -420,7 +447,13 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
420
447
|
PreparedProseFragment(
|
|
421
448
|
PreparedProseFragmentKind.BACKGROUND,
|
|
422
449
|
clip,
|
|
423
|
-
box = sheet.box(
|
|
450
|
+
box = sheet.box(
|
|
451
|
+
ancestor.nodeType,
|
|
452
|
+
visibleBlocks[index].containers.takeWhile {
|
|
453
|
+
it.identity !=
|
|
454
|
+
ancestor.identity
|
|
455
|
+
}.map { it.nodeType }
|
|
456
|
+
).scaled(density),
|
|
424
457
|
decorationBounds = bounds
|
|
425
458
|
)
|
|
426
459
|
}
|
|
@@ -545,17 +578,19 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
545
578
|
val firstMarkers = ancestorMarkers.filter { (ancestor, _) ->
|
|
546
579
|
ancestor.isFirstRenderableLeaf
|
|
547
580
|
}
|
|
548
|
-
fun listStyle(ancestor: ViewerListItemAncestor) =
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
581
|
+
fun listStyle(ancestor: ViewerListItemAncestor) =
|
|
582
|
+
theme.sourceTheme?.styleSheet?.resolveElement(
|
|
583
|
+
if (ancestor.context.kind ==
|
|
584
|
+
"task"
|
|
585
|
+
) {
|
|
586
|
+
"taskList"
|
|
587
|
+
} else if (ancestor.context.ordered) {
|
|
588
|
+
"orderedList"
|
|
589
|
+
} else {
|
|
590
|
+
"bulletList"
|
|
591
|
+
},
|
|
592
|
+
markerAncestors(block, ancestors.indexOf(ancestor)).dropLast(2)
|
|
593
|
+
)
|
|
559
594
|
fun listIndent(ancestor: ViewerListItemAncestor) =
|
|
560
595
|
listStyle(ancestor)?.indent?.times(theme.density)?.toInt() ?: theme.listIndentPx
|
|
561
596
|
val baseListInset = if (ancestors.isEmpty()) {
|
|
@@ -578,7 +613,7 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
578
613
|
marker.widthPx +
|
|
579
614
|
(
|
|
580
615
|
marker.checkbox?.gap?.times(theme.density)?.toInt()
|
|
581
|
-
?: theme.listMarkerGapPx
|
|
616
|
+
?: marker.gapPx ?: theme.listMarkerGapPx
|
|
582
617
|
)
|
|
583
618
|
)
|
|
584
619
|
}
|
|
@@ -722,10 +757,16 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
722
757
|
PreparedProseFragmentKind.IMAGE,
|
|
723
758
|
bounds,
|
|
724
759
|
color =
|
|
725
|
-
theme.sourceTheme?.styleSheet?.box(
|
|
760
|
+
theme.sourceTheme?.styleSheet?.box(
|
|
761
|
+
"image",
|
|
762
|
+
block.containers.map {
|
|
763
|
+
it.nodeType
|
|
764
|
+
}
|
|
765
|
+
)?.backgroundColor
|
|
726
766
|
?: 0xFFF2F2F7.toInt(),
|
|
727
767
|
box = theme.sourceTheme?.styleSheet?.box(
|
|
728
|
-
"image"
|
|
768
|
+
"image",
|
|
769
|
+
block.containers.map { it.nodeType }
|
|
729
770
|
)?.scaled(theme.density)?.let {
|
|
730
771
|
it.copy(
|
|
731
772
|
border = EditorEdges(),
|
|
@@ -736,7 +777,12 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
736
777
|
)
|
|
737
778
|
},
|
|
738
779
|
resizeMode =
|
|
739
|
-
theme.sourceTheme?.styleSheet?.
|
|
780
|
+
theme.sourceTheme?.styleSheet?.resolveElement(
|
|
781
|
+
"image",
|
|
782
|
+
block.containers.map {
|
|
783
|
+
it.nodeType
|
|
784
|
+
}
|
|
785
|
+
)?.resizeMode
|
|
740
786
|
?: "contain"
|
|
741
787
|
)
|
|
742
788
|
),
|
|
@@ -750,20 +796,32 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
750
796
|
}
|
|
751
797
|
}
|
|
752
798
|
if (block.nodeType == "horizontalRule" || block.nodeType == "horizontal_rule") {
|
|
799
|
+
val sheet = theme.sourceTheme?.styleSheet
|
|
800
|
+
val ancestors = block.containers.map { it.nodeType }
|
|
801
|
+
val ruleStyle = sheet?.resolveElement("horizontalRule", ancestors)
|
|
802
|
+
val thickness = if (
|
|
803
|
+
sheet?.hasMatchingRuleProperty("horizontalRule", ancestors, "height") == true
|
|
804
|
+
) {
|
|
805
|
+
ruleStyle?.height?.times(theme.density)?.toInt() ?: theme.ruleThicknessPx
|
|
806
|
+
} else {
|
|
807
|
+
theme.ruleThicknessPx
|
|
808
|
+
}
|
|
753
809
|
val ruleTop = cursorY + theme.ruleMarginPx
|
|
754
810
|
val ruleLeft = theme.insetLeftPx + listInset + quoteInset
|
|
755
811
|
val ruleRight = max(
|
|
756
812
|
ruleLeft + 1,
|
|
757
813
|
theme.insetLeftPx + contentWidth - listInset - quoteInset
|
|
758
814
|
)
|
|
759
|
-
val rule = Rect(ruleLeft, ruleTop, ruleRight, ruleTop +
|
|
815
|
+
val rule = Rect(ruleLeft, ruleTop, ruleRight, ruleTop + thickness)
|
|
760
816
|
val fragments =
|
|
761
817
|
mutableListOf(
|
|
762
818
|
PreparedProseFragment(
|
|
763
819
|
PreparedProseFragmentKind.RULE,
|
|
764
820
|
rule,
|
|
765
|
-
color =
|
|
766
|
-
|
|
821
|
+
color =
|
|
822
|
+
ruleStyle?.box?.backgroundColor ?: ruleStyle?.text?.color
|
|
823
|
+
?: theme.ruleColor,
|
|
824
|
+
strokeWidth = thickness.toFloat()
|
|
767
825
|
)
|
|
768
826
|
)
|
|
769
827
|
val end = rule.bottom + theme.ruleMarginPx
|
|
@@ -808,7 +866,13 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
808
866
|
}
|
|
809
867
|
|
|
810
868
|
val availableWidth = max(1, contentWidth - listInset - quoteInset - codeInset * 2)
|
|
811
|
-
val attributed = attributed(
|
|
869
|
+
val attributed = attributed(
|
|
870
|
+
block.inlines,
|
|
871
|
+
paint,
|
|
872
|
+
theme,
|
|
873
|
+
warningSemanticGeneration,
|
|
874
|
+
block.containers.map { it.nodeType } + block.nodeType
|
|
875
|
+
)
|
|
812
876
|
var highlightedCodeKey: String? = null
|
|
813
877
|
if (block.nodeType == "codeBlock") {
|
|
814
878
|
theme.codeHighlighting?.let { config ->
|
|
@@ -1126,7 +1190,8 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1126
1190
|
inlines: List<ViewerInline>,
|
|
1127
1191
|
base: PreparedTextPaint,
|
|
1128
1192
|
theme: PreparedProseTheme,
|
|
1129
|
-
warningSemanticGeneration: String
|
|
1193
|
+
warningSemanticGeneration: String,
|
|
1194
|
+
ancestors: List<String>
|
|
1130
1195
|
): AttributedBlock {
|
|
1131
1196
|
val source = StringBuilder()
|
|
1132
1197
|
val spans = mutableListOf<(SpannableString) -> Unit>()
|
|
@@ -1138,7 +1203,8 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1138
1203
|
val start = source.length
|
|
1139
1204
|
source.append(inline.text)
|
|
1140
1205
|
val end = source.length
|
|
1141
|
-
val markSpans =
|
|
1206
|
+
val markSpans =
|
|
1207
|
+
markSpans(inline.marks, base, theme, warningSemanticGeneration, ancestors)
|
|
1142
1208
|
spans +=
|
|
1143
1209
|
{ value ->
|
|
1144
1210
|
markSpans.forEach {
|
|
@@ -1169,7 +1235,13 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1169
1235
|
source.append('\n')
|
|
1170
1236
|
} else {
|
|
1171
1237
|
val appearance =
|
|
1172
|
-
atomAppearance(
|
|
1238
|
+
atomAppearance(
|
|
1239
|
+
inline.nodeType,
|
|
1240
|
+
inline.attrsJson,
|
|
1241
|
+
base,
|
|
1242
|
+
theme,
|
|
1243
|
+
ancestors
|
|
1244
|
+
)
|
|
1173
1245
|
val label = inline.label.ifEmpty { " " }
|
|
1174
1246
|
val labelPaint = appearance.paint.newTextPaint()
|
|
1175
1247
|
val atomInset =
|
|
@@ -1292,7 +1364,8 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1292
1364
|
marks: List<uniffi.editor_core.FfiViewerMark>,
|
|
1293
1365
|
base: PreparedTextPaint,
|
|
1294
1366
|
theme: PreparedProseTheme,
|
|
1295
|
-
warningSemanticGeneration: String
|
|
1367
|
+
warningSemanticGeneration: String,
|
|
1368
|
+
ancestors: List<String>
|
|
1296
1369
|
): List<Any> {
|
|
1297
1370
|
theme.sourceTheme?.styleSheet?.let { sheet ->
|
|
1298
1371
|
var resolved = base.resolvedStyle ?: EditorTextStyle()
|
|
@@ -1304,7 +1377,7 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1304
1377
|
resolved =
|
|
1305
1378
|
resolved.mergedWith(
|
|
1306
1379
|
com.apollohg.editor.semanticText(it)
|
|
1307
|
-
).mergedWith(sheet
|
|
1380
|
+
).mergedWith(sheet.resolveElement(it, ancestors)?.text)
|
|
1308
1381
|
}
|
|
1309
1382
|
marks.forEach { mark ->
|
|
1310
1383
|
val attrs = runCatching { org.json.JSONObject(mark.attrsJson) }.getOrNull()
|
|
@@ -1491,16 +1564,34 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1491
1564
|
.build()
|
|
1492
1565
|
}
|
|
1493
1566
|
|
|
1567
|
+
private fun markerAncestors(block: ViewerBlock, nestingDepth: Int): List<String> {
|
|
1568
|
+
val chain = block.containers.map { it.nodeType }
|
|
1569
|
+
val item = chain.indices.filter {
|
|
1570
|
+
com.apollohg.editor.canonicalElement(chain[it]) in
|
|
1571
|
+
listOf("listItem", "taskItem")
|
|
1572
|
+
}.getOrNull(nestingDepth)
|
|
1573
|
+
return item?.let { chain.take(it + 1) } ?: chain
|
|
1574
|
+
}
|
|
1575
|
+
|
|
1494
1576
|
private fun markerFor(
|
|
1495
1577
|
context: ViewerListContext,
|
|
1496
1578
|
nestingDepth: Int,
|
|
1497
1579
|
textPaint: PreparedTextPaint,
|
|
1498
|
-
theme: PreparedProseTheme
|
|
1580
|
+
theme: PreparedProseTheme,
|
|
1581
|
+
ancestors: List<String>
|
|
1499
1582
|
): PreparedMarker {
|
|
1583
|
+
val style = theme.sourceTheme?.styleSheet?.resolveElement("listMarker", ancestors)
|
|
1584
|
+
val theme = theme.copy(
|
|
1585
|
+
listMarkerColor = style?.text?.color ?: theme.listMarkerColor,
|
|
1586
|
+
listMarkerScale = style?.scale ?: theme.listMarkerScale,
|
|
1587
|
+
orderedListMarker = style?.ordered ?: theme.orderedListMarker
|
|
1588
|
+
)
|
|
1589
|
+
val gap = style?.gap?.times(theme.density)?.toInt()
|
|
1500
1590
|
if (context.kind == "task" && theme.sourceTheme?.styleSheet != null) {
|
|
1501
1591
|
val checkbox = com.apollohg.editor.resolvedCheckboxStyle(
|
|
1502
1592
|
theme.sourceTheme.styleSheet,
|
|
1503
|
-
context.checked
|
|
1593
|
+
context.checked,
|
|
1594
|
+
ancestors
|
|
1504
1595
|
)
|
|
1505
1596
|
val side = ((checkbox.size ?: 18f) * theme.density).toInt()
|
|
1506
1597
|
return PreparedMarker(
|
|
@@ -1511,7 +1602,9 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1511
1602
|
side / 2,
|
|
1512
1603
|
0,
|
|
1513
1604
|
context.checked,
|
|
1514
|
-
checkbox.copy(box = checkbox.box.scaled(theme.density))
|
|
1605
|
+
checkbox.copy(box = checkbox.box.scaled(theme.density)),
|
|
1606
|
+
theme.listMarkerColor,
|
|
1607
|
+
gap
|
|
1515
1608
|
)
|
|
1516
1609
|
}
|
|
1517
1610
|
val label = when {
|
|
@@ -1542,7 +1635,11 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1542
1635
|
markerPaint.newTextPaint().fontMetricsInt.descent -
|
|
1543
1636
|
markerPaint.newTextPaint().fontMetricsInt.ascent
|
|
1544
1637
|
)
|
|
1545
|
-
return PreparedMarker(
|
|
1638
|
+
return PreparedMarker(
|
|
1639
|
+
null, label, side, side, side / 2, 0, context.checked,
|
|
1640
|
+
color = theme.listMarkerColor,
|
|
1641
|
+
gapPx = gap
|
|
1642
|
+
)
|
|
1546
1643
|
}
|
|
1547
1644
|
val text = markerPaint.newTextPaint()
|
|
1548
1645
|
val layout =
|
|
@@ -1561,7 +1658,9 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1561
1658
|
ink.heightPx,
|
|
1562
1659
|
ink.ascentPx,
|
|
1563
1660
|
layout.getLineBaseline(0),
|
|
1564
|
-
context.checked
|
|
1661
|
+
context.checked,
|
|
1662
|
+
color = theme.listMarkerColor,
|
|
1663
|
+
gapPx = gap
|
|
1565
1664
|
)
|
|
1566
1665
|
}
|
|
1567
1666
|
|
|
@@ -1587,7 +1686,7 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1587
1686
|
marker.layout,
|
|
1588
1687
|
x,
|
|
1589
1688
|
layoutY,
|
|
1590
|
-
color = color,
|
|
1689
|
+
color = marker.color ?: color,
|
|
1591
1690
|
label = marker.label,
|
|
1592
1691
|
checked = marker.checked,
|
|
1593
1692
|
box = marker.checkbox?.box,
|
|
@@ -1599,7 +1698,8 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1599
1698
|
nodeType: String,
|
|
1600
1699
|
attrsJson: String,
|
|
1601
1700
|
base: PreparedTextPaint,
|
|
1602
|
-
theme: PreparedProseTheme
|
|
1701
|
+
theme: PreparedProseTheme,
|
|
1702
|
+
ancestors: List<String>
|
|
1603
1703
|
): PreparedAtomAppearance {
|
|
1604
1704
|
if (nodeType == "mention") {
|
|
1605
1705
|
val values = runCatching { org.json.JSONObject(attrsJson) }.getOrNull()
|
|
@@ -1608,7 +1708,8 @@ internal class StaticLayoutAndroidProseLayoutEngine : AndroidProseLayoutEngine {
|
|
|
1608
1708
|
val element = com.apollohg.editor.resolvedMentionStyle(
|
|
1609
1709
|
base.resolvedStyle ?: EditorTextStyle(),
|
|
1610
1710
|
theme.sourceTheme,
|
|
1611
|
-
local
|
|
1711
|
+
local,
|
|
1712
|
+
ancestors
|
|
1612
1713
|
)
|
|
1613
1714
|
val box = element.box.scaled(theme.density)
|
|
1614
1715
|
return PreparedAtomAppearance(
|
|
@@ -249,6 +249,43 @@ function normalizeEditorTheme(theme) {
|
|
|
249
249
|
const styles = {};
|
|
250
250
|
const result = { version: 1 };
|
|
251
251
|
for (const [key, value] of Object.entries(theme)) {
|
|
252
|
+
if (key === 'rules') {
|
|
253
|
+
if (value === undefined) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (!Array.isArray(value)) {
|
|
257
|
+
invalid('rules', 'expected an array');
|
|
258
|
+
}
|
|
259
|
+
result.rules = value.map((entry, index) => {
|
|
260
|
+
const indexedPath = `rules[${index}]`;
|
|
261
|
+
if (!record(entry)) {
|
|
262
|
+
invalid(indexedPath, 'expected a rule object');
|
|
263
|
+
}
|
|
264
|
+
for (const propertyName of Object.keys(entry)) {
|
|
265
|
+
if (propertyName !== 'path' && propertyName !== 'style') {
|
|
266
|
+
invalid(`${indexedPath}.${propertyName}`, 'unsupported property');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (!Array.isArray(entry.path) || entry.path.length === 0) {
|
|
270
|
+
invalid(`${indexedPath}.path`, 'expected a nonempty array');
|
|
271
|
+
}
|
|
272
|
+
entry.path.forEach((element, pathIndex) => {
|
|
273
|
+
if (typeof element !== 'string' ||
|
|
274
|
+
!Object.prototype.hasOwnProperty.call(fields, element)) {
|
|
275
|
+
invalid(`${indexedPath}.path[${pathIndex}]`, 'unsupported element');
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
if (!Object.prototype.hasOwnProperty.call(entry, 'style')) {
|
|
279
|
+
invalid(`${indexedPath}.style`, 'expected an own style property');
|
|
280
|
+
}
|
|
281
|
+
const target = entry.path[entry.path.length - 1];
|
|
282
|
+
return {
|
|
283
|
+
path: [...entry.path],
|
|
284
|
+
style: normalizeStyle(entry.style, fields[target], `${indexedPath}.style`),
|
|
285
|
+
};
|
|
286
|
+
});
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
252
289
|
if (key === 'toolbar') {
|
|
253
290
|
if (value !== undefined) {
|
|
254
291
|
if (!record(value)) {
|
|
@@ -123,9 +123,16 @@ export interface EditorStyleMap {
|
|
|
123
123
|
mention: EditorMentionStyle;
|
|
124
124
|
placeholder: EditorTypographyStyle;
|
|
125
125
|
}
|
|
126
|
+
export type EditorStyleRule = {
|
|
127
|
+
[K in keyof EditorStyleMap]: {
|
|
128
|
+
path: readonly [...(keyof EditorStyleMap)[], K];
|
|
129
|
+
style: EditorStyleProp<EditorStyleMap[K]>;
|
|
130
|
+
};
|
|
131
|
+
}[keyof EditorStyleMap];
|
|
126
132
|
export type EditorTheme = {
|
|
127
133
|
[K in keyof EditorStyleMap]?: EditorStyleProp<EditorStyleMap[K]>;
|
|
128
134
|
} & {
|
|
135
|
+
rules?: readonly EditorStyleRule[];
|
|
129
136
|
toolbar?: EditorToolbarTheme;
|
|
130
137
|
};
|
|
131
138
|
export type EditorLinkTheme = EditorInlineStyle;
|
|
@@ -138,6 +145,10 @@ export type NormalizedEditorStyle = Readonly<Record<string, unknown>>;
|
|
|
138
145
|
export interface NormalizedEditorTheme {
|
|
139
146
|
version: 1;
|
|
140
147
|
styles?: Partial<Record<keyof EditorStyleMap, NormalizedEditorStyle>>;
|
|
148
|
+
rules?: readonly {
|
|
149
|
+
path: readonly (keyof EditorStyleMap)[];
|
|
150
|
+
style: NormalizedEditorStyle;
|
|
151
|
+
}[];
|
|
141
152
|
toolbar?: EditorToolbarTheme;
|
|
142
153
|
}
|
|
143
154
|
type ExactStyle<T, Shape> = T extends false | null | undefined ? T : T extends readonly unknown[] ? {
|
|
@@ -145,7 +156,17 @@ type ExactStyle<T, Shape> = T extends false | null | undefined ? T : T extends r
|
|
|
145
156
|
} : Shape & {
|
|
146
157
|
[K in keyof T]: K extends keyof Shape ? K extends 'checked' ? ExactStyle<T[K], EditorCheckboxAppearance> : K extends 'ordered' ? ExactStyle<T[K], EditorOrderedListMarkerTheme> : Shape[K] : never;
|
|
147
158
|
};
|
|
159
|
+
type ExactRuleStyle<T, Shape> = [EditorStyleProp<Shape>] extends [T] ? [T] extends [EditorStyleProp<Shape>] ? T : ExactStyle<T, Shape> : ExactStyle<T, Shape>;
|
|
160
|
+
type ExactEditorStyleRule<T> = T extends {
|
|
161
|
+
readonly path: infer Path extends readonly (keyof EditorStyleMap)[];
|
|
162
|
+
readonly style: unknown;
|
|
163
|
+
} ? Path extends readonly [...(keyof EditorStyleMap)[], infer Target] ? Target extends keyof EditorStyleMap ? {
|
|
164
|
+
[K in keyof T]: K extends 'path' ? Path : K extends 'style' ? ExactRuleStyle<T[K], EditorStyleMap[Target]> : never;
|
|
165
|
+
} : never : never : never;
|
|
166
|
+
type ExactEditorStyleRules<T> = T extends undefined ? T : T extends readonly unknown[] ? {
|
|
167
|
+
[K in keyof T]: ExactEditorStyleRule<T[K]>;
|
|
168
|
+
} : never;
|
|
148
169
|
export type ExactEditorTheme<T> = {
|
|
149
|
-
[K in keyof T]: K extends keyof EditorStyleMap ? ExactStyle<T[K], EditorStyleMap[K]> : K extends 'toolbar' ? EditorToolbarTheme : never;
|
|
170
|
+
[K in keyof T]: K extends 'rules' ? ExactEditorStyleRules<T[K]> : K extends keyof EditorStyleMap ? ExactStyle<T[K], EditorStyleMap[K]> : K extends 'toolbar' ? EditorToolbarTheme : never;
|
|
150
171
|
};
|
|
151
172
|
export {};
|
package/dist/EditorTheme.js
CHANGED
|
@@ -27,12 +27,15 @@ function stripUndefined(value) {
|
|
|
27
27
|
*/
|
|
28
28
|
function serializeEditorTheme(theme, mentionTheme) {
|
|
29
29
|
const normalized = theme === undefined ? undefined : (0, EditorStyleSheetNormalization_1.normalizeEditorTheme)(theme);
|
|
30
|
-
const cleanedTheme = normalized && (normalized.styles || normalized.toolbar)
|
|
30
|
+
const cleanedTheme = normalized && (normalized.styles || normalized.rules || normalized.toolbar)
|
|
31
31
|
? stripUndefined(normalized)
|
|
32
32
|
: undefined;
|
|
33
33
|
const base = cleanedTheme && typeof cleanedTheme === 'object'
|
|
34
34
|
? cleanedTheme
|
|
35
35
|
: undefined;
|
|
36
|
+
if (base && normalized?.rules !== undefined) {
|
|
37
|
+
base.rules = normalized.rules;
|
|
38
|
+
}
|
|
36
39
|
const cleanedMentions = mentionTheme
|
|
37
40
|
? stripUndefined((0, EditorMentionThemeNormalization_1.normalizeEditorMentionTheme)(mentionTheme))
|
|
38
41
|
: undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { EditorToolbar, DEFAULT_EDITOR_TOOLBAR_ITEMS, type EditorToolbarProps, t
|
|
|
5
5
|
export type { EditorContentInsets, EditorTheme, EditorTextStyle, EditorLinkTheme, EditorHeadingTheme, EditorListTheme, EditorOrderedListNumberingScheme, EditorOrderedListMarkerTheme, EditorHorizontalRuleTheme, EditorBlockquoteTheme, EditorCodeBlockTheme, EditorMentionTheme, EditorMentionNodeTheme, EditorMentionSuggestionsTheme, EditorMentionSuggestionOptionTheme, EditorToolbarTheme, EditorToolbarAppearance, EditorFontStyle, EditorFontWeight, } from './EditorTheme';
|
|
6
6
|
export { serializeEditorTheme } from './EditorTheme';
|
|
7
7
|
export { EditorStyleSheet } from './EditorStyleSheet';
|
|
8
|
-
export type { EditorStyleProp, EditorStyleMap, EditorTypographyStyle, EditorBorderStyle, EditorPaddingStyle, EditorMarginStyle, EditorSurfaceStyle, EditorBoxStyle, EditorInlineStyle, EditorImageStyle, EditorListStyle, EditorListMarkerStyle, EditorCheckboxAppearance, EditorTaskCheckboxStyle, EditorHorizontalRuleStyle, EditorMentionStyle, } from './EditorStyleSheetTypes';
|
|
8
|
+
export type { EditorStyleProp, EditorStyleRule, EditorStyleMap, EditorTypographyStyle, EditorBorderStyle, EditorPaddingStyle, EditorMarginStyle, EditorSurfaceStyle, EditorBoxStyle, EditorInlineStyle, EditorImageStyle, EditorListStyle, EditorListMarkerStyle, EditorCheckboxAppearance, EditorTaskCheckboxStyle, EditorHorizontalRuleStyle, EditorMentionStyle, } from './EditorStyleSheetTypes';
|
|
9
9
|
export { createMentionsAddon, type EditorAddon, type EditorAddonEntry, type MentionsAddon, type MentionsAddonOptions, type MentionPressEvent, type CodeHighlightingAddon, type CodeHighlightingAddonOptions, } from './EditorAddon';
|
|
10
10
|
export { MENTION_NODE_NAME, mentionNodeSpec, withMentionsSchema, buildMentionFragmentJson, type EditorAddons, type MentionsAddonConfig, type MentionSuggestion, type MentionQueryChangeEvent, type MentionFragmentOptions, type MentionSelectionAttrsEvent, type MentionThemeResolveEvent, type MentionSelectEvent, type EditorAddonEvent, } from './addons';
|
|
11
11
|
export { defineAtomNode, withAtomsSchema, serializeEditorAtoms, type AtomAttrsUpdate, type AtomEditorActions, type InferAtomAttrs, type AtomComponent, type AtomComponentProps, type AtomNodeConfig, type AtomNodeDefinition, type SerializedEditorAtoms, } from './atoms';
|
|
@@ -190,13 +190,8 @@ function useRichTextEditorCommands(context) {
|
|
|
190
190
|
return;
|
|
191
191
|
}
|
|
192
192
|
applyEngineCommand({
|
|
193
|
-
type: '
|
|
193
|
+
type: 'applyListType',
|
|
194
194
|
listType,
|
|
195
|
-
itemType: listType === 'taskList'
|
|
196
|
-
? 'taskItem'
|
|
197
|
-
: listType === 'bullet_list' || listType === 'ordered_list'
|
|
198
|
-
? 'list_item'
|
|
199
|
-
: 'listItem',
|
|
200
195
|
});
|
|
201
196
|
}, [activeStateRef, applyEngineCommand]);
|
|
202
197
|
const commandIndentListItem = (0, react_1.useCallback)(() => applyEngineCommand({ type: 'indentListItem' }), [applyEngineCommand]);
|
|
@@ -8,32 +8,32 @@
|
|
|
8
8
|
<key>BinaryPath</key>
|
|
9
9
|
<string>libeditor_core.a</string>
|
|
10
10
|
<key>LibraryIdentifier</key>
|
|
11
|
-
<string>ios-
|
|
11
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
12
12
|
<key>LibraryPath</key>
|
|
13
13
|
<string>libeditor_core.a</string>
|
|
14
14
|
<key>SupportedArchitectures</key>
|
|
15
15
|
<array>
|
|
16
16
|
<string>arm64</string>
|
|
17
|
+
<string>x86_64</string>
|
|
17
18
|
</array>
|
|
18
19
|
<key>SupportedPlatform</key>
|
|
19
20
|
<string>ios</string>
|
|
21
|
+
<key>SupportedPlatformVariant</key>
|
|
22
|
+
<string>simulator</string>
|
|
20
23
|
</dict>
|
|
21
24
|
<dict>
|
|
22
25
|
<key>BinaryPath</key>
|
|
23
26
|
<string>libeditor_core.a</string>
|
|
24
27
|
<key>LibraryIdentifier</key>
|
|
25
|
-
<string>ios-
|
|
28
|
+
<string>ios-arm64</string>
|
|
26
29
|
<key>LibraryPath</key>
|
|
27
30
|
<string>libeditor_core.a</string>
|
|
28
31
|
<key>SupportedArchitectures</key>
|
|
29
32
|
<array>
|
|
30
33
|
<string>arm64</string>
|
|
31
|
-
<string>x86_64</string>
|
|
32
34
|
</array>
|
|
33
35
|
<key>SupportedPlatform</key>
|
|
34
36
|
<string>ios</string>
|
|
35
|
-
<key>SupportedPlatformVariant</key>
|
|
36
|
-
<string>simulator</string>
|
|
37
37
|
</dict>
|
|
38
38
|
</array>
|
|
39
39
|
<key>CFBundlePackageType</key>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 33937224 bytes
|
|
4
|
+
sha256: 932bff445a503ec5a28a4d75e1273280144e7112315aa22799a871730bc655f2
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
[diffend] Oversized file quarantined before diffing.
|
|
2
2
|
name: package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a
|
|
3
|
-
size:
|
|
4
|
-
sha256:
|
|
3
|
+
size: 67220840 bytes
|
|
4
|
+
sha256: 623edc61d1273935a1d1afc314a13fd89f7cce5724ae28f96adc6d8df0b0f718
|
|
@@ -503,7 +503,7 @@ final class EditorLayoutManager: NSLayoutManager, NSLayoutManagerDelegate {
|
|
|
503
503
|
|
|
504
504
|
if ordered {
|
|
505
505
|
let markerFont = attrs[editorStyledContentAttribute] != nil
|
|
506
|
-
? baseFont
|
|
506
|
+
? baseFont
|
|
507
507
|
: markerFont(for: listContext, baseFont: baseFont, markerScale: markerScale)
|
|
508
508
|
let markerText = attrs[RenderBridgeAttributes.orderedListMarkerLabel] as? String
|
|
509
509
|
?? RenderBridge.listMarkerString(listContext: listContext)
|
|
@@ -878,9 +878,7 @@ final class EditorLayoutManager: NSLayoutManager, NSLayoutManagerDelegate {
|
|
|
878
878
|
origin: CGPoint,
|
|
879
879
|
markerGap: CGFloat = LayoutConstants.listMarkerTextGap
|
|
880
880
|
) -> CGRect {
|
|
881
|
-
let
|
|
882
|
-
let bulletBounds = unorderedBulletGlyphBounds(for: markerFont)
|
|
883
|
-
let bulletDiameter = max(max(bulletBounds.width, bulletBounds.height), 1)
|
|
881
|
+
let bulletDiameter = max(baseFont.pointSize * markerScale * 0.32, 4)
|
|
884
882
|
let targetCenterAboveBaseline = (baseFont.xHeight > 0 ? baseFont.xHeight : baseFont.capHeight) / 2.0
|
|
885
883
|
let centerY = baselineY - targetCenterAboveBaseline
|
|
886
884
|
let referenceRect = usedRect.height > 0 ? usedRect : lineFragmentRect
|