react-native-enriched-markdown-scaffold 1.0.2-scaffold.3 → 1.0.2-scaffold.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.
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +15 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +3 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +15 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +3 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +122 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +100 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +20 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +124 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +145 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +19 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +22 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +85 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +22 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/LinkLayoutEvent.kt +36 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/SelectionChangeEvent.kt +42 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/LinkRenderer.kt +37 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +5 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/CodeBackgroundSpan.kt +13 -99
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/InlineChipPainter.kt +140 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkChipSpans.kt +177 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/spans/LinkSpan.kt +1 -6
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkStyle.kt +36 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/styles/LinkVariantEntry.kt +7 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +28 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/HTMLGenerator.kt +2 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/conversion/MarkdownExtractor.kt +6 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkLayoutCollector.kt +100 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +4 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionChangeReporter.kt +230 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +11 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +22 -1
- package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +8 -1
- package/docs/API_REFERENCE.md +111 -0
- package/docs/MENTIONS.md +8 -1
- package/docs/STYLES.md +27 -0
- package/docs/TEXT.md +2 -0
- package/ios/EnrichedMarkdown.mm +221 -1
- package/ios/EnrichedMarkdownText.mm +142 -1
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +122 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +100 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +20 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +124 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +50 -2
- package/ios/internals/MeasurementCache.h +5 -0
- package/ios/renderer/LinkRenderer.m +47 -15
- package/ios/styles/StyleConfig.h +16 -0
- package/ios/styles/StyleConfig.mm +60 -0
- package/ios/utils/CodeBackground.m +3 -65
- package/ios/utils/ENRMChipDrawing.h +24 -0
- package/ios/utils/ENRMChipDrawing.m +83 -0
- package/ios/utils/ENRMLinkChip.h +70 -0
- package/ios/utils/ENRMLinkChip.m +194 -0
- package/ios/utils/ENRMLinkLayout.h +50 -0
- package/ios/utils/ENRMLinkLayout.m +45 -0
- package/ios/utils/ENRMSelectionChange.h +96 -0
- package/ios/utils/ENRMSelectionChange.m +204 -0
- package/ios/utils/EditMenuUtils+macOS.m +4 -1
- package/ios/utils/EditMenuUtils.m +4 -1
- package/ios/utils/HTMLGenerator.m +2 -1
- package/ios/utils/MarkdownAccessibilityElementBuilder.m +3 -1
- package/ios/utils/MarkdownExtractor.m +2 -1
- package/ios/utils/RTFExportUtils.m +2 -0
- package/ios/utils/StylePropsUtils.h +37 -1
- package/ios/utils/TextViewLayoutManager.mm +3 -0
- package/ios/views/ENRMCodeBlockContainerView.h +3 -0
- package/ios/views/ENRMCodeBlockContainerView.m +3 -0
- package/ios/views/ENRMMathContainerView.h +3 -0
- package/ios/views/ENRMMathContainerView.m +3 -0
- package/ios/views/TableContainerView.h +6 -0
- package/ios/views/TableContainerView.m +24 -0
- package/lib/module/EnrichedMarkdownNativeComponent.ts +101 -1
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +101 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/jest/index.js +13 -0
- package/lib/module/jest/index.js.map +1 -1
- package/lib/module/native/EnrichedMarkdownText.js +138 -9
- package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.js +13 -2
- package/lib/module/normalizeMarkdownStyle.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.web.js +13 -2
- package/lib/module/normalizeMarkdownStyle.web.js.map +1 -1
- package/lib/module/web/EnrichedMarkdownText.js +6 -3
- package/lib/module/web/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/web/renderers/InlineRenderers.js +96 -3
- package/lib/module/web/renderers/InlineRenderers.js.map +1 -1
- package/lib/module/web/styles.js +18 -0
- package/lib/module/web/styles.js.map +1 -1
- package/lib/module/web/utils.js +13 -0
- package/lib/module/web/utils.js.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +93 -2
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +93 -2
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/index.web.d.ts +1 -1
- package/lib/typescript/src/index.web.d.ts.map +1 -1
- package/lib/typescript/src/jest/index.d.ts +1 -1
- package/lib/typescript/src/jest/index.d.ts.map +1 -1
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +5 -5
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/normalizeMarkdownStyle.web.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyle.d.ts +28 -5
- package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +10 -0
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts +76 -2
- package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.web.d.ts +41 -1
- package/lib/typescript/src/types/MarkdownTextProps.web.d.ts.map +1 -1
- package/lib/typescript/src/types/events.d.ts +36 -0
- package/lib/typescript/src/types/events.d.ts.map +1 -1
- package/lib/typescript/src/web/EnrichedMarkdownText.d.ts +1 -1
- package/lib/typescript/src/web/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/web/renderers/InlineRenderers.d.ts.map +1 -1
- package/lib/typescript/src/web/styles.d.ts.map +1 -1
- package/lib/typescript/src/web/types.d.ts +4 -0
- package/lib/typescript/src/web/types.d.ts.map +1 -1
- package/lib/typescript/src/web/utils.d.ts +4 -0
- package/lib/typescript/src/web/utils.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/EnrichedMarkdownNativeComponent.ts +101 -1
- package/src/EnrichedMarkdownTextNativeComponent.ts +101 -1
- package/src/index.tsx +5 -0
- package/src/index.web.tsx +5 -1
- package/src/jest/index.tsx +21 -1
- package/src/native/EnrichedMarkdownText.tsx +181 -8
- package/src/normalizeMarkdownStyle.ts +13 -0
- package/src/normalizeMarkdownStyle.web.ts +11 -0
- package/src/types/MarkdownStyle.ts +29 -5
- package/src/types/MarkdownStyleInternal.ts +10 -0
- package/src/types/MarkdownTextProps.ts +85 -1
- package/src/types/MarkdownTextProps.web.ts +43 -1
- package/src/types/events.ts +38 -0
- package/src/web/EnrichedMarkdownText.tsx +10 -3
- package/src/web/renderers/InlineRenderers.tsx +111 -3
- package/src/web/styles.ts +25 -0
- package/src/web/types.ts +4 -0
- package/src/web/utils.ts +8 -0
package/ios/EnrichedMarkdown.mm
CHANGED
|
@@ -19,6 +19,8 @@
|
|
|
19
19
|
#import "ENRMMathContainerView.h"
|
|
20
20
|
#endif
|
|
21
21
|
#import "ENRMCodeBlockContainerView.h"
|
|
22
|
+
#import "ENRMLinkLayout.h"
|
|
23
|
+
#import "ENRMSelectionChange.h"
|
|
22
24
|
#import "ENRMSpoilerCapable.h"
|
|
23
25
|
#import "ENRMSpoilerOverlayView.h"
|
|
24
26
|
#import "ENRMSpoilerTapUtils.h"
|
|
@@ -73,6 +75,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
73
75
|
+ (ENRMMd4cFlags *)flagsFromProps:(const EnrichedMarkdownMd4cFlagsStruct &)props;
|
|
74
76
|
- (void)emitLinkPress:(NSString *)url;
|
|
75
77
|
- (void)emitLinkLongPress:(NSString *)url;
|
|
78
|
+
- (void)emitLinkLayoutIfNeeded;
|
|
76
79
|
- (void)emitTaskListItemPress:(NSInteger)index checked:(BOOL)checked text:(NSString *)text;
|
|
77
80
|
- (void)emitCopyPress:(NSString *)code language:(NSString *)language;
|
|
78
81
|
- (void)emitCopyRequest:(NSString *)source markdown:(NSString *)markdown text:(NSString *)text whole:(BOOL)whole;
|
|
@@ -123,6 +126,18 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
123
126
|
|
|
124
127
|
ENRMSpoilerOverlay _spoilerOverlay;
|
|
125
128
|
|
|
129
|
+
BOOL _emitLinkLayout;
|
|
130
|
+
NSArray<NSDictionary *> *_lastLinkLayout;
|
|
131
|
+
|
|
132
|
+
ENRMSelectionChangeReporter *_selectionReporter;
|
|
133
|
+
// At most one segment holds a selection; a new one clears the previous.
|
|
134
|
+
__weak ENRMPlatformTextView *_selectionOwner;
|
|
135
|
+
// A block long-pressed while contextMenuHidden is on, reported as the selection.
|
|
136
|
+
__weak RCTUIView *_activeBlockView;
|
|
137
|
+
NSString *_activeBlockSource;
|
|
138
|
+
NSString *_activeBlockMarkdown;
|
|
139
|
+
NSString *_activeBlockText;
|
|
140
|
+
|
|
126
141
|
NSLineBreakStrategy _lineBreakStrategy;
|
|
127
142
|
|
|
128
143
|
ENRMWritingDirectionMode _writingDirectionMode;
|
|
@@ -194,10 +209,117 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
194
209
|
[strongSelf renderMarkdownContent:strongSelf->_cachedMarkdown];
|
|
195
210
|
}
|
|
196
211
|
};
|
|
212
|
+
|
|
213
|
+
_selectionReporter = [[ENRMSelectionChangeReporter alloc]
|
|
214
|
+
initWithSnapshot:^NSDictionary * {
|
|
215
|
+
EnrichedMarkdown *strongSelf = weakSelf;
|
|
216
|
+
return strongSelf ? [strongSelf selectionPayload] : ENRMInactiveSelectionPayload();
|
|
217
|
+
}
|
|
218
|
+
emit:^(NSDictionary *payload) {
|
|
219
|
+
EnrichedMarkdown *strongSelf = weakSelf;
|
|
220
|
+
if (!strongSelf)
|
|
221
|
+
return;
|
|
222
|
+
auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(strongSelf->_eventEmitter);
|
|
223
|
+
if (emitter)
|
|
224
|
+
emitter->onSelectionChange(
|
|
225
|
+
ENRMSelectionChangeEventFromPayload<EnrichedMarkdownEventEmitter::OnSelectionChange>(payload));
|
|
226
|
+
}];
|
|
197
227
|
}
|
|
198
228
|
return self;
|
|
199
229
|
}
|
|
200
230
|
|
|
231
|
+
#pragma mark - Selection
|
|
232
|
+
|
|
233
|
+
/// The current selection (or active block) as an `onSelectionChange` payload.
|
|
234
|
+
/// Markdown and `whole` follow the same rules as the intercepted Copy.
|
|
235
|
+
- (NSDictionary *)selectionPayload
|
|
236
|
+
{
|
|
237
|
+
RCTUIView *blockView = _activeBlockView;
|
|
238
|
+
if (blockView != nil) {
|
|
239
|
+
if (blockView.superview != self)
|
|
240
|
+
return ENRMInactiveSelectionPayload();
|
|
241
|
+
return ENRMBlockSelectionPayload(_activeBlockSource, _activeBlockMarkdown, _activeBlockText, blockView.frame);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
#if !TARGET_OS_OSX
|
|
245
|
+
UITextView *owner = _selectionOwner;
|
|
246
|
+
if (owner == nil || ![owner isDescendantOfView:self])
|
|
247
|
+
return ENRMInactiveSelectionPayload();
|
|
248
|
+
NSDictionary *selection = ENRMTextSelectionInTextView(owner, self);
|
|
249
|
+
if (!selection)
|
|
250
|
+
return ENRMInactiveSelectionPayload();
|
|
251
|
+
|
|
252
|
+
NSString *text = selection[ENRMSelectionTextKey];
|
|
253
|
+
// Segments only see their own text, so a selection is the whole component
|
|
254
|
+
// only when it spans the sole segment; the markdown is then the original.
|
|
255
|
+
BOOL whole = [selection[ENRMSelectionFullKey] boolValue] && _segmentViews.count == 1 && _cachedMarkdown.length > 0;
|
|
256
|
+
NSString *markdown =
|
|
257
|
+
whole ? _cachedMarkdown : extractMarkdownFromAttributedString(owner.attributedText, owner.selectedRange);
|
|
258
|
+
NSMutableDictionary *payload = [selection mutableCopy];
|
|
259
|
+
[payload removeObjectForKey:ENRMSelectionFullKey];
|
|
260
|
+
payload[ENRMSelectionActiveKey] = @YES;
|
|
261
|
+
payload[ENRMSelectionSourceKey] = @"selection";
|
|
262
|
+
payload[ENRMSelectionMarkdownKey] = markdown.length > 0 ? markdown : text;
|
|
263
|
+
payload[ENRMSelectionWholeKey] = @(whole);
|
|
264
|
+
return payload;
|
|
265
|
+
#else
|
|
266
|
+
return ENRMInactiveSelectionPayload();
|
|
267
|
+
#endif
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
- (void)clearActiveBlock
|
|
271
|
+
{
|
|
272
|
+
_activeBlockView = nil;
|
|
273
|
+
_activeBlockSource = nil;
|
|
274
|
+
_activeBlockMarkdown = nil;
|
|
275
|
+
_activeBlockText = nil;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
- (void)clearSegmentSelection:(ENRMPlatformTextView *)textView
|
|
279
|
+
{
|
|
280
|
+
#if !TARGET_OS_OSX
|
|
281
|
+
textView.selectedRange = NSMakeRange(0, 0);
|
|
282
|
+
[textView resignFirstResponder];
|
|
283
|
+
#endif
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
- (void)clearSelection
|
|
287
|
+
{
|
|
288
|
+
ENRMPlatformTextView *owner = _selectionOwner;
|
|
289
|
+
_selectionOwner = nil;
|
|
290
|
+
[self clearActiveBlock];
|
|
291
|
+
if (owner)
|
|
292
|
+
[self clearSegmentSelection:owner];
|
|
293
|
+
[_selectionReporter flushNow];
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
|
|
297
|
+
{
|
|
298
|
+
RCTEnrichedMarkdownHandleCommand(self, commandName, args);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
- (ENRMBlockCopyRequestBlock)blockMenuSuppressedForSource:(NSString *)source view:(RCTUIView *)view
|
|
302
|
+
{
|
|
303
|
+
__weak EnrichedMarkdown *weakSelf = self;
|
|
304
|
+
__weak RCTUIView *weakView = view;
|
|
305
|
+
return ^(NSString *markdown, NSString *text) {
|
|
306
|
+
EnrichedMarkdown *strongSelf = weakSelf;
|
|
307
|
+
RCTUIView *strongView = weakView;
|
|
308
|
+
if (!strongSelf || !strongView || !strongSelf->_selectionReporter.enabled)
|
|
309
|
+
return;
|
|
310
|
+
ENRMPlatformTextView *owner = strongSelf->_selectionOwner;
|
|
311
|
+
strongSelf->_selectionOwner = nil;
|
|
312
|
+
if (owner)
|
|
313
|
+
[strongSelf clearSegmentSelection:owner];
|
|
314
|
+
strongSelf->_activeBlockView = strongView;
|
|
315
|
+
strongSelf->_activeBlockSource = source;
|
|
316
|
+
strongSelf->_activeBlockMarkdown = markdown;
|
|
317
|
+
strongSelf->_activeBlockText = text;
|
|
318
|
+
[strongSelf->_selectionReporter forgetLastReported];
|
|
319
|
+
[strongSelf->_selectionReporter flushNow];
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
201
323
|
- (void)configureSegmentViewRegistry
|
|
202
324
|
{
|
|
203
325
|
__weak EnrichedMarkdown *weakSelf = self;
|
|
@@ -834,6 +956,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
834
956
|
tableView.contextMenuHidden = _contextMenuHidden;
|
|
835
957
|
tableView.menuOptions = [self blockMenuOptions];
|
|
836
958
|
tableView.onCopyRequest = [self blockCopyRequestForSource:@"table"];
|
|
959
|
+
tableView.onMenuSuppressed = [self blockMenuSuppressedForSource:@"table" view:tableView];
|
|
837
960
|
|
|
838
961
|
__weak EnrichedMarkdown *weakSelf = self;
|
|
839
962
|
|
|
@@ -876,6 +999,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
876
999
|
mathView.contextMenuHidden = _contextMenuHidden;
|
|
877
1000
|
mathView.menuOptions = [self blockMenuOptions];
|
|
878
1001
|
mathView.onCopyRequest = [self blockCopyRequestForSource:@"math"];
|
|
1002
|
+
mathView.onMenuSuppressed = [self blockMenuSuppressedForSource:@"math" view:mathView];
|
|
879
1003
|
[mathView applyLatex:mathSegment.latex];
|
|
880
1004
|
return mathView;
|
|
881
1005
|
}
|
|
@@ -889,6 +1013,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
889
1013
|
codeBlockView.contextMenuHidden = _contextMenuHidden;
|
|
890
1014
|
codeBlockView.menuOptions = [self blockMenuOptions];
|
|
891
1015
|
codeBlockView.onCopyRequest = [self blockCopyRequestForSource:@"code"];
|
|
1016
|
+
codeBlockView.onMenuSuppressed = [self blockMenuSuppressedForSource:@"code" view:codeBlockView];
|
|
892
1017
|
|
|
893
1018
|
__weak EnrichedMarkdown *weakSelf = self;
|
|
894
1019
|
codeBlockView.onCopyPress = ^(NSString *code, NSString *language) {
|
|
@@ -941,6 +1066,9 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
941
1066
|
{
|
|
942
1067
|
[super layoutSubviews];
|
|
943
1068
|
[self computeSegmentLayoutForWidth:self.bounds.size.width applyFrames:YES];
|
|
1069
|
+
[self emitLinkLayoutIfNeeded];
|
|
1070
|
+
// Segments may have moved or been replaced; re-read the selection (deduplicated).
|
|
1071
|
+
[_selectionReporter schedule];
|
|
944
1072
|
}
|
|
945
1073
|
|
|
946
1074
|
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
@@ -976,6 +1104,14 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
976
1104
|
|
|
977
1105
|
_selectable = newViewProps.selectable;
|
|
978
1106
|
|
|
1107
|
+
if (newViewProps.emitLinkLayout != _emitLinkLayout) {
|
|
1108
|
+
_emitLinkLayout = newViewProps.emitLinkLayout;
|
|
1109
|
+
_lastLinkLayout = nil;
|
|
1110
|
+
[self setNeedsLayout];
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
_selectionReporter.enabled = newViewProps.emitSelectionChange;
|
|
1114
|
+
|
|
979
1115
|
for (RCTUIView *segment in _segmentViews) {
|
|
980
1116
|
if ([segment isKindOfClass:[EnrichedMarkdownInternalText class]]) {
|
|
981
1117
|
EnrichedMarkdownInternalText *textSegment = (EnrichedMarkdownInternalText *)segment;
|
|
@@ -1171,6 +1307,11 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
1171
1307
|
_cachedMarkdown = nil;
|
|
1172
1308
|
_renderedMarkdown = nil;
|
|
1173
1309
|
_config = nil;
|
|
1310
|
+
_emitLinkLayout = NO;
|
|
1311
|
+
_lastLinkLayout = nil;
|
|
1312
|
+
_selectionReporter.enabled = NO;
|
|
1313
|
+
_selectionOwner = nil;
|
|
1314
|
+
[self clearActiveBlock];
|
|
1174
1315
|
_md4cFlags = [EnrichedMarkdown flagsFromProps:resetProps->md4cFlags];
|
|
1175
1316
|
_maxFontSizeMultiplier = 0;
|
|
1176
1317
|
_allowTrailingMargin = NO;
|
|
@@ -1281,6 +1422,40 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
|
|
|
1281
1422
|
emitter->onLinkPress({.url = std::string(url.UTF8String)});
|
|
1282
1423
|
}
|
|
1283
1424
|
|
|
1425
|
+
/// Reports link rects of every text segment (offset by the segment origin) to
|
|
1426
|
+
/// JS for `renderLinkDecoration`. Tables are skipped: their cells scroll
|
|
1427
|
+
/// horizontally, so rects would go stale. Runs after each segment layout pass;
|
|
1428
|
+
/// deduplicated so an unchanged layout sends nothing.
|
|
1429
|
+
- (void)emitLinkLayoutIfNeeded
|
|
1430
|
+
{
|
|
1431
|
+
if (!_emitLinkLayout || self.bounds.size.width <= 0)
|
|
1432
|
+
return;
|
|
1433
|
+
|
|
1434
|
+
auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(_eventEmitter);
|
|
1435
|
+
if (!emitter)
|
|
1436
|
+
return;
|
|
1437
|
+
|
|
1438
|
+
NSMutableArray<NSDictionary *> *fragments = [NSMutableArray array];
|
|
1439
|
+
NSInteger linkCount = 0;
|
|
1440
|
+
for (RCTUIView *segment in _segmentViews) {
|
|
1441
|
+
if ([segment isKindOfClass:[TableContainerView class]]) {
|
|
1442
|
+
linkCount += ((TableContainerView *)segment).linkCount;
|
|
1443
|
+
continue;
|
|
1444
|
+
}
|
|
1445
|
+
if (![segment isKindOfClass:[EnrichedMarkdownInternalText class]])
|
|
1446
|
+
continue;
|
|
1447
|
+
NSInteger segmentLinks = 0;
|
|
1448
|
+
[fragments addObjectsFromArray:ENRMCollectLinkLayoutFragments(((EnrichedMarkdownInternalText *)segment).textView,
|
|
1449
|
+
segment.frame.origin, linkCount, &segmentLinks)];
|
|
1450
|
+
linkCount += segmentLinks;
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
if (_lastLinkLayout != nil && [fragments isEqualToArray:_lastLinkLayout])
|
|
1454
|
+
return;
|
|
1455
|
+
_lastLinkLayout = [fragments copy];
|
|
1456
|
+
emitter->onLinkLayout(ENRMLinkLayoutEventFromFragments<EnrichedMarkdownEventEmitter::OnLinkLayout>(fragments));
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1284
1459
|
- (void)emitLinkLongPress:(NSString *)url
|
|
1285
1460
|
{
|
|
1286
1461
|
auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(_eventEmitter);
|
|
@@ -1358,14 +1533,33 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
|
|
|
1358
1533
|
ENRMHandleTapOnTextView(textView, recognizer, ^(NSString *url) { [self emitLinkPress:url]; });
|
|
1359
1534
|
}
|
|
1360
1535
|
|
|
1536
|
+
#if !TARGET_OS_OSX
|
|
1537
|
+
- (void)textViewDidChangeSelection:(UITextView *)textView
|
|
1538
|
+
{
|
|
1539
|
+
if (textView.selectedRange.length > 0) {
|
|
1540
|
+
// Take ownership first so the previous owner's own "cleared" callback is ignored.
|
|
1541
|
+
ENRMPlatformTextView *previous = _selectionOwner;
|
|
1542
|
+
_selectionOwner = textView;
|
|
1543
|
+
[self clearActiveBlock];
|
|
1544
|
+
if (previous && previous != textView)
|
|
1545
|
+
previous.selectedRange = NSMakeRange(0, 0);
|
|
1546
|
+
[_selectionReporter schedule];
|
|
1547
|
+
} else if (textView == _selectionOwner) {
|
|
1548
|
+
[_selectionReporter selectionChanged:NO];
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
#endif
|
|
1552
|
+
|
|
1361
1553
|
// TODO: Remove API_AVAILABLE(ios(16.0)) guard when the minimum iOS deployment target in RN is bumped to 16.
|
|
1362
1554
|
#if !TARGET_OS_OSX
|
|
1363
1555
|
- (UIMenu *)textView:(UITextView *)textView
|
|
1364
1556
|
editMenuForTextInRange:(NSRange)range
|
|
1365
1557
|
suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0))
|
|
1366
1558
|
{
|
|
1559
|
+
// An empty menu presents nothing; nil would make UIKit show its default menu
|
|
1560
|
+
// (the selection and handles stay either way).
|
|
1367
1561
|
if (_contextMenuHidden) {
|
|
1368
|
-
return
|
|
1562
|
+
return [UIMenu menuWithChildren:@[]];
|
|
1369
1563
|
}
|
|
1370
1564
|
|
|
1371
1565
|
__weak EnrichedMarkdown *weakSelf = self;
|
|
@@ -1400,6 +1594,32 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
|
|
|
1400
1594
|
customActions, _selectionMenuConfig, onCopyRequest);
|
|
1401
1595
|
}
|
|
1402
1596
|
|
|
1597
|
+
// Attachments other than images (inline math) are text, not items: without
|
|
1598
|
+
// this a long press on one in an unfocused text view starts UIKit's text item
|
|
1599
|
+
// interaction, which presents nothing and leaves the view unable to select.
|
|
1600
|
+
- (BOOL)textView:(UITextView *)textView
|
|
1601
|
+
shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment
|
|
1602
|
+
inRange:(NSRange)characterRange
|
|
1603
|
+
interaction:(UITextItemInteraction)interaction
|
|
1604
|
+
{
|
|
1605
|
+
if ([textAttachment isKindOfClass:[ENRMImageAttachment class]]) {
|
|
1606
|
+
return YES;
|
|
1607
|
+
}
|
|
1608
|
+
// A long press selects the attachment like a word, as it does once the view is focused.
|
|
1609
|
+
if (interaction == UITextItemInteractionPresentActions) {
|
|
1610
|
+
__weak UITextView *weakTextView = textView;
|
|
1611
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1612
|
+
UITextView *strongTextView = weakTextView;
|
|
1613
|
+
if (!strongTextView || NSMaxRange(characterRange) > strongTextView.attributedText.length) {
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1616
|
+
[strongTextView becomeFirstResponder];
|
|
1617
|
+
strongTextView.selectedRange = characterRange;
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1620
|
+
return NO;
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1403
1623
|
- (BOOL)textView:(UITextView *)textView
|
|
1404
1624
|
shouldInteractWithURL:(NSURL *)URL
|
|
1405
1625
|
inRange:(NSRange)characterRange
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
#import "ENRMAtomicSize.h"
|
|
7
7
|
#import "ENRMContextMenuTextView+macOS.h"
|
|
8
8
|
#import "ENRMImageAttachment.h"
|
|
9
|
+
#import "ENRMLinkLayout.h"
|
|
9
10
|
#import "ENRMMarkdownParser.h"
|
|
11
|
+
#import "ENRMSelectionChange.h"
|
|
10
12
|
#import "ENRMSpoilerOverlayManager.h"
|
|
11
13
|
#import "ENRMSpoilerTapUtils.h"
|
|
12
14
|
#import "ENRMTailFadeInAnimator.h"
|
|
@@ -25,6 +27,7 @@
|
|
|
25
27
|
#import "MarkdownExtractor.h"
|
|
26
28
|
#import "MeasurementCache.h"
|
|
27
29
|
#import "ParagraphStyleUtils.h"
|
|
30
|
+
#import "PasteboardUtils.h"
|
|
28
31
|
#import "RuntimeKeys.h"
|
|
29
32
|
#import "SelectionColorUtils.h"
|
|
30
33
|
#import "StylePropsUtils.h"
|
|
@@ -56,6 +59,7 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
56
59
|
- (void)setupLayoutManager;
|
|
57
60
|
- (void)emitLinkPress:(NSString *)url;
|
|
58
61
|
- (void)emitLinkLongPress:(NSString *)url;
|
|
62
|
+
- (void)emitLinkLayoutIfNeeded;
|
|
59
63
|
- (void)emitTaskListItemPress:(NSInteger)index checked:(BOOL)checked text:(NSString *)text;
|
|
60
64
|
- (void)emitContextMenuItemPress:(NSString *)itemText
|
|
61
65
|
selectedText:(NSString *)selectedText
|
|
@@ -110,6 +114,11 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
110
114
|
|
|
111
115
|
ENRMSpoilerOverlayManager *_spoilerManager;
|
|
112
116
|
|
|
117
|
+
BOOL _emitLinkLayout;
|
|
118
|
+
NSArray<NSDictionary *> *_lastLinkLayout;
|
|
119
|
+
|
|
120
|
+
ENRMSelectionChangeReporter *_selectionReporter;
|
|
121
|
+
|
|
113
122
|
NSLineBreakStrategy _lineBreakStrategy;
|
|
114
123
|
|
|
115
124
|
ENRMWritingDirectionMode _writingDirectionMode;
|
|
@@ -183,6 +192,9 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
183
192
|
[super updateLayoutMetrics:layoutMetrics oldLayoutMetrics:oldLayoutMetrics];
|
|
184
193
|
|
|
185
194
|
_lastCommittedSize.store(CGSizeMake(layoutMetrics.frame.size.width, layoutMetrics.frame.size.height));
|
|
195
|
+
#if TARGET_OS_OSX
|
|
196
|
+
[self emitLinkLayoutIfNeeded];
|
|
197
|
+
#endif
|
|
186
198
|
|
|
187
199
|
NSWritingDirection resolved = _resolvedLayoutDirection;
|
|
188
200
|
if (layoutMetrics.layoutDirection == LayoutDirection::RightToLeft) {
|
|
@@ -218,6 +230,7 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
218
230
|
if (needsHeightUpdate(measured, self.bounds)) {
|
|
219
231
|
[self requestHeightUpdate];
|
|
220
232
|
}
|
|
233
|
+
[self emitLinkLayoutIfNeeded];
|
|
221
234
|
}
|
|
222
235
|
}
|
|
223
236
|
|
|
@@ -263,11 +276,68 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
263
276
|
};
|
|
264
277
|
|
|
265
278
|
[self setupTextView];
|
|
279
|
+
[self setupSelectionReporter];
|
|
266
280
|
}
|
|
267
281
|
|
|
268
282
|
return self;
|
|
269
283
|
}
|
|
270
284
|
|
|
285
|
+
- (void)setupSelectionReporter
|
|
286
|
+
{
|
|
287
|
+
__weak EnrichedMarkdownText *weakSelf = self;
|
|
288
|
+
_selectionReporter = [[ENRMSelectionChangeReporter alloc]
|
|
289
|
+
initWithSnapshot:^NSDictionary * {
|
|
290
|
+
EnrichedMarkdownText *strongSelf = weakSelf;
|
|
291
|
+
return strongSelf ? [strongSelf selectionPayload] : ENRMInactiveSelectionPayload();
|
|
292
|
+
}
|
|
293
|
+
emit:^(NSDictionary *payload) {
|
|
294
|
+
EnrichedMarkdownText *strongSelf = weakSelf;
|
|
295
|
+
if (!strongSelf)
|
|
296
|
+
return;
|
|
297
|
+
auto emitter = std::static_pointer_cast<EnrichedMarkdownTextEventEmitter const>(strongSelf->_eventEmitter);
|
|
298
|
+
if (emitter)
|
|
299
|
+
emitter->onSelectionChange(
|
|
300
|
+
ENRMSelectionChangeEventFromPayload<EnrichedMarkdownTextEventEmitter::OnSelectionChange>(payload));
|
|
301
|
+
}];
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
/// The current selection as an `onSelectionChange` payload. Markdown and
|
|
305
|
+
/// `whole` follow the same rules as the intercepted Copy (`onCopyRequest`).
|
|
306
|
+
- (NSDictionary *)selectionPayload
|
|
307
|
+
{
|
|
308
|
+
#if !TARGET_OS_OSX
|
|
309
|
+
NSDictionary *selection = ENRMTextSelectionInTextView(_textView, self);
|
|
310
|
+
if (!selection)
|
|
311
|
+
return ENRMInactiveSelectionPayload();
|
|
312
|
+
|
|
313
|
+
NSString *text = selection[ENRMSelectionTextKey];
|
|
314
|
+
NSString *markdown = markdownForRange(_textView.attributedText, _textView.selectedRange, _cachedMarkdown);
|
|
315
|
+
NSMutableDictionary *payload = [selection mutableCopy];
|
|
316
|
+
[payload removeObjectForKey:ENRMSelectionFullKey];
|
|
317
|
+
payload[ENRMSelectionActiveKey] = @YES;
|
|
318
|
+
payload[ENRMSelectionSourceKey] = @"selection";
|
|
319
|
+
payload[ENRMSelectionMarkdownKey] = markdown.length > 0 ? markdown : text;
|
|
320
|
+
payload[ENRMSelectionWholeKey] = @([selection[ENRMSelectionFullKey] boolValue] && _cachedMarkdown.length > 0);
|
|
321
|
+
return payload;
|
|
322
|
+
#else
|
|
323
|
+
return ENRMInactiveSelectionPayload();
|
|
324
|
+
#endif
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
- (void)clearSelection
|
|
328
|
+
{
|
|
329
|
+
#if !TARGET_OS_OSX
|
|
330
|
+
_textView.selectedRange = NSMakeRange(0, 0);
|
|
331
|
+
[_textView resignFirstResponder];
|
|
332
|
+
#endif
|
|
333
|
+
[_selectionReporter flushNow];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
|
|
337
|
+
{
|
|
338
|
+
RCTEnrichedMarkdownTextHandleCommand(self, commandName, args);
|
|
339
|
+
}
|
|
340
|
+
|
|
271
341
|
- (void)setupTextView
|
|
272
342
|
{
|
|
273
343
|
#if !TARGET_OS_OSX
|
|
@@ -464,8 +534,13 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
464
534
|
if (forceHeightUpdate || needsHeightUpdate(measured, self.bounds)) {
|
|
465
535
|
[self requestHeightUpdate];
|
|
466
536
|
}
|
|
537
|
+
|
|
538
|
+
[self emitLinkLayoutIfNeeded];
|
|
467
539
|
}
|
|
468
540
|
|
|
541
|
+
// New text drops the selection; setting attributedText doesn't call the delegate.
|
|
542
|
+
[_selectionReporter schedule];
|
|
543
|
+
|
|
469
544
|
if (_textView.hidden) {
|
|
470
545
|
dispatch_async(dispatch_get_main_queue(), ^{ self->_textView.hidden = NO; });
|
|
471
546
|
}
|
|
@@ -484,6 +559,7 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
484
559
|
{
|
|
485
560
|
[super layoutSubviews];
|
|
486
561
|
[_spoilerManager updateIfNeeded];
|
|
562
|
+
[self emitLinkLayoutIfNeeded];
|
|
487
563
|
}
|
|
488
564
|
#endif
|
|
489
565
|
|
|
@@ -521,6 +597,14 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
521
597
|
}
|
|
522
598
|
}
|
|
523
599
|
|
|
600
|
+
if (newViewProps.emitLinkLayout != _emitLinkLayout) {
|
|
601
|
+
_emitLinkLayout = newViewProps.emitLinkLayout;
|
|
602
|
+
_lastLinkLayout = nil;
|
|
603
|
+
[self emitLinkLayoutIfNeeded];
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
_selectionReporter.enabled = newViewProps.emitSelectionChange;
|
|
607
|
+
|
|
524
608
|
if (_textView.selectable != newViewProps.selectable) {
|
|
525
609
|
_textView.selectable = newViewProps.selectable;
|
|
526
610
|
}
|
|
@@ -697,6 +781,9 @@ typedef NS_OPTIONS(NSUInteger, ENRMDirtyFlags) {
|
|
|
697
781
|
_accessibilityNeedsRebuild = NO;
|
|
698
782
|
[_spoilerManager removeAllOverlays];
|
|
699
783
|
_spoilerManager = nil;
|
|
784
|
+
_emitLinkLayout = NO;
|
|
785
|
+
_lastLinkLayout = nil;
|
|
786
|
+
_selectionReporter.enabled = NO;
|
|
700
787
|
if (_textView != nil) {
|
|
701
788
|
ENRMSetAttributedText(_textView, [[NSAttributedString alloc] initWithString:@""]);
|
|
702
789
|
_textView.hidden = YES;
|
|
@@ -742,6 +829,25 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownTextCls(void)
|
|
|
742
829
|
emitter->onLinkLongPress({.url = std::string(url.UTF8String)});
|
|
743
830
|
}
|
|
744
831
|
|
|
832
|
+
/// Reports link rects to JS (for `renderLinkDecoration`). Called after every
|
|
833
|
+
/// render and layout change; deduplicated so an unchanged layout — the common
|
|
834
|
+
/// case during streaming — sends nothing.
|
|
835
|
+
- (void)emitLinkLayoutIfNeeded
|
|
836
|
+
{
|
|
837
|
+
if (!_emitLinkLayout || _renderedMarkdown == nil || self.bounds.size.width <= 0)
|
|
838
|
+
return;
|
|
839
|
+
|
|
840
|
+
auto emitter = std::static_pointer_cast<EnrichedMarkdownTextEventEmitter const>(_eventEmitter);
|
|
841
|
+
if (!emitter)
|
|
842
|
+
return;
|
|
843
|
+
|
|
844
|
+
NSArray<NSDictionary *> *fragments = ENRMCollectLinkLayoutFragments(_textView, CGPointZero, 0, NULL);
|
|
845
|
+
if (_lastLinkLayout != nil && [fragments isEqualToArray:_lastLinkLayout])
|
|
846
|
+
return;
|
|
847
|
+
_lastLinkLayout = fragments;
|
|
848
|
+
emitter->onLinkLayout(ENRMLinkLayoutEventFromFragments<EnrichedMarkdownTextEventEmitter::OnLinkLayout>(fragments));
|
|
849
|
+
}
|
|
850
|
+
|
|
745
851
|
- (void)emitTaskListItemPress:(NSInteger)index checked:(BOOL)checked text:(NSString *)text
|
|
746
852
|
{
|
|
747
853
|
auto emitter = std::static_pointer_cast<EnrichedMarkdownTextEventEmitter const>(_eventEmitter);
|
|
@@ -800,6 +906,32 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownTextCls(void)
|
|
|
800
906
|
#pragma mark - UITextViewDelegate (Link Interaction)
|
|
801
907
|
|
|
802
908
|
#if !TARGET_OS_OSX
|
|
909
|
+
// Attachments other than images (inline math) are text, not items: without
|
|
910
|
+
// this a long press on one in an unfocused text view starts UIKit's text item
|
|
911
|
+
// interaction, which presents nothing and leaves the view unable to select.
|
|
912
|
+
- (BOOL)textView:(UITextView *)textView
|
|
913
|
+
shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment
|
|
914
|
+
inRange:(NSRange)characterRange
|
|
915
|
+
interaction:(UITextItemInteraction)interaction
|
|
916
|
+
{
|
|
917
|
+
if ([textAttachment isKindOfClass:[ENRMImageAttachment class]]) {
|
|
918
|
+
return YES;
|
|
919
|
+
}
|
|
920
|
+
// A long press selects the attachment like a word, as it does once the view is focused.
|
|
921
|
+
if (interaction == UITextItemInteractionPresentActions) {
|
|
922
|
+
__weak UITextView *weakTextView = textView;
|
|
923
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
924
|
+
UITextView *strongTextView = weakTextView;
|
|
925
|
+
if (!strongTextView || NSMaxRange(characterRange) > strongTextView.attributedText.length) {
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
[strongTextView becomeFirstResponder];
|
|
929
|
+
strongTextView.selectedRange = characterRange;
|
|
930
|
+
});
|
|
931
|
+
}
|
|
932
|
+
return NO;
|
|
933
|
+
}
|
|
934
|
+
|
|
803
935
|
- (BOOL)textView:(ENRMPlatformTextView *)textView
|
|
804
936
|
shouldInteractWithURL:(NSURL *)URL
|
|
805
937
|
inRange:(NSRange)characterRange
|
|
@@ -819,6 +951,13 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownTextCls(void)
|
|
|
819
951
|
return NO;
|
|
820
952
|
}
|
|
821
953
|
|
|
954
|
+
#pragma mark - UITextViewDelegate (Selection)
|
|
955
|
+
|
|
956
|
+
- (void)textViewDidChangeSelection:(UITextView *)textView
|
|
957
|
+
{
|
|
958
|
+
[_selectionReporter selectionChanged:textView.selectedRange.length > 0];
|
|
959
|
+
}
|
|
960
|
+
|
|
822
961
|
#pragma mark - UITextViewDelegate (Edit Menu)
|
|
823
962
|
|
|
824
963
|
// TODO: Remove API_AVAILABLE(ios(16.0)) guard when the minimum iOS deployment target in RN is bumped to 16.
|
|
@@ -826,8 +965,10 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownTextCls(void)
|
|
|
826
965
|
editMenuForTextInRange:(NSRange)range
|
|
827
966
|
suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0))
|
|
828
967
|
{
|
|
968
|
+
// An empty menu presents nothing; nil would make UIKit show its default menu
|
|
969
|
+
// (the selection and handles stay either way).
|
|
829
970
|
if (_contextMenuHidden) {
|
|
830
|
-
return
|
|
971
|
+
return [UIMenu menuWithChildren:@[]];
|
|
831
972
|
}
|
|
832
973
|
|
|
833
974
|
__weak EnrichedMarkdownText *weakSelf = self;
|