@openeditor/react-native-prose-editor 0.0.9 → 0.0.10
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 +5 -9
- package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +23 -0
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +2 -2
- package/android/src/main/java/com/apollohg/editor/NativeBlockEditorSurface.kt +381 -49
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +110 -169
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +1 -1
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +124 -337
- package/dist/EditorTheme.d.ts +8 -0
- package/ios/NativeBlockEditorSurface.swift +268 -58
- package/ios/NativeEditorExpoView.swift +114 -117
- package/ios/NativeInputSupport.swift +0 -302
- package/ios/PositionBridge.swift +22 -548
- package/ios/RichTextEditorView.swift +217 -4671
- package/package.json +1 -1
- package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +0 -50
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +0 -4068
- package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +0 -1009
- package/android/src/main/java/com/apollohg/editor/InputSnapshotSupport.kt +0 -6
|
@@ -1974,7 +1974,7 @@ final class EditorAccessoryPlaceholderView: UIView {
|
|
|
1974
1974
|
}
|
|
1975
1975
|
}
|
|
1976
1976
|
|
|
1977
|
-
class NativeEditorExpoView: ExpoView,
|
|
1977
|
+
class NativeEditorExpoView: ExpoView, UIGestureRecognizerDelegate {
|
|
1978
1978
|
|
|
1979
1979
|
// MARK: - Subviews
|
|
1980
1980
|
|
|
@@ -2091,29 +2091,23 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2091
2091
|
self.invalidateIntrinsicContentSize()
|
|
2092
2092
|
self.emitContentHeightIfNeeded(force: true, measuredHeight: measuredHeight)
|
|
2093
2093
|
}
|
|
2094
|
-
richTextView.
|
|
2094
|
+
richTextView.onSelectionChange = { [weak self] anchor, head in
|
|
2095
|
+
self?.handleSelectionChange(anchor: anchor, head: head)
|
|
2096
|
+
}
|
|
2097
|
+
richTextView.onEditorUpdate = { [weak self] updateJSON in
|
|
2098
|
+
self?.handleEditorUpdate(updateJSON)
|
|
2099
|
+
}
|
|
2100
|
+
richTextView.onFocusChange = { [weak self] focused in
|
|
2101
|
+
if focused { self?.handleTextDidBeginEditing() }
|
|
2102
|
+
else { self?.handleTextDidEndEditing() }
|
|
2103
|
+
}
|
|
2095
2104
|
configureAccessoryToolbar()
|
|
2096
2105
|
|
|
2097
|
-
// Observe UITextView focus changes via NotificationCenter.
|
|
2098
|
-
NotificationCenter.default.addObserver(
|
|
2099
|
-
self,
|
|
2100
|
-
selector: #selector(textViewDidBeginEditing(_:)),
|
|
2101
|
-
name: UITextView.textDidBeginEditingNotification,
|
|
2102
|
-
object: richTextView.textView
|
|
2103
|
-
)
|
|
2104
|
-
NotificationCenter.default.addObserver(
|
|
2105
|
-
self,
|
|
2106
|
-
selector: #selector(textViewDidEndEditing(_:)),
|
|
2107
|
-
name: UITextView.textDidEndEditingNotification,
|
|
2108
|
-
object: richTextView.textView
|
|
2109
|
-
)
|
|
2110
|
-
|
|
2111
2106
|
addSubview(richTextView)
|
|
2112
2107
|
}
|
|
2113
2108
|
|
|
2114
2109
|
deinit {
|
|
2115
2110
|
NativeEditorViewRegistry.shared.unregister(editorId: richTextView.editorId, view: self)
|
|
2116
|
-
NotificationCenter.default.removeObserver(self)
|
|
2117
2111
|
}
|
|
2118
2112
|
|
|
2119
2113
|
// MARK: - Layout
|
|
@@ -2142,7 +2136,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2142
2136
|
|
|
2143
2137
|
override func didMoveToWindow() {
|
|
2144
2138
|
super.didMoveToWindow()
|
|
2145
|
-
if richTextView.
|
|
2139
|
+
if richTextView.isEditorFocused {
|
|
2146
2140
|
installOutsideTapRecognizerIfNeeded()
|
|
2147
2141
|
} else {
|
|
2148
2142
|
uninstallOutsideTapRecognizer()
|
|
@@ -2153,7 +2147,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2153
2147
|
|
|
2154
2148
|
func handleEditorDestroyed(_ editorId: UInt64) {
|
|
2155
2149
|
guard editorId != 0 else { return }
|
|
2156
|
-
guard richTextView.editorId == editorId
|
|
2150
|
+
guard richTextView.editorId == editorId else {
|
|
2157
2151
|
NativeEditorViewRegistry.shared.unregister(editorId: editorId, view: self)
|
|
2158
2152
|
return
|
|
2159
2153
|
}
|
|
@@ -2166,7 +2160,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2166
2160
|
clearPendingAccessoryRetry()
|
|
2167
2161
|
clearPendingMentionSuggestionRetry()
|
|
2168
2162
|
lastMentionEventJSON = nil
|
|
2169
|
-
|
|
2163
|
+
richTextView.blurEditor()
|
|
2170
2164
|
richTextView.editorId = 0
|
|
2171
2165
|
mentionQueryState = nil
|
|
2172
2166
|
_ = accessoryToolbar.setMentionSuggestions([])
|
|
@@ -2246,8 +2240,8 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2246
2240
|
accessoryToolbar.apply(theme: theme?.toolbar)
|
|
2247
2241
|
accessoryToolbar.apply(mentionTheme: theme?.mentions ?? addons.mentions?.theme)
|
|
2248
2242
|
refreshSystemAssistantToolbarIfNeeded()
|
|
2249
|
-
if richTextView.
|
|
2250
|
-
(richTextView.
|
|
2243
|
+
if richTextView.isEditorFocused,
|
|
2244
|
+
(richTextView.editorInputAccessoryView === accessoryToolbar || shouldUseSystemAssistantToolbar)
|
|
2251
2245
|
{
|
|
2252
2246
|
reloadInputViewsAfterPreparingOrRetry()
|
|
2253
2247
|
}
|
|
@@ -2325,10 +2319,10 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2325
2319
|
}
|
|
2326
2320
|
|
|
2327
2321
|
private func prepareForInputAccessoryMutationOrRetry(_ action: PendingAccessoryRetryAction) -> Bool {
|
|
2328
|
-
guard richTextView.editorId != 0, richTextView.
|
|
2322
|
+
guard richTextView.editorId != 0, richTextView.isEditorFocused else {
|
|
2329
2323
|
return true
|
|
2330
2324
|
}
|
|
2331
|
-
guard richTextView.
|
|
2325
|
+
guard richTextView.activeTextView?.markedTextRange == nil else {
|
|
2332
2326
|
scheduleAccessoryRetry(action)
|
|
2333
2327
|
return false
|
|
2334
2328
|
}
|
|
@@ -2337,7 +2331,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2337
2331
|
|
|
2338
2332
|
private func reloadInputViewsAfterPreparingOrRetry() {
|
|
2339
2333
|
guard prepareForInputAccessoryMutationOrRetry(.reloadInputViews) else { return }
|
|
2340
|
-
richTextView.
|
|
2334
|
+
richTextView.reloadEditorInputViews()
|
|
2341
2335
|
markAccessoryMutationSucceeded(.reloadInputViews)
|
|
2342
2336
|
}
|
|
2343
2337
|
|
|
@@ -2434,7 +2428,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2434
2428
|
|
|
2435
2429
|
private func hasActiveMentionQueryForCurrentAddons() -> Bool {
|
|
2436
2430
|
guard richTextView.editorId != 0,
|
|
2437
|
-
richTextView.
|
|
2431
|
+
richTextView.isEditorFocused,
|
|
2438
2432
|
let mentions = addons.mentions
|
|
2439
2433
|
else {
|
|
2440
2434
|
return false
|
|
@@ -2446,7 +2440,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2446
2440
|
guard lastAddonsJSON != addonsJson else { return }
|
|
2447
2441
|
lastAddonsJSON = addonsJson
|
|
2448
2442
|
addons = NativeEditorAddons.from(json: addonsJson)
|
|
2449
|
-
accessoryToolbar.apply(mentionTheme: richTextView.
|
|
2443
|
+
accessoryToolbar.apply(mentionTheme: richTextView.editorTheme?.mentions ?? addons.mentions?.theme)
|
|
2450
2444
|
refreshMentionQuery()
|
|
2451
2445
|
}
|
|
2452
2446
|
|
|
@@ -2458,9 +2452,9 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2458
2452
|
|
|
2459
2453
|
func setEditable(_ editable: Bool) {
|
|
2460
2454
|
if !editable,
|
|
2461
|
-
richTextView.
|
|
2455
|
+
richTextView.editorIsEditable,
|
|
2462
2456
|
richTextView.editorId != 0,
|
|
2463
|
-
|
|
2457
|
+
richTextView.activeTextView?.markedTextRange != nil
|
|
2464
2458
|
{
|
|
2465
2459
|
scheduleEditableRetry(editable)
|
|
2466
2460
|
return
|
|
@@ -2504,19 +2498,36 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2504
2498
|
}
|
|
2505
2499
|
|
|
2506
2500
|
func setAutoCapitalize(_ autoCapitalize: String?) {
|
|
2507
|
-
|
|
2501
|
+
let value: UITextAutocapitalizationType
|
|
2502
|
+
switch autoCapitalize {
|
|
2503
|
+
case "none": value = .none
|
|
2504
|
+
case "words": value = .words
|
|
2505
|
+
case "characters": value = .allCharacters
|
|
2506
|
+
default: value = .sentences
|
|
2507
|
+
}
|
|
2508
|
+
richTextView.configureEditorInputTraits(autocapitalizationType: value)
|
|
2508
2509
|
}
|
|
2509
2510
|
|
|
2510
2511
|
func setAutoCorrect(_ autoCorrect: Bool?) {
|
|
2511
|
-
richTextView.
|
|
2512
|
+
richTextView.configureEditorInputTraits(autocorrectionType: (autoCorrect ?? false) ? .yes : .no)
|
|
2512
2513
|
}
|
|
2513
2514
|
|
|
2514
2515
|
func setKeyboardType(_ keyboardType: String?) {
|
|
2515
|
-
|
|
2516
|
+
let value: UIKeyboardType
|
|
2517
|
+
switch keyboardType {
|
|
2518
|
+
case "email-address": value = .emailAddress
|
|
2519
|
+
case "numeric", "number-pad": value = .numberPad
|
|
2520
|
+
case "decimal-pad": value = .decimalPad
|
|
2521
|
+
case "phone-pad": value = .phonePad
|
|
2522
|
+
case "url": value = .URL
|
|
2523
|
+
default: value = .default
|
|
2524
|
+
}
|
|
2525
|
+
richTextView.configureEditorInputTraits(keyboardType: value)
|
|
2516
2526
|
}
|
|
2517
2527
|
|
|
2518
2528
|
func setKeyboardAppearance(_ keyboardAppearance: String?) {
|
|
2519
|
-
|
|
2529
|
+
let value: UIKeyboardAppearance = keyboardAppearance == "dark" ? .dark : keyboardAppearance == "light" ? .light : .default
|
|
2530
|
+
richTextView.configureEditorInputTraits(keyboardAppearance: value)
|
|
2520
2531
|
}
|
|
2521
2532
|
|
|
2522
2533
|
func setShowToolbar(_ showToolbar: Bool) {
|
|
@@ -2648,13 +2659,13 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2648
2659
|
/// resulting delegate callback is NOT re-dispatched back to JS.
|
|
2649
2660
|
@discardableResult
|
|
2650
2661
|
func applyEditorUpdate(_ updateJson: String) -> Bool {
|
|
2651
|
-
guard richTextView.
|
|
2662
|
+
guard richTextView.activeTextView?.markedTextRange == nil else {
|
|
2652
2663
|
scheduleViewCommandUpdateRetry(updateJson)
|
|
2653
2664
|
return false
|
|
2654
2665
|
}
|
|
2655
2666
|
isApplyingJSUpdate = true
|
|
2656
2667
|
defer { isApplyingJSUpdate = false }
|
|
2657
|
-
richTextView.
|
|
2668
|
+
richTextView.applyEditorUpdate(updateJson)
|
|
2658
2669
|
return true
|
|
2659
2670
|
}
|
|
2660
2671
|
|
|
@@ -2697,23 +2708,22 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2697
2708
|
func prepareForEditorCommandJSON() -> String {
|
|
2698
2709
|
isApplyingJSUpdate = true
|
|
2699
2710
|
defer { isApplyingJSUpdate = false }
|
|
2700
|
-
let preparation = richTextView.textView.prepareForExternalEditorCommand()
|
|
2701
2711
|
return NativeEditorViewRegistry.commandPreparationJSON(
|
|
2702
|
-
ready:
|
|
2703
|
-
updateJSON:
|
|
2704
|
-
blockedReason:
|
|
2712
|
+
ready: richTextView.activeTextView?.markedTextRange == nil,
|
|
2713
|
+
updateJSON: richTextView.editorId == 0 ? nil : editorGetSelectionState(id: richTextView.editorId),
|
|
2714
|
+
blockedReason: richTextView.activeTextView?.markedTextRange == nil ? nil : "composition"
|
|
2705
2715
|
)
|
|
2706
2716
|
}
|
|
2707
2717
|
|
|
2708
2718
|
// MARK: - Focus Commands
|
|
2709
2719
|
|
|
2710
2720
|
func focus() {
|
|
2711
|
-
_ = richTextView.
|
|
2721
|
+
_ = richTextView.focusEditor()
|
|
2712
2722
|
}
|
|
2713
2723
|
|
|
2714
2724
|
func blur() {
|
|
2715
2725
|
clearRecentToolbarTouch()
|
|
2716
|
-
|
|
2726
|
+
richTextView.blurEditor()
|
|
2717
2727
|
}
|
|
2718
2728
|
|
|
2719
2729
|
func getCaretRectJson() -> String? {
|
|
@@ -2742,30 +2752,28 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2742
2752
|
|
|
2743
2753
|
// MARK: - Focus Notifications
|
|
2744
2754
|
|
|
2745
|
-
|
|
2755
|
+
private func handleTextDidBeginEditing() {
|
|
2746
2756
|
installOutsideTapRecognizerIfNeeded()
|
|
2747
|
-
richTextView.textView.refreshSelectionVisualState()
|
|
2748
2757
|
refreshMentionQuery()
|
|
2749
2758
|
onFocusChange(["isFocused": true])
|
|
2750
2759
|
}
|
|
2751
2760
|
|
|
2752
|
-
|
|
2761
|
+
private func handleTextDidEndEditing() {
|
|
2753
2762
|
if consumeToolbarFocusPreservationForBlur() {
|
|
2754
2763
|
DispatchQueue.main.async { [weak self] in
|
|
2755
|
-
_ = self?.richTextView.
|
|
2764
|
+
_ = self?.richTextView.focusEditor()
|
|
2756
2765
|
}
|
|
2757
2766
|
return
|
|
2758
2767
|
}
|
|
2759
2768
|
|
|
2760
2769
|
uninstallOutsideTapRecognizer()
|
|
2761
|
-
richTextView.textView.refreshSelectionVisualState()
|
|
2762
2770
|
clearMentionQueryStateAndHidePopover()
|
|
2763
2771
|
onFocusChange(["isFocused": false])
|
|
2764
2772
|
}
|
|
2765
2773
|
|
|
2766
2774
|
@objc private func handleOutsideTap(_ recognizer: UITapGestureRecognizer) {
|
|
2767
2775
|
guard recognizer.state == .ended else { return }
|
|
2768
|
-
guard richTextView.
|
|
2776
|
+
guard richTextView.isEditorFocused else { return }
|
|
2769
2777
|
guard let tapWindow = gestureWindow ?? window else { return }
|
|
2770
2778
|
let locationInWindow = recognizer.location(in: tapWindow)
|
|
2771
2779
|
guard shouldHandleOutsideTap(locationInWindow: locationInWindow, touchedView: nil) else {
|
|
@@ -2863,9 +2871,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2863
2871
|
return true
|
|
2864
2872
|
}
|
|
2865
2873
|
|
|
2866
|
-
|
|
2867
|
-
|
|
2868
|
-
func editorTextView(_ textView: EditorTextView, selectionDidChange anchor: UInt32, head: UInt32) {
|
|
2874
|
+
private func handleSelectionChange(anchor: UInt32, head: UInt32) {
|
|
2869
2875
|
let stateJSON = refreshToolbarStateFromEditorSelection()
|
|
2870
2876
|
refreshSystemAssistantToolbarIfNeeded()
|
|
2871
2877
|
refreshMentionQuery()
|
|
@@ -2877,7 +2883,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2877
2883
|
onSelectionChange(event)
|
|
2878
2884
|
}
|
|
2879
2885
|
|
|
2880
|
-
func
|
|
2886
|
+
private func handleEditorUpdate(_ updateJSON: String) {
|
|
2881
2887
|
if let state = NativeToolbarState(updateJSON: updateJSON) {
|
|
2882
2888
|
toolbarState = state
|
|
2883
2889
|
accessoryToolbar.apply(state: state)
|
|
@@ -2913,7 +2919,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2913
2919
|
|
|
2914
2920
|
private func refreshMentionQuery() {
|
|
2915
2921
|
guard richTextView.editorId != 0,
|
|
2916
|
-
richTextView.
|
|
2922
|
+
richTextView.isEditorFocused,
|
|
2917
2923
|
let mentions = addons.mentions
|
|
2918
2924
|
else {
|
|
2919
2925
|
clearMentionQueryStateAndHidePopover()
|
|
@@ -2929,14 +2935,14 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2929
2935
|
|
|
2930
2936
|
let suggestions = filteredMentionSuggestions(for: queryState, config: mentions)
|
|
2931
2937
|
mentionQueryState = queryState
|
|
2932
|
-
accessoryToolbar.apply(mentionTheme: richTextView.
|
|
2938
|
+
accessoryToolbar.apply(mentionTheme: richTextView.editorTheme?.mentions ?? mentions.theme)
|
|
2933
2939
|
let didChangeToolbarHeight = accessoryToolbar.setMentionSuggestions(suggestions)
|
|
2934
2940
|
refreshSystemAssistantToolbarIfNeeded()
|
|
2935
2941
|
if didChangeToolbarHeight,
|
|
2936
|
-
richTextView.
|
|
2937
|
-
richTextView.
|
|
2942
|
+
richTextView.isEditorFocused,
|
|
2943
|
+
richTextView.editorInputAccessoryView === accessoryToolbar
|
|
2938
2944
|
{
|
|
2939
|
-
richTextView.
|
|
2945
|
+
richTextView.reloadEditorInputViews()
|
|
2940
2946
|
}
|
|
2941
2947
|
markAccessoryMutationSucceeded(.refreshMentionQuery)
|
|
2942
2948
|
emitMentionQueryChange(
|
|
@@ -2954,10 +2960,10 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
2954
2960
|
let didChangeToolbarHeight = accessoryToolbar.setMentionSuggestions([])
|
|
2955
2961
|
refreshSystemAssistantToolbarIfNeeded()
|
|
2956
2962
|
if didChangeToolbarHeight,
|
|
2957
|
-
richTextView.
|
|
2958
|
-
richTextView.
|
|
2963
|
+
richTextView.isEditorFocused,
|
|
2964
|
+
richTextView.editorInputAccessoryView === accessoryToolbar
|
|
2959
2965
|
{
|
|
2960
|
-
richTextView.
|
|
2966
|
+
richTextView.reloadEditorInputViews()
|
|
2961
2967
|
}
|
|
2962
2968
|
markAccessoryMutationSucceeded(.clearMentionQueryState)
|
|
2963
2969
|
}
|
|
@@ -3091,15 +3097,16 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3091
3097
|
}
|
|
3092
3098
|
|
|
3093
3099
|
private func currentMentionQueryState(trigger: String) -> MentionQueryState? {
|
|
3094
|
-
guard let
|
|
3100
|
+
guard let activeTextView = richTextView.activeTextView,
|
|
3101
|
+
let selectedTextRange = activeTextView.selectedTextRange,
|
|
3095
3102
|
selectedTextRange.isEmpty
|
|
3096
3103
|
else {
|
|
3097
3104
|
return nil
|
|
3098
3105
|
}
|
|
3099
3106
|
|
|
3100
|
-
let currentText =
|
|
3101
|
-
let cursorUtf16Offset =
|
|
3102
|
-
from:
|
|
3107
|
+
let currentText = activeTextView.text ?? ""
|
|
3108
|
+
let cursorUtf16Offset = activeTextView.offset(
|
|
3109
|
+
from: activeTextView.beginningOfDocument,
|
|
3103
3110
|
to: selectedTextRange.start
|
|
3104
3111
|
)
|
|
3105
3112
|
let visibleCursorScalar = PositionBridge.utf16OffsetToScalar(
|
|
@@ -3114,7 +3121,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3114
3121
|
isCaretInsideMention: isCaretInsideMention(
|
|
3115
3122
|
cursorScalar: PositionBridge.textViewToScalar(
|
|
3116
3123
|
selectedTextRange.start,
|
|
3117
|
-
in:
|
|
3124
|
+
in: activeTextView
|
|
3118
3125
|
)
|
|
3119
3126
|
)
|
|
3120
3127
|
) else {
|
|
@@ -3130,39 +3137,22 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3130
3137
|
in: currentText
|
|
3131
3138
|
)
|
|
3132
3139
|
|
|
3140
|
+
guard let anchorDoc = richTextView.activeDocPosition(forUTF16Offset: anchorUtf16Offset),
|
|
3141
|
+
let headDoc = richTextView.activeDocPosition(forUTF16Offset: headUtf16Offset)
|
|
3142
|
+
else { return nil }
|
|
3133
3143
|
return MentionQueryState(
|
|
3134
3144
|
query: visibleQueryState.query,
|
|
3135
3145
|
trigger: visibleQueryState.trigger,
|
|
3136
|
-
anchor:
|
|
3137
|
-
|
|
3138
|
-
in: richTextView.textView
|
|
3139
|
-
),
|
|
3140
|
-
head: PositionBridge.utf16OffsetToScalar(
|
|
3141
|
-
headUtf16Offset,
|
|
3142
|
-
in: richTextView.textView
|
|
3143
|
-
)
|
|
3146
|
+
anchor: editorDocToScalar(id: richTextView.editorId, docPos: anchorDoc),
|
|
3147
|
+
head: editorDocToScalar(id: richTextView.editorId, docPos: headDoc)
|
|
3144
3148
|
)
|
|
3145
3149
|
}
|
|
3146
3150
|
|
|
3147
3151
|
private func isCaretInsideMention(cursorScalar: UInt32) -> Bool {
|
|
3148
|
-
let
|
|
3149
|
-
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
let textStorage = richTextView.textView.textStorage
|
|
3153
|
-
guard textStorage.length > 0 else { return false }
|
|
3154
|
-
let candidateOffsets = [
|
|
3155
|
-
min(max(utf16Offset, 0), max(textStorage.length - 1, 0)),
|
|
3156
|
-
min(max(utf16Offset - 1, 0), max(textStorage.length - 1, 0)),
|
|
3157
|
-
]
|
|
3158
|
-
|
|
3159
|
-
for offset in candidateOffsets where offset >= 0 && offset < textStorage.length {
|
|
3160
|
-
if let nodeType = textStorage.attribute(InputCoordinatorAttributes.voidNodeType, at: offset, effectiveRange: nil) as? String,
|
|
3161
|
-
nodeType == "mention" {
|
|
3162
|
-
return true
|
|
3163
|
-
}
|
|
3164
|
-
}
|
|
3165
|
-
return false
|
|
3152
|
+
guard let activeTextView = richTextView.activeTextView else { return false }
|
|
3153
|
+
let utf16Offset = PositionBridge.scalarToUtf16Offset(cursorScalar, in: activeTextView.text ?? "")
|
|
3154
|
+
return richTextView.activeInlineAtomType(atUTF16Offset: utf16Offset) == "mention"
|
|
3155
|
+
|| richTextView.activeInlineAtomType(atUTF16Offset: max(0, utf16Offset - 1)) == "mention"
|
|
3166
3156
|
}
|
|
3167
3157
|
|
|
3168
3158
|
private func insertMentionSuggestion(
|
|
@@ -3200,8 +3190,9 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3200
3190
|
clearMentionQueryStateAndHidePopover()
|
|
3201
3191
|
return
|
|
3202
3192
|
}
|
|
3203
|
-
let
|
|
3204
|
-
|
|
3193
|
+
let compositionActive = richTextView.activeTextView?.markedTextRange != nil
|
|
3194
|
+
let preflightUpdateJSON = editorGetSelectionState(id: richTextView.editorId)
|
|
3195
|
+
guard !compositionActive else {
|
|
3205
3196
|
scheduleMentionSuggestionRetry(
|
|
3206
3197
|
PendingMentionSuggestionRetry(
|
|
3207
3198
|
suggestionKey: suggestionKey,
|
|
@@ -3211,13 +3202,13 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3211
3202
|
anchor: scopedQueryState.anchor,
|
|
3212
3203
|
head: scopedQueryState.head,
|
|
3213
3204
|
documentVersion: currentDocumentVersion(),
|
|
3214
|
-
textSnapshot: richTextView.
|
|
3205
|
+
textSnapshot: richTextView.activeTextView?.text ?? ""
|
|
3215
3206
|
)
|
|
3216
3207
|
)
|
|
3217
3208
|
return
|
|
3218
3209
|
}
|
|
3219
3210
|
let queryState = currentMentionQueryState(trigger: mentions.trigger)
|
|
3220
|
-
?? (richTextView.
|
|
3211
|
+
?? (richTextView.isEditorFocused ? nil : mentionQueryState)
|
|
3221
3212
|
guard let queryState else {
|
|
3222
3213
|
clearMentionQueryStateAndHidePopover()
|
|
3223
3214
|
return
|
|
@@ -3226,8 +3217,8 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3226
3217
|
!doesMentionQueryState(
|
|
3227
3218
|
queryState,
|
|
3228
3219
|
match: retryScope,
|
|
3229
|
-
acceptingPreflightDocumentVersion: documentVersion(fromUpdateJSON:
|
|
3230
|
-
currentText: richTextView.
|
|
3220
|
+
acceptingPreflightDocumentVersion: documentVersion(fromUpdateJSON: preflightUpdateJSON),
|
|
3221
|
+
currentText: richTextView.activeTextView?.text ?? ""
|
|
3231
3222
|
)
|
|
3232
3223
|
{
|
|
3233
3224
|
return
|
|
@@ -3248,7 +3239,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3248
3239
|
suggestion: currentSuggestion,
|
|
3249
3240
|
attrs: attrs,
|
|
3250
3241
|
range: queryState,
|
|
3251
|
-
preflightUpdateJSON:
|
|
3242
|
+
preflightUpdateJSON: preflightUpdateJSON
|
|
3252
3243
|
)
|
|
3253
3244
|
lastMentionEventJSON = nil
|
|
3254
3245
|
clearMentionQueryStateAndHidePopover()
|
|
@@ -3273,7 +3264,8 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3273
3264
|
scalarHead: queryState.head,
|
|
3274
3265
|
json: json
|
|
3275
3266
|
)
|
|
3276
|
-
richTextView.
|
|
3267
|
+
richTextView.applyEditorUpdate(updateJSON)
|
|
3268
|
+
handleEditorUpdate(updateJSON)
|
|
3277
3269
|
emitMentionSelect(trigger: mentions.trigger, suggestion: currentSuggestion, attrs: attrs)
|
|
3278
3270
|
lastMentionEventJSON = nil
|
|
3279
3271
|
clearMentionQueryStateAndHidePopover()
|
|
@@ -3477,15 +3469,15 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3477
3469
|
}
|
|
3478
3470
|
|
|
3479
3471
|
func inputAccessoryViewForTesting() -> UIView? {
|
|
3480
|
-
richTextView.
|
|
3472
|
+
richTextView.editorInputAccessoryView
|
|
3481
3473
|
}
|
|
3482
3474
|
|
|
3483
3475
|
func isUsingAccessoryToolbarForTesting() -> Bool {
|
|
3484
|
-
richTextView.
|
|
3476
|
+
richTextView.editorInputAccessoryView === accessoryToolbar
|
|
3485
3477
|
}
|
|
3486
3478
|
|
|
3487
3479
|
func isUsingAccessoryPlaceholderForTesting() -> Bool {
|
|
3488
|
-
richTextView.
|
|
3480
|
+
richTextView.editorInputAccessoryView === accessoryPlaceholder
|
|
3489
3481
|
}
|
|
3490
3482
|
|
|
3491
3483
|
func markRecentToolbarTouchForTesting() {
|
|
@@ -3516,19 +3508,19 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3516
3508
|
let nextAccessoryView: UIView?
|
|
3517
3509
|
if showsToolbar &&
|
|
3518
3510
|
toolbarPlacement == "keyboard" &&
|
|
3519
|
-
richTextView.
|
|
3511
|
+
richTextView.editorIsEditable &&
|
|
3520
3512
|
!shouldUseSystemAssistantToolbar
|
|
3521
3513
|
{
|
|
3522
3514
|
nextAccessoryView = accessoryToolbar
|
|
3523
|
-
} else if richTextView.
|
|
3515
|
+
} else if richTextView.editorIsEditable && !shouldUseSystemAssistantToolbar {
|
|
3524
3516
|
nextAccessoryView = accessoryPlaceholder
|
|
3525
3517
|
} else {
|
|
3526
3518
|
nextAccessoryView = nil
|
|
3527
3519
|
}
|
|
3528
|
-
if richTextView.
|
|
3529
|
-
richTextView.
|
|
3530
|
-
if richTextView.
|
|
3531
|
-
richTextView.
|
|
3520
|
+
if richTextView.editorInputAccessoryView !== nextAccessoryView {
|
|
3521
|
+
richTextView.setEditorInputAccessoryView(nextAccessoryView)
|
|
3522
|
+
if richTextView.isEditorFocused {
|
|
3523
|
+
richTextView.reloadEditorInputViews()
|
|
3532
3524
|
}
|
|
3533
3525
|
}
|
|
3534
3526
|
markAccessoryMutationSucceeded(.updateAccessoryToolbarVisibility)
|
|
@@ -3541,7 +3533,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3541
3533
|
private func refreshSystemAssistantToolbarIfNeeded() {
|
|
3542
3534
|
guard #available(iOS 26.0, *) else { return }
|
|
3543
3535
|
|
|
3544
|
-
let assistantItem = richTextView.
|
|
3536
|
+
guard let assistantItem = richTextView.editorInputAssistantItem else { return }
|
|
3545
3537
|
assistantItem.allowsHidingShortcuts = false
|
|
3546
3538
|
assistantItem.leadingBarButtonGroups = []
|
|
3547
3539
|
assistantItem.trailingBarButtonGroups = []
|
|
@@ -3549,38 +3541,43 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
3549
3541
|
|
|
3550
3542
|
private func handleListToggle(_ listType: String) {
|
|
3551
3543
|
let isActive = toolbarState.nodes[listType] == true
|
|
3552
|
-
richTextView.
|
|
3544
|
+
guard richTextView.editorId != 0 else { return }
|
|
3545
|
+
_ = richTextView.performEditorCommand {
|
|
3546
|
+
isActive
|
|
3547
|
+
? editorUnwrapFromList(id: richTextView.editorId)
|
|
3548
|
+
: editorWrapInList(id: richTextView.editorId, listType: listType)
|
|
3549
|
+
}
|
|
3553
3550
|
}
|
|
3554
3551
|
|
|
3555
3552
|
private func handleToolbarItemPress(_ item: NativeToolbarItem) {
|
|
3556
3553
|
switch item.type {
|
|
3557
3554
|
case .mark:
|
|
3558
3555
|
guard let mark = item.mark else { return }
|
|
3559
|
-
richTextView.
|
|
3556
|
+
_ = richTextView.performEditorCommand { editorToggleMark(id: richTextView.editorId, markName: mark) }
|
|
3560
3557
|
case .heading:
|
|
3561
3558
|
guard let level = item.headingLevel else { return }
|
|
3562
|
-
richTextView.
|
|
3559
|
+
_ = richTextView.performEditorCommand { editorToggleHeading(id: richTextView.editorId, level: UInt8(level)) }
|
|
3563
3560
|
case .blockquote:
|
|
3564
|
-
richTextView.
|
|
3561
|
+
_ = richTextView.performEditorCommand { editorToggleBlockquote(id: richTextView.editorId) }
|
|
3565
3562
|
case .list:
|
|
3566
3563
|
guard let listType = item.listType?.rawValue else { return }
|
|
3567
3564
|
handleListToggle(listType)
|
|
3568
3565
|
case .command:
|
|
3569
3566
|
switch item.command {
|
|
3570
3567
|
case .indentList:
|
|
3571
|
-
richTextView.
|
|
3568
|
+
_ = richTextView.performEditorCommand { editorIndentListItem(id: richTextView.editorId) }
|
|
3572
3569
|
case .outdentList:
|
|
3573
|
-
richTextView.
|
|
3570
|
+
_ = richTextView.performEditorCommand { editorOutdentListItem(id: richTextView.editorId) }
|
|
3574
3571
|
case .undo:
|
|
3575
|
-
richTextView.
|
|
3572
|
+
_ = richTextView.performEditorCommand { editorUndo(id: richTextView.editorId) }
|
|
3576
3573
|
case .redo:
|
|
3577
|
-
richTextView.
|
|
3574
|
+
_ = richTextView.performEditorCommand { editorRedo(id: richTextView.editorId) }
|
|
3578
3575
|
case .none:
|
|
3579
3576
|
break
|
|
3580
3577
|
}
|
|
3581
3578
|
case .node:
|
|
3582
3579
|
guard let nodeType = item.nodeType else { return }
|
|
3583
|
-
richTextView.
|
|
3580
|
+
_ = richTextView.performEditorCommand { editorInsertNode(id: richTextView.editorId, nodeType: nodeType) }
|
|
3584
3581
|
case .action:
|
|
3585
3582
|
guard let key = item.key else { return }
|
|
3586
3583
|
onToolbarAction(["key": key])
|