react-native-enriched-markdown-scaffold 0.6.0-scaffold.8 → 0.7.4-scaffold.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ReactNativeEnrichedMarkdown.podspec +62 -11
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerDelegate.java +3 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownManagerInterface.java +1 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerDelegate.java +9 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextInputManagerInterface.java +3 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerDelegate.java +3 -0
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedMarkdownTextManagerInterface.java +1 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.cpp +20 -0
- package/android/generated/jni/react/renderer/components/EnrichedMarkdownTextSpec/Props.h +744 -0
- package/android/src/main/baseline-prof.txt +75 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdown.kt +62 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownInternalText.kt +10 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownManager.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownText.kt +9 -2
- package/android/src/main/java/com/swmansion/enriched/markdown/EnrichedMarkdownTextManager.kt +10 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/accessibility/AccessibilityLabels.kt +15 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/accessibility/MarkdownAccessibilityHelper.kt +44 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputManager.kt +52 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/EnrichedMarkdownTextInputView.kt +89 -11
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/FormattingStore.kt +19 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputParser.kt +22 -3
- package/android/src/main/java/com/swmansion/enriched/markdown/input/formatting/InputRemend.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/input/layout/InputMeasurementStore.kt +7 -1
- package/android/src/main/java/com/swmansion/enriched/markdown/input/toolbar/InputContextMenu.kt +54 -13
- package/android/src/main/java/com/swmansion/enriched/markdown/parser/MarkdownASTNode.kt +17 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/renderer/SpanStyleCache.kt +4 -4
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/common/MarkdownViewManagerUtils.kt +30 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/LinkEvents.kt +22 -0
- package/android/src/main/java/com/swmansion/enriched/markdown/utils/text/view/SelectionActionMode.kt +36 -7
- package/android/src/main/java/com/swmansion/enriched/markdown/views/TableContainerView.kt +73 -3
- package/android/src/main/jni/CMakeLists.txt +15 -1
- package/android/src/math/java/com/swmansion/enriched/markdown/views/MathContainerView.kt +24 -2
- package/app.plugin.js +2 -1
- package/cpp/parser/MD4CParser.cpp +1 -1
- package/cpp/wasm/build.sh +9 -9
- package/docs/ACCESSIBILITY.md +172 -0
- package/docs/API_REFERENCE.md +948 -0
- package/docs/COPY_OPTIONS.md +127 -0
- package/docs/ELEMENTS_STRUCTURE.md +112 -0
- package/docs/IMAGE_CACHING.md +25 -0
- package/docs/INPUT.md +233 -0
- package/docs/LATEX_MATH.md +136 -0
- package/docs/MACOS.md +19 -0
- package/docs/MARKDOWN_STREAMING.md +36 -0
- package/docs/MENTIONS.md +97 -0
- package/docs/RTL.md +82 -0
- package/docs/STYLES.md +519 -0
- package/docs/TEXT.md +110 -0
- package/docs/WEB.md +49 -0
- package/ios/EnrichedMarkdown.mm +82 -25
- package/ios/EnrichedMarkdownText.mm +37 -5
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.cpp +20 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/Props.h +744 -0
- package/ios/generated/ReactCodegen/EnrichedMarkdownTextSpec/RCTComponentViewHelpers.h +15 -0
- package/ios/input/ENRMFormattingStore.h +5 -0
- package/ios/input/ENRMFormattingStore.mm +34 -0
- package/ios/input/ENRMInputTextView.h +2 -0
- package/ios/input/ENRMInputTextView.mm +8 -17
- package/ios/input/ENRMMarkdownSerializer.mm +1 -1
- package/ios/input/EnrichedMarkdownTextInput+ContextMenu.mm +71 -53
- package/ios/input/EnrichedMarkdownTextInput+Internal.h +26 -0
- package/ios/input/EnrichedMarkdownTextInput.h +1 -0
- package/ios/input/EnrichedMarkdownTextInput.mm +230 -29
- package/ios/input/styles/ENRMBoldStyleHandler.mm +3 -2
- package/ios/input/styles/ENRMItalicStyleHandler.mm +3 -2
- package/ios/renderer/AttributedRenderer.m +51 -6
- package/ios/renderer/ENRMMathInlineRenderer.m +5 -1
- package/ios/styles/ENRMFontSlot.h +1 -1
- package/ios/utils/ENRMAccessibilityLabels.h +36 -0
- package/ios/utils/ENRMAccessibilityLabels.m +5 -0
- package/ios/utils/EditMenuUtils+macOS.m +4 -5
- package/ios/utils/EditMenuUtils.h +70 -0
- package/ios/utils/EditMenuUtils.m +14 -11
- package/ios/utils/LastElementUtils.h +39 -2
- package/ios/utils/MarkdownAccessibilityElementBuilder.h +18 -16
- package/ios/utils/MarkdownAccessibilityElementBuilder.m +64 -32
- package/ios/utils/ParagraphStyleUtils.m +21 -2
- package/ios/views/ENRMMathContainerView.h +8 -0
- package/ios/views/ENRMMathContainerView.m +7 -5
- package/ios/views/ENRMTableIOSGridView.h +40 -0
- package/ios/views/ENRMTableIOSGridView.m +216 -0
- package/ios/views/EnrichedMarkdownInternalText.h +2 -0
- package/ios/views/EnrichedMarkdownInternalText.m +2 -0
- package/ios/views/TableContainerView.h +8 -0
- package/ios/views/TableContainerView.m +72 -128
- package/lib/module/EnrichedMarkdownNativeComponent.ts +37 -0
- package/lib/module/EnrichedMarkdownTextInput.js +48 -1
- package/lib/module/EnrichedMarkdownTextInput.js.map +1 -1
- package/lib/module/EnrichedMarkdownTextInputNativeComponent.ts +35 -0
- package/lib/module/EnrichedMarkdownTextNativeComponent.ts +37 -0
- package/lib/module/accessibilityLabelDefaults.js +64 -0
- package/lib/module/accessibilityLabelDefaults.js.map +1 -0
- package/lib/module/index.js +1 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/native/EnrichedMarkdownText.js +72 -4
- package/lib/module/native/EnrichedMarkdownText.js.map +1 -1
- package/lib/module/normalizeMenuItem.js +38 -0
- package/lib/module/normalizeMenuItem.js.map +1 -0
- package/lib/module/types/AccessibilityLabels.js +2 -0
- package/lib/module/types/AccessibilityLabels.js.map +1 -0
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts +34 -0
- package/lib/typescript/src/EnrichedMarkdownNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts +55 -1
- package/lib/typescript/src/EnrichedMarkdownTextInput.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts +30 -0
- package/lib/typescript/src/EnrichedMarkdownTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts +34 -0
- package/lib/typescript/src/EnrichedMarkdownTextNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/accessibilityLabelDefaults.d.ts +16 -0
- package/lib/typescript/src/accessibilityLabelDefaults.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +4 -2
- 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/normalizeMenuItem.d.ts +7 -0
- package/lib/typescript/src/normalizeMenuItem.d.ts.map +1 -0
- package/lib/typescript/src/types/AccessibilityLabels.d.ts +114 -0
- package/lib/typescript/src/types/AccessibilityLabels.d.ts.map +1 -0
- package/lib/typescript/src/types/MarkdownTextProps.d.ts +78 -9
- package/lib/typescript/src/types/MarkdownTextProps.d.ts.map +1 -1
- package/package.json +4 -2
- package/plugin/build/withAndroidMath.js +19 -0
- package/plugin/build/withIosMath.js +28 -0
- package/plugin/build/withReactNativeEnrichedMarkdown.js +11 -0
- package/plugin/tsconfig.build.json +16 -0
- package/src/EnrichedMarkdownNativeComponent.ts +37 -0
- package/src/EnrichedMarkdownTextInput.tsx +117 -1
- package/src/EnrichedMarkdownTextInputNativeComponent.ts +35 -0
- package/src/EnrichedMarkdownTextNativeComponent.ts +37 -0
- package/src/accessibilityLabelDefaults.ts +83 -0
- package/src/index.tsx +11 -0
- package/src/native/EnrichedMarkdownText.tsx +120 -6
- package/src/normalizeMenuItem.ts +52 -0
- package/src/types/AccessibilityLabels.ts +118 -0
- package/src/types/MarkdownTextProps.ts +74 -9
- package/lib/module/plugin/withAndroidMath.js +0 -20
- package/lib/module/plugin/withAndroidMath.js.map +0 -1
- package/lib/module/plugin/withIosMath.js +0 -23
- package/lib/module/plugin/withIosMath.js.map +0 -1
- package/lib/module/plugin/withReactNativeEnrichedMarkdown.js +0 -16
- package/lib/module/plugin/withReactNativeEnrichedMarkdown.js.map +0 -1
- package/lib/typescript/src/plugin/withAndroidMath.d.ts +0 -5
- package/lib/typescript/src/plugin/withAndroidMath.d.ts.map +0 -1
- package/lib/typescript/src/plugin/withIosMath.d.ts +0 -5
- package/lib/typescript/src/plugin/withIosMath.d.ts.map +0 -1
- package/lib/typescript/src/plugin/withReactNativeEnrichedMarkdown.d.ts +0 -6
- package/lib/typescript/src/plugin/withReactNativeEnrichedMarkdown.d.ts.map +0 -1
- /package/{src/plugin → plugin/src}/withAndroidMath.ts +0 -0
- /package/{src/plugin → plugin/src}/withIosMath.ts +0 -0
- /package/{src/plugin → plugin/src}/withReactNativeEnrichedMarkdown.ts +0 -0
|
@@ -9,14 +9,84 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
9
9
|
typedef struct {
|
|
10
10
|
BOOL copyAsMarkdown;
|
|
11
11
|
BOOL copyImageURL;
|
|
12
|
+
// The owner must keep these strings alive for the duration of the call (the
|
|
13
|
+
// view holds them in strong ivars).
|
|
14
|
+
__unsafe_unretained NSString *_Nullable copyLabel;
|
|
15
|
+
__unsafe_unretained NSString *_Nullable copyAsMarkdownLabel;
|
|
16
|
+
__unsafe_unretained NSString *_Nullable copyImageUrlLabel;
|
|
17
|
+
__unsafe_unretained NSString *_Nullable copyImageUrlsLabel;
|
|
18
|
+
__unsafe_unretained NSArray<NSString *> *_Nullable copyImageUrlPluralTemplates;
|
|
12
19
|
} ENRMSelectionMenuConfig;
|
|
13
20
|
|
|
21
|
+
/// Resolves the "Copy Image URL(s)" menu title for a given image count.
|
|
22
|
+
/// Uses the precomputed plural templates (indexed by count, 0..100) when present;
|
|
23
|
+
/// counts > 100 use the "other" form (copyImageUrlsLabel). Otherwise the
|
|
24
|
+
/// singular/`{count}` templates. All labels are resolved JS-side.
|
|
25
|
+
static inline NSString *ENRMResolveImageURLsTitle(ENRMSelectionMenuConfig config, NSUInteger count)
|
|
26
|
+
{
|
|
27
|
+
NSArray<NSString *> *templates = config.copyImageUrlPluralTemplates;
|
|
28
|
+
NSString *titleTemplate;
|
|
29
|
+
if (count < templates.count) {
|
|
30
|
+
titleTemplate = templates[count];
|
|
31
|
+
} else if (count == 1) {
|
|
32
|
+
titleTemplate = config.copyImageUrlLabel;
|
|
33
|
+
} else {
|
|
34
|
+
titleTemplate = config.copyImageUrlsLabel;
|
|
35
|
+
}
|
|
36
|
+
return [titleTemplate stringByReplacingOccurrencesOfString:@"{count}" withString:[@(count) stringValue]];
|
|
37
|
+
}
|
|
38
|
+
|
|
14
39
|
#ifdef __cplusplus
|
|
40
|
+
|
|
41
|
+
/// Strong owners for the label strings in ENRMSelectionMenuConfig.
|
|
42
|
+
struct ENRMSelectionMenuLabels {
|
|
43
|
+
NSString *copyLabel = nil;
|
|
44
|
+
NSString *copyAsMarkdownLabel = nil;
|
|
45
|
+
NSString *copyImageUrlLabel = nil;
|
|
46
|
+
NSString *copyImageUrlsLabel = nil;
|
|
47
|
+
NSArray<NSString *> *copyImageUrlPluralTemplates = nil;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/// Converts a codegen SelectionMenuConfig struct into strong NSString labels.
|
|
51
|
+
template <typename T> static inline ENRMSelectionMenuLabels ENRMParseSelectionMenuLabels(const T &src)
|
|
52
|
+
{
|
|
53
|
+
NSMutableArray<NSString *> *templates = [NSMutableArray array];
|
|
54
|
+
for (const auto &s : src.copyImageUrlPluralTemplates) {
|
|
55
|
+
[templates addObject:[[NSString alloc] initWithUTF8String:s.c_str()]];
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
[[NSString alloc] initWithUTF8String:src.copyLabel.c_str()],
|
|
59
|
+
[[NSString alloc] initWithUTF8String:src.copyAsMarkdownLabel.c_str()],
|
|
60
|
+
[[NSString alloc] initWithUTF8String:src.copyImageUrlLabel.c_str()],
|
|
61
|
+
[[NSString alloc] initWithUTF8String:src.copyImageUrlsLabel.c_str()],
|
|
62
|
+
templates,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/// Builds an ENRMSelectionMenuConfig from pre-parsed labels and boolean flags.
|
|
67
|
+
static inline ENRMSelectionMenuConfig ENRMBuildSelectionMenuConfig(const ENRMSelectionMenuLabels &labels,
|
|
68
|
+
bool copyAsMarkdown, bool copyImageUrl)
|
|
69
|
+
{
|
|
70
|
+
return (ENRMSelectionMenuConfig){
|
|
71
|
+
.copyAsMarkdown = copyAsMarkdown,
|
|
72
|
+
.copyImageURL = copyImageUrl,
|
|
73
|
+
.copyLabel = labels.copyLabel,
|
|
74
|
+
.copyAsMarkdownLabel = labels.copyAsMarkdownLabel,
|
|
75
|
+
.copyImageUrlLabel = labels.copyImageUrlLabel,
|
|
76
|
+
.copyImageUrlsLabel = labels.copyImageUrlsLabel,
|
|
77
|
+
.copyImageUrlPluralTemplates = labels.copyImageUrlPluralTemplates,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
15
81
|
extern "C" {
|
|
16
82
|
#endif
|
|
17
83
|
|
|
18
84
|
#if !TARGET_OS_OSX
|
|
19
85
|
// TODO: Remove API_AVAILABLE(ios(16.0)) guard when the minimum iOS deployment target in RN is bumped to 16.
|
|
86
|
+
// TODO: selectionMenuConfig labels do NOT reach the system "Save to Camera Roll /
|
|
87
|
+
// Copy" sheet shown when long-pressing an NSTextAttachment image — UIKit owns
|
|
88
|
+
// that sheet end-to-end. Hooking UIContextMenuInteraction on image regions and
|
|
89
|
+
// returning a custom UIContextMenuConfiguration would let us relabel those items.
|
|
20
90
|
UIMenu *buildEditMenuForSelection(NSAttributedString *attributedText, NSRange range, NSString *_Nullable cachedMarkdown,
|
|
21
91
|
StyleConfig *styleConfig, NSArray<UIMenuElement *> *suggestedActions,
|
|
22
92
|
NSArray<UIAction *> *_Nullable customActions,
|
|
@@ -10,9 +10,10 @@ 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, StyleConfig *styleConfig
|
|
13
|
+
static UIAction *createCopyAction(NSAttributedString *selectedText, NSString *markdown, StyleConfig *styleConfig,
|
|
14
|
+
NSString *copyLabel)
|
|
14
15
|
{
|
|
15
|
-
return [UIAction actionWithTitle
|
|
16
|
+
return [UIAction actionWithTitle:copyLabel
|
|
16
17
|
image:[RCTUIImage systemImageNamed:@"doc.on.doc"]
|
|
17
18
|
identifier:kActionIdentifierCopy
|
|
18
19
|
handler:^(__kindof UIAction *action) {
|
|
@@ -20,26 +21,25 @@ static UIAction *createCopyAction(NSAttributedString *selectedText, NSString *ma
|
|
|
20
21
|
}];
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
static UIAction *_Nullable createCopyMarkdownAction(NSString *markdown)
|
|
24
|
+
static UIAction *_Nullable createCopyMarkdownAction(NSString *markdown, NSString *copyAsMarkdownLabel)
|
|
24
25
|
{
|
|
25
26
|
if (markdown.length == 0)
|
|
26
27
|
return nil;
|
|
27
28
|
|
|
28
|
-
return [UIAction actionWithTitle
|
|
29
|
+
return [UIAction actionWithTitle:copyAsMarkdownLabel
|
|
29
30
|
image:[RCTUIImage systemImageNamed:@"doc.text"]
|
|
30
31
|
identifier:kActionIdentifierCopyMarkdown
|
|
31
32
|
handler:^(__kindof UIAction *action) { copyStringToPasteboard(markdown); }];
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
static UIAction *_Nullable createCopyImageURLAction(NSArray<NSString *> *imageURLs
|
|
35
|
+
static UIAction *_Nullable createCopyImageURLAction(NSArray<NSString *> *imageURLs,
|
|
36
|
+
ENRMSelectionMenuConfig selectionMenuConfig)
|
|
35
37
|
{
|
|
36
38
|
if (imageURLs.count == 0)
|
|
37
39
|
return nil;
|
|
38
40
|
|
|
39
41
|
NSString *urlsToCopy = [imageURLs componentsJoinedByString:@"\n"];
|
|
40
|
-
NSString *title = (imageURLs.count
|
|
41
|
-
? @"Copy Image URL"
|
|
42
|
-
: [NSString stringWithFormat:@"Copy %lu Image URLs", (unsigned long)imageURLs.count];
|
|
42
|
+
NSString *title = ENRMResolveImageURLsTitle(selectionMenuConfig, imageURLs.count);
|
|
43
43
|
|
|
44
44
|
return [UIAction actionWithTitle:title
|
|
45
45
|
image:[RCTUIImage systemImageNamed:@"link"]
|
|
@@ -80,9 +80,12 @@ UIMenu *buildEditMenuForSelection(NSAttributedString *attributedText, NSRange ra
|
|
|
80
80
|
NSString *markdown = markdownForRange(attributedText, range, cachedMarkdown);
|
|
81
81
|
NSArray<NSString *> *imageURLs = imageURLsInRange(attributedText, range);
|
|
82
82
|
|
|
83
|
-
UIAction *copyAction = createCopyAction(selectedText, markdown, styleConfig);
|
|
84
|
-
UIAction *copyMarkdownAction = selectionMenuConfig.copyAsMarkdown
|
|
85
|
-
|
|
83
|
+
UIAction *copyAction = createCopyAction(selectedText, markdown, styleConfig, selectionMenuConfig.copyLabel);
|
|
84
|
+
UIAction *copyMarkdownAction = selectionMenuConfig.copyAsMarkdown
|
|
85
|
+
? createCopyMarkdownAction(markdown, selectionMenuConfig.copyAsMarkdownLabel)
|
|
86
|
+
: nil;
|
|
87
|
+
UIAction *copyImageURLAction =
|
|
88
|
+
selectionMenuConfig.copyImageURL ? createCopyImageURLAction(imageURLs, selectionMenuConfig) : nil;
|
|
86
89
|
|
|
87
90
|
NSMutableArray<UIMenuElement *> *result = [NSMutableArray array];
|
|
88
91
|
BOOL foundStandardEditMenu = NO;
|
|
@@ -6,8 +6,45 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
6
6
|
static NSString *const CodeBlockAttributeName = @"CodeBlock";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Returns YES when the last semantic block in the storage is a code block, ignoring any
|
|
10
|
+
* trailing newline spacers (e.g. codeBlockMarginBottom) that may follow it.
|
|
11
|
+
*
|
|
12
|
+
* Detection is based on the last non-newline character: if it sits inside a CodeBlock
|
|
13
|
+
* attribute run, the answer is YES regardless of what newlines come after.
|
|
14
|
+
*
|
|
15
|
+
* Use this in render-time cleanup paths (e.g. removeTrailingSpacing) that need to know
|
|
16
|
+
* "is the document ending with a code block?" before any trailing newlines have been trimmed.
|
|
17
|
+
* The stricter `isLastElementCodeBlock` would answer NO in that pre-trim state because
|
|
18
|
+
* the CodeBlock range hasn't yet been pushed to text.length.
|
|
19
|
+
*/
|
|
20
|
+
static inline BOOL isLastBlockACodeBlock(NSAttributedString *text)
|
|
21
|
+
{
|
|
22
|
+
if (text.length == 0)
|
|
23
|
+
return NO;
|
|
24
|
+
|
|
25
|
+
NSRange lastContent = [text.string rangeOfCharacterFromSet:[[NSCharacterSet newlineCharacterSet] invertedSet]
|
|
26
|
+
options:NSBackwardsSearch];
|
|
27
|
+
if (lastContent.location == NSNotFound)
|
|
28
|
+
return NO;
|
|
29
|
+
|
|
30
|
+
NSNumber *isCodeBlock = [text attribute:CodeBlockAttributeName atIndex:lastContent.location effectiveRange:nil];
|
|
31
|
+
return isCodeBlock.boolValue;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Stricter sibling of `isLastBlockACodeBlock`: requires both that the last non-newline
|
|
36
|
+
* character is inside a CodeBlock run AND that the CodeBlock range extends literally to
|
|
37
|
+
* text.length (i.e. no characters at all live after the code block).
|
|
38
|
+
*
|
|
39
|
+
* Used in the iOS measurement path (and the analogous drawing-compensation check) to
|
|
40
|
+
* decide whether to add `codeBlockPadding` to make up for iOS not measuring/drawing
|
|
41
|
+
* trailing newlines that carry custom minimum/maximum line heights.
|
|
42
|
+
*
|
|
43
|
+
* After `removeTrailingSpacing` runs, the bottom padding spacer is preserved together
|
|
44
|
+
* with a single trailing newline outside the code block range, so this returns NO and
|
|
45
|
+
* the compensation is skipped — the bottom padding spacer is no longer the trailing
|
|
46
|
+
* paragraph terminator, so iOS lays its line fragment out as part of usedRect normally.
|
|
47
|
+
* The check still exists as a safety net for unexpected storage shapes.
|
|
11
48
|
*/
|
|
12
49
|
static inline BOOL isLastElementCodeBlock(NSAttributedString *text)
|
|
13
50
|
{
|
|
@@ -2,38 +2,40 @@
|
|
|
2
2
|
#include <TargetConditionals.h>
|
|
3
3
|
|
|
4
4
|
@class AccessibilityInfo;
|
|
5
|
+
@class ENRMAccessibilityLabels;
|
|
5
6
|
|
|
6
7
|
NS_ASSUME_NONNULL_BEGIN
|
|
7
8
|
|
|
8
|
-
/**
|
|
9
|
-
* Builds accessibility elements from markdown content for assistive technologies.
|
|
10
|
-
* On iOS this provides VoiceOver support; on macOS this is a no-op stub pending full NSAccessibility implementation.
|
|
11
|
-
*/
|
|
12
9
|
@interface MarkdownAccessibilityElementBuilder : NSObject
|
|
13
10
|
|
|
14
11
|
#if !TARGET_OS_OSX
|
|
15
|
-
/**
|
|
16
|
-
* Builds UIAccessibilityElement objects from markdown content for VoiceOver.
|
|
17
|
-
*/
|
|
18
12
|
+ (NSMutableArray<UIAccessibilityElement *> *)buildElementsForTextView:(UITextView *)textView
|
|
19
13
|
info:(AccessibilityInfo *)info
|
|
14
|
+
labels:(ENRMAccessibilityLabels *)labels
|
|
20
15
|
container:(id)container;
|
|
21
16
|
+ (NSArray<UIAccessibilityElement *> *)filterHeadingElements:(NSArray<UIAccessibilityElement *> *)elements;
|
|
22
17
|
+ (NSArray<UIAccessibilityElement *> *)filterLinkElements:(NSArray<UIAccessibilityElement *> *)elements;
|
|
23
18
|
+ (NSArray<UIAccessibilityElement *> *)filterImageElements:(NSArray<UIAccessibilityElement *> *)elements;
|
|
24
|
-
+ (UIAccessibilityCustomRotor *)createHeadingRotorWithElements:(NSArray<UIAccessibilityElement *> *)elements
|
|
25
|
-
|
|
26
|
-
+ (UIAccessibilityCustomRotor *)
|
|
27
|
-
|
|
19
|
+
+ (UIAccessibilityCustomRotor *)createHeadingRotorWithElements:(NSArray<UIAccessibilityElement *> *)elements
|
|
20
|
+
name:(NSString *)name;
|
|
21
|
+
+ (UIAccessibilityCustomRotor *)createLinkRotorWithElements:(NSArray<UIAccessibilityElement *> *)elements
|
|
22
|
+
name:(NSString *)name;
|
|
23
|
+
+ (UIAccessibilityCustomRotor *)createImageRotorWithElements:(NSArray<UIAccessibilityElement *> *)elements
|
|
24
|
+
name:(NSString *)name;
|
|
25
|
+
+ (NSArray<UIAccessibilityCustomRotor *> *)buildRotorsFromElements:(NSArray<UIAccessibilityElement *> *)elements
|
|
26
|
+
labels:(ENRMAccessibilityLabels *)labels;
|
|
28
27
|
#else
|
|
29
|
-
+ (NSMutableArray *)buildElementsForTextView:(id)textView
|
|
28
|
+
+ (NSMutableArray *)buildElementsForTextView:(id)textView
|
|
29
|
+
info:(AccessibilityInfo *)info
|
|
30
|
+
labels:(ENRMAccessibilityLabels *)labels
|
|
31
|
+
container:(id)container;
|
|
30
32
|
+ (NSArray *)filterHeadingElements:(NSArray *)elements;
|
|
31
33
|
+ (NSArray *)filterLinkElements:(NSArray *)elements;
|
|
32
34
|
+ (NSArray *)filterImageElements:(NSArray *)elements;
|
|
33
|
-
+ (id _Nullable)createHeadingRotorWithElements:(NSArray *)elements;
|
|
34
|
-
+ (id _Nullable)createLinkRotorWithElements:(NSArray *)elements;
|
|
35
|
-
+ (id _Nullable)createImageRotorWithElements:(NSArray *)elements;
|
|
36
|
-
+ (NSArray *)buildRotorsFromElements:(NSArray *)elements;
|
|
35
|
+
+ (id _Nullable)createHeadingRotorWithElements:(NSArray *)elements name:(NSString *)name;
|
|
36
|
+
+ (id _Nullable)createLinkRotorWithElements:(NSArray *)elements name:(NSString *)name;
|
|
37
|
+
+ (id _Nullable)createImageRotorWithElements:(NSArray *)elements name:(NSString *)name;
|
|
38
|
+
+ (NSArray *)buildRotorsFromElements:(NSArray *)elements labels:(ENRMAccessibilityLabels *)labels;
|
|
37
39
|
#endif
|
|
38
40
|
|
|
39
41
|
@end
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#import "MarkdownAccessibilityElementBuilder.h"
|
|
2
2
|
#import "AccessibilityInfo.h"
|
|
3
|
+
#import "BlockquoteBorder.h"
|
|
4
|
+
#import "ENRMAccessibilityLabels.h"
|
|
3
5
|
#include <TargetConditionals.h>
|
|
4
6
|
|
|
5
7
|
typedef NS_ENUM(NSInteger, ElementType) { ElementTypeText, ElementTypeLink, ElementTypeImage };
|
|
@@ -14,6 +16,7 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
14
16
|
|
|
15
17
|
+ (NSMutableArray<UIAccessibilityElement *> *)buildElementsForTextView:(UITextView *)textView
|
|
16
18
|
info:(AccessibilityInfo *)info
|
|
19
|
+
labels:(ENRMAccessibilityLabels *)labels
|
|
17
20
|
container:(id)container
|
|
18
21
|
{
|
|
19
22
|
NSString *fullString = textView.attributedText.string;
|
|
@@ -45,6 +48,7 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
45
48
|
isLinked:NO
|
|
46
49
|
heading:level
|
|
47
50
|
listInfo:list
|
|
51
|
+
labels:labels
|
|
48
52
|
view:textView
|
|
49
53
|
container:container]];
|
|
50
54
|
} else {
|
|
@@ -53,6 +57,7 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
53
57
|
headingLevel:level
|
|
54
58
|
listInfo:list
|
|
55
59
|
specials:specials
|
|
60
|
+
labels:labels
|
|
56
61
|
inTextView:textView
|
|
57
62
|
container:container]];
|
|
58
63
|
}
|
|
@@ -77,6 +82,7 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
77
82
|
headingLevel:(NSInteger)headingLevel
|
|
78
83
|
listInfo:(NSDictionary *)listInfo
|
|
79
84
|
specials:(NSArray *)specials
|
|
85
|
+
labels:(ENRMAccessibilityLabels *)labels
|
|
80
86
|
inTextView:(UITextView *)textView
|
|
81
87
|
container:(id)container
|
|
82
88
|
{
|
|
@@ -100,6 +106,7 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
100
106
|
isLinked:NO
|
|
101
107
|
heading:headingLevel
|
|
102
108
|
listInfo:listInfo
|
|
109
|
+
labels:labels
|
|
103
110
|
view:textView
|
|
104
111
|
container:container]];
|
|
105
112
|
}
|
|
@@ -113,6 +120,7 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
113
120
|
isLinked:isImg ? [item[@"isLinked"] boolValue] : YES
|
|
114
121
|
heading:0
|
|
115
122
|
listInfo:listInfo
|
|
123
|
+
labels:labels
|
|
116
124
|
view:textView
|
|
117
125
|
container:container]];
|
|
118
126
|
segmentStart = NSMaxRange(itemRange);
|
|
@@ -129,6 +137,7 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
129
137
|
isLinked:NO
|
|
130
138
|
heading:headingLevel
|
|
131
139
|
listInfo:listInfo
|
|
140
|
+
labels:labels
|
|
132
141
|
view:textView
|
|
133
142
|
container:container]];
|
|
134
143
|
}
|
|
@@ -144,11 +153,13 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
144
153
|
isLinked:(BOOL)linked
|
|
145
154
|
heading:(NSInteger)level
|
|
146
155
|
listInfo:(NSDictionary *)listInfo
|
|
156
|
+
labels:(ENRMAccessibilityLabels *)labels
|
|
147
157
|
view:(UITextView *)textView
|
|
148
158
|
container:(id)container
|
|
149
159
|
{
|
|
150
160
|
UIAccessibilityElement *element = [[UIAccessibilityElement alloc] initWithAccessibilityContainer:container];
|
|
151
|
-
|
|
161
|
+
if (text.length > 0)
|
|
162
|
+
element.accessibilityLabel = text;
|
|
152
163
|
|
|
153
164
|
UIBezierPath *path = [self accessibilityPathForRange:range inTextView:textView];
|
|
154
165
|
if (path) {
|
|
@@ -157,41 +168,58 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
157
168
|
element.accessibilityFrameInContainerSpace = [self frameForRange:range inTextView:textView container:container];
|
|
158
169
|
}
|
|
159
170
|
|
|
160
|
-
NSMutableArray *values = [NSMutableArray array];
|
|
161
|
-
|
|
162
171
|
if (type == ElementTypeImage) {
|
|
163
172
|
element.accessibilityTraits =
|
|
164
173
|
linked ? (UIAccessibilityTraitImage | UIAccessibilityTraitLink) : UIAccessibilityTraitImage;
|
|
174
|
+
|
|
165
175
|
} else if (type == ElementTypeLink) {
|
|
166
176
|
element.accessibilityTraits = UIAccessibilityTraitLink;
|
|
167
177
|
} else if (level > 0) {
|
|
168
178
|
element.accessibilityTraits = UIAccessibilityTraitHeader;
|
|
169
|
-
[values addObject:[NSString stringWithFormat:NSLocalizedString(@"heading level %ld", @""), (long)level]];
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (element.accessibilityTraits & UIAccessibilityTraitLink) {
|
|
173
|
-
element.accessibilityHint = NSLocalizedString(@"Tap to open link", @"");
|
|
174
179
|
}
|
|
175
180
|
|
|
181
|
+
NSMutableArray<NSString *> *valueParts = [NSMutableArray array];
|
|
176
182
|
if (listInfo && type != ElementTypeImage) {
|
|
177
|
-
[
|
|
183
|
+
[valueParts addObject:[self formatListAnnouncement:listInfo labels:labels]];
|
|
178
184
|
}
|
|
179
|
-
|
|
180
|
-
if (
|
|
181
|
-
|
|
185
|
+
NSString *blockquoteAnnouncement = [self blockquoteAnnouncementForRange:range textView:textView labels:labels];
|
|
186
|
+
if (blockquoteAnnouncement) {
|
|
187
|
+
[valueParts addObject:blockquoteAnnouncement];
|
|
188
|
+
}
|
|
189
|
+
if (valueParts.count > 0) {
|
|
190
|
+
element.accessibilityValue = [valueParts componentsJoinedByString:@", "];
|
|
182
191
|
}
|
|
183
192
|
|
|
184
193
|
return element;
|
|
185
194
|
}
|
|
186
195
|
|
|
196
|
+
+ (NSString *)blockquoteAnnouncementForRange:(NSRange)range
|
|
197
|
+
textView:(UITextView *)textView
|
|
198
|
+
labels:(ENRMAccessibilityLabels *)labels
|
|
199
|
+
{
|
|
200
|
+
if (textView.attributedText.length == 0 || range.location >= textView.attributedText.length)
|
|
201
|
+
return nil;
|
|
202
|
+
NSRange effective;
|
|
203
|
+
NSNumber *depth = [textView.attributedText attribute:BlockquoteDepthAttributeName
|
|
204
|
+
atIndex:range.location
|
|
205
|
+
effectiveRange:&effective];
|
|
206
|
+
if (depth == nil)
|
|
207
|
+
return nil;
|
|
208
|
+
return depth.integerValue >= 1 ? labels.nestedBlockquote : labels.blockquote;
|
|
209
|
+
}
|
|
210
|
+
|
|
187
211
|
#pragma mark - Helpers
|
|
188
212
|
|
|
189
|
-
+ (NSString *)formatListAnnouncement:(NSDictionary *)info
|
|
213
|
+
+ (NSString *)formatListAnnouncement:(NSDictionary *)info labels:(ENRMAccessibilityLabels *)labels
|
|
190
214
|
{
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
215
|
+
BOOL nested = [info[@"depth"] integerValue] > 1;
|
|
216
|
+
if ([info[@"isOrdered"] boolValue]) {
|
|
217
|
+
long position = (long)[info[@"position"] integerValue];
|
|
218
|
+
NSString *template = nested ? labels.nestedOrderedItem : labels.orderedItem;
|
|
219
|
+
return [template stringByReplacingOccurrencesOfString:@"{n}"
|
|
220
|
+
withString:[NSString stringWithFormat:@"%ld", position]];
|
|
221
|
+
}
|
|
222
|
+
return nested ? labels.nestedBulletPoint : labels.bulletPoint;
|
|
195
223
|
}
|
|
196
224
|
|
|
197
225
|
+ (NSRange)clampedRange:(NSRange)range forText:(NSString *)text
|
|
@@ -370,36 +398,37 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
370
398
|
{
|
|
371
399
|
return [self filterElements:elements withTrait:UIAccessibilityTraitImage];
|
|
372
400
|
}
|
|
373
|
-
+ (UIAccessibilityCustomRotor *)createHeadingRotorWithElements:(NSArray *)elements
|
|
401
|
+
+ (UIAccessibilityCustomRotor *)createHeadingRotorWithElements:(NSArray *)elements name:(NSString *)name
|
|
374
402
|
{
|
|
375
|
-
return [self createRotorWithName:
|
|
403
|
+
return [self createRotorWithName:name elements:elements];
|
|
376
404
|
}
|
|
377
|
-
+ (UIAccessibilityCustomRotor *)createLinkRotorWithElements:(NSArray *)elements
|
|
405
|
+
+ (UIAccessibilityCustomRotor *)createLinkRotorWithElements:(NSArray *)elements name:(NSString *)name
|
|
378
406
|
{
|
|
379
|
-
return [self createRotorWithName:
|
|
407
|
+
return [self createRotorWithName:name elements:elements];
|
|
380
408
|
}
|
|
381
|
-
+ (UIAccessibilityCustomRotor *)createImageRotorWithElements:(NSArray *)elements
|
|
409
|
+
+ (UIAccessibilityCustomRotor *)createImageRotorWithElements:(NSArray *)elements name:(NSString *)name
|
|
382
410
|
{
|
|
383
|
-
return [self createRotorWithName:
|
|
411
|
+
return [self createRotorWithName:name elements:elements];
|
|
384
412
|
}
|
|
385
413
|
|
|
386
414
|
+ (NSArray<UIAccessibilityCustomRotor *> *)buildRotorsFromElements:(NSArray<UIAccessibilityElement *> *)elements
|
|
415
|
+
labels:(ENRMAccessibilityLabels *)labels
|
|
387
416
|
{
|
|
388
417
|
NSMutableArray<UIAccessibilityCustomRotor *> *rotors = [NSMutableArray array];
|
|
389
418
|
|
|
390
419
|
NSArray<UIAccessibilityElement *> *headingElements = [self filterHeadingElements:elements];
|
|
391
420
|
if (headingElements.count > 0) {
|
|
392
|
-
[rotors addObject:[self createHeadingRotorWithElements:headingElements]];
|
|
421
|
+
[rotors addObject:[self createHeadingRotorWithElements:headingElements name:labels.rotorHeadings]];
|
|
393
422
|
}
|
|
394
423
|
|
|
395
424
|
NSArray<UIAccessibilityElement *> *linkElements = [self filterLinkElements:elements];
|
|
396
425
|
if (linkElements.count > 0) {
|
|
397
|
-
[rotors addObject:[self createLinkRotorWithElements:linkElements]];
|
|
426
|
+
[rotors addObject:[self createLinkRotorWithElements:linkElements name:labels.rotorLinks]];
|
|
398
427
|
}
|
|
399
428
|
|
|
400
429
|
NSArray<UIAccessibilityElement *> *imageElements = [self filterImageElements:elements];
|
|
401
430
|
if (imageElements.count > 0) {
|
|
402
|
-
[rotors addObject:[self createImageRotorWithElements:imageElements]];
|
|
431
|
+
[rotors addObject:[self createImageRotorWithElements:imageElements name:labels.rotorImages]];
|
|
403
432
|
}
|
|
404
433
|
|
|
405
434
|
return rotors;
|
|
@@ -412,7 +441,10 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
412
441
|
// attributes, and exposing them via NSAccessibilityElement so VoiceOver can navigate the
|
|
413
442
|
// rendered markdown content. The iOS implementation above can serve as a reference.
|
|
414
443
|
|
|
415
|
-
+ (NSMutableArray *)buildElementsForTextView:(id)textView
|
|
444
|
+
+ (NSMutableArray *)buildElementsForTextView:(id)textView
|
|
445
|
+
info:(AccessibilityInfo *)info
|
|
446
|
+
labels:(ENRMAccessibilityLabels *)labels
|
|
447
|
+
container:(id)container
|
|
416
448
|
{
|
|
417
449
|
return [NSMutableArray array];
|
|
418
450
|
}
|
|
@@ -428,23 +460,23 @@ static const CGFloat kFocusRectPadding = 2.0;
|
|
|
428
460
|
{
|
|
429
461
|
return @[];
|
|
430
462
|
}
|
|
431
|
-
+ (id)createHeadingRotorWithElements:(NSArray *)elements
|
|
463
|
+
+ (id)createHeadingRotorWithElements:(NSArray *)elements name:(NSString *)name
|
|
432
464
|
{
|
|
433
465
|
return nil;
|
|
434
466
|
}
|
|
435
|
-
+ (id)createLinkRotorWithElements:(NSArray *)elements
|
|
467
|
+
+ (id)createLinkRotorWithElements:(NSArray *)elements name:(NSString *)name
|
|
436
468
|
{
|
|
437
469
|
return nil;
|
|
438
470
|
}
|
|
439
|
-
+ (id)createImageRotorWithElements:(NSArray *)elements
|
|
471
|
+
+ (id)createImageRotorWithElements:(NSArray *)elements name:(NSString *)name
|
|
440
472
|
{
|
|
441
473
|
return nil;
|
|
442
474
|
}
|
|
443
|
-
+ (NSArray *)buildRotorsFromElements:(NSArray *)elements
|
|
475
|
+
+ (NSArray *)buildRotorsFromElements:(NSArray *)elements labels:(ENRMAccessibilityLabels *)labels
|
|
444
476
|
{
|
|
445
477
|
return @[];
|
|
446
478
|
}
|
|
447
479
|
|
|
448
480
|
#endif
|
|
449
481
|
|
|
450
|
-
@end
|
|
482
|
+
@end
|
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
#import "ParagraphStyleUtils.h"
|
|
2
|
+
#import "ENRMFeatureFlags.h"
|
|
2
3
|
#import "LastElementUtils.h"
|
|
3
4
|
#import <React/RCTI18nUtil.h>
|
|
4
5
|
|
|
6
|
+
#if ENRICHED_MARKDOWN_MATH
|
|
7
|
+
#import "ENRMMathInlineAttachment.h"
|
|
8
|
+
#endif
|
|
9
|
+
|
|
5
10
|
NSAttributedString *kNewlineAttributedString;
|
|
6
11
|
static NSParagraphStyle *kBlockSpacerTemplate;
|
|
7
12
|
|
|
@@ -247,10 +252,24 @@ void applyLineHeight(NSMutableAttributedString *output, NSRange range, CGFloat l
|
|
|
247
252
|
return;
|
|
248
253
|
}
|
|
249
254
|
|
|
255
|
+
__block BOOL hasMath = NO;
|
|
256
|
+
|
|
257
|
+
#if ENRICHED_MARKDOWN_MATH
|
|
258
|
+
[output enumerateAttribute:NSAttachmentAttributeName
|
|
259
|
+
inRange:range
|
|
260
|
+
options:0
|
|
261
|
+
usingBlock:^(id value, __unused NSRange attrRange, BOOL *stop) {
|
|
262
|
+
if ([value isKindOfClass:[ENRMMathInlineAttachment class]]) {
|
|
263
|
+
hasMath = YES;
|
|
264
|
+
*stop = YES;
|
|
265
|
+
}
|
|
266
|
+
}];
|
|
267
|
+
#endif
|
|
268
|
+
|
|
250
269
|
NSMutableParagraphStyle *style = getOrCreateParagraphStyle(output, range.location);
|
|
251
270
|
|
|
252
271
|
style.minimumLineHeight = lineHeight;
|
|
253
|
-
style.maximumLineHeight = lineHeight;
|
|
272
|
+
style.maximumLineHeight = hasMath ? 0 : lineHeight;
|
|
254
273
|
|
|
255
274
|
[output addAttribute:NSParagraphStyleAttributeName value:style range:range];
|
|
256
275
|
}
|
|
@@ -288,7 +307,7 @@ void applyBaselineOffset(NSMutableAttributedString *output, NSRange range)
|
|
|
288
307
|
if (!font) {
|
|
289
308
|
return;
|
|
290
309
|
}
|
|
291
|
-
maximumFontLineHeight = MAX(font
|
|
310
|
+
maximumFontLineHeight = MAX(UIFontLineHeight(font), maximumFontLineHeight);
|
|
292
311
|
}];
|
|
293
312
|
|
|
294
313
|
if (maximumFontLineHeight <= 0 || maximumLineHeight < maximumFontLineHeight) {
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
#import "ENRMUIKit.h"
|
|
3
3
|
#import "StyleConfig.h"
|
|
4
4
|
|
|
5
|
+
@class ENRMAccessibilityLabels;
|
|
6
|
+
|
|
5
7
|
NS_ASSUME_NONNULL_BEGIN
|
|
6
8
|
|
|
7
9
|
@interface ENRMMathContainerView : RCTUIView
|
|
@@ -14,6 +16,12 @@ NS_ASSUME_NONNULL_BEGIN
|
|
|
14
16
|
|
|
15
17
|
@property (nonatomic, strong) StyleConfig *config;
|
|
16
18
|
@property (nonatomic, copy, readonly) NSString *cachedLatex;
|
|
19
|
+
@property (nonatomic, strong, nullable) ENRMAccessibilityLabels *accessibilityLabels;
|
|
20
|
+
|
|
21
|
+
// Renamed getters avoid the Cocoa `copy` method family (which signals +1
|
|
22
|
+
// retained returns). Property names are unchanged so call sites stay the same.
|
|
23
|
+
@property (nonatomic, copy, nullable, getter=menuCopyLabel) NSString *copyLabel;
|
|
24
|
+
@property (nonatomic, copy, nullable, getter=menuCopyAsMarkdownLabel) NSString *copyAsMarkdownLabel;
|
|
17
25
|
|
|
18
26
|
@end
|
|
19
27
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#import "ENRMMathContainerView.h"
|
|
2
|
+
#import "ENRMAccessibilityLabels.h"
|
|
2
3
|
#import "ENRMFeatureFlags.h"
|
|
3
4
|
#include <TargetConditionals.h>
|
|
4
5
|
|
|
@@ -119,13 +120,13 @@
|
|
|
119
120
|
previewProvider:nil
|
|
120
121
|
actionProvider:^UIMenu *(NSArray<UIMenuElement *> *suggestedActions) {
|
|
121
122
|
UIAction *copyPlainText =
|
|
122
|
-
[UIAction actionWithTitle
|
|
123
|
+
[UIAction actionWithTitle:self.copyLabel
|
|
123
124
|
image:[RCTUIImage systemImageNamed:@"doc.on.doc"]
|
|
124
125
|
identifier:nil
|
|
125
126
|
handler:^(__kindof UIAction *action) { [self copyLatexToPasteboard]; }];
|
|
126
127
|
|
|
127
128
|
UIAction *copyMarkdown =
|
|
128
|
-
[UIAction actionWithTitle
|
|
129
|
+
[UIAction actionWithTitle:self.copyAsMarkdownLabel
|
|
129
130
|
image:[RCTUIImage systemImageNamed:@"doc.text"]
|
|
130
131
|
identifier:nil
|
|
131
132
|
handler:^(__kindof UIAction *action) { [self copyMarkdownToPasteboard]; }];
|
|
@@ -139,8 +140,8 @@
|
|
|
139
140
|
- (NSMenu *)menuForEvent:(NSEvent *)event
|
|
140
141
|
{
|
|
141
142
|
NSMenu *menu = [[NSMenu alloc] initWithTitle:@""];
|
|
142
|
-
[menu addItem:ENRMCreateMenuItem(
|
|
143
|
-
[menu addItem:ENRMCreateMenuItem(
|
|
143
|
+
[menu addItem:ENRMCreateMenuItem(self.copyLabel, ^{ [self copyLatexToPasteboard]; })];
|
|
144
|
+
[menu addItem:ENRMCreateMenuItem(self.copyAsMarkdownLabel, ^{ [self copyMarkdownToPasteboard]; })];
|
|
144
145
|
return menu;
|
|
145
146
|
}
|
|
146
147
|
#endif
|
|
@@ -212,7 +213,8 @@
|
|
|
212
213
|
|
|
213
214
|
- (NSString *)accessibilityLabel
|
|
214
215
|
{
|
|
215
|
-
return [
|
|
216
|
+
return [_accessibilityLabels.mathEquation stringByReplacingOccurrencesOfString:@"{latex}"
|
|
217
|
+
withString:_cachedLatex ?: @""];
|
|
216
218
|
}
|
|
217
219
|
|
|
218
220
|
#if !TARGET_OS_OSX
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <TargetConditionals.h>
|
|
4
|
+
#if !TARGET_OS_OSX
|
|
5
|
+
|
|
6
|
+
#import <UIKit/UIKit.h>
|
|
7
|
+
|
|
8
|
+
NS_ASSUME_NONNULL_BEGIN
|
|
9
|
+
|
|
10
|
+
typedef void (^ENRMTableIOSLinkBlock)(NSString *url);
|
|
11
|
+
|
|
12
|
+
@interface ENRMTableIOSRowData : NSObject
|
|
13
|
+
@property (nonatomic, strong) NSArray<NSAttributedString *> *cellTexts;
|
|
14
|
+
@property (nonatomic, strong) UIColor *backgroundColor;
|
|
15
|
+
@end
|
|
16
|
+
|
|
17
|
+
/// Draws the entire table grid in a single drawRect: pass, avoiding
|
|
18
|
+
/// per-cell UITextView allocation and the TextKit layout storms that
|
|
19
|
+
/// cause multi-second main-thread hangs on large tables.
|
|
20
|
+
@interface ENRMTableIOSGridView : UIView
|
|
21
|
+
|
|
22
|
+
@property (nonatomic, copy, nullable) ENRMTableIOSLinkBlock onLinkTap;
|
|
23
|
+
@property (nonatomic, copy, nullable) ENRMTableIOSLinkBlock onLinkLongTap;
|
|
24
|
+
|
|
25
|
+
- (void)updateWithRows:(NSArray<ENRMTableIOSRowData *> *)rows
|
|
26
|
+
columnWidths:(NSArray<NSNumber *> *)columnWidths
|
|
27
|
+
rowHeights:(NSArray<NSNumber *> *)rowHeights
|
|
28
|
+
borderColor:(UIColor *)borderColor
|
|
29
|
+
borderWidth:(CGFloat)borderWidth
|
|
30
|
+
horizontalCellPadding:(CGFloat)horizontalCellPadding
|
|
31
|
+
verticalCellPadding:(CGFloat)verticalCellPadding
|
|
32
|
+
cornerRadius:(CGFloat)cornerRadius;
|
|
33
|
+
|
|
34
|
+
- (void)fadeInRowsFrom:(NSUInteger)startRow duration:(NSTimeInterval)duration;
|
|
35
|
+
|
|
36
|
+
@end
|
|
37
|
+
|
|
38
|
+
NS_ASSUME_NONNULL_END
|
|
39
|
+
|
|
40
|
+
#endif // !TARGET_OS_OSX
|