@apollohg/react-native-rich-text-editor 2.0.4 → 2.0.5

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.
Files changed (44) hide show
  1. package/android/src/main/java/com/apollohg/editor/EditorCheckboxSpan.kt +8 -3
  2. package/android/src/main/java/com/apollohg/editor/EditorEditTextConfiguration.kt +5 -1
  3. package/android/src/main/java/com/apollohg/editor/EditorEditTextToolbar.kt +1 -1
  4. package/android/src/main/java/com/apollohg/editor/EditorMentionStyle.kt +3 -2
  5. package/android/src/main/java/com/apollohg/editor/EditorStyleSheet.kt +196 -8
  6. package/android/src/main/java/com/apollohg/editor/EditorV2Adapter.kt +7 -0
  7. package/android/src/main/java/com/apollohg/editor/EditorV2Driver.kt +1 -0
  8. package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +48 -9
  9. package/android/src/main/java/com/apollohg/editor/RenderBridgeBlockStyles.kt +17 -4
  10. package/android/src/main/java/com/apollohg/editor/RenderBridgeElements.kt +83 -25
  11. package/android/src/main/java/com/apollohg/editor/RenderBridgeInlineAndAtoms.kt +28 -14
  12. package/android/src/main/java/com/apollohg/editor/RenderParagraphSpans.kt +5 -1
  13. package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +5 -1
  14. package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +141 -40
  15. package/dist/EditorStyleSheetNormalization.js +37 -0
  16. package/dist/EditorStyleSheetTypes.d.ts +22 -1
  17. package/dist/EditorTheme.js +4 -1
  18. package/dist/index.d.ts +1 -1
  19. package/dist/useRichTextEditorCommands.js +1 -6
  20. package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
  21. package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
  22. package/ios/EditorLayoutManager.swift +2 -4
  23. package/ios/EditorStyleSheet.swift +77 -11
  24. package/ios/EditorTextView+Commands.swift +1 -1
  25. package/ios/EditorTextView.swift +5 -2
  26. package/ios/EditorTheme.swift +3 -1
  27. package/ios/EditorV2Adapter+Commands.swift +10 -0
  28. package/ios/EditorV2Shadow.swift +4 -0
  29. package/ios/RenderBridge+Atoms.swift +15 -6
  30. package/ios/RenderBridge+Style.swift +59 -17
  31. package/ios/RenderBridge.swift +19 -16
  32. package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +31 -13
  33. package/ios/Viewer/CoreTextProseLayoutEngine+Models.swift +17 -2
  34. package/ios/Viewer/CoreTextProseLayoutEngine.swift +39 -21
  35. package/package.json +3 -3
  36. package/rust/android/arm64-v8a/libeditor_core.so +0 -0
  37. package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
  38. package/rust/android/x86/libeditor_core.so +0 -0
  39. package/rust/android/x86_64/libeditor_core.so +0 -0
  40. package/src/EditorStyleSheetNormalization.ts +50 -0
  41. package/src/EditorStyleSheetTypes.ts +51 -6
  42. package/src/EditorTheme.ts +5 -1
  43. package/src/index.ts +1 -0
  44. package/src/useRichTextEditorCommands.tsx +1 -7
@@ -101,14 +101,20 @@ internal fun RenderBridge.appendElements(
101
101
  atomId != null,
102
102
  state.blockStack.isEmpty(),
103
103
  state.reusableImages,
104
- state.blockStack.fold(EditorEdges()) { total, block ->
104
+ state.blockStack.foldIndexed(EditorEdges()) { index, total, block ->
105
105
  total +
106
106
  (
107
- theme?.styleSheet?.box(block.nodeType)?.outerInset?.scaled(density)
107
+ theme?.styleSheet?.box(
108
+ block.nodeType,
109
+ state.blockStack.take(index).map {
110
+ it.nodeType
111
+ }
112
+ )?.outerInset?.scaled(density)
108
113
  ?: EditorEdges()
109
114
  )
110
115
  },
111
- state.blockStack.size
116
+ state.blockStack.size,
117
+ state.blockStack.map { it.nodeType }
112
118
  )
113
119
  }
114
120
 
@@ -279,6 +285,12 @@ internal fun RenderBridge.appendElements(
279
285
  }
280
286
 
