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
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#import "ENRMUIKit.h"
|
|
3
|
+
|
|
4
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
5
|
+
|
|
6
|
+
#ifdef __cplusplus
|
|
7
|
+
extern "C" {
|
|
8
|
+
#endif
|
|
9
|
+
|
|
10
|
+
/// Collects the chip rect of every link fragment laid out in `textView`, for
|
|
11
|
+
/// the `onLinkLayout` event. Each entry is
|
|
12
|
+
/// `@{linkIndex, url, text, x, y, width, height}` (NSNumber/NSString), so a
|
|
13
|
+
/// payload compares with `-isEqualToArray:` for deduplication.
|
|
14
|
+
///
|
|
15
|
+
/// - `offset` is added to every rect (a segment's origin inside its container).
|
|
16
|
+
/// - Links are numbered in document order starting at `indexBase`; the number
|
|
17
|
+
/// of links found is written to `outLinkCount` so the next segment continues.
|
|
18
|
+
///
|
|
19
|
+
/// Rects are in points relative to the text view, which the markdown views
|
|
20
|
+
/// configure with zero insets and no line-fragment padding.
|
|
21
|
+
NSArray<NSDictionary *> *ENRMCollectLinkLayoutFragments(ENRMPlatformTextView *textView, CGPoint offset,
|
|
22
|
+
NSInteger indexBase, NSInteger *_Nullable outLinkCount);
|
|
23
|
+
|
|
24
|
+
#ifdef __cplusplus
|
|
25
|
+
}
|
|
26
|
+
#endif
|
|
27
|
+
|
|
28
|
+
#ifdef __cplusplus
|
|
29
|
+
/// Converts collected fragments into a codegen `OnLinkLayout` payload. Templated
|
|
30
|
+
/// because each component has its own (structurally identical) event struct.
|
|
31
|
+
template <typename Event> inline Event ENRMLinkLayoutEventFromFragments(NSArray<NSDictionary *> *fragments)
|
|
32
|
+
{
|
|
33
|
+
Event event;
|
|
34
|
+
event.fragments.reserve(fragments.count);
|
|
35
|
+
for (NSDictionary *fragment in fragments) {
|
|
36
|
+
typename decltype(event.fragments)::value_type item;
|
|
37
|
+
item.linkIndex = [fragment[@"linkIndex"] intValue];
|
|
38
|
+
item.url = std::string([fragment[@"url"] UTF8String] ?: "");
|
|
39
|
+
item.text = std::string([fragment[@"text"] UTF8String] ?: "");
|
|
40
|
+
item.x = [fragment[@"x"] doubleValue];
|
|
41
|
+
item.y = [fragment[@"y"] doubleValue];
|
|
42
|
+
item.width = [fragment[@"width"] doubleValue];
|
|
43
|
+
item.height = [fragment[@"height"] doubleValue];
|
|
44
|
+
event.fragments.push_back(item);
|
|
45
|
+
}
|
|
46
|
+
return event;
|
|
47
|
+
}
|
|
48
|
+
#endif
|
|
49
|
+
|
|
50
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#import "ENRMLinkLayout.h"
|
|
2
|
+
#import "ENRMLinkChip.h"
|
|
3
|
+
|
|
4
|
+
NSArray<NSDictionary *> *ENRMCollectLinkLayoutFragments(ENRMPlatformTextView *textView, CGPoint offset,
|
|
5
|
+
NSInteger indexBase, NSInteger *outLinkCount)
|
|
6
|
+
{
|
|
7
|
+
NSLayoutManager *layoutManager = textView.layoutManager;
|
|
8
|
+
NSTextContainer *textContainer = textView.textContainer;
|
|
9
|
+
NSTextStorage *storage = layoutManager.textStorage;
|
|
10
|
+
NSMutableArray<NSDictionary *> *fragments = [NSMutableArray array];
|
|
11
|
+
__block NSInteger linkIndex = indexBase;
|
|
12
|
+
|
|
13
|
+
if (storage.length > 0 && textContainer != nil) {
|
|
14
|
+
// LinkRenderer attaches one ENRMLinkChip instance per link, so each
|
|
15
|
+
// attribute run is exactly one link (adjacent links never coalesce).
|
|
16
|
+
[storage enumerateAttribute:ENRMLinkChipAttributeName
|
|
17
|
+
inRange:NSMakeRange(0, storage.length)
|
|
18
|
+
options:0
|
|
19
|
+
usingBlock:^(ENRMLinkChip *chip, NSRange range, BOOL *stop) {
|
|
20
|
+
if (!chip)
|
|
21
|
+
return;
|
|
22
|
+
id link = [storage attribute:NSLinkAttributeName atIndex:range.location effectiveRange:NULL];
|
|
23
|
+
NSString *url = [link isKindOfClass:[NSURL class]] ? [(NSURL *)link absoluteString] : link;
|
|
24
|
+
NSString *text = ENRMPlainTextByRemovingLinkSpacers(storage, range);
|
|
25
|
+
NSNumber *index = @(linkIndex++);
|
|
26
|
+
|
|
27
|
+
ENRMEnumerateLinkChipFragments(layoutManager, textContainer, range, chip,
|
|
28
|
+
^(CGRect rect, BOOL isFirst, BOOL isLast) {
|
|
29
|
+
[fragments addObject:@{
|
|
30
|
+
@"linkIndex" : index,
|
|
31
|
+
@"url" : url ?: @"",
|
|
32
|
+
@"text" : text,
|
|
33
|
+
@"x" : @(rect.origin.x + offset.x),
|
|
34
|
+
@"y" : @(rect.origin.y + offset.y),
|
|
35
|
+
@"width" : @(rect.size.width),
|
|
36
|
+
@"height" : @(rect.size.height),
|
|
37
|
+
}];
|
|
38
|
+
});
|
|
39
|
+
}];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (outLinkCount)
|
|
43
|
+
*outLinkCount = linkIndex - indexBase;
|
|
44
|
+
return fragments;
|
|
45
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#import "ENRMUIKit.h"
|
|
3
|
+
|
|
4
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
5
|
+
|
|
6
|
+
/// Keys of an `onSelectionChange` payload. Rects are `NSValue`-wrapped CGRects in
|
|
7
|
+
/// the reporting view's coordinates, so a payload compares with `-isEqual:` for
|
|
8
|
+
/// deduplication.
|
|
9
|
+
extern NSString *const ENRMSelectionActiveKey;
|
|
10
|
+
extern NSString *const ENRMSelectionSourceKey;
|
|
11
|
+
extern NSString *const ENRMSelectionTextKey;
|
|
12
|
+
extern NSString *const ENRMSelectionMarkdownKey;
|
|
13
|
+
extern NSString *const ENRMSelectionWholeKey;
|
|
14
|
+
extern NSString *const ENRMSelectionRectKey;
|
|
15
|
+
extern NSString *const ENRMSelectionStartRectKey;
|
|
16
|
+
extern NSString *const ENRMSelectionEndRectKey;
|
|
17
|
+
/// Only in `ENRMTextSelectionInTextView` results: whether the range spans the
|
|
18
|
+
/// whole text (same threshold `markdownForRange` uses for the cached markdown).
|
|
19
|
+
extern NSString *const ENRMSelectionFullKey;
|
|
20
|
+
|
|
21
|
+
#ifdef __cplusplus
|
|
22
|
+
extern "C" {
|
|
23
|
+
#endif
|
|
24
|
+
|
|
25
|
+
/// The payload reported once the selection is gone.
|
|
26
|
+
NSDictionary *ENRMInactiveSelectionPayload(void);
|
|
27
|
+
|
|
28
|
+
/// A long-pressed block (table / math / code): `whole`, every rect is `frame`.
|
|
29
|
+
NSDictionary *ENRMBlockSelectionPayload(NSString *source, NSString *markdown, NSString *text, CGRect frame);
|
|
30
|
+
|
|
31
|
+
#if !TARGET_OS_OSX
|
|
32
|
+
/// Text, full-selection flag and rects of `textView`'s selection, converted to
|
|
33
|
+
/// `targetView`'s coordinates; nil when nothing is selected. `rect` is the union
|
|
34
|
+
/// of the selection rects, `startRect` / `endRect` the rects holding its start
|
|
35
|
+
/// and end. The owner adds source, markdown and whole to build the payload.
|
|
36
|
+
NSDictionary *_Nullable ENRMTextSelectionInTextView(UITextView *textView, UIView *targetView);
|
|
37
|
+
#endif
|
|
38
|
+
|
|
39
|
+
#ifdef __cplusplus
|
|
40
|
+
}
|
|
41
|
+
#endif
|
|
42
|
+
|
|
43
|
+
/// Coalesces selection changes into at most one report per run loop turn. The
|
|
44
|
+
/// payload is read when the turn fires, so the latest state always reaches JS;
|
|
45
|
+
/// a cleared selection is reported right away and drops any pending update.
|
|
46
|
+
/// Identical payloads are sent once.
|
|
47
|
+
@interface ENRMSelectionChangeReporter : NSObject
|
|
48
|
+
|
|
49
|
+
- (instancetype)initWithSnapshot:(NSDictionary * (^)(void))snapshot
|
|
50
|
+
emit:(void (^)(NSDictionary *payload))emit NS_DESIGNATED_INITIALIZER;
|
|
51
|
+
- (instancetype)init NS_UNAVAILABLE;
|
|
52
|
+
|
|
53
|
+
/// Off by default. Toggling resets the last reported payload to inactive.
|
|
54
|
+
@property (nonatomic, assign) BOOL enabled;
|
|
55
|
+
|
|
56
|
+
- (void)schedule;
|
|
57
|
+
- (void)selectionChanged:(BOOL)hasSelection;
|
|
58
|
+
- (void)flushNow;
|
|
59
|
+
- (void)cancel;
|
|
60
|
+
/// Makes the next report go out even if it equals the last one — a block
|
|
61
|
+
/// long-pressed again is a new request for the app's menu, not a repeat.
|
|
62
|
+
- (void)forgetLastReported;
|
|
63
|
+
|
|
64
|
+
@end
|
|
65
|
+
|
|
66
|
+
#ifdef __cplusplus
|
|
67
|
+
/// Converts a payload into a codegen `OnSelectionChange` struct. Templated
|
|
68
|
+
/// because each component has its own (structurally identical) event struct.
|
|
69
|
+
template <typename Event> inline Event ENRMSelectionChangeEventFromPayload(NSDictionary *payload)
|
|
70
|
+
{
|
|
71
|
+
Event event;
|
|
72
|
+
event.active = [payload[ENRMSelectionActiveKey] boolValue];
|
|
73
|
+
event.source = std::string([payload[ENRMSelectionSourceKey] UTF8String] ?: "selection");
|
|
74
|
+
event.text = std::string([payload[ENRMSelectionTextKey] UTF8String] ?: "");
|
|
75
|
+
event.markdown = std::string([payload[ENRMSelectionMarkdownKey] UTF8String] ?: "");
|
|
76
|
+
event.whole = [payload[ENRMSelectionWholeKey] boolValue];
|
|
77
|
+
|
|
78
|
+
auto assign = [](auto &target, NSValue *value) {
|
|
79
|
+
#if TARGET_OS_OSX
|
|
80
|
+
CGRect rect = value ? NSRectToCGRect(value.rectValue) : CGRectZero;
|
|
81
|
+
#else
|
|
82
|
+
CGRect rect = value ? value.CGRectValue : CGRectZero;
|
|
83
|
+
#endif
|
|
84
|
+
target.x = rect.origin.x;
|
|
85
|
+
target.y = rect.origin.y;
|
|
86
|
+
target.width = rect.size.width;
|
|
87
|
+
target.height = rect.size.height;
|
|
88
|
+
};
|
|
89
|
+
assign(event.rect, payload[ENRMSelectionRectKey]);
|
|
90
|
+
assign(event.startRect, payload[ENRMSelectionStartRectKey]);
|
|
91
|
+
assign(event.endRect, payload[ENRMSelectionEndRectKey]);
|
|
92
|
+
return event;
|
|
93
|
+
}
|
|
94
|
+
#endif
|
|
95
|
+
|
|
96
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#import "ENRMSelectionChange.h"
|
|
2
|
+
#import "ENRMLinkChip.h"
|
|
3
|
+
|
|
4
|
+
NSString *const ENRMSelectionActiveKey = @"active";
|
|
5
|
+
NSString *const ENRMSelectionSourceKey = @"source";
|
|
6
|
+
NSString *const ENRMSelectionTextKey = @"text";
|
|
7
|
+
NSString *const ENRMSelectionMarkdownKey = @"markdown";
|
|
8
|
+
NSString *const ENRMSelectionWholeKey = @"whole";
|
|
9
|
+
NSString *const ENRMSelectionRectKey = @"rect";
|
|
10
|
+
NSString *const ENRMSelectionStartRectKey = @"startRect";
|
|
11
|
+
NSString *const ENRMSelectionEndRectKey = @"endRect";
|
|
12
|
+
NSString *const ENRMSelectionFullKey = @"full";
|
|
13
|
+
|
|
14
|
+
static NSValue *ENRMRectValue(CGRect rect)
|
|
15
|
+
{
|
|
16
|
+
#if TARGET_OS_OSX
|
|
17
|
+
return [NSValue valueWithRect:NSRectFromCGRect(rect)];
|
|
18
|
+
#else
|
|
19
|
+
return [NSValue valueWithCGRect:rect];
|
|
20
|
+
#endif
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
NSDictionary *ENRMInactiveSelectionPayload(void)
|
|
24
|
+
{
|
|
25
|
+
static NSDictionary *payload;
|
|
26
|
+
static dispatch_once_t onceToken;
|
|
27
|
+
dispatch_once(&onceToken, ^{
|
|
28
|
+
NSValue *zero = ENRMRectValue(CGRectZero);
|
|
29
|
+
payload = @{
|
|
30
|
+
ENRMSelectionActiveKey : @NO,
|
|
31
|
+
ENRMSelectionSourceKey : @"selection",
|
|
32
|
+
ENRMSelectionTextKey : @"",
|
|
33
|
+
ENRMSelectionMarkdownKey : @"",
|
|
34
|
+
ENRMSelectionWholeKey : @NO,
|
|
35
|
+
ENRMSelectionRectKey : zero,
|
|
36
|
+
ENRMSelectionStartRectKey : zero,
|
|
37
|
+
ENRMSelectionEndRectKey : zero,
|
|
38
|
+
};
|
|
39
|
+
});
|
|
40
|
+
return payload;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
NSDictionary *ENRMBlockSelectionPayload(NSString *source, NSString *markdown, NSString *text, CGRect frame)
|
|
44
|
+
{
|
|
45
|
+
NSValue *rect = ENRMRectValue(frame);
|
|
46
|
+
return @{
|
|
47
|
+
ENRMSelectionActiveKey : @YES,
|
|
48
|
+
ENRMSelectionSourceKey : source,
|
|
49
|
+
ENRMSelectionTextKey : text ?: @"",
|
|
50
|
+
ENRMSelectionMarkdownKey : markdown ?: @"",
|
|
51
|
+
ENRMSelectionWholeKey : @YES,
|
|
52
|
+
ENRMSelectionRectKey : rect,
|
|
53
|
+
ENRMSelectionStartRectKey : rect,
|
|
54
|
+
ENRMSelectionEndRectKey : rect,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
#if !TARGET_OS_OSX
|
|
59
|
+
NSDictionary *ENRMTextSelectionInTextView(UITextView *textView, UIView *targetView)
|
|
60
|
+
{
|
|
61
|
+
NSRange range = textView.selectedRange;
|
|
62
|
+
UITextRange *textRange = textView.selectedTextRange;
|
|
63
|
+
if (range.location == NSNotFound || range.length == 0 || textRange == nil || textRange.isEmpty) {
|
|
64
|
+
return nil;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
NSAttributedString *attributedText = textView.attributedText;
|
|
68
|
+
if (NSMaxRange(range) > attributedText.length) {
|
|
69
|
+
return nil;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
CGRect unionRect = CGRectNull;
|
|
73
|
+
CGRect startRect = CGRectNull;
|
|
74
|
+
CGRect endRect = CGRectNull;
|
|
75
|
+
CGRect firstRect = CGRectNull;
|
|
76
|
+
CGRect lastRect = CGRectNull;
|
|
77
|
+
for (UITextSelectionRect *selectionRect in [textView selectionRectsForRange:textRange]) {
|
|
78
|
+
CGRect rect = selectionRect.rect;
|
|
79
|
+
if (CGRectIsNull(rect) || CGRectIsInfinite(rect) || CGRectIsEmpty(rect)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
unionRect = CGRectUnion(unionRect, rect);
|
|
83
|
+
if (CGRectIsNull(firstRect)) {
|
|
84
|
+
firstRect = rect;
|
|
85
|
+
}
|
|
86
|
+
lastRect = rect;
|
|
87
|
+
if (selectionRect.containsStart && CGRectIsNull(startRect)) {
|
|
88
|
+
startRect = rect;
|
|
89
|
+
}
|
|
90
|
+
if (selectionRect.containsEnd) {
|
|
91
|
+
endRect = rect;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Not laid out yet: report once layout gives the range a position.
|
|
95
|
+
if (CGRectIsNull(unionRect)) {
|
|
96
|
+
return nil;
|
|
97
|
+
}
|
|
98
|
+
if (CGRectIsNull(startRect)) {
|
|
99
|
+
startRect = firstRect;
|
|
100
|
+
}
|
|
101
|
+
if (CGRectIsNull(endRect)) {
|
|
102
|
+
endRect = lastRect;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return @{
|
|
106
|
+
ENRMSelectionTextKey : ENRMPlainTextByRemovingLinkSpacers(attributedText, range),
|
|
107
|
+
ENRMSelectionFullKey : @(range.location == 0 && range.length >= attributedText.length - 1),
|
|
108
|
+
ENRMSelectionRectKey : ENRMRectValue([textView convertRect:unionRect toView:targetView]),
|
|
109
|
+
ENRMSelectionStartRectKey : ENRMRectValue([textView convertRect:startRect toView:targetView]),
|
|
110
|
+
ENRMSelectionEndRectKey : ENRMRectValue([textView convertRect:endRect toView:targetView]),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
#endif
|
|
114
|
+
|
|
115
|
+
@implementation ENRMSelectionChangeReporter {
|
|
116
|
+
NSDictionary * (^_snapshot)(void);
|
|
117
|
+
void (^_emit)(NSDictionary *);
|
|
118
|
+
NSDictionary *_lastReported;
|
|
119
|
+
BOOL _pending;
|
|
120
|
+
// Bumped on cancel so a scheduled block that already left the queue is a no-op.
|
|
121
|
+
NSUInteger _generation;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
- (instancetype)initWithSnapshot:(NSDictionary * (^)(void))snapshot emit:(void (^)(NSDictionary *))emit
|
|
125
|
+
{
|
|
126
|
+
if (self = [super init]) {
|
|
127
|
+
_snapshot = [snapshot copy];
|
|
128
|
+
_emit = [emit copy];
|
|
129
|
+
_lastReported = ENRMInactiveSelectionPayload();
|
|
130
|
+
}
|
|
131
|
+
return self;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
- (void)setEnabled:(BOOL)enabled
|
|
135
|
+
{
|
|
136
|
+
if (_enabled == enabled) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
_enabled = enabled;
|
|
140
|
+
[self cancel];
|
|
141
|
+
_lastReported = ENRMInactiveSelectionPayload();
|
|
142
|
+
if (enabled) {
|
|
143
|
+
[self schedule];
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
- (void)schedule
|
|
148
|
+
{
|
|
149
|
+
if (!_enabled || _pending) {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
_pending = YES;
|
|
153
|
+
NSUInteger generation = _generation;
|
|
154
|
+
__weak ENRMSelectionChangeReporter *weakSelf = self;
|
|
155
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
156
|
+
ENRMSelectionChangeReporter *strongSelf = weakSelf;
|
|
157
|
+
if (!strongSelf || !strongSelf->_pending || strongSelf->_generation != generation) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
strongSelf->_pending = NO;
|
|
161
|
+
[strongSelf report];
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
- (void)selectionChanged:(BOOL)hasSelection
|
|
166
|
+
{
|
|
167
|
+
if (hasSelection) {
|
|
168
|
+
[self schedule];
|
|
169
|
+
} else {
|
|
170
|
+
[self flushNow];
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
- (void)flushNow
|
|
175
|
+
{
|
|
176
|
+
[self cancel];
|
|
177
|
+
[self report];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
- (void)forgetLastReported
|
|
181
|
+
{
|
|
182
|
+
_lastReported = ENRMInactiveSelectionPayload();
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
- (void)cancel
|
|
186
|
+
{
|
|
187
|
+
_pending = NO;
|
|
188
|
+
_generation++;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
- (void)report
|
|
192
|
+
{
|
|
193
|
+
if (!_enabled) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
NSDictionary *current = _snapshot();
|
|
197
|
+
if ([current isEqualToDictionary:_lastReported]) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
_lastReported = current;
|
|
201
|
+
_emit(current);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
@end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#import "ContextMenuUtils.h"
|
|
2
|
+
#import "ENRMLinkChip.h"
|
|
2
3
|
#import "ENRMMenuAction.h"
|
|
3
4
|
#import "EditMenuUtils.h"
|
|
4
5
|
#import "PasteboardUtils.h"
|
|
@@ -19,7 +20,9 @@ NSMenu *_Nullable buildEditMenuForSelection(NSAttributedString *attributedText,
|
|
|
19
20
|
return menu;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
// Link spacers only reserve layout space; keep them out of every copied flavor.
|
|
24
|
+
NSAttributedString *selectedText =
|
|
25
|
+
ENRMAttributedStringByRemovingLinkSpacers([attributedText attributedSubstringFromRange:range]);
|
|
23
26
|
NSString *markdown = markdownForRange(attributedText, range, cachedMarkdown);
|
|
24
27
|
NSArray<NSString *> *imageURLs = imageURLsInRange(attributedText, range);
|
|
25
28
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#import "EditMenuUtils.h"
|
|
2
|
+
#import "ENRMLinkChip.h"
|
|
2
3
|
#import "PasteboardUtils.h"
|
|
3
4
|
#import "StyleConfig.h"
|
|
4
5
|
#include <TargetConditionals.h>
|
|
@@ -121,7 +122,9 @@ UIMenu *buildEditMenuForSelection(NSAttributedString *attributedText, NSRange ra
|
|
|
121
122
|
ENRMSelectionMenuConfig selectionMenuConfig,
|
|
122
123
|
ENRMCopyRequestHandler _Nullable onCopyRequest) API_AVAILABLE(ios(16.0))
|
|
123
124
|
{
|
|
124
|
-
|
|
125
|
+
// Link spacers only reserve layout space; keep them out of every copied flavor.
|
|
126
|
+
NSAttributedString *selectedText =
|
|
127
|
+
ENRMAttributedStringByRemovingLinkSpacers([attributedText attributedSubstringFromRange:range]);
|
|
125
128
|
NSString *markdown = markdownForRange(attributedText, range, cachedMarkdown);
|
|
126
129
|
NSArray<NSString *> *imageURLs = imageURLsInRange(attributedText, range);
|
|
127
130
|
// Same threshold markdownForRange uses to return the cached markdown.
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
#import "CodeBackground.h"
|
|
4
4
|
#import "ENRMFeatureFlags.h"
|
|
5
5
|
#import "ENRMImageAttachment.h"
|
|
6
|
+
#import "ENRMLinkChip.h"
|
|
6
7
|
#import "HighlightRenderer.h"
|
|
7
8
|
#if ENRICHED_MARKDOWN_MATH
|
|
8
9
|
#import "ENRMMathInlineAttachment.h"
|
|
@@ -460,7 +461,7 @@ static void generateInlineHTML(NSMutableString *html, NSAttributedString *attrib
|
|
|
460
461
|
usingBlock:^(NSDictionary<NSAttributedStringKey, id> *attrs, NSRange attrRange, BOOL *stop) {
|
|
461
462
|
NSString *text = [string substringWithRange:attrRange];
|
|
462
463
|
|
|
463
|
-
if ([text isEqualToString:@"\n"])
|
|
464
|
+
if ([text isEqualToString:@"\n"] || attrs[ENRMLinkSpacerAttributeName])
|
|
464
465
|
return;
|
|
465
466
|
|
|
466
467
|
if ([text containsString:kObjectReplacementChar]) {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#import "AccessibilityInfo.h"
|
|
3
3
|
#import "BlockquoteBorder.h"
|
|
4
4
|
#import "ENRMAccessibilityLabels.h"
|
|
5
|
+
#import "ENRMLinkChip.h"
|
|
5
6
|
#include <TargetConditionals.h>
|
|
6
7
|
|
|
7
8
|
typedef NS_ENUM(NSInteger, ElementType) { ElementTypeText, ElementTypeLink, ElementTypeImage };
|
|
@@ -113,7 +114,8 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
113
114
|
}
|
|
114
115
|
|
|
115
116
|
BOOL isImg = item[@"altText"] != nil;
|
|
116
|
-
|
|
117
|
+
// Link chips pad their text with spacer characters; keep them out of the spoken label.
|
|
118
|
+
NSString *label = isImg ? item[@"altText"] : ENRMPlainTextByRemovingLinkSpacers(textView.attributedText, itemRange);
|
|
117
119
|
[elements addObject:[self createElementForRange:itemRange
|
|
118
120
|
type:isImg ? ElementTypeImage : ElementTypeLink
|
|
119
121
|
text:label
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#import "BlockquoteBorder.h"
|
|
3
3
|
#import "ENRMFeatureFlags.h"
|
|
4
4
|
#import "ENRMImageAttachment.h"
|
|
5
|
+
#import "ENRMLinkChip.h"
|
|
5
6
|
#import "ENRMUIKit.h"
|
|
6
7
|
#import "HighlightRenderer.h"
|
|
7
8
|
#include <TargetConditionals.h>
|
|
@@ -152,7 +153,7 @@ NSString *_Nullable extractMarkdownFromAttributedString(NSAttributedString *attr
|
|
|
152
153
|
options:0
|
|
153
154
|
usingBlock:^(NSDictionary<NSAttributedStringKey, id> *attrs, NSRange attrRange, BOOL *stop) {
|
|
154
155
|
NSString *text = [[attributedText attributedSubstringFromRange:attrRange] string];
|
|
155
|
-
if (text.length == 0)
|
|
156
|
+
if (text.length == 0 || attrs[ENRMLinkSpacerAttributeName])
|
|
156
157
|
return;
|
|
157
158
|
|
|
158
159
|
// Images and Thematic Breaks
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#import "RTFExportUtils.h"
|
|
2
2
|
#import "BlockquoteBorder.h"
|
|
3
3
|
#import "CodeBackground.h"
|
|
4
|
+
#import "ENRMLinkChip.h"
|
|
4
5
|
#import "LastElementUtils.h"
|
|
5
6
|
#import "ListItemRenderer.h"
|
|
6
7
|
#import "RenderContext.h"
|
|
@@ -288,6 +289,7 @@ NSAttributedString *prepareAttributedStringForRTFExport(NSAttributedString *attr
|
|
|
288
289
|
RCTUIColor *blockquoteBgColor = [styleConfig blockquoteBackgroundColor];
|
|
289
290
|
|
|
290
291
|
processThematicBreaks(prepared);
|
|
292
|
+
ENRMApplyLinkChipBackgroundsAsTextBackground(prepared);
|
|
291
293
|
processCodes(prepared, codeBgColor);
|
|
292
294
|
processCodeBlocks(prepared, codeBlockBgColor);
|
|
293
295
|
processBlockquotes(prepared, blockquoteBgColor);
|
|
@@ -523,6 +523,32 @@ BOOL applyMarkdownStyleToConfig(StyleConfig *config, const MarkdownStyle &newSty
|
|
|
523
523
|
changed = YES;
|
|
524
524
|
}
|
|
525
525
|
|
|
526
|
+
if (newStyle.link.borderRadius != oldStyle.link.borderRadius) {
|
|
527
|
+
[config setLinkBorderRadius:newStyle.link.borderRadius];
|
|
528
|
+
changed = YES;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
if (newStyle.link.paddingHorizontal != oldStyle.link.paddingHorizontal) {
|
|
532
|
+
[config setLinkPaddingHorizontal:newStyle.link.paddingHorizontal];
|
|
533
|
+
changed = YES;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (newStyle.link.paddingVertical != oldStyle.link.paddingVertical) {
|
|
537
|
+
[config setLinkPaddingVertical:newStyle.link.paddingVertical];
|
|
538
|
+
changed = YES;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
if (newStyle.link.borderColor != oldStyle.link.borderColor) {
|
|
542
|
+
RCTUIColor *color = RCTUIColorFromSharedColor(newStyle.link.borderColor);
|
|
543
|
+
[config setLinkBorderColor:CGColorGetAlpha(color.CGColor) > 0 ? color : nil];
|
|
544
|
+
changed = YES;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
if (newStyle.link.borderWidth != oldStyle.link.borderWidth) {
|
|
548
|
+
[config setLinkBorderWidth:newStyle.link.borderWidth];
|
|
549
|
+
changed = YES;
|
|
550
|
+
}
|
|
551
|
+
|
|
526
552
|
{
|
|
527
553
|
BOOL linkVariantsChanged = newStyle.linkVariants.size() != oldStyle.linkVariants.size();
|
|
528
554
|
if (!linkVariantsChanged) {
|
|
@@ -530,7 +556,11 @@ BOOL applyMarkdownStyleToConfig(StyleConfig *config, const MarkdownStyle &newSty
|
|
|
530
556
|
const auto &newVariant = newStyle.linkVariants[i];
|
|
531
557
|
const auto &oldVariant = oldStyle.linkVariants[i];
|
|
532
558
|
if (newVariant.pattern != oldVariant.pattern || newVariant.color != oldVariant.color ||
|
|
533
|
-
newVariant.underline != oldVariant.underline || newVariant.backgroundColor != oldVariant.backgroundColor
|
|
559
|
+
newVariant.underline != oldVariant.underline || newVariant.backgroundColor != oldVariant.backgroundColor ||
|
|
560
|
+
newVariant.borderRadius != oldVariant.borderRadius ||
|
|
561
|
+
newVariant.paddingHorizontal != oldVariant.paddingHorizontal ||
|
|
562
|
+
newVariant.paddingVertical != oldVariant.paddingVertical ||
|
|
563
|
+
newVariant.borderColor != oldVariant.borderColor || newVariant.borderWidth != oldVariant.borderWidth) {
|
|
534
564
|
linkVariantsChanged = YES;
|
|
535
565
|
break;
|
|
536
566
|
}
|
|
@@ -545,6 +575,12 @@ BOOL applyMarkdownStyleToConfig(StyleConfig *config, const MarkdownStyle &newSty
|
|
|
545
575
|
variant.underline = entry.underline;
|
|
546
576
|
RCTUIColor *backgroundColor = RCTUIColorFromSharedColor(entry.backgroundColor);
|
|
547
577
|
variant.backgroundColor = CGColorGetAlpha(backgroundColor.CGColor) > 0 ? backgroundColor : nil;
|
|
578
|
+
variant.borderRadius = entry.borderRadius;
|
|
579
|
+
variant.paddingHorizontal = entry.paddingHorizontal;
|
|
580
|
+
variant.paddingVertical = entry.paddingVertical;
|
|
581
|
+
RCTUIColor *borderColor = RCTUIColorFromSharedColor(entry.borderColor);
|
|
582
|
+
variant.borderColor = CGColorGetAlpha(borderColor.CGColor) > 0 ? borderColor : nil;
|
|
583
|
+
variant.borderWidth = entry.borderWidth;
|
|
548
584
|
[variants addObject:variant];
|
|
549
585
|
}
|
|
550
586
|
[config setLinkVariants:variants];
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
#import "BlockquoteBorder.h"
|
|
3
3
|
#import "CodeBackground.h"
|
|
4
4
|
#import "CodeBlockBackground.h"
|
|
5
|
+
#import "ENRMLinkChip.h"
|
|
5
6
|
#import "ListMarkerDrawer.h"
|
|
6
7
|
#import "RuntimeKeys.h"
|
|
7
8
|
#import "StyleConfig.h"
|
|
@@ -31,6 +32,8 @@
|
|
|
31
32
|
// We fetch the objects into local variables FIRST to ensure they don't
|
|
32
33
|
// disappear mid-method if setConfig: is called on another thread.
|
|
33
34
|
|
|
35
|
+
ENRMDrawLinkChipsForGlyphRange(self, textContainer, glyphsToShow, origin);
|
|
36
|
+
|
|
34
37
|
CodeBackground *codeBg = [self getCodeBackgroundWithConfig:config];
|
|
35
38
|
[codeBg drawBackgroundsForGlyphRange:glyphsToShow layoutManager:self textContainer:textContainer atPoint:origin];
|
|
36
39
|
|
|
@@ -45,6 +45,9 @@ typedef void (^ENRMCodeBlockCopyBlock)(NSString *code, NSString *language);
|
|
|
45
45
|
/// Set by the host; receives the Copy item's content when
|
|
46
46
|
/// `menuOptions.interceptCopy` is set.
|
|
47
47
|
@property (nonatomic, copy, nullable) ENRMBlockCopyRequestBlock onCopyRequest;
|
|
48
|
+
/// Called instead of the menu on long press while `contextMenuHidden` is on, so
|
|
49
|
+
/// the owner can report the block (`onSelectionChange`) and show its own UI.
|
|
50
|
+
@property (nonatomic, copy, nullable) ENRMBlockCopyRequestBlock onMenuSuppressed;
|
|
48
51
|
|
|
49
52
|
// Fired when the code is copied (header button, context-menu Copy, or the
|
|
50
53
|
// VoiceOver copy action); set by the host to bridge up to the JS onCopyPress
|
|
@@ -495,6 +495,9 @@ static BOOL ENRMColorIsDark(RCTUIColor *color)
|
|
|
495
495
|
{
|
|
496
496
|
// Returning nil is the UIKit-sanctioned way to suppress a context menu.
|
|
497
497
|
if (_pending || self.contextMenuHidden) {
|
|
498
|
+
if (!_pending && self.onMenuSuppressed) {
|
|
499
|
+
self.onMenuSuppressed([self fencedMarkdown], _cachedCode ?: @"");
|
|
500
|
+
}
|
|
498
501
|
return nil;
|
|
499
502
|
}
|
|
500
503
|
__weak __typeof(self) weakSelf = self;
|
|
@@ -42,6 +42,9 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
42
42
|
/// Set by the host; receives the Copy item's content when
|
|
43
43
|
/// `menuOptions.interceptCopy` is set.
|
|
44
44
|
@property (nonatomic, copy, nullable) ENRMBlockCopyRequestBlock onCopyRequest;
|
|
45
|
+
/// Called instead of the menu on long press while `contextMenuHidden` is on, so
|
|
46
|
+
/// the owner can report the block (`onSelectionChange`) and show its own UI.
|
|
47
|
+
@property (nonatomic, copy, nullable) ENRMBlockCopyRequestBlock onMenuSuppressed;
|
|
45
48
|
|
|
46
49
|
@end
|
|
47
50
|
|
|
@@ -128,6 +128,9 @@
|
|
|
128
128
|
{
|
|
129
129
|
// Returning nil is the UIKit-sanctioned way to suppress a context menu.
|
|
130
130
|
if (self.contextMenuHidden) {
|
|
131
|
+
if (self.onMenuSuppressed) {
|
|
132
|
+
self.onMenuSuppressed([NSString stringWithFormat:@"$$\n%@\n$$", _cachedLatex ?: @""], _cachedLatex ?: @"");
|
|
133
|
+
}
|
|
131
134
|
return nil;
|
|
132
135
|
}
|
|
133
136
|
__weak __typeof(self) weakSelf = self;
|
|
@@ -63,8 +63,14 @@ typedef void (^TableLinkPressBlock)(NSString *url);
|
|
|
63
63
|
/// Set by the host; receives the Copy item's content when
|
|
64
64
|
/// `menuOptions.interceptCopy` is set.
|
|
65
65
|
@property (nonatomic, copy, nullable) ENRMBlockCopyRequestBlock onCopyRequest;
|
|
66
|
+
/// Called instead of the menu on long press while `contextMenuHidden` is on, so
|
|
67
|
+
/// the owner can report the block (`onSelectionChange`) and show its own UI.
|
|
68
|
+
@property (nonatomic, copy, nullable) ENRMBlockCopyRequestBlock onMenuSuppressed;
|
|
66
69
|
|
|
67
70
|
@property (nonatomic, readonly) NSUInteger rowCount;
|
|
71
|
+
/// Number of links across all cells. Tables are not reported by `onLinkLayout`,
|
|
72
|
+
/// but their links still count so later link indices stay in document order.
|
|
73
|
+
@property (nonatomic, readonly) NSInteger linkCount;
|
|
68
74
|
|
|
69
75
|
- (void)animateNewRowsFromPreviousCount:(NSUInteger)previousRowCount duration:(NSTimeInterval)duration;
|
|
70
76
|
|