react-native-enriched-markdown-scaffold 1.0.2-scaffold.1 → 1.0.2-scaffold.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/android/build.gradle +2 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +18 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +4 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +18 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +4 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +148 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +120 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +30 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +210 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +176 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +45 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +35 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +112 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +34 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/MeasurementStore.kt +46 -8
- package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyRequestEvent.kt +28 -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 +43 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +25 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/input/editing/InputConnectionWrapper.kt +7 -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/InlineMathStyle.kt +16 -1
- 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/styles/MathStyle.kt +3 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +51 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/input/PasteTextUtils.kt +34 -2
- 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/HeadlessActionMode.kt +88 -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 +53 -14
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionChangeReporter.kt +232 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/views/BlockMenuOptions.kt +64 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/views/CodeBlockContainerView.kt +29 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +30 -7
- package/android/src/math/java/com/swmansion/enriched/markdown/renderer/MathInlineRenderer.kt +1 -0
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathInlineSpan.kt +13 -6
- package/android/src/math/java/com/swmansion/enriched/markdown/spans/MathMeasureHelper.kt +2 -1
- package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +25 -9
- package/docs/API_REFERENCE.md +183 -4
- package/docs/COPY_OPTIONS.md +40 -0
- package/docs/INPUT.md +3 -1
- package/docs/MENTIONS.md +8 -1
- package/docs/STYLES.md +27 -0
- package/docs/TEXT.md +2 -0
- package/docs/WEB.md +1 -0
- package/ios/EnrichedMarkdown.mm +321 -9
- package/ios/EnrichedMarkdownText.mm +203 -6
- package/ios/attachments/ENRMMathInlineAttachment+macOS.m +2 -1
- package/ios/attachments/ENRMMathInlineAttachment.h +3 -0
- package/ios/attachments/ENRMMathInlineAttachment.m +2 -1
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +148 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +120 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +30 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +210 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +50 -2
- package/ios/internals/MeasurementCache.h +8 -2
- package/ios/math/ENRMRaTeXBridge.swift +23 -5
- package/ios/renderer/ENRMMathInlineRenderer.m +1 -0
- package/ios/renderer/LinkRenderer.m +47 -15
- package/ios/styles/StyleConfig.h +20 -0
- package/ios/styles/StyleConfig.mm +84 -0
- package/ios/utils/CodeBackground.m +3 -65
- package/ios/utils/ENRMBlockMenu.h +66 -0
- 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 +213 -0
- package/ios/utils/ENRMLinkLayout.h +50 -0
- package/ios/utils/ENRMLinkLayout.m +45 -0
- package/ios/utils/ENRMSelectionChange.h +120 -0
- package/ios/utils/ENRMSelectionChange.m +282 -0
- package/ios/utils/EditMenuUtils+macOS.m +4 -1
- package/ios/utils/EditMenuUtils.h +35 -6
- package/ios/utils/EditMenuUtils.m +171 -15
- 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 +47 -1
- package/ios/utils/TextViewLayoutManager.mm +3 -0
- package/ios/views/ENRMCodeBlockContainerView.h +16 -2
- package/ios/views/ENRMCodeBlockContainerView.m +22 -19
- package/ios/views/ENRMMathContainerView.h +11 -0
- package/ios/views/ENRMMathContainerView.m +17 -20
- package/ios/views/TableContainerView.h +14 -0
- package/ios/views/TableContainerView.m +34 -18
- package/lib/module/EnrichedMarkdownNativeComponent.ts +138 -1
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +138 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/jest/index.js +14 -0
- package/lib/module/jest/index.js.map +1 -1
- package/lib/module/native/EnrichedMarkdownText.js +169 -11
- package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.js +17 -4
- package/lib/module/normalizeMarkdownStyle.js.map +1 -1
- package/lib/module/normalizeMarkdownStyle.web.js +17 -4
- 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 +123 -2
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +123 -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 +50 -5
- package/lib/typescript/src/types/MarkdownStyle.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts +12 -0
- package/lib/typescript/src/types/MarkdownStyleInternal.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts +144 -11
- 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 +66 -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 +138 -1
- package/src/EnrichedMarkdownTextNativeComponent.ts +138 -1
- package/src/index.tsx +6 -0
- package/src/index.web.tsx +5 -1
- package/src/jest/index.tsx +22 -1
- package/src/native/EnrichedMarkdownText.tsx +220 -9
- package/src/normalizeMarkdownStyle.ts +15 -0
- package/src/normalizeMarkdownStyle.web.ts +13 -0
- package/src/types/MarkdownStyle.ts +51 -5
- package/src/types/MarkdownStyleInternal.ts +12 -0
- package/src/types/MarkdownTextProps.ts +151 -11
- package/src/types/MarkdownTextProps.web.ts +43 -1
- package/src/types/events.ts +69 -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,8 +75,10 @@ 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;
|
|
81
|
+
- (void)emitCopyRequest:(NSString *)source markdown:(NSString *)markdown text:(NSString *)text whole:(BOOL)whole;
|
|
78
82
|
- (void)emitContextMenuItemPress:(NSString *)itemText
|
|
79
83
|
selectedText:(NSString *)selectedText
|
|
80
84
|
selectionStart:(NSUInteger)selectionStart
|
|
@@ -103,6 +107,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
103
107
|
BOOL _allowTrailingMargin;
|
|
104
108
|
BOOL _selectable;
|
|
105
109
|
BOOL _enableLinkPreview;
|
|
110
|
+
BOOL _linkLongPressSelects;
|
|
106
111
|
BOOL _enableTaskListItemToggle;
|
|
107
112
|
BOOL _contextMenuHidden;
|
|
108
113
|
BOOL _streamingAnimation;
|
|
@@ -122,6 +127,21 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
122
127
|
|
|
123
128
|
ENRMSpoilerOverlay _spoilerOverlay;
|
|
124
129
|
|
|
130
|
+
BOOL _emitLinkLayout;
|
|
131
|
+
NSArray<NSDictionary *> *_lastLinkLayout;
|
|
132
|
+
|
|
133
|
+
ENRMSelectionChangeReporter *_selectionReporter;
|
|
134
|
+
#if !TARGET_OS_OSX
|
|
135
|
+
ENRMSelectionGestureTracker *_selectionGestures;
|
|
136
|
+
#endif
|
|
137
|
+
// At most one segment holds a selection; a new one clears the previous.
|
|
138
|
+
__weak ENRMPlatformTextView *_selectionOwner;
|
|
139
|
+
// A block long-pressed while contextMenuHidden is on, reported as the selection.
|
|
140
|
+
__weak RCTUIView *_activeBlockView;
|
|
141
|
+
NSString *_activeBlockSource;
|
|
142
|
+
NSString *_activeBlockMarkdown;
|
|
143
|
+
NSString *_activeBlockText;
|
|
144
|
+
|
|
125
145
|
NSLineBreakStrategy _lineBreakStrategy;
|
|
126
146
|
|
|
127
147
|
ENRMWritingDirectionMode _writingDirectionMode;
|
|
@@ -173,7 +193,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
173
193
|
_tableStreamingMode = ENRMTableStreamingModeProgressive;
|
|
174
194
|
_codeBlockStreamingMode = ENRMCodeBlockStreamingModeProgressive;
|
|
175
195
|
_contextMenuHidden = NO;
|
|
176
|
-
_selectionMenuConfig = (
|
|
196
|
+
_selectionMenuConfig = ENRMDefaultSelectionMenuConfig();
|
|
177
197
|
_lineBreakStrategy = NSLineBreakStrategyNone;
|
|
178
198
|
_writingDirectionMode = ENRMWritingDirectionModeFirstStrong;
|
|
179
199
|
_resolvedLayoutDirection =
|
|
@@ -193,10 +213,132 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
193
213
|
[strongSelf renderMarkdownContent:strongSelf->_cachedMarkdown];
|
|
194
214
|
}
|
|
195
215
|
};
|
|
216
|
+
|
|
217
|
+
_selectionReporter = [[ENRMSelectionChangeReporter alloc]
|
|
218
|
+
initWithSnapshot:^NSDictionary * {
|
|
219
|
+
EnrichedMarkdown *strongSelf = weakSelf;
|
|
220
|
+
return strongSelf ? [strongSelf selectionPayload] : ENRMInactiveSelectionPayload();
|
|
221
|
+
}
|
|
222
|
+
emit:^(NSDictionary *payload) {
|
|
223
|
+
EnrichedMarkdown *strongSelf = weakSelf;
|
|
224
|
+
if (!strongSelf)
|
|
225
|
+
return;
|
|
226
|
+
auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(strongSelf->_eventEmitter);
|
|
227
|
+
if (emitter)
|
|
228
|
+
emitter->onSelectionChange(
|
|
229
|
+
ENRMSelectionChangeEventFromPayload<EnrichedMarkdownEventEmitter::OnSelectionChange>(payload));
|
|
230
|
+
}];
|
|
231
|
+
#if !TARGET_OS_OSX
|
|
232
|
+
// A handle drag starting or ending is reported right away, even when the
|
|
233
|
+
// selection itself is unchanged.
|
|
234
|
+
_selectionGestures = [[ENRMSelectionGestureTracker alloc] initWithChange:^{
|
|
235
|
+
EnrichedMarkdown *strongSelf = weakSelf;
|
|
236
|
+
if (!strongSelf)
|
|
237
|
+
return;
|
|
238
|
+
UITextView *owner = strongSelf->_selectionOwner;
|
|
239
|
+
if (owner)
|
|
240
|
+
[strongSelf->_selectionGestures trackTextView:owner];
|
|
241
|
+
[strongSelf->_selectionReporter forgetLastReported];
|
|
242
|
+
[strongSelf->_selectionReporter flushNow];
|
|
243
|
+
}];
|
|
244
|
+
#endif
|
|
196
245
|
}
|
|
197
246
|
return self;
|
|
198
247
|
}
|
|
199
248
|
|
|
249
|
+
#pragma mark - Selection
|
|
250
|
+
|
|
251
|
+
/// The current selection (or active block) as an `onSelectionChange` payload.
|
|
252
|
+
/// Markdown and `whole` follow the same rules as the intercepted Copy.
|
|
253
|
+
- (NSDictionary *)selectionPayload
|
|
254
|
+
{
|
|
255
|
+
RCTUIView *blockView = _activeBlockView;
|
|
256
|
+
if (blockView != nil) {
|
|
257
|
+
if (blockView.superview != self)
|
|
258
|
+
return ENRMInactiveSelectionPayload();
|
|
259
|
+
return ENRMBlockSelectionPayload(_activeBlockSource, _activeBlockMarkdown, _activeBlockText, blockView.frame);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
#if !TARGET_OS_OSX
|
|
263
|
+
UITextView *owner = _selectionOwner;
|
|
264
|
+
if (owner == nil || ![owner isDescendantOfView:self])
|
|
265
|
+
return ENRMInactiveSelectionPayload();
|
|
266
|
+
NSDictionary *selection = ENRMTextSelectionInTextView(owner, self);
|
|
267
|
+
if (!selection)
|
|
268
|
+
return ENRMInactiveSelectionPayload();
|
|
269
|
+
|
|
270
|
+
NSString *text = selection[ENRMSelectionTextKey];
|
|
271
|
+
// Segments only see their own text, so a selection is the whole component
|
|
272
|
+
// only when it spans the sole segment; the markdown is then the original.
|
|
273
|
+
BOOL whole = [selection[ENRMSelectionFullKey] boolValue] && _segmentViews.count == 1 && _cachedMarkdown.length > 0;
|
|
274
|
+
NSString *markdown =
|
|
275
|
+
whole ? _cachedMarkdown : extractMarkdownFromAttributedString(owner.attributedText, owner.selectedRange);
|
|
276
|
+
NSMutableDictionary *payload = [selection mutableCopy];
|
|
277
|
+
[payload removeObjectForKey:ENRMSelectionFullKey];
|
|
278
|
+
payload[ENRMSelectionActiveKey] = @YES;
|
|
279
|
+
payload[ENRMSelectionSourceKey] = @"selection";
|
|
280
|
+
payload[ENRMSelectionMarkdownKey] = markdown.length > 0 ? markdown : text;
|
|
281
|
+
payload[ENRMSelectionWholeKey] = @(whole);
|
|
282
|
+
payload[ENRMSelectionAdjustingKey] = @(_selectionGestures.adjusting);
|
|
283
|
+
return payload;
|
|
284
|
+
#else
|
|
285
|
+
return ENRMInactiveSelectionPayload();
|
|
286
|
+
#endif
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
- (void)clearActiveBlock
|
|
290
|
+
{
|
|
291
|
+
_activeBlockView = nil;
|
|
292
|
+
_activeBlockSource = nil;
|
|
293
|
+
_activeBlockMarkdown = nil;
|
|
294
|
+
_activeBlockText = nil;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
- (void)clearSegmentSelection:(ENRMPlatformTextView *)textView
|
|
298
|
+
{
|
|
299
|
+
#if !TARGET_OS_OSX
|
|
300
|
+
textView.selectedRange = NSMakeRange(0, 0);
|
|
301
|
+
[textView resignFirstResponder];
|
|
302
|
+
#endif
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
- (void)clearSelection
|
|
306
|
+
{
|
|
307
|
+
ENRMPlatformTextView *owner = _selectionOwner;
|
|
308
|
+
_selectionOwner = nil;
|
|
309
|
+
[self clearActiveBlock];
|
|
310
|
+
if (owner)
|
|
311
|
+
[self clearSegmentSelection:owner];
|
|
312
|
+
[_selectionReporter flushNow];
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
- (void)handleCommand:(const NSString *)commandName args:(const NSArray *)args
|
|
316
|
+
{
|
|
317
|
+
RCTEnrichedMarkdownHandleCommand(self, commandName, args);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
- (ENRMBlockCopyRequestBlock)blockMenuSuppressedForSource:(NSString *)source view:(RCTUIView *)view
|
|
321
|
+
{
|
|
322
|
+
__weak EnrichedMarkdown *weakSelf = self;
|
|
323
|
+
__weak RCTUIView *weakView = view;
|
|
324
|
+
return ^(NSString *markdown, NSString *text) {
|
|
325
|
+
EnrichedMarkdown *strongSelf = weakSelf;
|
|
326
|
+
RCTUIView *strongView = weakView;
|
|
327
|
+
if (!strongSelf || !strongView || !strongSelf->_selectionReporter.enabled)
|
|
328
|
+
return;
|
|
329
|
+
ENRMPlatformTextView *owner = strongSelf->_selectionOwner;
|
|
330
|
+
strongSelf->_selectionOwner = nil;
|
|
331
|
+
if (owner)
|
|
332
|
+
[strongSelf clearSegmentSelection:owner];
|
|
333
|
+
strongSelf->_activeBlockView = strongView;
|
|
334
|
+
strongSelf->_activeBlockSource = source;
|
|
335
|
+
strongSelf->_activeBlockMarkdown = markdown;
|
|
336
|
+
strongSelf->_activeBlockText = text;
|
|
337
|
+
[strongSelf->_selectionReporter forgetLastReported];
|
|
338
|
+
[strongSelf->_selectionReporter flushNow];
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
|
|
200
342
|
- (void)configureSegmentViewRegistry
|
|
201
343
|
{
|
|
202
344
|
__weak EnrichedMarkdown *weakSelf = self;
|
|
@@ -505,9 +647,30 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
505
647
|
}
|
|
506
648
|
}
|
|
507
649
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
650
|
+
- (ENRMBlockMenuOptions)blockMenuOptions
|
|
651
|
+
{
|
|
652
|
+
return (ENRMBlockMenuOptions){
|
|
653
|
+
.copyHidden = !_selectionMenuConfig.copyEnabled,
|
|
654
|
+
.copyWritesMarkdown = _selectionMenuConfig.copyFormatMarkdown,
|
|
655
|
+
.copyAsMarkdownHidden = !_selectionMenuConfig.copyAsMarkdown,
|
|
656
|
+
.copyButtonHidden = !_selectionMenuConfig.codeBlockCopyButton,
|
|
657
|
+
.interceptCopy = _selectionMenuConfig.interceptCopy,
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
- (ENRMBlockCopyRequestBlock)blockCopyRequestForSource:(NSString *)source
|
|
662
|
+
{
|
|
663
|
+
__weak EnrichedMarkdown *weakSelf = self;
|
|
664
|
+
return ^(NSString *markdown, NSString *text) {
|
|
665
|
+
EnrichedMarkdown *strongSelf = weakSelf;
|
|
666
|
+
if (strongSelf)
|
|
667
|
+
[strongSelf emitCopyRequest:source markdown:markdown text:text whole:YES];
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// Table, math and code block views cache the copy labels and menu options at
|
|
672
|
+
// creation time, so re-push them on prop updates (e.g. a language change
|
|
673
|
+
// without a remount) to avoid stale values.
|
|
511
674
|
- (void)pushSelectionMenuLabelsToSegments
|
|
512
675
|
{
|
|
513
676
|
for (RCTUIView *segment in _segmentViews) {
|
|
@@ -516,6 +679,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
516
679
|
tableView.copyLabel = _selectionMenuLabels.copyLabel;
|
|
517
680
|
tableView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
|
|
518
681
|
tableView.contextMenuHidden = _contextMenuHidden;
|
|
682
|
+
tableView.menuOptions = [self blockMenuOptions];
|
|
519
683
|
}
|
|
520
684
|
#if ENRICHED_MARKDOWN_MATH
|
|
521
685
|
else if ([segment isKindOfClass:[ENRMMathContainerView class]]) {
|
|
@@ -523,6 +687,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
523
687
|
mathView.copyLabel = _selectionMenuLabels.copyLabel;
|
|
524
688
|
mathView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
|
|
525
689
|
mathView.contextMenuHidden = _contextMenuHidden;
|
|
690
|
+
mathView.menuOptions = [self blockMenuOptions];
|
|
526
691
|
}
|
|
527
692
|
#endif
|
|
528
693
|
else if ([segment isKindOfClass:[ENRMCodeBlockContainerView class]]) {
|
|
@@ -530,6 +695,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
530
695
|
codeBlockView.copyLabel = _selectionMenuLabels.copyLabel;
|
|
531
696
|
codeBlockView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
|
|
532
697
|
codeBlockView.contextMenuHidden = _contextMenuHidden;
|
|
698
|
+
codeBlockView.menuOptions = [self blockMenuOptions];
|
|
533
699
|
}
|
|
534
700
|
}
|
|
535
701
|
}
|
|
@@ -767,6 +933,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
767
933
|
|
|
768
934
|
#if !TARGET_OS_OSX
|
|
769
935
|
view.textView.delegate = self;
|
|
936
|
+
[_selectionGestures trackTextView:view.textView];
|
|
770
937
|
#else
|
|
771
938
|
__weak EnrichedMarkdown *weakSelf = self;
|
|
772
939
|
[view setContextMenuProvider:^NSMenu *_Nullable(NSMenu *baseMenu, NSTextView *textView) {
|
|
@@ -807,6 +974,9 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
807
974
|
tableView.copyLabel = _selectionMenuLabels.copyLabel;
|
|
808
975
|
tableView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
|
|
809
976
|
tableView.contextMenuHidden = _contextMenuHidden;
|
|
977
|
+
tableView.menuOptions = [self blockMenuOptions];
|
|
978
|
+
tableView.onCopyRequest = [self blockCopyRequestForSource:@"table"];
|
|
979
|
+
tableView.onMenuSuppressed = [self blockMenuSuppressedForSource:@"table" view:tableView];
|
|
810
980
|
|
|
811
981
|
__weak EnrichedMarkdown *weakSelf = self;
|
|
812
982
|
|
|
@@ -847,6 +1017,9 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
847
1017
|
mathView.copyLabel = _selectionMenuLabels.copyLabel;
|
|
848
1018
|
mathView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
|
|
849
1019
|
mathView.contextMenuHidden = _contextMenuHidden;
|
|
1020
|
+
mathView.menuOptions = [self blockMenuOptions];
|
|
1021
|
+
mathView.onCopyRequest = [self blockCopyRequestForSource:@"math"];
|
|
1022
|
+
mathView.onMenuSuppressed = [self blockMenuSuppressedForSource:@"math" view:mathView];
|
|
850
1023
|
[mathView applyLatex:mathSegment.latex];
|
|
851
1024
|
return mathView;
|
|
852
1025
|
}
|
|
@@ -858,6 +1031,9 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
858
1031
|
codeBlockView.copyLabel = _selectionMenuLabels.copyLabel;
|
|
859
1032
|
codeBlockView.copyAsMarkdownLabel = _selectionMenuLabels.copyAsMarkdownLabel;
|
|
860
1033
|
codeBlockView.contextMenuHidden = _contextMenuHidden;
|
|
1034
|
+
codeBlockView.menuOptions = [self blockMenuOptions];
|
|
1035
|
+
codeBlockView.onCopyRequest = [self blockCopyRequestForSource:@"code"];
|
|
1036
|
+
codeBlockView.onMenuSuppressed = [self blockMenuSuppressedForSource:@"code" view:codeBlockView];
|
|
861
1037
|
|
|
862
1038
|
__weak EnrichedMarkdown *weakSelf = self;
|
|
863
1039
|
codeBlockView.onCopyPress = ^(NSString *code, NSString *language) {
|
|
@@ -910,6 +1086,9 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
910
1086
|
{
|
|
911
1087
|
[super layoutSubviews];
|
|
912
1088
|
[self computeSegmentLayoutForWidth:self.bounds.size.width applyFrames:YES];
|
|
1089
|
+
[self emitLinkLayoutIfNeeded];
|
|
1090
|
+
// Segments may have moved or been replaced; re-read the selection (deduplicated).
|
|
1091
|
+
[_selectionReporter schedule];
|
|
913
1092
|
}
|
|
914
1093
|
|
|
915
1094
|
- (void)updateProps:(Props::Shared const &)props oldProps:(Props::Shared const &)oldProps
|
|
@@ -945,6 +1124,14 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
945
1124
|
|
|
946
1125
|
_selectable = newViewProps.selectable;
|
|
947
1126
|
|
|
1127
|
+
if (newViewProps.emitLinkLayout != _emitLinkLayout) {
|
|
1128
|
+
_emitLinkLayout = newViewProps.emitLinkLayout;
|
|
1129
|
+
_lastLinkLayout = nil;
|
|
1130
|
+
[self setNeedsLayout];
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1133
|
+
_selectionReporter.enabled = newViewProps.emitSelectionChange;
|
|
1134
|
+
|
|
948
1135
|
for (RCTUIView *segment in _segmentViews) {
|
|
949
1136
|
if ([segment isKindOfClass:[EnrichedMarkdownInternalText class]]) {
|
|
950
1137
|
EnrichedMarkdownInternalText *textSegment = (EnrichedMarkdownInternalText *)segment;
|
|
@@ -986,6 +1173,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
986
1173
|
}
|
|
987
1174
|
|
|
988
1175
|
_enableLinkPreview = newViewProps.enableLinkPreview;
|
|
1176
|
+
_linkLongPressSelects = newViewProps.linkLongPressSelects;
|
|
989
1177
|
_enableTaskListItemToggle = newViewProps.enableTaskListItemToggle;
|
|
990
1178
|
_contextMenuHidden = newViewProps.contextMenuHidden;
|
|
991
1179
|
|
|
@@ -1026,9 +1214,7 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
1026
1214
|
}
|
|
1027
1215
|
|
|
1028
1216
|
_selectionMenuLabels = ENRMParseSelectionMenuLabels(newViewProps.selectionMenuConfig);
|
|
1029
|
-
_selectionMenuConfig =
|
|
1030
|
-
ENRMBuildSelectionMenuConfig(_selectionMenuLabels, newViewProps.selectionMenuConfig.copyAsMarkdown,
|
|
1031
|
-
newViewProps.selectionMenuConfig.copyImageUrl);
|
|
1217
|
+
_selectionMenuConfig = ENRMBuildSelectionMenuConfig(_selectionMenuLabels, newViewProps.selectionMenuConfig);
|
|
1032
1218
|
[self pushSelectionMenuLabelsToSegments];
|
|
1033
1219
|
|
|
1034
1220
|
if (ENRMAccessibilityLabelsChanged(oldViewProps.accessibilityLabels, newViewProps.accessibilityLabels)) {
|
|
@@ -1142,6 +1328,14 @@ static char kENRMSegmentFadeAnimatorKey;
|
|
|
1142
1328
|
_cachedMarkdown = nil;
|
|
1143
1329
|
_renderedMarkdown = nil;
|
|
1144
1330
|
_config = nil;
|
|
1331
|
+
_emitLinkLayout = NO;
|
|
1332
|
+
_lastLinkLayout = nil;
|
|
1333
|
+
_selectionReporter.enabled = NO;
|
|
1334
|
+
#if !TARGET_OS_OSX
|
|
1335
|
+
[_selectionGestures reset];
|
|
1336
|
+
#endif
|
|
1337
|
+
_selectionOwner = nil;
|
|
1338
|
+
[self clearActiveBlock];
|
|
1145
1339
|
_md4cFlags = [EnrichedMarkdown flagsFromProps:resetProps->md4cFlags];
|
|
1146
1340
|
_maxFontSizeMultiplier = 0;
|
|
1147
1341
|
_allowTrailingMargin = NO;
|
|
@@ -1252,6 +1446,40 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
|
|
|
1252
1446
|
emitter->onLinkPress({.url = std::string(url.UTF8String)});
|
|
1253
1447
|
}
|
|
1254
1448
|
|
|
1449
|
+
/// Reports link rects of every text segment (offset by the segment origin) to
|
|
1450
|
+
/// JS for `renderLinkDecoration`. Tables are skipped: their cells scroll
|
|
1451
|
+
/// horizontally, so rects would go stale. Runs after each segment layout pass;
|
|
1452
|
+
/// deduplicated so an unchanged layout sends nothing.
|
|
1453
|
+
- (void)emitLinkLayoutIfNeeded
|
|
1454
|
+
{
|
|
1455
|
+
if (!_emitLinkLayout || self.bounds.size.width <= 0)
|
|
1456
|
+
return;
|
|
1457
|
+
|
|
1458
|
+
auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(_eventEmitter);
|
|
1459
|
+
if (!emitter)
|
|
1460
|
+
return;
|
|
1461
|
+
|
|
1462
|
+
NSMutableArray<NSDictionary *> *fragments = [NSMutableArray array];
|
|
1463
|
+
NSInteger linkCount = 0;
|
|
1464
|
+
for (RCTUIView *segment in _segmentViews) {
|
|
1465
|
+
if ([segment isKindOfClass:[TableContainerView class]]) {
|
|
1466
|
+
linkCount += ((TableContainerView *)segment).linkCount;
|
|
1467
|
+
continue;
|
|
1468
|
+
}
|
|
1469
|
+
if (![segment isKindOfClass:[EnrichedMarkdownInternalText class]])
|
|
1470
|
+
continue;
|
|
1471
|
+
NSInteger segmentLinks = 0;
|
|
1472
|
+
[fragments addObjectsFromArray:ENRMCollectLinkLayoutFragments(((EnrichedMarkdownInternalText *)segment).textView,
|
|
1473
|
+
segment.frame.origin, linkCount, &segmentLinks)];
|
|
1474
|
+
linkCount += segmentLinks;
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
if (_lastLinkLayout != nil && [fragments isEqualToArray:_lastLinkLayout])
|
|
1478
|
+
return;
|
|
1479
|
+
_lastLinkLayout = [fragments copy];
|
|
1480
|
+
emitter->onLinkLayout(ENRMLinkLayoutEventFromFragments<EnrichedMarkdownEventEmitter::OnLinkLayout>(fragments));
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1255
1483
|
- (void)emitLinkLongPress:(NSString *)url
|
|
1256
1484
|
{
|
|
1257
1485
|
auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(_eventEmitter);
|
|
@@ -1274,6 +1502,18 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
|
|
|
1274
1502
|
{.code = std::string(code.UTF8String ?: ""), .language = std::string(language.UTF8String ?: "")});
|
|
1275
1503
|
}
|
|
1276
1504
|
|
|
1505
|
+
- (void)emitCopyRequest:(NSString *)source markdown:(NSString *)markdown text:(NSString *)text whole:(BOOL)whole
|
|
1506
|
+
{
|
|
1507
|
+
auto emitter = std::static_pointer_cast<EnrichedMarkdownEventEmitter const>(_eventEmitter);
|
|
1508
|
+
if (emitter)
|
|
1509
|
+
emitter->onCopyRequest({
|
|
1510
|
+
.source = std::string(source.UTF8String),
|
|
1511
|
+
.markdown = std::string(markdown.UTF8String ?: ""),
|
|
1512
|
+
.text = std::string(text.UTF8String ?: ""),
|
|
1513
|
+
.whole = (bool)whole,
|
|
1514
|
+
});
|
|
1515
|
+
}
|
|
1516
|
+
|
|
1277
1517
|
- (void)emitContextMenuItemPress:(NSString *)itemText
|
|
1278
1518
|
selectedText:(NSString *)selectedText
|
|
1279
1519
|
selectionStart:(NSUInteger)selectionStart
|
|
@@ -1317,14 +1557,35 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
|
|
|
1317
1557
|
ENRMHandleTapOnTextView(textView, recognizer, ^(NSString *url) { [self emitLinkPress:url]; });
|
|
1318
1558
|
}
|
|
1319
1559
|
|
|
1560
|
+
#if !TARGET_OS_OSX
|
|
1561
|
+
- (void)textViewDidChangeSelection:(UITextView *)textView
|
|
1562
|
+
{
|
|
1563
|
+
[_selectionGestures trackTextView:textView];
|
|
1564
|
+
ENRMDismissSelectionEditMenuIfMoved(textView);
|
|
1565
|
+
if (textView.selectedRange.length > 0) {
|
|
1566
|
+
// Take ownership first so the previous owner's own "cleared" callback is ignored.
|
|
1567
|
+
ENRMPlatformTextView *previous = _selectionOwner;
|
|
1568
|
+
_selectionOwner = textView;
|
|
1569
|
+
[self clearActiveBlock];
|
|
1570
|
+
if (previous && previous != textView)
|
|
1571
|
+
previous.selectedRange = NSMakeRange(0, 0);
|
|
1572
|
+
[_selectionReporter schedule];
|
|
1573
|
+
} else if (textView == _selectionOwner) {
|
|
1574
|
+
[_selectionReporter selectionChanged:NO];
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
#endif
|
|
1578
|
+
|
|
1320
1579
|
// TODO: Remove API_AVAILABLE(ios(16.0)) guard when the minimum iOS deployment target in RN is bumped to 16.
|
|
1321
1580
|
#if !TARGET_OS_OSX
|
|
1322
1581
|
- (UIMenu *)textView:(UITextView *)textView
|
|
1323
1582
|
editMenuForTextInRange:(NSRange)range
|
|
1324
1583
|
suggestedActions:(NSArray<UIMenuElement *> *)suggestedActions API_AVAILABLE(ios(16.0))
|
|
1325
1584
|
{
|
|
1585
|
+
// An empty menu presents nothing; nil would make UIKit show its default menu
|
|
1586
|
+
// (the selection and handles stay either way).
|
|
1326
1587
|
if (_contextMenuHidden) {
|
|
1327
|
-
return
|
|
1588
|
+
return [UIMenu menuWithChildren:@[]];
|
|
1328
1589
|
}
|
|
1329
1590
|
|
|
1330
1591
|
__weak EnrichedMarkdown *weakSelf = self;
|
|
@@ -1341,8 +1602,47 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
|
|
|
1341
1602
|
ENRMBuildContextMenuActions(_contextMenuItemTexts, _contextMenuItemIcons, textView, range, handler);
|
|
1342
1603
|
|
|
1343
1604
|
NSString *segmentMarkdown = extractMarkdownFromAttributedString(textView.attributedText, range);
|
|
1605
|
+
|
|
1606
|
+
// Segments only see their own text, so a selection is the whole component
|
|
1607
|
+
// only when it spans the sole segment; the markdown is then the original.
|
|
1608
|
+
ENRMCopyRequestHandler onCopyRequest = ^(NSString *markdown, NSString *text, BOOL fullSelection) {
|
|
1609
|
+
EnrichedMarkdown *strongSelf = weakSelf;
|
|
1610
|
+
if (!strongSelf)
|
|
1611
|
+
return;
|
|
1612
|
+
BOOL whole = fullSelection && strongSelf->_segmentViews.count == 1 && strongSelf->_cachedMarkdown.length > 0;
|
|
1613
|
+
[strongSelf emitCopyRequest:@"selection"
|
|
1614
|
+
markdown:whole ? strongSelf->_cachedMarkdown : markdown
|
|
1615
|
+
text:text
|
|
1616
|
+
whole:whole];
|
|
1617
|
+
};
|
|
1618
|
+
|
|
1344
1619
|
return buildEditMenuForSelection(textView.attributedText, range, segmentMarkdown, _config, suggestedActions,
|
|
1345
|
-
customActions, _selectionMenuConfig);
|
|
1620
|
+
customActions, _selectionMenuConfig, onCopyRequest);
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
// Attachments other than images (inline math) are text, not items: without
|
|
1624
|
+
// this a long press on one in an unfocused text view starts UIKit's text item
|
|
1625
|
+
// interaction, which presents nothing and leaves the view unable to select.
|
|
1626
|
+
- (BOOL)textView:(UITextView *)textView
|
|
1627
|
+
shouldInteractWithTextAttachment:(NSTextAttachment *)textAttachment
|
|
1628
|
+
inRange:(NSRange)characterRange
|
|
1629
|
+
interaction:(UITextItemInteraction)interaction
|
|
1630
|
+
{
|
|
1631
|
+
if ([textAttachment isKindOfClass:[ENRMImageAttachment class]]) {
|
|
1632
|
+
return YES;
|
|
1633
|
+
}
|
|
1634
|
+
// A long press selects the attachment like a word, as it does once the view is focused.
|
|
1635
|
+
if (interaction == UITextItemInteractionPresentActions) {
|
|
1636
|
+
__weak UITextView *weakTextView = textView;
|
|
1637
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1638
|
+
UITextView *strongTextView = weakTextView;
|
|
1639
|
+
if (!strongTextView || NSMaxRange(characterRange) > strongTextView.attributedText.length) {
|
|
1640
|
+
return;
|
|
1641
|
+
}
|
|
1642
|
+
ENRMSelectRangeAndPresentEditMenu(strongTextView, characterRange);
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
return NO;
|
|
1346
1646
|
}
|
|
1347
1647
|
|
|
1348
1648
|
- (BOOL)textView:(UITextView *)textView
|
|
@@ -1356,6 +1656,18 @@ Class<RCTComponentViewProtocol> EnrichedMarkdownCls(void)
|
|
|
1356
1656
|
|
|
1357
1657
|
NSString *urlString = linkURLAtRange(textView, characterRange);
|
|
1358
1658
|
|
|
1659
|
+
if (urlString && _linkLongPressSelects) {
|
|
1660
|
+
__weak UITextView *weakTextView = textView;
|
|
1661
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
1662
|
+
UITextView *strongTextView = weakTextView;
|
|
1663
|
+
if (!strongTextView || NSMaxRange(characterRange) > strongTextView.attributedText.length) {
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
ENRMSelectRangeAndPresentEditMenu(strongTextView, characterRange);
|
|
1667
|
+
});
|
|
1668
|
+
return NO;
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1359
1671
|
if (!urlString || _enableLinkPreview) {
|
|
1360
1672
|
return YES;
|
|
1361
1673
|
}
|