@apollohg/react-native-prose-editor 0.4.0 → 0.4.2
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 +21 -2
- package/android/build.gradle +23 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +502 -39
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +56 -28
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +6 -0
- package/android/src/main/java/com/apollohg/editor/PositionBridge.kt +57 -27
- package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +147 -78
- package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +249 -71
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +7 -6
- package/dist/NativeEditorBridge.d.ts +37 -1
- package/dist/NativeEditorBridge.js +192 -97
- package/dist/NativeRichTextEditor.d.ts +3 -2
- package/dist/NativeRichTextEditor.js +164 -56
- package/dist/YjsCollaboration.d.ts +2 -0
- package/dist/YjsCollaboration.js +142 -20
- package/dist/schemas.d.ts +2 -0
- package/dist/schemas.js +63 -0
- package/ios/EditorCore.xcframework/Info.plist +5 -5
- package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
- package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
- package/ios/EditorLayoutManager.swift +3 -3
- package/ios/Generated_editor_core.swift +41 -0
- package/ios/NativeEditorExpoView.swift +43 -11
- package/ios/NativeEditorModule.swift +6 -0
- package/ios/PositionBridge.swift +310 -75
- package/ios/RenderBridge.swift +362 -27
- package/ios/RichTextEditorView.swift +1983 -187
- package/ios/editor_coreFFI/editor_coreFFI.h +33 -0
- package/package.json +11 -2
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +63 -0
package/dist/schemas.js
CHANGED
|
@@ -4,6 +4,8 @@ exports.prosemirrorSchema = exports.tiptapSchema = exports.IMAGE_NODE_NAME = voi
|
|
|
4
4
|
exports.imageNodeSpec = imageNodeSpec;
|
|
5
5
|
exports.withImagesSchema = withImagesSchema;
|
|
6
6
|
exports.buildImageFragmentJson = buildImageFragmentJson;
|
|
7
|
+
exports.defaultEmptyDocument = defaultEmptyDocument;
|
|
8
|
+
exports.normalizeDocumentJson = normalizeDocumentJson;
|
|
7
9
|
exports.IMAGE_NODE_NAME = 'image';
|
|
8
10
|
const HEADING_LEVELS = [1, 2, 3, 4, 5, 6];
|
|
9
11
|
function imageNodeSpec(name = exports.IMAGE_NODE_NAME) {
|
|
@@ -129,6 +131,67 @@ exports.tiptapSchema = {
|
|
|
129
131
|
],
|
|
130
132
|
marks: MARKS,
|
|
131
133
|
};
|
|
134
|
+
function acceptingGroupsForChildCount(content, existingChildCount) {
|
|
135
|
+
const tokens = content
|
|
136
|
+
.trim()
|
|
137
|
+
.split(/\s+/)
|
|
138
|
+
.filter(Boolean)
|
|
139
|
+
.map((token) => {
|
|
140
|
+
const quantifier = token[token.length - 1];
|
|
141
|
+
if (quantifier === '+' || quantifier === '*' || quantifier === '?') {
|
|
142
|
+
return {
|
|
143
|
+
group: token.slice(0, -1),
|
|
144
|
+
min: quantifier === '+' ? 1 : 0,
|
|
145
|
+
max: quantifier === '?' ? 1 : null,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
return {
|
|
149
|
+
group: token,
|
|
150
|
+
min: 1,
|
|
151
|
+
max: 1,
|
|
152
|
+
};
|
|
153
|
+
});
|
|
154
|
+
let remaining = existingChildCount;
|
|
155
|
+
const acceptingGroups = [];
|
|
156
|
+
for (const token of tokens) {
|
|
157
|
+
if (remaining >= token.min) {
|
|
158
|
+
const consumed = token.max == null ? remaining : Math.min(remaining, token.max);
|
|
159
|
+
remaining = Math.max(0, remaining - consumed);
|
|
160
|
+
const atMax = token.max != null && consumed >= token.max;
|
|
161
|
+
if (!atMax) {
|
|
162
|
+
acceptingGroups.push(token.group);
|
|
163
|
+
}
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
acceptingGroups.push(token.group);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
return acceptingGroups;
|
|
170
|
+
}
|
|
171
|
+
function defaultEmptyDocument(schema = exports.tiptapSchema) {
|
|
172
|
+
const docNode = schema.nodes.find((node) => node.role === 'doc' || node.name === 'doc');
|
|
173
|
+
const acceptingGroups = docNode == null ? [] : acceptingGroupsForChildCount(docNode.content ?? '', 0);
|
|
174
|
+
const matchingTextBlocks = schema.nodes.filter((node) => node.role === 'textBlock' &&
|
|
175
|
+
acceptingGroups.some((group) => node.name === group || node.group === group));
|
|
176
|
+
const preferredTextBlock = matchingTextBlocks.find((node) => node.htmlTag === 'p' || node.name === 'paragraph') ??
|
|
177
|
+
matchingTextBlocks[0] ??
|
|
178
|
+
schema.nodes.find((node) => node.htmlTag === 'p' || node.name === 'paragraph') ??
|
|
179
|
+
schema.nodes.find((node) => node.role === 'textBlock');
|
|
180
|
+
return {
|
|
181
|
+
type: 'doc',
|
|
182
|
+
content: [{ type: preferredTextBlock?.name ?? 'paragraph' }],
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function normalizeDocumentJson(doc, schema = exports.tiptapSchema) {
|
|
186
|
+
const root = doc;
|
|
187
|
+
if (root?.type !== 'doc') {
|
|
188
|
+
return doc;
|
|
189
|
+
}
|
|
190
|
+
if (Array.isArray(root.content) && root.content.length > 0) {
|
|
191
|
+
return doc;
|
|
192
|
+
}
|
|
193
|
+
return defaultEmptyDocument(schema);
|
|
194
|
+
}
|
|
132
195
|
exports.prosemirrorSchema = {
|
|
133
196
|
nodes: [
|
|
134
197
|
{
|
|
@@ -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>
|
|
Binary file
|
|
Binary file
|
|
@@ -23,7 +23,7 @@ final class EditorLayoutManager: NSLayoutManager {
|
|
|
23
23
|
textStorage.enumerateAttribute(
|
|
24
24
|
RenderBridgeAttributes.blockquoteBorderColor,
|
|
25
25
|
in: characterRange,
|
|
26
|
-
options: []
|
|
26
|
+
options: [.longestEffectiveRangeNotRequired]
|
|
27
27
|
) { value, range, _ in
|
|
28
28
|
guard range.length > 0, let color = value as? UIColor else { return }
|
|
29
29
|
|
|
@@ -68,7 +68,7 @@ final class EditorLayoutManager: NSLayoutManager {
|
|
|
68
68
|
textStorage.enumerateAttribute(
|
|
69
69
|
RenderBridgeAttributes.listMarkerContext,
|
|
70
70
|
in: characterRange,
|
|
71
|
-
options: []
|
|
71
|
+
options: [.longestEffectiveRangeNotRequired]
|
|
72
72
|
) { value, range, _ in
|
|
73
73
|
guard range.length > 0, let listContext = value as? [String: Any] else { return }
|
|
74
74
|
|
|
@@ -90,7 +90,7 @@ final class EditorLayoutManager: NSLayoutManager {
|
|
|
90
90
|
textStorage.enumerateAttribute(
|
|
91
91
|
RenderBridgeAttributes.blockquoteBorderColor,
|
|
92
92
|
in: characterRange,
|
|
93
|
-
options: []
|
|
93
|
+
options: [.longestEffectiveRangeNotRequired]
|
|
94
94
|
) { value, range, _ in
|
|
95
95
|
guard range.length > 0, let color = value as? UIColor else { return }
|
|
96
96
|
|
|
@@ -694,6 +694,18 @@ public func editorDeleteAndSplitScalar(id: UInt64, scalarFrom: UInt32, scalarTo:
|
|
|
694
694
|
)
|
|
695
695
|
})
|
|
696
696
|
}
|
|
697
|
+
/**
|
|
698
|
+
* Delete backward relative to an explicit scalar selection. Returns an update JSON string.
|
|
699
|
+
*/
|
|
700
|
+
public func editorDeleteBackwardAtSelectionScalar(id: UInt64, scalarAnchor: UInt32, scalarHead: UInt32) -> String {
|
|
701
|
+
return try! FfiConverterString.lift(try! rustCall() {
|
|
702
|
+
uniffi_editor_core_fn_func_editor_delete_backward_at_selection_scalar(
|
|
703
|
+
FfiConverterUInt64.lower(id),
|
|
704
|
+
FfiConverterUInt32.lower(scalarAnchor),
|
|
705
|
+
FfiConverterUInt32.lower(scalarHead),$0
|
|
706
|
+
)
|
|
707
|
+
})
|
|
708
|
+
}
|
|
697
709
|
/**
|
|
698
710
|
* Delete a range. Returns an update JSON string.
|
|
699
711
|
*/
|
|
@@ -738,6 +750,16 @@ public func editorDocToScalar(id: UInt64, docPos: UInt32) -> UInt32 {
|
|
|
738
750
|
)
|
|
739
751
|
})
|
|
740
752
|
}
|
|
753
|
+
/**
|
|
754
|
+
* Get both HTML and ProseMirror JSON content in one payload.
|
|
755
|
+
*/
|
|
756
|
+
public func editorGetContentSnapshot(id: UInt64) -> String {
|
|
757
|
+
return try! FfiConverterString.lift(try! rustCall() {
|
|
758
|
+
uniffi_editor_core_fn_func_editor_get_content_snapshot(
|
|
759
|
+
FfiConverterUInt64.lower(id),$0
|
|
760
|
+
)
|
|
761
|
+
})
|
|
762
|
+
}
|
|
741
763
|
/**
|
|
742
764
|
* Get the current editor state (render elements, selection, active state,
|
|
743
765
|
* history state) without performing any edits. Used by native views to pull
|
|
@@ -780,6 +802,16 @@ public func editorGetSelection(id: UInt64) -> String {
|
|
|
780
802
|
)
|
|
781
803
|
})
|
|
782
804
|
}
|
|
805
|
+
/**
|
|
806
|
+
* Get the current selection-related editor state without render elements.
|
|
807
|
+
*/
|
|
808
|
+
public func editorGetSelectionState(id: UInt64) -> String {
|
|
809
|
+
return try! FfiConverterString.lift(try! rustCall() {
|
|
810
|
+
uniffi_editor_core_fn_func_editor_get_selection_state(
|
|
811
|
+
FfiConverterUInt64.lower(id),$0
|
|
812
|
+
)
|
|
813
|
+
})
|
|
814
|
+
}
|
|
783
815
|
/**
|
|
784
816
|
* Indent the current list item into a nested list. Returns an update JSON string.
|
|
785
817
|
*/
|
|
@@ -1296,6 +1328,9 @@ private let initializationResult: InitializationResult = {
|
|
|
1296
1328
|
if (uniffi_editor_core_checksum_func_editor_delete_and_split_scalar() != 13764) {
|
|
1297
1329
|
return InitializationResult.apiChecksumMismatch
|
|
1298
1330
|
}
|
|
1331
|
+
if (uniffi_editor_core_checksum_func_editor_delete_backward_at_selection_scalar() != 7697) {
|
|
1332
|
+
return InitializationResult.apiChecksumMismatch
|
|
1333
|
+
}
|
|
1299
1334
|
if (uniffi_editor_core_checksum_func_editor_delete_range() != 6109) {
|
|
1300
1335
|
return InitializationResult.apiChecksumMismatch
|
|
1301
1336
|
}
|
|
@@ -1308,6 +1343,9 @@ private let initializationResult: InitializationResult = {
|
|
|
1308
1343
|
if (uniffi_editor_core_checksum_func_editor_doc_to_scalar() != 48291) {
|
|
1309
1344
|
return InitializationResult.apiChecksumMismatch
|
|
1310
1345
|
}
|
|
1346
|
+
if (uniffi_editor_core_checksum_func_editor_get_content_snapshot() != 32837) {
|
|
1347
|
+
return InitializationResult.apiChecksumMismatch
|
|
1348
|
+
}
|
|
1311
1349
|
if (uniffi_editor_core_checksum_func_editor_get_current_state() != 13946) {
|
|
1312
1350
|
return InitializationResult.apiChecksumMismatch
|
|
1313
1351
|
}
|
|
@@ -1320,6 +1358,9 @@ private let initializationResult: InitializationResult = {
|
|
|
1320
1358
|
if (uniffi_editor_core_checksum_func_editor_get_selection() != 20571) {
|
|
1321
1359
|
return InitializationResult.apiChecksumMismatch
|
|
1322
1360
|
}
|
|
1361
|
+
if (uniffi_editor_core_checksum_func_editor_get_selection_state() != 16471) {
|
|
1362
|
+
return InitializationResult.apiChecksumMismatch
|
|
1363
|
+
}
|
|
1323
1364
|
if (uniffi_editor_core_checksum_func_editor_indent_list_item() != 10818) {
|
|
1324
1365
|
return InitializationResult.apiChecksumMismatch
|
|
1325
1366
|
}
|
|
@@ -1596,6 +1596,11 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
1596
1596
|
private var addons = NativeEditorAddons(mentions: nil)
|
|
1597
1597
|
private var mentionQueryState: MentionQueryState?
|
|
1598
1598
|
private var lastMentionEventJSON: String?
|
|
1599
|
+
private var lastThemeJSON: String?
|
|
1600
|
+
private var lastAddonsJSON: String?
|
|
1601
|
+
private var lastRemoteSelectionsJSON: String?
|
|
1602
|
+
private var lastToolbarItemsJSON: String?
|
|
1603
|
+
private var lastToolbarFrameJSON: String?
|
|
1599
1604
|
private var pendingEditorUpdateJSON: String?
|
|
1600
1605
|
private var pendingEditorUpdateRevision = 0
|
|
1601
1606
|
private var appliedEditorUpdateRevision = 0
|
|
@@ -1624,17 +1629,18 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
1624
1629
|
let onToolbarAction = EventDispatcher()
|
|
1625
1630
|
let onAddonEvent = EventDispatcher()
|
|
1626
1631
|
private var lastEmittedContentHeight: CGFloat = 0
|
|
1632
|
+
private var cachedAutoGrowContentHeight: CGFloat = 0
|
|
1627
1633
|
|
|
1628
1634
|
// MARK: - Initialization
|
|
1629
1635
|
|
|
1630
1636
|
required init(appContext: AppContext? = nil) {
|
|
1631
1637
|
richTextView = RichTextEditorView(frame: .zero)
|
|
1632
1638
|
super.init(appContext: appContext)
|
|
1633
|
-
richTextView.onHeightMayChange = { [weak self] in
|
|
1639
|
+
richTextView.onHeightMayChange = { [weak self] measuredHeight in
|
|
1634
1640
|
guard let self, self.heightBehavior == .autoGrow else { return }
|
|
1641
|
+
self.cachedAutoGrowContentHeight = measuredHeight
|
|
1635
1642
|
self.invalidateIntrinsicContentSize()
|
|
1636
|
-
self.
|
|
1637
|
-
self.emitContentHeightIfNeeded(force: true)
|
|
1643
|
+
self.emitContentHeightIfNeeded(force: true, measuredHeight: measuredHeight)
|
|
1638
1644
|
}
|
|
1639
1645
|
richTextView.textView.editorDelegate = self
|
|
1640
1646
|
configureAccessoryToolbar()
|
|
@@ -1666,6 +1672,9 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
1666
1672
|
guard heightBehavior == .autoGrow else {
|
|
1667
1673
|
return CGSize(width: UIView.noIntrinsicMetric, height: UIView.noIntrinsicMetric)
|
|
1668
1674
|
}
|
|
1675
|
+
if cachedAutoGrowContentHeight > 0 {
|
|
1676
|
+
return CGSize(width: UIView.noIntrinsicMetric, height: cachedAutoGrowContentHeight)
|
|
1677
|
+
}
|
|
1669
1678
|
return richTextView.intrinsicContentSize
|
|
1670
1679
|
}
|
|
1671
1680
|
|
|
@@ -1676,6 +1685,7 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
1676
1685
|
let currentWidth = bounds.width.rounded(.towardZero)
|
|
1677
1686
|
guard currentWidth != lastAutoGrowWidth else { return }
|
|
1678
1687
|
lastAutoGrowWidth = currentWidth
|
|
1688
|
+
cachedAutoGrowContentHeight = 0
|
|
1679
1689
|
invalidateIntrinsicContentSize()
|
|
1680
1690
|
emitContentHeightIfNeeded(force: true)
|
|
1681
1691
|
}
|
|
@@ -1711,6 +1721,8 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
1711
1721
|
}
|
|
1712
1722
|
|
|
1713
1723
|
func setThemeJson(_ themeJson: String?) {
|
|
1724
|
+
guard lastThemeJSON != themeJson else { return }
|
|
1725
|
+
lastThemeJSON = themeJson
|
|
1714
1726
|
let theme = EditorTheme.from(json: themeJson)
|
|
1715
1727
|
richTextView.applyTheme(theme)
|
|
1716
1728
|
accessoryToolbar.apply(theme: theme?.toolbar)
|
|
@@ -1724,12 +1736,16 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
1724
1736
|
}
|
|
1725
1737
|
|
|
1726
1738
|
func setAddonsJson(_ addonsJson: String?) {
|
|
1739
|
+
guard lastAddonsJSON != addonsJson else { return }
|
|
1740
|
+
lastAddonsJSON = addonsJson
|
|
1727
1741
|
addons = NativeEditorAddons.from(json: addonsJson)
|
|
1728
1742
|
accessoryToolbar.apply(mentionTheme: richTextView.textView.theme?.mentions ?? addons.mentions?.theme)
|
|
1729
1743
|
refreshMentionQuery()
|
|
1730
1744
|
}
|
|
1731
1745
|
|
|
1732
1746
|
func setRemoteSelectionsJson(_ remoteSelectionsJson: String?) {
|
|
1747
|
+
guard lastRemoteSelectionsJSON != remoteSelectionsJson else { return }
|
|
1748
|
+
lastRemoteSelectionsJSON = remoteSelectionsJson
|
|
1733
1749
|
richTextView.setRemoteSelections(RemoteSelectionDecoration.from(json: remoteSelectionsJson))
|
|
1734
1750
|
}
|
|
1735
1751
|
|
|
@@ -1758,6 +1774,9 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
1758
1774
|
let nextBehavior = EditorHeightBehavior(rawValue: rawHeightBehavior) ?? .fixed
|
|
1759
1775
|
guard nextBehavior != heightBehavior else { return }
|
|
1760
1776
|
heightBehavior = nextBehavior
|
|
1777
|
+
if nextBehavior != .autoGrow {
|
|
1778
|
+
cachedAutoGrowContentHeight = 0
|
|
1779
|
+
}
|
|
1761
1780
|
richTextView.heightBehavior = nextBehavior
|
|
1762
1781
|
invalidateIntrinsicContentSize()
|
|
1763
1782
|
setNeedsLayout()
|
|
@@ -1770,22 +1789,29 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
1770
1789
|
richTextView.allowImageResizing = allowImageResizing
|
|
1771
1790
|
}
|
|
1772
1791
|
|
|
1773
|
-
private func emitContentHeightIfNeeded(force: Bool = false) {
|
|
1792
|
+
private func emitContentHeightIfNeeded(force: Bool = false, measuredHeight: CGFloat? = nil) {
|
|
1774
1793
|
guard heightBehavior == .autoGrow else { return }
|
|
1775
|
-
let
|
|
1794
|
+
let resolvedHeight = measuredHeight
|
|
1795
|
+
?? (cachedAutoGrowContentHeight > 0 ? cachedAutoGrowContentHeight : richTextView.intrinsicContentSize.height)
|
|
1796
|
+
let contentHeight = ceil(resolvedHeight)
|
|
1776
1797
|
guard contentHeight > 0 else { return }
|
|
1777
1798
|
guard force || abs(contentHeight - lastEmittedContentHeight) > 0.5 else { return }
|
|
1799
|
+
cachedAutoGrowContentHeight = contentHeight
|
|
1778
1800
|
lastEmittedContentHeight = contentHeight
|
|
1779
1801
|
onContentHeightChange(["contentHeight": contentHeight])
|
|
1780
1802
|
}
|
|
1781
1803
|
|
|
1782
1804
|
func setToolbarButtonsJson(_ toolbarButtonsJson: String?) {
|
|
1805
|
+
guard lastToolbarItemsJSON != toolbarButtonsJson else { return }
|
|
1806
|
+
lastToolbarItemsJSON = toolbarButtonsJson
|
|
1783
1807
|
toolbarItems = NativeToolbarItem.from(json: toolbarButtonsJson)
|
|
1784
1808
|
accessoryToolbar.setItems(toolbarItems)
|
|
1785
1809
|
refreshSystemAssistantToolbarIfNeeded()
|
|
1786
1810
|
}
|
|
1787
1811
|
|
|
1788
1812
|
func setToolbarFrameJson(_ toolbarFrameJson: String?) {
|
|
1813
|
+
guard lastToolbarFrameJSON != toolbarFrameJson else { return }
|
|
1814
|
+
lastToolbarFrameJSON = toolbarFrameJson
|
|
1789
1815
|
guard let toolbarFrameJson,
|
|
1790
1816
|
let data = toolbarFrameJson.data(using: .utf8),
|
|
1791
1817
|
let raw = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
|
|
@@ -1917,11 +1943,15 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
1917
1943
|
// MARK: - EditorTextViewDelegate
|
|
1918
1944
|
|
|
1919
1945
|
func editorTextView(_ textView: EditorTextView, selectionDidChange anchor: UInt32, head: UInt32) {
|
|
1920
|
-
refreshToolbarStateFromEditorSelection()
|
|
1946
|
+
let stateJSON = refreshToolbarStateFromEditorSelection()
|
|
1921
1947
|
refreshSystemAssistantToolbarIfNeeded()
|
|
1922
1948
|
refreshMentionQuery()
|
|
1923
1949
|
richTextView.refreshRemoteSelections()
|
|
1924
|
-
|
|
1950
|
+
var event: [String: Any] = ["anchor": Int(anchor), "head": Int(head)]
|
|
1951
|
+
if let stateJSON {
|
|
1952
|
+
event["stateJson"] = stateJSON
|
|
1953
|
+
}
|
|
1954
|
+
onSelectionChange(event)
|
|
1925
1955
|
}
|
|
1926
1956
|
|
|
1927
1957
|
func editorTextView(_ textView: EditorTextView, didReceiveUpdate updateJSON: String) {
|
|
@@ -1936,12 +1966,14 @@ class NativeEditorExpoView: ExpoView, EditorTextViewDelegate, UIGestureRecognize
|
|
|
1936
1966
|
onEditorUpdate(["updateJson": updateJSON])
|
|
1937
1967
|
}
|
|
1938
1968
|
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1969
|
+
@discardableResult
|
|
1970
|
+
private func refreshToolbarStateFromEditorSelection() -> String? {
|
|
1971
|
+
guard richTextView.editorId != 0 else { return nil }
|
|
1972
|
+
let stateJSON = editorGetSelectionState(id: richTextView.editorId)
|
|
1973
|
+
guard let state = NativeToolbarState(updateJSON: stateJSON) else { return nil }
|
|
1943
1974
|
toolbarState = state
|
|
1944
1975
|
accessoryToolbar.apply(state: state)
|
|
1976
|
+
return stateJSON
|
|
1945
1977
|
}
|
|
1946
1978
|
|
|
1947
1979
|
private func configureAccessoryToolbar() {
|
|
@@ -58,6 +58,9 @@ public class NativeEditorModule: Module {
|
|
|
58
58
|
Function("editorGetJson") { (id: Int) -> String in
|
|
59
59
|
editorGetJson(id: UInt64(id))
|
|
60
60
|
}
|
|
61
|
+
Function("editorGetContentSnapshot") { (id: Int) -> String in
|
|
62
|
+
editorGetContentSnapshot(id: UInt64(id))
|
|
63
|
+
}
|
|
61
64
|
Function("editorInsertText") { (id: Int, pos: Int, text: String) -> String in
|
|
62
65
|
editorInsertText(id: UInt64(id), pos: UInt32(pos), text: text)
|
|
63
66
|
}
|
|
@@ -234,6 +237,9 @@ public class NativeEditorModule: Module {
|
|
|
234
237
|
Function("editorGetSelection") { (id: Int) -> String in
|
|
235
238
|
editorGetSelection(id: UInt64(id))
|
|
236
239
|
}
|
|
240
|
+
Function("editorGetSelectionState") { (id: Int) -> String in
|
|
241
|
+
editorGetSelectionState(id: UInt64(id))
|
|
242
|
+
}
|
|
237
243
|
Function("editorDocToScalar") { (id: Int, docPos: Int) -> Int in
|
|
238
244
|
Int(editorDocToScalar(id: UInt64(id), docPos: UInt32(docPos)))
|
|
239
245
|
}
|