@openeditor/react-native-prose-editor 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -214,6 +214,7 @@ final class NativeBlockEditorSurface: UIScrollView {
214
214
  }
215
215
 
216
216
  private let stack = UIStackView()
217
+ private let placeholderLabel = UILabel()
217
218
  private var currentEditorId: UInt64 = 0
218
219
  private var isApplyingRemoteUpdate = false
219
220
  private var currentTheme: EditorTheme?
@@ -232,6 +233,13 @@ final class NativeBlockEditorSurface: UIScrollView {
232
233
  private var selectedTextPos: UInt32?
233
234
  private let caretView = UIView()
234
235
 
236
+ var placeholder: String = "" {
237
+ didSet {
238
+ placeholderLabel.text = placeholder
239
+ refreshPlaceholder()
240
+ }
241
+ }
242
+
235
243
  var baseFont: UIFont = .systemFont(ofSize: 16)
236
244
  var baseTextColor: UIColor = .label
237
245
  var baseBackgroundColor: UIColor = .systemBackground {
@@ -270,6 +278,11 @@ final class NativeBlockEditorSurface: UIScrollView {
270
278
  stack.spacing = 8
271
279
  stack.alignment = .fill
272
280
  addSubview(stack)
281
+ placeholderLabel.numberOfLines = 0
282
+ placeholderLabel.isUserInteractionEnabled = false
283
+ placeholderLabel.isHidden = true
284
+ placeholderLabel.layer.zPosition = 1
285
+ addSubview(placeholderLabel)
273
286
  caretView.backgroundColor = .systemBlue
274
287
  caretView.isUserInteractionEnabled = false
275
288
  caretView.layer.cornerRadius = 1
@@ -297,6 +310,16 @@ final class NativeBlockEditorSurface: UIScrollView {
297
310
  ).height
298
311
  stack.frame.size.height = contentHeight
299
312
  stack.layoutIfNeeded()
313
+ placeholderLabel.font = font(for: ["nodeType": "paragraph"])
314
+ placeholderLabel.textColor = currentTheme?.placeholderColor ?? .placeholderText
315
+ placeholderLabel.frame = CGRect(
316
+ x: leftInset,
317
+ y: topInset,
318
+ width: contentWidth,
319
+ height: placeholderLabel.sizeThatFits(
320
+ CGSize(width: contentWidth, height: .greatestFiniteMagnitude)
321
+ ).height
322
+ )
300
323
  contentSize = CGSize(width: bounds.width, height: stack.frame.maxY + bottomInset)
301
324
  refreshCaret()
302
325
  onContentHeightMayChange?(contentSize.height)
@@ -304,13 +327,16 @@ final class NativeBlockEditorSurface: UIScrollView {
304
327
 
305
328
  func applyTheme(_ theme: EditorTheme?) {
306
329
  currentTheme = theme
330
+ backgroundColor = theme?.backgroundColor ?? baseBackgroundColor
307
331
  contentInset = .zero
308
332
  verticalScrollIndicatorInsets = .zero
309
- if let currentRoot {
310
- rebuildAll(from: currentRoot)
333
+ if let root = currentRoot {
334
+ rebuildAll(from: root)
335
+ currentRoot = root
311
336
  refreshTableCellRegistry()
312
337
  refreshImageSelection()
313
338
  }
339
+ refreshPlaceholder()
314
340
  setNeedsLayout()
315
341
  }
316
342
 
@@ -351,6 +377,7 @@ final class NativeBlockEditorSurface: UIScrollView {
351
377
  refreshImageSelection()
352
378
  refreshCaret()
353
379
  currentRoot = root
380
+ refreshPlaceholder()
354
381
  isApplyingRemoteUpdate = false
355
382
  setNeedsLayout()
356
383
  layoutIfNeeded()
@@ -375,6 +402,22 @@ final class NativeBlockEditorSurface: UIScrollView {
375
402
  }
376
403
  }
377
404
 
405
+ private func refreshPlaceholder() {
406
+ placeholderLabel.isHidden = placeholder.isEmpty || !isRenderedDocumentEmpty()
407
+ setNeedsLayout()
408
+ }
409
+
410
+ private func isRenderedDocumentEmpty() -> Bool {
411
+ guard let root = currentRoot else { return false }
412
+ let children = root["children"] as? [[String: Any]] ?? []
413
+ guard children.count <= 1 else { return false }
414
+ guard let child = children.first else {
415
+ return textLeafViews.count == 1 && textLeafViews[0].attributedText.length == 0
416
+ }
417
+ guard (child["nodeType"] as? String ?? "paragraph") == "paragraph" else { return false }
418
+ return textLeafViews.count == 1 && textLeafViews[0].attributedText.length == 0
419
+ }
420
+
378
421
  /// Applies canonical recursive patches at the smallest native view boundary.
379
422
  /// Metadata-only changes mutate bindings in place; node replacements stop at
380
423
  /// the nearest rendered owner (normally the edited text block); and root
@@ -546,6 +589,7 @@ final class NativeBlockEditorSurface: UIScrollView {
546
589
  view.removeFromSuperview()
547
590
  }
548
591
  currentRoot = nil
592
+ refreshPlaceholder()
549
593
  nodeViews.removeAll()
550
594
  tableCells.removeAll()
551
595
  imageViews.removeAll()
@@ -734,6 +778,14 @@ final class NativeBlockEditorSurface: UIScrollView {
734
778
  stack.arrangedSubviews.count
735
779
  }
736
780
 
781
+ func isPlaceholderVisibleForTesting() -> Bool {
782
+ !placeholderLabel.isHidden
783
+ }
784
+
785
+ func placeholderColorForTesting() -> UIColor? {
786
+ placeholderLabel.textColor
787
+ }
788
+
737
789
  func firstTextAttributesForTesting() -> [NSAttributedString.Key: Any]? {
738
790
  func find(in view: UIView) -> TextLeafView? {
739
791
  if let leaf = view as? TextLeafView, leaf.attributedText.length > 0 {
@@ -962,7 +1014,7 @@ final class NativeBlockEditorSurface: UIScrollView {
962
1014
  attributes[.strikethroughStyle] = NSUnderlineStyle.single.rawValue
963
1015
  }
964
1016
  if markTypes.contains("link") {
965
- attributes[.foregroundColor] = tintColor
1017
+ attributes[.foregroundColor] = currentTheme?.links?.color ?? tintColor
966
1018
  }
967
1019
  if markTypes.contains("code") {
968
1020
  attributes[.font] = UIFont.monospacedSystemFont(ofSize: baseFont.pointSize, weight: .regular)
@@ -538,7 +538,7 @@ public class NativeEditorModule: Module {
538
538
  view.setEditable(editable)
539
539
  }
540
540
  Prop("placeholder") { (view: NativeEditorExpoView, placeholder: String) in
541
- view.richTextView.textView.placeholder = placeholder
541
+ view.richTextView.placeholder = placeholder
542
542
  }
543
543
  Prop("autoFocus") { (view: NativeEditorExpoView, autoFocus: Bool) in
544
544
  view.setAutoFocus(autoFocus)
@@ -5064,6 +5064,13 @@ final class RichTextEditorView: UIView {
5064
5064
  /// The editor text view that handles input interception.
5065
5065
  let textView: EditorTextView
5066
5066
  private let blockSurface = NativeBlockEditorSurface()
5067
+
5068
+ var placeholder: String = "" {
5069
+ didSet {
5070
+ textView.placeholder = placeholder
5071
+ blockSurface.placeholder = placeholder
5072
+ }
5073
+ }
5067
5074
  private let remoteSelectionOverlayView = RemoteSelectionOverlayView()
5068
5075
  private let taskListMarkerTapOverlayView = TaskListMarkerTapOverlayView()
5069
5076
  private let imageTapOverlayView = ImageTapOverlayView()
@@ -5400,6 +5407,18 @@ final class RichTextEditorView: UIView {
5400
5407
  blockSurface.topLevelViewCountForTesting
5401
5408
  }
5402
5409
 
5410
+ func isBlockSurfacePlaceholderVisibleForTesting() -> Bool {
5411
+ blockSurface.isPlaceholderVisibleForTesting()
5412
+ }
5413
+
5414
+ func blockSurfaceBackgroundColorForTesting() -> UIColor? {
5415
+ blockSurface.backgroundColor
5416
+ }
5417
+
5418
+ func blockSurfacePlaceholderColorForTesting() -> UIColor? {
5419
+ blockSurface.placeholderColorForTesting()
5420
+ }
5421
+
5403
5422
  func blockSurfaceFirstTextAttributesForTesting() -> [NSAttributedString.Key: Any]? {
5404
5423
  blockSurface.firstTextAttributesForTesting()
5405
5424
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openeditor/react-native-prose-editor",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "description": "OpenEditor-owned native rich text editor engine for React Native",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/EasyLink-HQ/openeditor",