react-native-enriched-markdown-scaffold 1.0.2-scaffold.1 → 1.0.2-scaffold.3
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/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.cpp +24 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/EventEmitters.h +18 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +60 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +31 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +5 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +4 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/events/CopyRequestEvent.kt +28 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +23 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +50 -13
- 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 +19 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +7 -5
- package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +6 -4
- package/docs/API_REFERENCE.md +60 -4
- package/docs/COPY_OPTIONS.md +40 -0
- package/ios/EnrichedMarkdown.mm +63 -8
- package/ios/EnrichedMarkdownText.mm +26 -5
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.cpp +24 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/EventEmitters.h +18 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +60 -0
- package/ios/utils/ENRMBlockMenu.h +66 -0
- package/ios/utils/EditMenuUtils.h +32 -6
- package/ios/utils/EditMenuUtils.m +51 -14
- package/ios/views/ENRMCodeBlockContainerView.h +13 -2
- package/ios/views/ENRMCodeBlockContainerView.m +19 -19
- package/ios/views/ENRMMathContainerView.h +8 -0
- package/ios/views/ENRMMathContainerView.m +10 -18
- package/ios/views/TableContainerView.h +8 -0
- package/ios/views/TableContainerView.m +10 -18
- package/lib/module/EnrichedMarkdownNativeComponent.ts +24 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +24 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/EnrichedMarkdownText.js +29 -2
- package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +17 -0
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +17 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +1 -1
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts +3 -3
- package/lib/typescript/src/native/EnrichedMarkdownText.d.ts.map +1 -1
- package/lib/typescript/src/types/MarkdownTextProps.d.ts +53 -10
- package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
- package/lib/typescript/src/types/events.d.ts +22 -0
- package/lib/typescript/src/types/events.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/EnrichedMarkdownNativeComponent.ts +24 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +24 -0
- package/src/index.tsx +1 -0
- package/src/native/EnrichedMarkdownText.tsx +37 -1
- package/src/types/MarkdownTextProps.ts +50 -10
- package/src/types/events.ts +23 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
#import "ENRMUIKit.h"
|
|
3
|
+
#include <TargetConditionals.h>
|
|
4
|
+
|
|
5
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
6
|
+
|
|
7
|
+
/// Selection-menu options that table, math and code block views need for their
|
|
8
|
+
/// own long-press menus. Every field is phrased so that the zero value keeps the
|
|
9
|
+
/// legacy behavior, so a block view that has not received options yet behaves
|
|
10
|
+
/// exactly as before.
|
|
11
|
+
typedef struct {
|
|
12
|
+
// `selectionMenuConfig.copy.enabled == false`.
|
|
13
|
+
BOOL copyHidden;
|
|
14
|
+
// `selectionMenuConfig.copy.format == 'markdown'`: the Copy item writes the
|
|
15
|
+
// block's Markdown instead of its raw content.
|
|
16
|
+
BOOL copyWritesMarkdown;
|
|
17
|
+
// `selectionMenuConfig.copyAsMarkdown.enabled == false`.
|
|
18
|
+
BOOL copyAsMarkdownHidden;
|
|
19
|
+
// `selectionMenuConfig.codeBlockCopyButton.enabled == false`.
|
|
20
|
+
BOOL copyButtonHidden;
|
|
21
|
+
// JS `onCopy` is set: the Copy item hands the block's content to the view's
|
|
22
|
+
// `onCopyRequest` instead of writing the pasteboard.
|
|
23
|
+
BOOL interceptCopy;
|
|
24
|
+
} ENRMBlockMenuOptions;
|
|
25
|
+
|
|
26
|
+
/// Receives an intercepted block Copy: the block's Markdown and its raw content.
|
|
27
|
+
typedef void (^ENRMBlockCopyRequestBlock)(NSString *markdown, NSString *text);
|
|
28
|
+
|
|
29
|
+
#if !TARGET_OS_OSX
|
|
30
|
+
/// Builds the long-press menu shared by the table, math and code block views.
|
|
31
|
+
/// Returns nil when no item is left — nil is how UIKit suppresses a context
|
|
32
|
+
/// menu, whereas an empty menu would still lift the view. `requestCopy` replaces
|
|
33
|
+
/// the Copy item's handler when `options.interceptCopy` is set.
|
|
34
|
+
static inline UIContextMenuConfiguration *_Nullable ENRMBlockContextMenuConfiguration(
|
|
35
|
+
ENRMBlockMenuOptions options, NSString *_Nullable copyLabel, NSString *_Nullable copyAsMarkdownLabel,
|
|
36
|
+
void (^copyRaw)(void), void (^copyMarkdown)(void), void (^_Nullable requestCopy)(void))
|
|
37
|
+
{
|
|
38
|
+
if (options.copyHidden && options.copyAsMarkdownHidden) {
|
|
39
|
+
return nil;
|
|
40
|
+
}
|
|
41
|
+
void (^copyHandler)(void) = (options.interceptCopy && requestCopy) ? requestCopy
|
|
42
|
+
: options.copyWritesMarkdown ? copyMarkdown
|
|
43
|
+
: copyRaw;
|
|
44
|
+
return [UIContextMenuConfiguration
|
|
45
|
+
configurationWithIdentifier:nil
|
|
46
|
+
previewProvider:nil
|
|
47
|
+
actionProvider:^UIMenu *(NSArray<UIMenuElement *> *suggestedActions) {
|
|
48
|
+
NSMutableArray<UIMenuElement *> *children = [NSMutableArray arrayWithCapacity:2];
|
|
49
|
+
if (!options.copyHidden) {
|
|
50
|
+
[children addObject:[UIAction actionWithTitle:copyLabel ?: @""
|
|
51
|
+
image:[RCTUIImage systemImageNamed:@"doc.on.doc"]
|
|
52
|
+
identifier:nil
|
|
53
|
+
handler:^(__kindof UIAction *action) { copyHandler(); }]];
|
|
54
|
+
}
|
|
55
|
+
if (!options.copyAsMarkdownHidden) {
|
|
56
|
+
[children addObject:[UIAction actionWithTitle:copyAsMarkdownLabel ?: @""
|
|
57
|
+
image:[RCTUIImage systemImageNamed:@"doc.text"]
|
|
58
|
+
identifier:nil
|
|
59
|
+
handler:^(__kindof UIAction *action) { copyMarkdown(); }]];
|
|
60
|
+
}
|
|
61
|
+
return [UIMenu menuWithTitle:@"" children:children];
|
|
62
|
+
}];
|
|
63
|
+
}
|
|
64
|
+
#endif
|
|
65
|
+
|
|
66
|
+
NS_ASSUME_NONNULL_END
|
|
@@ -9,6 +9,15 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
9
9
|
typedef struct {
|
|
10
10
|
BOOL copyAsMarkdown;
|
|
11
11
|
BOOL copyImageURL;
|
|
12
|
+
BOOL copyEnabled;
|
|
13
|
+
// Copy writes Markdown as plain text instead of rich text.
|
|
14
|
+
BOOL copyFormatMarkdown;
|
|
15
|
+
// Drop every system-provided item from the text selection menu.
|
|
16
|
+
BOOL systemItemsNone;
|
|
17
|
+
BOOL codeBlockCopyButton;
|
|
18
|
+
// Copy hands the selection to the owner (JS `onCopy`) instead of writing the
|
|
19
|
+
// pasteboard.
|
|
20
|
+
BOOL interceptCopy;
|
|
12
21
|
// The owner must keep these strings alive for the duration of the call (the
|
|
13
22
|
// view holds them in strong ivars).
|
|
14
23
|
__unsafe_unretained NSString *_Nullable copyLabel;
|
|
@@ -18,6 +27,10 @@ typedef struct {
|
|
|
18
27
|
__unsafe_unretained NSArray<NSString *> *_Nullable copyImageUrlPluralTemplates;
|
|
19
28
|
} ENRMSelectionMenuConfig;
|
|
20
29
|
|
|
30
|
+
/// Receives an intercepted Copy. `fullSelection` is whether the range spans the
|
|
31
|
+
/// whole attributed text, in which case `markdown` is the cached markdown.
|
|
32
|
+
typedef void (^ENRMCopyRequestHandler)(NSString *markdown, NSString *text, BOOL fullSelection);
|
|
33
|
+
|
|
21
34
|
/// Resolves the "Copy Image URL(s)" menu title for a given image count.
|
|
22
35
|
/// Uses the precomputed plural templates (indexed by count, 0..100) when present;
|
|
23
36
|
/// counts > 100 use the "other" form (copyImageUrlsLabel). Otherwise the
|
|
@@ -36,6 +49,13 @@ static inline NSString *ENRMResolveImageURLsTitle(ENRMSelectionMenuConfig config
|
|
|
36
49
|
return [titleTemplate stringByReplacingOccurrencesOfString:@"{count}" withString:[@(count) stringValue]];
|
|
37
50
|
}
|
|
38
51
|
|
|
52
|
+
/// Defaults matching the JS-side defaults, used until the first props update.
|
|
53
|
+
static inline ENRMSelectionMenuConfig ENRMDefaultSelectionMenuConfig(void)
|
|
54
|
+
{
|
|
55
|
+
return (ENRMSelectionMenuConfig){
|
|
56
|
+
.copyAsMarkdown = YES, .copyImageURL = YES, .copyEnabled = YES, .codeBlockCopyButton = YES};
|
|
57
|
+
}
|
|
58
|
+
|
|
39
59
|
#ifdef __cplusplus
|
|
40
60
|
|
|
41
61
|
/// Strong owners for the label strings in ENRMSelectionMenuConfig.
|
|
@@ -63,13 +83,18 @@ template <typename T> static inline ENRMSelectionMenuLabels ENRMParseSelectionMe
|
|
|
63
83
|
};
|
|
64
84
|
}
|
|
65
85
|
|
|
66
|
-
/// Builds an ENRMSelectionMenuConfig from pre-parsed labels and
|
|
67
|
-
|
|
68
|
-
|
|
86
|
+
/// Builds an ENRMSelectionMenuConfig from pre-parsed labels and the codegen struct's flags.
|
|
87
|
+
template <typename T>
|
|
88
|
+
static inline ENRMSelectionMenuConfig ENRMBuildSelectionMenuConfig(const ENRMSelectionMenuLabels &labels, const T &src)
|
|
69
89
|
{
|
|
70
90
|
return (ENRMSelectionMenuConfig){
|
|
71
|
-
.copyAsMarkdown = copyAsMarkdown,
|
|
72
|
-
.copyImageURL = copyImageUrl,
|
|
91
|
+
.copyAsMarkdown = src.copyAsMarkdown,
|
|
92
|
+
.copyImageURL = src.copyImageUrl,
|
|
93
|
+
.copyEnabled = src.copyEnabled,
|
|
94
|
+
.copyFormatMarkdown = src.copyFormatMarkdown,
|
|
95
|
+
.systemItemsNone = src.systemItemsNone,
|
|
96
|
+
.codeBlockCopyButton = src.codeBlockCopyButton,
|
|
97
|
+
.interceptCopy = src.interceptCopy,
|
|
73
98
|
.copyLabel = labels.copyLabel,
|
|
74
99
|
.copyAsMarkdownLabel = labels.copyAsMarkdownLabel,
|
|
75
100
|
.copyImageUrlLabel = labels.copyImageUrlLabel,
|
|
@@ -90,7 +115,8 @@ extern "C" {
|
|
|
90
115
|
UIMenu *buildEditMenuForSelection(NSAttributedString *attributedText, NSRange range, NSString *_Nullable cachedMarkdown,
|
|
91
116
|
StyleConfig *styleConfig, NSArray<UIMenuElement *> *suggestedActions,
|
|
92
117
|
NSArray<UIAction *> *_Nullable customActions,
|
|
93
|
-
ENRMSelectionMenuConfig selectionMenuConfig
|
|
118
|
+
ENRMSelectionMenuConfig selectionMenuConfig,
|
|
119
|
+
ENRMCopyRequestHandler _Nullable onCopyRequest) API_AVAILABLE(ios(16.0));
|
|
94
120
|
#else
|
|
95
121
|
NSMenu *_Nullable buildEditMenuForSelection(NSAttributedString *attributedText, NSRange range,
|
|
96
122
|
NSString *_Nullable cachedMarkdown, StyleConfig *styleConfig,
|
|
@@ -10,14 +10,34 @@ static NSString *const kActionIdentifierCopy = @"com.swmansion.enriched.markdown
|
|
|
10
10
|
static NSString *const kActionIdentifierCopyMarkdown = @"com.swmansion.enriched.markdown.copyMarkdown";
|
|
11
11
|
static NSString *const kActionIdentifierCopyImageURL = @"com.swmansion.enriched.markdown.copyImageURL";
|
|
12
12
|
|
|
13
|
-
static UIAction *createCopyAction(NSAttributedString *selectedText, NSString *markdown,
|
|
14
|
-
|
|
13
|
+
static UIAction *_Nullable createCopyAction(NSAttributedString *selectedText, NSString *markdown, BOOL fullSelection,
|
|
14
|
+
StyleConfig *styleConfig, ENRMSelectionMenuConfig selectionMenuConfig,
|
|
15
|
+
ENRMCopyRequestHandler _Nullable onCopyRequest)
|
|
15
16
|
{
|
|
16
|
-
|
|
17
|
+
if (!selectionMenuConfig.copyEnabled)
|
|
18
|
+
return nil;
|
|
19
|
+
|
|
20
|
+
if (selectionMenuConfig.interceptCopy && onCopyRequest) {
|
|
21
|
+
NSString *text = selectedText.string;
|
|
22
|
+
NSString *copied = markdown.length > 0 ? markdown : text;
|
|
23
|
+
return [UIAction actionWithTitle:selectionMenuConfig.copyLabel
|
|
24
|
+
image:[RCTUIImage systemImageNamed:@"doc.on.doc"]
|
|
25
|
+
identifier:kActionIdentifierCopy
|
|
26
|
+
handler:^(__kindof UIAction *action) { onCopyRequest(copied, text, fullSelection); }];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
BOOL writesMarkdown = selectionMenuConfig.copyFormatMarkdown;
|
|
30
|
+
return [UIAction actionWithTitle:selectionMenuConfig.copyLabel
|
|
17
31
|
image:[RCTUIImage systemImageNamed:@"doc.on.doc"]
|
|
18
32
|
identifier:kActionIdentifierCopy
|
|
19
33
|
handler:^(__kindof UIAction *action) {
|
|
20
|
-
|
|
34
|
+
if (!writesMarkdown) {
|
|
35
|
+
copyAttributedStringToPasteboard(selectedText, markdown, styleConfig);
|
|
36
|
+
} else {
|
|
37
|
+
// Plain text only: with HTML/RTF alongside, rich-paste targets
|
|
38
|
+
// would take those and drop the Markdown.
|
|
39
|
+
copyStringToPasteboard(markdown.length > 0 ? markdown : selectedText.string);
|
|
40
|
+
}
|
|
21
41
|
}];
|
|
22
42
|
}
|
|
23
43
|
|
|
@@ -48,10 +68,11 @@ static UIAction *_Nullable createCopyImageURLAction(NSArray<NSString *> *imageUR
|
|
|
48
68
|
}
|
|
49
69
|
|
|
50
70
|
/// Rebuilds the standard-edit menu with the system Copy swapped for the enhanced
|
|
51
|
-
/// copy action
|
|
52
|
-
///
|
|
53
|
-
/// long-press menu of a
|
|
54
|
-
|
|
71
|
+
/// copy action, or dropped when `copyAction` is nil (Copy disabled). All other
|
|
72
|
+
/// system items are kept — dropping them would remove Select All, which on
|
|
73
|
+
/// iOS 16+ is the only way to start a selection from the long-press menu of a
|
|
74
|
+
/// non-editable text view.
|
|
75
|
+
static UIMenu *createEnhancedStandardEditMenu(UIMenu *originalMenu, UIAction *_Nullable copyAction)
|
|
55
76
|
{
|
|
56
77
|
NSMutableArray<UIMenuElement *> *children = [NSMutableArray arrayWithCapacity:originalMenu.children.count];
|
|
57
78
|
BOOL replacedCopy = NO;
|
|
@@ -59,14 +80,16 @@ static UIMenu *createEnhancedStandardEditMenu(UIMenu *originalMenu, UIAction *co
|
|
|
59
80
|
for (UIMenuElement *element in originalMenu.children) {
|
|
60
81
|
if (!replacedCopy && [element isKindOfClass:[UICommand class]] &&
|
|
61
82
|
((UICommand *)element).action == @selector(copy:)) {
|
|
62
|
-
|
|
83
|
+
if (copyAction) {
|
|
84
|
+
[children addObject:copyAction];
|
|
85
|
+
}
|
|
63
86
|
replacedCopy = YES;
|
|
64
87
|
continue;
|
|
65
88
|
}
|
|
66
89
|
[children addObject:element];
|
|
67
90
|
}
|
|
68
91
|
|
|
69
|
-
if (!replacedCopy) {
|
|
92
|
+
if (!replacedCopy && copyAction) {
|
|
70
93
|
[children insertObject:copyAction atIndex:0];
|
|
71
94
|
}
|
|
72
95
|
|
|
@@ -95,13 +118,17 @@ static void insertOptionalAction(NSMutableArray<UIMenuElement *> *array, UIActio
|
|
|
95
118
|
UIMenu *buildEditMenuForSelection(NSAttributedString *attributedText, NSRange range, NSString *_Nullable cachedMarkdown,
|
|
96
119
|
StyleConfig *styleConfig, NSArray<UIMenuElement *> *suggestedActions,
|
|
97
120
|
NSArray<UIAction *> *_Nullable customActions,
|
|
98
|
-
ENRMSelectionMenuConfig selectionMenuConfig
|
|
121
|
+
ENRMSelectionMenuConfig selectionMenuConfig,
|
|
122
|
+
ENRMCopyRequestHandler _Nullable onCopyRequest) API_AVAILABLE(ios(16.0))
|
|
99
123
|
{
|
|
100
124
|
NSAttributedString *selectedText = [attributedText attributedSubstringFromRange:range];
|
|
101
125
|
NSString *markdown = markdownForRange(attributedText, range, cachedMarkdown);
|
|
102
126
|
NSArray<NSString *> *imageURLs = imageURLsInRange(attributedText, range);
|
|
127
|
+
// Same threshold markdownForRange uses to return the cached markdown.
|
|
128
|
+
BOOL fullSelection = range.location == 0 && range.length >= attributedText.length - 1;
|
|
103
129
|
|
|
104
|
-
UIAction *copyAction =
|
|
130
|
+
UIAction *copyAction =
|
|
131
|
+
createCopyAction(selectedText, markdown, fullSelection, styleConfig, selectionMenuConfig, onCopyRequest);
|
|
105
132
|
UIAction *copyMarkdownAction = selectionMenuConfig.copyAsMarkdown
|
|
106
133
|
? createCopyMarkdownAction(markdown, selectionMenuConfig.copyAsMarkdownLabel)
|
|
107
134
|
: nil;
|
|
@@ -109,6 +136,16 @@ UIMenu *buildEditMenuForSelection(NSAttributedString *attributedText, NSRange ra
|
|
|
109
136
|
selectionMenuConfig.copyImageURL ? createCopyImageURLAction(imageURLs, selectionMenuConfig) : nil;
|
|
110
137
|
|
|
111
138
|
NSMutableArray<UIMenuElement *> *result = [NSMutableArray array];
|
|
139
|
+
|
|
140
|
+
if (selectionMenuConfig.systemItemsNone) {
|
|
141
|
+
// Skip suggestedActions entirely. An empty result still yields an (empty)
|
|
142
|
+
// menu: returning nil would make UIKit present its default menu.
|
|
143
|
+
addOptionalAction(result, copyAction);
|
|
144
|
+
addOptionalAction(result, copyMarkdownAction);
|
|
145
|
+
addOptionalAction(result, copyImageURLAction);
|
|
146
|
+
return [UIMenu menuWithChildren:[(customActions ?: @[]) arrayByAddingObjectsFromArray:result]];
|
|
147
|
+
}
|
|
148
|
+
|
|
112
149
|
BOOL foundStandardEditMenu = NO;
|
|
113
150
|
|
|
114
151
|
for (UIMenuElement *element in suggestedActions) {
|
|
@@ -128,8 +165,8 @@ UIMenu *buildEditMenuForSelection(NSAttributedString *attributedText, NSRange ra
|
|
|
128
165
|
}
|
|
129
166
|
|
|
130
167
|
if (!foundStandardEditMenu) {
|
|
131
|
-
|
|
132
|
-
insertOptionalAction(result,
|
|
168
|
+
insertOptionalAction(result, copyMarkdownAction, 0);
|
|
169
|
+
insertOptionalAction(result, copyAction, 0);
|
|
133
170
|
addOptionalAction(result, copyImageURLAction);
|
|
134
171
|
}
|
|
135
172
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#pragma once
|
|
2
|
+
#import "ENRMBlockMenu.h"
|
|
2
3
|
#import "ENRMUIKit.h"
|
|
3
4
|
#import "StyleConfig.h"
|
|
4
5
|
|
|
@@ -33,12 +34,22 @@ typedef void (^ENRMCodeBlockCopyBlock)(NSString *code, NSString *language);
|
|
|
33
34
|
|
|
34
35
|
/// Suppresses the long-press copy menu on this block. Mirrors the
|
|
35
36
|
/// `contextMenuHidden` prop; the header copy button is a visible control, not a
|
|
36
|
-
/// context menu, and stays.
|
|
37
|
+
/// context menu, and stays (hide it with `menuOptions.copyButtonHidden`).
|
|
37
38
|
@property (nonatomic, assign) BOOL contextMenuHidden;
|
|
38
39
|
|
|
40
|
+
/// Copy / Copy as Markdown visibility, Copy format and the header copy button
|
|
41
|
+
/// from `selectionMenuConfig`. `copyHidden` also drops the VoiceOver copy action;
|
|
42
|
+
/// the header button and VoiceOver action always copy the raw code.
|
|
43
|
+
@property (nonatomic, assign) ENRMBlockMenuOptions menuOptions;
|
|
44
|
+
|
|
45
|
+
/// Set by the host; receives the Copy item's content when
|
|
46
|
+
/// `menuOptions.interceptCopy` is set.
|
|
47
|
+
@property (nonatomic, copy, nullable) ENRMBlockCopyRequestBlock onCopyRequest;
|
|
48
|
+
|
|
39
49
|
// Fired when the code is copied (header button, context-menu Copy, or the
|
|
40
50
|
// VoiceOver copy action); set by the host to bridge up to the JS onCopyPress
|
|
41
|
-
// event. Not fired for "Copy as Markdown"
|
|
51
|
+
// event. Not fired for "Copy as Markdown", nor for the context-menu Copy when
|
|
52
|
+
// `menuOptions.copyWritesMarkdown` is set.
|
|
42
53
|
@property (nonatomic, copy, nullable) ENRMCodeBlockCopyBlock onCopyPress;
|
|
43
54
|
|
|
44
55
|
@end
|
|
@@ -175,6 +175,13 @@ static BOOL ENRMColorIsDark(RCTUIColor *color)
|
|
|
175
175
|
_copyButton.accessibilityLabel = _copyLabel;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
- (void)setMenuOptions:(ENRMBlockMenuOptions)menuOptions
|
|
179
|
+
{
|
|
180
|
+
_menuOptions = menuOptions;
|
|
181
|
+
// Hiding the button leaves the header (and its height) untouched.
|
|
182
|
+
_copyButton.hidden = menuOptions.copyButtonHidden;
|
|
183
|
+
}
|
|
184
|
+
|
|
178
185
|
- (void)setPending:(BOOL)pending
|
|
179
186
|
{
|
|
180
187
|
if (_pending == pending) {
|
|
@@ -490,24 +497,17 @@ static BOOL ENRMColorIsDark(RCTUIColor *color)
|
|
|
490
497
|
if (_pending || self.contextMenuHidden) {
|
|
491
498
|
return nil;
|
|
492
499
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
[UIAction actionWithTitle:self.copyAsMarkdownLabel
|
|
505
|
-
image:[RCTUIImage systemImageNamed:@"doc.text"]
|
|
506
|
-
identifier:nil
|
|
507
|
-
handler:^(__kindof UIAction *action) { [self copyMarkdownToPasteboard]; }];
|
|
508
|
-
|
|
509
|
-
return [UIMenu menuWithTitle:@"" children:@[ copyCode, copyMarkdown ]];
|
|
510
|
-
}];
|
|
500
|
+
__weak __typeof(self) weakSelf = self;
|
|
501
|
+
return ENRMBlockContextMenuConfiguration(
|
|
502
|
+
self.menuOptions, self.copyLabel, self.copyAsMarkdownLabel, ^{ [weakSelf copyCodeToPasteboard]; },
|
|
503
|
+
^{ [weakSelf copyMarkdownToPasteboard]; }, ^{ [weakSelf requestCopy]; });
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
- (void)requestCopy
|
|
507
|
+
{
|
|
508
|
+
if (self.onCopyRequest) {
|
|
509
|
+
self.onCopyRequest([self fencedMarkdown], _cachedCode ?: @"");
|
|
510
|
+
}
|
|
511
511
|
}
|
|
512
512
|
#endif
|
|
513
513
|
|
|
@@ -546,7 +546,7 @@ static BOOL ENRMColorIsDark(RCTUIColor *color)
|
|
|
546
546
|
// button subview from VoiceOver; expose the copy action explicitly instead.
|
|
547
547
|
- (NSArray<UIAccessibilityCustomAction *> *)accessibilityCustomActions
|
|
548
548
|
{
|
|
549
|
-
if (_pending || self.copyLabel.length == 0) {
|
|
549
|
+
if (_pending || self.menuOptions.copyHidden || self.copyLabel.length == 0) {
|
|
550
550
|
return @[];
|
|
551
551
|
}
|
|
552
552
|
UIAccessibilityCustomAction *copyAction =
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#pragma once
|
|
2
|
+
#import "ENRMBlockMenu.h"
|
|
2
3
|
#import "ENRMUIKit.h"
|
|
3
4
|
#import "StyleConfig.h"
|
|
4
5
|
|
|
@@ -35,6 +36,13 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
35
36
|
/// `contextMenuHidden` prop; selection and link handling are unaffected.
|
|
36
37
|
@property (nonatomic, assign) BOOL contextMenuHidden;
|
|
37
38
|
|
|
39
|
+
/// Copy / Copy as Markdown visibility and Copy format from `selectionMenuConfig`.
|
|
40
|
+
@property (nonatomic, assign) ENRMBlockMenuOptions menuOptions;
|
|
41
|
+
|
|
42
|
+
/// Set by the host; receives the Copy item's content when
|
|
43
|
+
/// `menuOptions.interceptCopy` is set.
|
|
44
|
+
@property (nonatomic, copy, nullable) ENRMBlockCopyRequestBlock onCopyRequest;
|
|
45
|
+
|
|
38
46
|
@end
|
|
39
47
|
|
|
40
48
|
NS_ASSUME_NONNULL_END
|
|
@@ -130,25 +130,17 @@
|
|
|
130
130
|
if (self.contextMenuHidden) {
|
|
131
131
|
return nil;
|
|
132
132
|
}
|
|
133
|
+
__weak __typeof(self) weakSelf = self;
|
|
134
|
+
return ENRMBlockContextMenuConfiguration(
|
|
135
|
+
self.menuOptions, self.copyLabel, self.copyAsMarkdownLabel, ^{ [weakSelf copyLatexToPasteboard]; },
|
|
136
|
+
^{ [weakSelf copyMarkdownToPasteboard]; }, ^{ [weakSelf requestCopy]; });
|
|
137
|
+
}
|
|
133
138
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
[UIAction actionWithTitle:self.copyLabel
|
|
140
|
-
image:[RCTUIImage systemImageNamed:@"doc.on.doc"]
|
|
141
|
-
identifier:nil
|
|
142
|
-
handler:^(__kindof UIAction *action) { [self copyLatexToPasteboard]; }];
|
|
143
|
-
|
|
144
|
-
UIAction *copyMarkdown =
|
|
145
|
-
[UIAction actionWithTitle:self.copyAsMarkdownLabel
|
|
146
|
-
image:[RCTUIImage systemImageNamed:@"doc.text"]
|
|
147
|
-
identifier:nil
|
|
148
|
-
handler:^(__kindof UIAction *action) { [self copyMarkdownToPasteboard]; }];
|
|
149
|
-
|
|
150
|
-
return [UIMenu menuWithTitle:@"" children:@[ copyPlainText, copyMarkdown ]];
|
|
151
|
-
}];
|
|
139
|
+
- (void)requestCopy
|
|
140
|
+
{
|
|
141
|
+
if (self.onCopyRequest) {
|
|
142
|
+
self.onCopyRequest([NSString stringWithFormat:@"$$\n%@\n$$", _cachedLatex ?: @""], _cachedLatex ?: @"");
|
|
143
|
+
}
|
|
152
144
|
}
|
|
153
145
|
#endif
|
|
154
146
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#pragma once
|
|
2
|
+
#import "ENRMBlockMenu.h"
|
|
2
3
|
#import "ENRMUIKit.h"
|
|
3
4
|
#import "ParagraphStyleUtils.h"
|
|
4
5
|
#import "StyleConfig.h"
|
|
@@ -56,6 +57,13 @@ typedef void (^TableLinkPressBlock)(NSString *url);
|
|
|
56
57
|
/// `contextMenuHidden` prop; selection and link handling are unaffected.
|
|
57
58
|
@property (nonatomic, assign) BOOL contextMenuHidden;
|
|
58
59
|
|
|
60
|
+
/// Copy / Copy as Markdown visibility and Copy format from `selectionMenuConfig`.
|
|
61
|
+
@property (nonatomic, assign) ENRMBlockMenuOptions menuOptions;
|
|
62
|
+
|
|
63
|
+
/// Set by the host; receives the Copy item's content when
|
|
64
|
+
/// `menuOptions.interceptCopy` is set.
|
|
65
|
+
@property (nonatomic, copy, nullable) ENRMBlockCopyRequestBlock onCopyRequest;
|
|
66
|
+
|
|
59
67
|
@property (nonatomic, readonly) NSUInteger rowCount;
|
|
60
68
|
|
|
61
69
|
- (void)animateNewRowsFromPreviousCount:(NSUInteger)previousRowCount duration:(NSTimeInterval)duration;
|
|
@@ -467,25 +467,17 @@ static void ENRMTableComputeLayout(NSArray<NSArray<TableCellData *> *> *rows, NS
|
|
|
467
467
|
if (self.contextMenuHidden) {
|
|
468
468
|
return nil;
|
|
469
469
|
}
|
|
470
|
+
__weak __typeof(self) weakSelf = self;
|
|
471
|
+
return ENRMBlockContextMenuConfiguration(
|
|
472
|
+
self.menuOptions, self.copyLabel, self.copyAsMarkdownLabel, ^{ [weakSelf copyTableToPasteboard]; },
|
|
473
|
+
^{ [weakSelf copyMarkdownToPasteboard]; }, ^{ [weakSelf requestCopy]; });
|
|
474
|
+
}
|
|
470
475
|
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
[UIAction actionWithTitle:self.copyAsMarkdownLabel
|
|
477
|
-
image:[RCTUIImage systemImageNamed:@"doc.text"]
|
|
478
|
-
identifier:nil
|
|
479
|
-
handler:^(__kindof UIAction *action) { [self copyMarkdownToPasteboard]; }];
|
|
480
|
-
|
|
481
|
-
UIAction *copyPlainText =
|
|
482
|
-
[UIAction actionWithTitle:self.copyLabel
|
|
483
|
-
image:[RCTUIImage systemImageNamed:@"doc.on.doc"]
|
|
484
|
-
identifier:nil
|
|
485
|
-
handler:^(__kindof UIAction *action) { [self copyTableToPasteboard]; }];
|
|
486
|
-
|
|
487
|
-
return [UIMenu menuWithTitle:@"" children:@[ copyPlainText, copyMarkdown ]];
|
|
488
|
-
}];
|
|
476
|
+
- (void)requestCopy
|
|
477
|
+
{
|
|
478
|
+
if (self.onCopyRequest) {
|
|
479
|
+
self.onCopyRequest(_cachedMarkdown ?: @"", [self buildPlainTextFromRows] ?: @"");
|
|
480
|
+
}
|
|
489
481
|
}
|
|
490
482
|
#endif // !TARGET_OS_OSX
|
|
491
483
|
|
|
@@ -256,6 +256,14 @@ export interface CopyPressEvent {
|
|
|
256
256
|
language: string;
|
|
257
257
|
}
|
|
258
258
|
|
|
259
|
+
export interface CopyRequestEvent {
|
|
260
|
+
// 'selection' | 'math' | 'table' | 'code'
|
|
261
|
+
source: string;
|
|
262
|
+
markdown: string;
|
|
263
|
+
text: string;
|
|
264
|
+
whole: boolean;
|
|
265
|
+
}
|
|
266
|
+
|
|
259
267
|
export interface ContextMenuItemConfig {
|
|
260
268
|
text: string;
|
|
261
269
|
icon?: string;
|
|
@@ -276,6 +284,16 @@ export interface SelectionMenuConfig {
|
|
|
276
284
|
// Precomputed plural templates indexed by image count (0..100). Empty when no
|
|
277
285
|
// pluralLabels are set; native then uses copyImageUrlLabel/copyImageUrlsLabel.
|
|
278
286
|
copyImageUrlPluralTemplates: ReadonlyArray<string>;
|
|
287
|
+
// false removes the Copy item from the text and block menus.
|
|
288
|
+
copyEnabled: boolean;
|
|
289
|
+
// true: Copy writes Markdown (plain text) instead of rich text / raw block content.
|
|
290
|
+
copyFormatMarkdown: boolean;
|
|
291
|
+
// true: the text selection menu drops every system-provided item.
|
|
292
|
+
systemItemsNone: boolean;
|
|
293
|
+
// false hides the copy button in the code block header.
|
|
294
|
+
codeBlockCopyButton: boolean;
|
|
295
|
+
// true: the Copy item emits onCopyRequest instead of writing the clipboard.
|
|
296
|
+
interceptCopy: boolean;
|
|
279
297
|
}
|
|
280
298
|
|
|
281
299
|
export interface OnContextMenuItemPressEvent {
|
|
@@ -402,6 +420,12 @@ export interface NativeProps extends ViewProps {
|
|
|
402
420
|
* action. Receives the copied code and its language.
|
|
403
421
|
*/
|
|
404
422
|
onCopyPress?: CodegenTypes.BubblingEventHandler<CopyPressEvent>;
|
|
423
|
+
/**
|
|
424
|
+
* Fired instead of writing the clipboard when the Copy item of the text
|
|
425
|
+
* selection menu or a table / math / code block menu is pressed while
|
|
426
|
+
* `selectionMenuConfig.interceptCopy` is set.
|
|
427
|
+
*/
|
|
428
|
+
onCopyRequest?: CodegenTypes.BubblingEventHandler<CopyRequestEvent>;
|
|
405
429
|
/**
|
|
406
430
|
* Controls whether the system link preview is shown on long press (iOS only).
|
|
407
431
|
*
|
|
@@ -257,6 +257,14 @@ export interface CopyPressEvent {
|
|
|
257
257
|
language: string;
|
|
258
258
|
}
|
|
259
259
|
|
|
260
|
+
export interface CopyRequestEvent {
|
|
261
|
+
// 'selection' | 'math' | 'table' | 'code'
|
|
262
|
+
source: string;
|
|
263
|
+
markdown: string;
|
|
264
|
+
text: string;
|
|
265
|
+
whole: boolean;
|
|
266
|
+
}
|
|
267
|
+
|
|
260
268
|
export interface ContextMenuItemConfig {
|
|
261
269
|
text: string;
|
|
262
270
|
icon?: string;
|
|
@@ -277,6 +285,16 @@ export interface SelectionMenuConfig {
|
|
|
277
285
|
// Precomputed plural templates indexed by image count (0..100). Empty when no
|
|
278
286
|
// pluralLabels are set; native then uses copyImageUrlLabel/copyImageUrlsLabel.
|
|
279
287
|
copyImageUrlPluralTemplates: ReadonlyArray<string>;
|
|
288
|
+
// false removes the Copy item from the text and block menus.
|
|
289
|
+
copyEnabled: boolean;
|
|
290
|
+
// true: Copy writes Markdown (plain text) instead of rich text / raw block content.
|
|
291
|
+
copyFormatMarkdown: boolean;
|
|
292
|
+
// true: the text selection menu drops every system-provided item.
|
|
293
|
+
systemItemsNone: boolean;
|
|
294
|
+
// false hides the copy button in the code block header.
|
|
295
|
+
codeBlockCopyButton: boolean;
|
|
296
|
+
// true: the Copy item emits onCopyRequest instead of writing the clipboard.
|
|
297
|
+
interceptCopy: boolean;
|
|
280
298
|
}
|
|
281
299
|
|
|
282
300
|
export interface OnContextMenuItemPressEvent {
|
|
@@ -403,6 +421,12 @@ export interface NativeProps extends ViewProps {
|
|
|
403
421
|
* action. Receives the copied code and its language.
|
|
404
422
|
*/
|
|
405
423
|
onCopyPress?: CodegenTypes.BubblingEventHandler<CopyPressEvent>;
|
|
424
|
+
/**
|
|
425
|
+
* Fired instead of writing the clipboard when the Copy item of the text
|
|
426
|
+
* selection menu or a table / math / code block menu is pressed while
|
|
427
|
+
* `selectionMenuConfig.interceptCopy` is set.
|
|
428
|
+
*/
|
|
429
|
+
onCopyRequest?: CodegenTypes.BubblingEventHandler<CopyRequestEvent>;
|
|
406
430
|
/**
|
|
407
431
|
* Controls whether the system link preview is shown on long press (iOS only).
|
|
408
432
|
*
|
package/lib/module/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["default","EnrichedMarkdownText","DEFAULT_ACCESSIBILITY_LABELS","resolveAccessibilityLabels","EnrichedMarkdownTextInput"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,OAAO,IAAIC,oBAAoB,QAAQ,kCAA+B;
|
|
1
|
+
{"version":3,"names":["default","EnrichedMarkdownText","DEFAULT_ACCESSIBILITY_LABELS","resolveAccessibilityLabels","EnrichedMarkdownTextInput"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":";;AAAA,SAASA,OAAO,IAAIC,oBAAoB,QAAQ,kCAA+B;AAsB/E,SACEC,4BAA4B,EAC5BC,0BAA0B,QACrB,iCAA8B;AAErC,SAASC,yBAAyB,QAAQ,gCAA6B","ignoreList":[]}
|
|
@@ -73,6 +73,7 @@ export const EnrichedMarkdownText = ({
|
|
|
73
73
|
onTaskListItemPress,
|
|
74
74
|
enableTaskListItemToggle = true,
|
|
75
75
|
onCopyPress,
|
|
76
|
+
onCopy,
|
|
76
77
|
enableLinkPreview,
|
|
77
78
|
selectable = true,
|
|
78
79
|
md4cFlags = defaultMd4cFlags,
|
|
@@ -183,6 +184,24 @@ export const EnrichedMarkdownText = ({
|
|
|
183
184
|
language
|
|
184
185
|
});
|
|
185
186
|
}, [onCopyPress]);
|
|
187
|
+
const handleCopyRequest = useCallback(
|
|
188
|
+
// Codegen types `source` as a plain string; native only sends the four
|
|
189
|
+
// values of CopyEvent['source'].
|
|
190
|
+
e => {
|
|
191
|
+
const {
|
|
192
|
+
source,
|
|
193
|
+
markdown: copied,
|
|
194
|
+
text,
|
|
195
|
+
whole
|
|
196
|
+
} = e.nativeEvent;
|
|
197
|
+
onCopy?.({
|
|
198
|
+
source: source,
|
|
199
|
+
markdown: copied,
|
|
200
|
+
text,
|
|
201
|
+
whole
|
|
202
|
+
});
|
|
203
|
+
}, [onCopy]);
|
|
204
|
+
const interceptCopy = onCopy != null;
|
|
186
205
|
const tableMode = streamingConfig?.tableMode ?? 'progressive';
|
|
187
206
|
const codeBlockMode = streamingConfig?.codeBlockMode ?? 'progressive';
|
|
188
207
|
const normalizedStreamingConfig = useMemo(() => ({
|
|
@@ -194,6 +213,8 @@ export const EnrichedMarkdownText = ({
|
|
|
194
213
|
// `as unknown` cast; the public type only exposes the object shape.
|
|
195
214
|
const config = selectionMenuConfig;
|
|
196
215
|
const copy = normalizeMenuItem(config?.copy, true, DEFAULT_COPY_LABEL);
|
|
216
|
+
const copyFormat = config?.copy?.format;
|
|
217
|
+
const codeBlockCopyButton = normalizeMenuItem(config?.codeBlockCopyButton, true, '');
|
|
197
218
|
const copyAsMarkdown = normalizeLegacyBooleanMenuItem(config?.copyAsMarkdown, 'selectionMenuConfig', 'copyAsMarkdown', true, DEFAULT_COPY_AS_MARKDOWN_LABEL);
|
|
198
219
|
const copyImageUrl = normalizeLegacyBooleanMenuItem(config?.copyImageUrl, 'selectionMenuConfig', 'copyImageUrl', true, DEFAULT_COPY_IMAGE_URL_LABEL);
|
|
199
220
|
const pluralLabels = config?.copyImageUrl?.pluralLabels;
|
|
@@ -204,9 +225,14 @@ export const EnrichedMarkdownText = ({
|
|
|
204
225
|
copyAsMarkdownLabel: copyAsMarkdown.label,
|
|
205
226
|
copyImageUrlLabel: copyImageUrl.label,
|
|
206
227
|
copyImageUrlsLabel: pluralLabels?.other ?? DEFAULT_COPY_IMAGE_URLS_LABEL,
|
|
207
|
-
copyImageUrlPluralTemplates: buildPluralTemplates(pluralLabels, copyImageUrl.label)
|
|
228
|
+
copyImageUrlPluralTemplates: buildPluralTemplates(pluralLabels, copyImageUrl.label),
|
|
229
|
+
copyEnabled: copy.enabled,
|
|
230
|
+
copyFormatMarkdown: copyFormat === 'markdown',
|
|
231
|
+
systemItemsNone: config?.systemItems === 'none',
|
|
232
|
+
codeBlockCopyButton: codeBlockCopyButton.enabled,
|
|
233
|
+
interceptCopy
|
|
208
234
|
};
|
|
209
|
-
}, [selectionMenuConfig]);
|
|
235
|
+
}, [selectionMenuConfig, interceptCopy]);
|
|
210
236
|
const resolvedAccessibilityLabels = useMemo(() => resolveAccessibilityLabels(accessibilityLabels), [accessibilityLabels]);
|
|
211
237
|
const sharedProps = {
|
|
212
238
|
markdown,
|
|
@@ -216,6 +242,7 @@ export const EnrichedMarkdownText = ({
|
|
|
216
242
|
onTaskListItemPress: handleTaskListItemPress,
|
|
217
243
|
enableTaskListItemToggle,
|
|
218
244
|
onCopyPress: handleCopyPress,
|
|
245
|
+
onCopyRequest: handleCopyRequest,
|
|
219
246
|
enableLinkPreview: onLinkLongPress == null && (enableLinkPreview ?? true),
|
|
220
247
|
selectable,
|
|
221
248
|
md4cFlags: normalizedMd4cFlags,
|