281
287
  if (markerListContext != null) {
288
+ val ownerIndex = state.blockStack.indexOfLast { it.listContext != null }
289
+ val markerAncestors = state.blockStack.take(ownerIndex + 1).map { it.nodeType }
290
+ val markerStyle = theme?.styleSheet?.resolveElement(
291
+ "listMarker",
292
+ markerAncestors
293
+ )
282
294
  val ordered = markerListContext.optBoolean("ordered", false)
283
295
  val isTask = markerListContext.optString("kind", "") == "task"
284
296
  val visualListDepth = (
@@ -295,34 +307,62 @@ internal fun RenderBridge.appendElements(
295
307
  OrderedListMarkerFormatter.label(
296
308
  index,
297
309
  visualListDepth,
298
- theme?.list?.orderedMarker
310
+ markerStyle?.ordered ?: theme?.list?.orderedMarker
299
311
  )
300
312
  } else {
301
313
  null
302
314
  }
303
315
  val marker = listMarkerString(markerListContext)
304
- val markerBaseSize =
305
- resolveTextStyle(
306
- nodeType,
307
- theme,
308
- blockquoteDepth(state.blockStack) > 0
309
- ).fontSize?.times(density) ?: baseFontSize
316
+ val markerInBlockquote = blockquoteDepth(state.blockStack) > 0
317
+ var markerTextStyle = theme?.styleSheet?.resolveBaseText(
318
+ nodeType,
319
+ if (markerInBlockquote) listOf("blockquote") else emptyList()
320
+ ) ?: resolveTextStyle(
321
+ nodeType,
322
+ theme,
323
+ markerInBlockquote
324
+ )
325
+ theme?.styleSheet?.let { sheet ->
326
+ val ancestors = state.blockStack.dropLast(1).map { it.nodeType }
327
+ val resolved = sheet.resolveText(nodeType, ancestors)
328
+ markerTextStyle = markerTextStyle.copy(
329
+ fontSize = if (sheet.hasMatchingRuleProperty(
330
+ nodeType,
331
+ ancestors,
332
+ "fontSize"
333
+ )
334
+ ) {
335
+ resolved.fontSize
336
+ } else {
337
+ markerTextStyle.fontSize
338
+ },
339
+ color = if (sheet.hasMatchingRuleProperty(
340
+ nodeType,
341
+ ancestors,
342
+ "color"
343
+ )
344
+ ) {
345
+ resolved.color
346
+ } else {
347
+ markerTextStyle.color
348
+ }
349
+ )
350
+ }
351
+ val markerBaseSize = markerTextStyle.fontSize?.times(density) ?: baseFontSize
310
352
  val resolvedMarkerBaseSize = if (isTask) {
311
353
  markerBaseSize * LayoutConstants.TASK_LIST_MARKER_FONT_SCALE
312
354
  } else {
313
355
  markerBaseSize
314
356
  }
315
- val markerTextStyle = resolveTextStyle(
316
- nodeType,
317
- theme,
318
- blockquoteDepth(state.blockStack) > 0
319
- )
357
+ val markerColor = markerStyle?.text?.color ?: theme?.list?.markerColor
358
+ ?: markerTextStyle.color
359
+ ?: textColor
320
360
  appendStyledText(
321
361
  state.result,
322
362
  marker,
323
363
  emptyList(),
324
364
  resolvedMarkerBaseSize,
325
- theme?.list?.markerColor ?: textColor,
365
+ markerColor,
326
366
  state.blockStack,
327
367
  state.pendingLeadingMargins,
328
368
  null,
@@ -348,7 +388,11 @@ internal fun RenderBridge.appendElements(
348
388
  }
349
389
  val checkbox = if (isTask) {
350
390
  theme?.styleSheet?.let {
351
- resolvedCheckboxStyle(it, markerListContext.optBoolean("checked"))
391
+ resolvedCheckboxStyle(
392
+ it,
393
+ markerListContext.optBoolean("checked"),
394
+ markerAncestors
395
+ )
352
396
  }
353
397
  } else {
354
398
  null
@@ -367,7 +411,7 @@ internal fun RenderBridge.appendElements(
367
411
  }
368
412
  val markerGapPx =
369
413
  (
370
- checkbox?.gap ?: theme?.list?.markerGap
414
+ checkbox?.gap ?: markerStyle?.gap ?: theme?.list?.markerGap
371
415
  ?: LayoutConstants.LIST_MARKER_TEXT_GAP
372
416
  ) *
373
417
  density
@@ -381,7 +425,7 @@ internal fun RenderBridge.appendElements(
381
425
  }
382
426
  if (ordered && !isTask && presentationLabel != null && marker.endsWith(' ')) {
383
427
  state.result.setSpan(
384
- OrderedListMarkerSpan(presentationLabel),
428
+ OrderedListMarkerSpan(presentationLabel, markerColor),
385
429
  markerStart,
386
430
  markerEnd - 1,
387
431
  Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
@@ -389,15 +433,19 @@ internal fun RenderBridge.appendElements(
389
433
  }
390
434
  if (!ordered && !isTask) {
391
435
  val markerScale =
392
- theme?.list?.markerScale
436
+ markerStyle?.scale ?: theme?.list?.markerScale
393
437
  ?: LayoutConstants.UNORDERED_LIST_MARKER_FONT_SCALE
394
- val markerWidth = calculateMarkerWidth(density)
395
438
  val bulletRadius = ((markerBaseSize * markerScale) * 0.16f).coerceAtLeast(
396
439
  2f * density
397
440
  )
441
+ val markerWidth = if (theme?.styleSheet != null) {
442
+ (bulletRadius * 2f) + markerGapPx
443
+ } else {
444
+ calculateMarkerWidth(density)
445
+ }
398
446
  state.result.setSpan(
399
447
  CenteredBulletSpan(
400
- textColor = theme?.list?.markerColor ?: textColor,
448
+ textColor = markerColor,
401
449
  markerWidthPx = markerWidth,
402
450
  bulletRadiusPx = bulletRadius,
403
451
  bodyFontSizePx = resolvedMarkerBaseSize,
@@ -420,6 +468,7 @@ internal fun RenderBridge.appendElements(
420
468
 
421
469
  "blockEnd" -> {
422
470
  if (state.blockStack.isNotEmpty()) {
471
+ val endedAncestors = state.blockStack.dropLast(1).map { it.nodeType }
423
472
  val endedBlock = state.blockStack.removeAt(state.blockStack.lastIndex)
424
473
  appendTrailingHardBreakPlaceholderIfNeeded(
425
474
  builder = state.result,
@@ -434,9 +483,18 @@ internal fun RenderBridge.appendElements(
434
483
  theme?.styleSheet?.let { sheet ->
435
484
  val start = endedBlock.renderStart
436
485
  if (start <= state.result.length) {
437
- val ancestors = state.blockStack.fold(EditorEdges()) { total, block ->
486
+ val ancestors = state.blockStack.foldIndexed(EditorEdges()) {
487
+ index,
488
+ total,
489
+ block
490
+ ->
438
491
  total +
439
- sheet.box(block.nodeType).outerInset.scaled(density)
492
+ sheet.box(
493
+ block.nodeType,
494
+ state.blockStack.take(index).map {
495
+ it.nodeType
496
+ }
497
+ ).outerInset.scaled(density)
440
498
  }
441
499
  val empty = start == state.result.length
442
500
  val flags = if (empty) {
@@ -446,7 +504,7 @@ internal fun RenderBridge.appendElements(
446
504
  }
447
505
  state.result.setSpan(
448
506
  EditorBlockBoxSpan(
449
- sheet.box(endedBlock.nodeType).scaled(density),
507
+ sheet.box(endedBlock.nodeType, endedAncestors).scaled(density),
450
508
  ancestors,
451
509
  state.blockStack.size,
452
510
  endedBlock.nodeType
@@ -317,7 +317,8 @@ internal fun RenderBridge.appendVoidBlock(
317
317
  isDirectRootChild: Boolean,
318
318
  reusableImages: MutableList<BlockImageSpan> = mutableListOf(),
319
319
  ancestorBoxInset: EditorEdges = EditorEdges(),
320
- containerDepth: Int = 0
320
+ containerDepth: Int = 0,
321
+ ancestors: List<String> = emptyList()
321
322
  ) {
322
323
  if (docPos != null && atomConfiguration?.registeredNodeTypes?.contains(nodeType) == true) {
323
324
  val start = builder.length
@@ -356,18 +357,21 @@ internal fun RenderBridge.appendVoidBlock(
356
357
  val start = builder.length
357
358
  builder.append(LayoutConstants.OBJECT_REPLACEMENT_CHARACTER)
358
359
  val end = builder.length
359
- val ruleColor = theme?.horizontalRule?.color ?: Color.argb(
360
- (Color.alpha(textColor) * 0.3f).toInt(),
361
- Color.red(textColor),
362
- Color.green(textColor),
363
- Color.blue(textColor)
364
- )
360
+ val ruleStyle = theme?.styleSheet?.resolveElement("horizontalRule", ancestors)
361
+ val ruleBox = theme?.styleSheet?.box("horizontalRule", ancestors)
362
+ val ruleColor =
363
+ ruleStyle?.box?.backgroundColor ?: theme?.horizontalRule?.color ?: Color.argb(
364
+ (Color.alpha(textColor) * 0.3f).toInt(),
365
+ Color.red(textColor),
366
+ Color.green(textColor),
367
+ Color.blue(textColor)
368
+ )
365
369
  builder.setSpan(
366
370
  HorizontalRuleSpan(
367
371
  lineColor = ruleColor,
368
372
  lineHeight =
369
373
  (
370
- theme?.horizontalRule?.thickness
374
+ ruleStyle?.height ?: theme?.horizontalRule?.thickness
371
375
  ?: LayoutConstants.HORIZONTAL_RULE_HEIGHT
372
376
  ) *
373
377
  density,
@@ -378,17 +382,17 @@ internal fun RenderBridge.appendVoidBlock(
378
382
  ) *
379
383
  density,
380
384
  boxInset =
381
- theme?.styleSheet?.box("horizontalRule")?.outerInset?.scaled(density)
385
+ ruleBox?.outerInset?.scaled(density)
382
386
  ?: EditorEdges()
383
387
  ),
384
388
  start,
385
389
  end,
386
390
  Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
387
391
  )
388
- theme?.styleSheet?.let {
392
+ ruleBox?.let { box ->
389
393
  builder.setSpan(
390
394
  EditorBlockBoxSpan(
391
- it.box("horizontalRule").scaled(density),
395
+ box.scaled(density),
392
396
  ancestorBoxInset,
393
397
  containerDepth,
394
398
  "horizontalRule"
@@ -417,8 +421,8 @@ internal fun RenderBridge.appendVoidBlock(
417
421
  builder.append(LayoutConstants.OBJECT_REPLACEMENT_CHARACTER)
418
422
  val end = builder.length
419
423
  val imageStyle = theme?.styleSheet?.let {
420
- it["image"]
421
- ?: EditorElementStyle(EditorTextStyle(), it.box("image"))
424
+ it.resolveElement("image", ancestors)
425
+ ?: EditorElementStyle(EditorTextStyle(), it.box("image", ancestors))
422
426
  }
423
427
  val reused = reusableImages.firstOrNull {
424
428
  it.matches(source, preferredWidthDp, preferredHeightDp)
@@ -494,7 +498,17 @@ internal fun RenderBridge.appendOpaqueInlineAtom(
494
498
  )
495
499
  )
496
500
  builder.setSpan(
497
- EditorMentionSpan(resolvedMentionStyle(base, theme, mentionTheme), density),
501
+ EditorMentionSpan(
502
+ resolvedMentionStyle(
503
+ base,
504
+ theme,
505
+ mentionTheme,
506
+ blockStack.map {
507
+ it.nodeType
508
+ }
509
+ ),
510
+ density
511
+ ),
498
512
  start,
499
513
  end,
500
514
  Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
@@ -106,7 +106,8 @@ class MarkerGapSpan(private val widthPx: Float) : ReplacementSpan() {
106
106
  ) = Unit
107
107
  }
108
108
 
109
- internal class OrderedListMarkerSpan(internal val label: String) : ReplacementSpan() {
109
+ internal class OrderedListMarkerSpan(internal val label: String, private val textColor: Int) :
110
+ ReplacementSpan() {
110
111
  override fun getSize(
111
112
  paint: Paint,
112
113
  text: CharSequence,
@@ -126,7 +127,10 @@ internal class OrderedListMarkerSpan(internal val label: String) : ReplacementSp
126
127
  bottom: Int,
127
128
  paint: Paint
128
129
  ) {
130
+ val previousColor = paint.color
131
+ paint.color = textColor
129
132
  canvas.drawText(label, x, y.toFloat(), paint)
133
+ paint.color = previousColor
130
134
  }
131
135
  }
132
136
 
@@ -946,7 +946,11 @@ class RichTextEditorView @JvmOverloads constructor(
946
946
  internal fun selectedImageSpanForResize(): BlockImageSpan? =
947
947
  editorEditText.selectedImageSpanForResize()
948
948
 
949
- internal fun previewImageResize(span: BlockImageSpan, widthPx: Float, heightPx: Float): Boolean {
949
+ internal fun previewImageResize(
950
+ span: BlockImageSpan,
951
+ widthPx: Float,
952
+ heightPx: Float
953
+ ): Boolean {
950
954
  if (!span.setTemporarySizePx(widthPx, heightPx)) return false
951
955
  editorEditText.relayoutImageResizePreview(span)
952
956
  return true