react-native-enriched 0.1.5 → 0.2.0
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/README.md +3 -9
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerDelegate.java +4 -1
- package/android/generated/java/com/facebook/react/viewmanagers/EnrichedTextInputViewManagerInterface.java +2 -1
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.cpp +5 -0
- package/android/generated/jni/react/renderer/components/RNEnrichedTextInputViewSpec/Props.h +1 -45
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputView.kt +53 -12
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewLayoutManager.kt +7 -56
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewManager.kt +19 -22
- package/android/src/main/java/com/swmansion/enriched/EnrichedTextInputViewPackage.kt +2 -0
- package/android/src/main/java/com/swmansion/enriched/MeasurementStore.kt +158 -0
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedCodeBlockSpan.kt +36 -1
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedImageSpan.kt +132 -11
- package/android/src/main/java/com/swmansion/enriched/spans/EnrichedSpans.kt +65 -46
- package/android/src/main/java/com/swmansion/enriched/spans/utils/ForceRedrawSpan.kt +13 -0
- package/android/src/main/java/com/swmansion/enriched/styles/HtmlStyle.kt +2 -9
- package/android/src/main/java/com/swmansion/enriched/styles/InlineStyles.kt +1 -0
- package/android/src/main/java/com/swmansion/enriched/styles/ParagraphStyles.kt +110 -3
- package/android/src/main/java/com/swmansion/enriched/styles/ParametrizedStyles.kt +75 -32
- package/android/src/main/java/com/swmansion/enriched/utils/AsyncDrawable.kt +91 -0
- package/android/src/main/java/com/swmansion/enriched/utils/EnrichedParser.java +38 -15
- package/android/src/main/java/com/swmansion/enriched/utils/ResourceManager.kt +26 -0
- package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedSpanWatcher.kt +3 -1
- package/android/src/main/java/com/swmansion/enriched/watchers/EnrichedTextWatcher.kt +1 -1
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.cpp +15 -2
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputMeasurementManager.h +1 -0
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/EnrichedTextInputShadowNode.cpp +1 -2
- package/android/src/main/new_arch/react/renderer/components/RNEnrichedTextInputViewSpec/conversions.h +27 -0
- package/android/src/main/res/drawable/broken_image.xml +10 -0
- package/ios/EnrichedTextInputView.h +3 -1
- package/ios/EnrichedTextInputView.mm +167 -68
- package/ios/config/InputConfig.h +6 -0
- package/ios/config/InputConfig.mm +32 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/Props.cpp +5 -0
- package/ios/generated/RNEnrichedTextInputViewSpec/Props.h +1 -45
- package/ios/generated/RNEnrichedTextInputViewSpec/RCTComponentViewHelpers.h +20 -4
- package/ios/inputParser/InputParser.mm +179 -31
- package/ios/inputTextView/InputTextView.mm +3 -5
- package/ios/internals/EnrichedTextInputViewShadowNode.h +1 -0
- package/ios/internals/EnrichedTextInputViewShadowNode.mm +29 -17
- package/ios/styles/BlockQuoteStyle.mm +5 -26
- package/ios/styles/BoldStyle.mm +2 -0
- package/ios/styles/CodeBlockStyle.mm +228 -0
- package/ios/styles/H1Style.mm +1 -0
- package/ios/styles/H2Style.mm +1 -0
- package/ios/styles/H3Style.mm +1 -0
- package/ios/styles/ImageStyle.mm +158 -0
- package/ios/styles/InlineCodeStyle.mm +2 -0
- package/ios/styles/ItalicStyle.mm +2 -0
- package/ios/styles/LinkStyle.mm +15 -7
- package/ios/styles/MentionStyle.mm +133 -36
- package/ios/styles/OrderedListStyle.mm +5 -8
- package/ios/styles/StrikethroughStyle.mm +2 -0
- package/ios/styles/UnderlineStyle.mm +2 -0
- package/ios/styles/UnorderedListStyle.mm +5 -8
- package/ios/utils/BaseStyleProtocol.h +1 -0
- package/ios/utils/ImageData.h +10 -0
- package/ios/utils/ImageData.mm +4 -0
- package/ios/utils/LayoutManagerExtension.mm +118 -3
- package/ios/utils/OccurenceUtils.h +4 -0
- package/ios/utils/OccurenceUtils.mm +47 -0
- package/ios/utils/ParagraphAttributesUtils.h +1 -0
- package/ios/utils/ParagraphAttributesUtils.mm +87 -20
- package/ios/utils/StringExtension.h +1 -1
- package/ios/utils/StringExtension.mm +17 -8
- package/ios/utils/StyleHeaders.h +12 -0
- package/ios/utils/ZeroWidthSpaceUtils.mm +22 -10
- package/lib/module/EnrichedTextInput.js +4 -2
- package/lib/module/EnrichedTextInput.js.map +1 -1
- package/lib/module/EnrichedTextInputNativeComponent.ts +7 -5
- package/lib/module/normalizeHtmlStyle.js +0 -4
- package/lib/module/normalizeHtmlStyle.js.map +1 -1
- package/lib/typescript/src/EnrichedTextInput.d.ts +3 -6
- package/lib/typescript/src/EnrichedTextInput.d.ts.map +1 -1
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts +2 -5
- package/lib/typescript/src/EnrichedTextInputNativeComponent.d.ts.map +1 -1
- package/lib/typescript/src/normalizeHtmlStyle.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/EnrichedTextInput.tsx +6 -7
- package/src/EnrichedTextInputNativeComponent.ts +7 -5
- package/src/normalizeHtmlStyle.ts +0 -4
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#import "StyleHeaders.h"
|
|
2
|
+
#import "EnrichedTextInputView.h"
|
|
3
|
+
#import "FontExtension.h"
|
|
4
|
+
#import "OccurenceUtils.h"
|
|
5
|
+
#import "ParagraphsUtils.h"
|
|
6
|
+
#import "TextInsertionUtils.h"
|
|
7
|
+
#import "ColorExtension.h"
|
|
8
|
+
|
|
9
|
+
@implementation CodeBlockStyle {
|
|
10
|
+
EnrichedTextInputView *_input;
|
|
11
|
+
NSArray *_stylesToExclude;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
+ (StyleType)getStyleType { return CodeBlock; }
|
|
15
|
+
|
|
16
|
+
+ (BOOL)isParagraphStyle { return YES; }
|
|
17
|
+
|
|
18
|
+
- (instancetype)initWithInput:(id)input {
|
|
19
|
+
self = [super init];
|
|
20
|
+
_input = (EnrichedTextInputView *)input;
|
|
21
|
+
_stylesToExclude = @[ @(InlineCode), @(Mention), @(Link) ];
|
|
22
|
+
return self;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
- (void)applyStyle:(NSRange)range {
|
|
26
|
+
BOOL isStylePresent = [self detectStyle:range];
|
|
27
|
+
if(range.length >= 1) {
|
|
28
|
+
isStylePresent ? [self removeAttributes:range] : [self addAttributes:range];
|
|
29
|
+
} else {
|
|
30
|
+
isStylePresent ? [self removeTypingAttributes] : [self addTypingAttributes];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
- (void)addAttributes:(NSRange)range {
|
|
35
|
+
NSTextList *codeBlockList = [[NSTextList alloc] initWithMarkerFormat:@"codeblock" options:0];
|
|
36
|
+
NSArray *paragraphs = [ParagraphsUtils getSeparateParagraphsRangesIn:_input->textView range:range];
|
|
37
|
+
// if we fill empty lines with zero width spaces, we need to offset later ranges
|
|
38
|
+
NSInteger offset = 0;
|
|
39
|
+
NSRange preModificationRange = _input->textView.selectedRange;
|
|
40
|
+
|
|
41
|
+
// to not emit any space filling selection/text changes
|
|
42
|
+
_input->blockEmitting = YES;
|
|
43
|
+
|
|
44
|
+
for (NSValue *value in paragraphs) {
|
|
45
|
+
NSRange pRange = NSMakeRange([value rangeValue].location + offset, [value rangeValue].length);
|
|
46
|
+
// length 0 with first line, length 1 and newline with some empty lines in the middle
|
|
47
|
+
if(pRange.length == 0 ||
|
|
48
|
+
(pRange.length == 1 &&
|
|
49
|
+
[[NSCharacterSet newlineCharacterSet] characterIsMember: [_input->textView.textStorage.string characterAtIndex:pRange.location]])
|
|
50
|
+
) {
|
|
51
|
+
[TextInsertionUtils insertText:@"\u200B" at:pRange.location additionalAttributes:nullptr input:_input withSelection:NO];
|
|
52
|
+
pRange = NSMakeRange(pRange.location, pRange.length + 1);
|
|
53
|
+
offset += 1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
[_input->textView.textStorage enumerateAttribute:NSParagraphStyleAttributeName inRange:pRange options:0
|
|
57
|
+
usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
|
|
58
|
+
NSMutableParagraphStyle *pStyle = [(NSParagraphStyle *)value mutableCopy];
|
|
59
|
+
pStyle.textLists = @[codeBlockList];
|
|
60
|
+
[_input->textView.textStorage addAttribute:NSParagraphStyleAttributeName value:pStyle range:range];
|
|
61
|
+
}
|
|
62
|
+
];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// back to emitting
|
|
66
|
+
_input->blockEmitting = NO;
|
|
67
|
+
|
|
68
|
+
if(preModificationRange.length == 0) {
|
|
69
|
+
// fix selection if only one line was possibly made a list and filled with a space
|
|
70
|
+
_input->textView.selectedRange = preModificationRange;
|
|
71
|
+
} else {
|
|
72
|
+
// in other cases, fix the selection with newly made offsets
|
|
73
|
+
_input->textView.selectedRange = NSMakeRange(preModificationRange.location, preModificationRange.length + offset);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// also add typing attributes
|
|
77
|
+
NSMutableDictionary *typingAttrs = [_input->textView.typingAttributes mutableCopy];
|
|
78
|
+
NSMutableParagraphStyle *pStyle = [typingAttrs[NSParagraphStyleAttributeName] mutableCopy];
|
|
79
|
+
pStyle.textLists = @[codeBlockList];
|
|
80
|
+
typingAttrs[NSParagraphStyleAttributeName] = pStyle;
|
|
81
|
+
|
|
82
|
+
_input->textView.typingAttributes = typingAttrs;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
- (void)addTypingAttributes {
|
|
86
|
+
[self addAttributes:_input->textView.selectedRange];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
- (void)removeAttributes:(NSRange)range {
|
|
90
|
+
NSArray *paragraphs = [ParagraphsUtils getSeparateParagraphsRangesIn:_input->textView range:range];
|
|
91
|
+
|
|
92
|
+
[_input->textView.textStorage beginEditing];
|
|
93
|
+
|
|
94
|
+
for(NSValue *value in paragraphs) {
|
|
95
|
+
NSRange pRange = [value rangeValue];
|
|
96
|
+
|
|
97
|
+
[_input->textView.textStorage enumerateAttribute:NSParagraphStyleAttributeName inRange:pRange options:0
|
|
98
|
+
usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
|
|
99
|
+
NSMutableParagraphStyle *pStyle = [(NSParagraphStyle *)value mutableCopy];
|
|
100
|
+
pStyle.textLists = @[];
|
|
101
|
+
[_input->textView.textStorage addAttribute:NSParagraphStyleAttributeName value:pStyle range:range];
|
|
102
|
+
}
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
[_input->textView.textStorage endEditing];
|
|
107
|
+
|
|
108
|
+
// also remove typing attributes
|
|
109
|
+
NSMutableDictionary *typingAttrs = [_input->textView.typingAttributes mutableCopy];
|
|
110
|
+
NSMutableParagraphStyle *pStyle = [typingAttrs[NSParagraphStyleAttributeName] mutableCopy];
|
|
111
|
+
pStyle.textLists = @[];
|
|
112
|
+
|
|
113
|
+
typingAttrs[NSParagraphStyleAttributeName] = pStyle;
|
|
114
|
+
|
|
115
|
+
_input->textView.typingAttributes = typingAttrs;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
- (void)removeTypingAttributes {
|
|
119
|
+
[self removeAttributes:_input->textView.selectedRange];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
- (BOOL)handleBackspaceInRange:(NSRange)range replacementText:(NSString *)text {
|
|
123
|
+
if([self detectStyle:_input->textView.selectedRange] && text.length == 0) {
|
|
124
|
+
// backspace while the style is active
|
|
125
|
+
|
|
126
|
+
NSRange paragraphRange = [_input->textView.textStorage.string paragraphRangeForRange:_input->textView.selectedRange];
|
|
127
|
+
|
|
128
|
+
if(NSEqualRanges(_input->textView.selectedRange, NSMakeRange(0, 0))) {
|
|
129
|
+
// a backspace on the very first input's line quote
|
|
130
|
+
// it doesn't run textVieDidChange so we need to manually remove attributes
|
|
131
|
+
[self removeAttributes:paragraphRange];
|
|
132
|
+
return YES;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return NO;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
- (BOOL)styleCondition:(id _Nullable)value :(NSRange)range {
|
|
139
|
+
NSParagraphStyle *paragraph = (NSParagraphStyle *)value;
|
|
140
|
+
return paragraph != nullptr && paragraph.textLists.count == 1 && [paragraph.textLists.firstObject.markerFormat isEqualToString:@"codeblock"];
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
- (BOOL)detectStyle:(NSRange)range {
|
|
144
|
+
if(range.length >= 1) {
|
|
145
|
+
return [OccurenceUtils detect:NSParagraphStyleAttributeName withInput:_input inRange:range
|
|
146
|
+
withCondition: ^BOOL(id _Nullable value, NSRange range) {
|
|
147
|
+
return [self styleCondition:value :range];
|
|
148
|
+
}
|
|
149
|
+
];
|
|
150
|
+
} else {
|
|
151
|
+
return [OccurenceUtils detect:NSParagraphStyleAttributeName withInput:_input atIndex:range.location checkPrevious:YES
|
|
152
|
+
withCondition:^BOOL(id _Nullable value, NSRange range) {
|
|
153
|
+
return [self styleCondition:value :range];
|
|
154
|
+
}
|
|
155
|
+
];
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
- (BOOL)anyOccurence:(NSRange)range {
|
|
160
|
+
return [OccurenceUtils any:NSParagraphStyleAttributeName withInput:_input inRange:range
|
|
161
|
+
withCondition:^BOOL(id _Nullable value, NSRange range) {
|
|
162
|
+
return [self styleCondition:value :range];
|
|
163
|
+
}
|
|
164
|
+
];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
- (NSArray<StylePair *> *_Nullable)findAllOccurences:(NSRange)range {
|
|
168
|
+
return [OccurenceUtils all:NSParagraphStyleAttributeName withInput:_input inRange:range
|
|
169
|
+
withCondition:^BOOL(id _Nullable value, NSRange range) {
|
|
170
|
+
return [self styleCondition:value :range];
|
|
171
|
+
}
|
|
172
|
+
];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
- (void)manageCodeBlockFontAndColor {
|
|
176
|
+
if([[_input->config codeBlockFgColor] isEqualToColor:[_input->config primaryColor]]) {
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
NSRange wholeRange = NSMakeRange(0, _input->textView.textStorage.string.length);
|
|
181
|
+
NSArray *paragraphs = [ParagraphsUtils getSeparateParagraphsRangesIn:_input->textView range:wholeRange];
|
|
182
|
+
|
|
183
|
+
for(NSValue *pValue in paragraphs) {
|
|
184
|
+
NSRange paragraphRange = [pValue rangeValue];
|
|
185
|
+
NSArray *properRanges = [OccurenceUtils getRangesWithout:_stylesToExclude withInput:_input inRange:paragraphRange];
|
|
186
|
+
|
|
187
|
+
for(NSValue *value in properRanges) {
|
|
188
|
+
NSRange currRange = [value rangeValue];
|
|
189
|
+
BOOL selfDetected = [self detectStyle:currRange];
|
|
190
|
+
|
|
191
|
+
[_input->textView.textStorage enumerateAttribute:NSFontAttributeName inRange:currRange options:0
|
|
192
|
+
usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
|
|
193
|
+
UIFont *currentFont = (UIFont *)value;
|
|
194
|
+
UIFont *newFont = nullptr;
|
|
195
|
+
|
|
196
|
+
BOOL isCodeFont = [[currentFont familyName] isEqualToString:[[_input->config monospacedFont] familyName]];
|
|
197
|
+
|
|
198
|
+
if (isCodeFont && !selfDetected) {
|
|
199
|
+
newFont = [[[_input->config primaryFont] withFontTraits:currentFont] setSize:currentFont.pointSize];
|
|
200
|
+
} else if (!isCodeFont && selfDetected) {
|
|
201
|
+
newFont = [[[_input->config monospacedFont] withFontTraits:currentFont] setSize:currentFont.pointSize];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (newFont != nullptr) {
|
|
205
|
+
[_input->textView.textStorage addAttribute:NSFontAttributeName value:newFont range:range];
|
|
206
|
+
}
|
|
207
|
+
}];
|
|
208
|
+
|
|
209
|
+
[_input->textView.textStorage enumerateAttribute:NSForegroundColorAttributeName inRange:currRange options:0
|
|
210
|
+
usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
|
|
211
|
+
UIColor *newColor = nullptr;
|
|
212
|
+
BOOL colorApplied = [(UIColor *)value isEqualToColor:[_input->config codeBlockFgColor]];
|
|
213
|
+
|
|
214
|
+
if(colorApplied && !selfDetected) {
|
|
215
|
+
newColor = [_input->config primaryColor];
|
|
216
|
+
} else if(!colorApplied && selfDetected) {
|
|
217
|
+
newColor = [_input->config codeBlockFgColor];
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if(newColor != nullptr) {
|
|
221
|
+
[_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:newColor range:range];
|
|
222
|
+
}
|
|
223
|
+
}];
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
@end
|
package/ios/styles/H1Style.mm
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
@implementation H1Style
|
|
5
5
|
+ (StyleType)getStyleType { return H1; }
|
|
6
|
+
+ (BOOL)isParagraphStyle { return YES; }
|
|
6
7
|
- (CGFloat)getHeadingFontSize { return [((EnrichedTextInputView *)input)->config h1FontSize]; }
|
|
7
8
|
- (BOOL)isHeadingBold {
|
|
8
9
|
return [((EnrichedTextInputView *)input)->config h1Bold];
|
package/ios/styles/H2Style.mm
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
@implementation H2Style
|
|
5
5
|
+ (StyleType)getStyleType { return H2; }
|
|
6
|
+
+ (BOOL)isParagraphStyle { return YES; }
|
|
6
7
|
- (CGFloat)getHeadingFontSize { return [((EnrichedTextInputView *)input)->config h2FontSize]; }
|
|
7
8
|
- (BOOL)isHeadingBold {
|
|
8
9
|
return [((EnrichedTextInputView *)input)->config h2Bold];
|
package/ios/styles/H3Style.mm
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
@implementation H3Style
|
|
5
5
|
+ (StyleType)getStyleType { return H3; }
|
|
6
|
+
+ (BOOL)isParagraphStyle { return YES; }
|
|
6
7
|
- (CGFloat)getHeadingFontSize { return [((EnrichedTextInputView *)input)->config h3FontSize]; }
|
|
7
8
|
- (BOOL)isHeadingBold {
|
|
8
9
|
return [((EnrichedTextInputView *)input)->config h3Bold];
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#import "StyleHeaders.h"
|
|
2
|
+
#import "EnrichedTextInputView.h"
|
|
3
|
+
#import "OccurenceUtils.h"
|
|
4
|
+
#import "TextInsertionUtils.h"
|
|
5
|
+
|
|
6
|
+
// custom NSAttributedStringKey to differentiate the image
|
|
7
|
+
static NSString *const ImageAttributeName = @"ImageAttributeName";
|
|
8
|
+
|
|
9
|
+
@implementation ImageStyle {
|
|
10
|
+
EnrichedTextInputView *_input;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
+ (StyleType)getStyleType { return Image; }
|
|
14
|
+
|
|
15
|
+
+ (BOOL)isParagraphStyle { return NO; }
|
|
16
|
+
|
|
17
|
+
- (instancetype)initWithInput:(id)input {
|
|
18
|
+
self = [super init];
|
|
19
|
+
_input = (EnrichedTextInputView *)input;
|
|
20
|
+
return self;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
- (void)applyStyle:(NSRange)range {
|
|
24
|
+
// no-op for image
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
- (void)addAttributes:(NSRange)range {
|
|
28
|
+
// no-op for image
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
- (void)addTypingAttributes {
|
|
32
|
+
// no-op for image
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
- (void)removeAttributes:(NSRange)range {
|
|
36
|
+
[_input->textView.textStorage beginEditing];
|
|
37
|
+
[_input->textView.textStorage removeAttribute:ImageAttributeName range:range];
|
|
38
|
+
[_input->textView.textStorage removeAttribute:NSAttachmentAttributeName range:range];
|
|
39
|
+
[_input->textView.textStorage endEditing];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
- (void)removeTypingAttributes {
|
|
43
|
+
NSMutableDictionary *currentAttributes = [_input->textView.typingAttributes mutableCopy];
|
|
44
|
+
[currentAttributes removeObjectForKey:ImageAttributeName];
|
|
45
|
+
[currentAttributes removeObjectForKey:NSAttachmentAttributeName];
|
|
46
|
+
_input->textView.typingAttributes = currentAttributes;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
- (BOOL)styleCondition:(id _Nullable)value :(NSRange)range {
|
|
50
|
+
return [value isKindOfClass:[ImageData class]];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
- (BOOL)anyOccurence:(NSRange)range {
|
|
54
|
+
return [OccurenceUtils any:ImageAttributeName withInput:_input inRange:range
|
|
55
|
+
withCondition:^BOOL(id _Nullable value, NSRange range) {
|
|
56
|
+
return [self styleCondition:value :range];
|
|
57
|
+
}
|
|
58
|
+
];
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
- (BOOL)detectStyle:(NSRange)range {
|
|
62
|
+
if (range.length >= 1) {
|
|
63
|
+
return [OccurenceUtils detect:ImageAttributeName withInput:_input inRange:range
|
|
64
|
+
withCondition:^BOOL(id _Nullable value, NSRange range) {
|
|
65
|
+
return [self styleCondition:value :range];
|
|
66
|
+
}
|
|
67
|
+
];
|
|
68
|
+
} else {
|
|
69
|
+
return [OccurenceUtils detect:ImageAttributeName withInput:_input atIndex:range.location checkPrevious:YES
|
|
70
|
+
withCondition:^BOOL(id _Nullable value, NSRange range) {
|
|
71
|
+
return [self styleCondition:value :range];
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
- (NSArray<StylePair *> * _Nullable)findAllOccurences:(NSRange)range {
|
|
78
|
+
return [OccurenceUtils all:ImageAttributeName withInput:_input inRange:range
|
|
79
|
+
withCondition:^BOOL(id _Nullable value, NSRange range) {
|
|
80
|
+
return [self styleCondition:value :range];
|
|
81
|
+
}
|
|
82
|
+
];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
- (ImageData *)getImageDataAt:(NSUInteger)location
|
|
86
|
+
{
|
|
87
|
+
NSRange imageRange = NSMakeRange(0, 0);
|
|
88
|
+
NSRange inputRange = NSMakeRange(0, _input->textView.textStorage.length);
|
|
89
|
+
|
|
90
|
+
// don't search at the very end of input
|
|
91
|
+
NSUInteger searchLocation = location;
|
|
92
|
+
if(searchLocation == _input->textView.textStorage.length) {
|
|
93
|
+
return nullptr;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
ImageData *imageData = [_input->textView.textStorage
|
|
97
|
+
attribute:ImageAttributeName
|
|
98
|
+
atIndex:searchLocation
|
|
99
|
+
longestEffectiveRange: &imageRange
|
|
100
|
+
inRange:inputRange
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
return imageData;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
- (void)addImageAtRange:(NSRange)range imageData:(ImageData *)imageData withSelection:(BOOL)withSelection
|
|
107
|
+
{
|
|
108
|
+
UIImage *img = [self prepareImageFromUri:imageData.uri];
|
|
109
|
+
|
|
110
|
+
NSDictionary *attributes = [@{
|
|
111
|
+
NSAttachmentAttributeName: [self prepareImageAttachement:img width:imageData.width height:imageData.height],
|
|
112
|
+
ImageAttributeName: imageData,
|
|
113
|
+
} mutableCopy];
|
|
114
|
+
|
|
115
|
+
// Use the Object Replacement Character for Image.
|
|
116
|
+
// This tells TextKit "something non-text goes here".
|
|
117
|
+
NSString *imagePlaceholder = @"\uFFFC";
|
|
118
|
+
|
|
119
|
+
if (range.length == 0) {
|
|
120
|
+
[TextInsertionUtils insertText:imagePlaceholder at:range.location additionalAttributes:attributes input:_input withSelection:withSelection];
|
|
121
|
+
} else {
|
|
122
|
+
[TextInsertionUtils replaceText:imagePlaceholder
|
|
123
|
+
at:range
|
|
124
|
+
additionalAttributes:attributes
|
|
125
|
+
input:_input
|
|
126
|
+
withSelection:withSelection];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
- (void)addImage:(NSString *)uri width:(CGFloat)width height:(CGFloat)height {
|
|
131
|
+
ImageData *data = [[ImageData alloc] init];
|
|
132
|
+
data.uri = uri;
|
|
133
|
+
data.width = width;
|
|
134
|
+
data.height = height;
|
|
135
|
+
|
|
136
|
+
[self addImageAtRange:_input->textView.selectedRange imageData:data withSelection:YES];
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
-(NSTextAttachment *)prepareImageAttachement:(UIImage *)image width:(CGFloat)width height:(CGFloat)height
|
|
140
|
+
{
|
|
141
|
+
|
|
142
|
+
NSTextAttachment *attachment = [[NSTextAttachment alloc] init];
|
|
143
|
+
attachment.image = image;
|
|
144
|
+
attachment.bounds = CGRectMake(0, 0, width, height);
|
|
145
|
+
|
|
146
|
+
return attachment;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
- (UIImage *)prepareImageFromUri:(NSString *)uri
|
|
150
|
+
{
|
|
151
|
+
NSURL *url = [NSURL URLWithString:uri];
|
|
152
|
+
NSData *imgData = [NSData dataWithContentsOfURL:url];
|
|
153
|
+
UIImage *image = [UIImage imageWithData:imgData];
|
|
154
|
+
|
|
155
|
+
return image;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
@end
|
package/ios/styles/LinkStyle.mm
CHANGED
|
@@ -15,6 +15,8 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
|
|
|
15
15
|
|
|
16
16
|
+ (StyleType)getStyleType { return Link; }
|
|
17
17
|
|
|
18
|
+
+ (BOOL)isParagraphStyle { return NO; }
|
|
19
|
+
|
|
18
20
|
- (instancetype)initWithInput:(id)input {
|
|
19
21
|
self = [super init];
|
|
20
22
|
_input = (EnrichedTextInputView *)input;
|
|
@@ -167,7 +169,6 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
|
|
|
167
169
|
}
|
|
168
170
|
|
|
169
171
|
[self manageLinkTypingAttributes];
|
|
170
|
-
[_input anyTextMayHaveBeenModified];
|
|
171
172
|
}
|
|
172
173
|
|
|
173
174
|
// get exact link data at the given location if it exists
|
|
@@ -224,20 +225,22 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
|
|
|
224
225
|
}
|
|
225
226
|
}
|
|
226
227
|
|
|
227
|
-
[_input->textView.textStorage
|
|
228
|
+
NSString *manualLink = [_input->textView.textStorage
|
|
228
229
|
attribute:ManualLinkAttributeName
|
|
229
230
|
atIndex:searchLocation
|
|
230
231
|
longestEffectiveRange: &manualLinkRange
|
|
231
232
|
inRange:inputRange
|
|
232
233
|
];
|
|
233
|
-
[_input->textView.textStorage
|
|
234
|
+
NSString *automaticLink = [_input->textView.textStorage
|
|
234
235
|
attribute:AutomaticLinkAttributeName
|
|
235
236
|
atIndex:searchLocation
|
|
236
237
|
longestEffectiveRange: &automaticLinkRange
|
|
237
238
|
inRange:inputRange
|
|
238
239
|
];
|
|
239
240
|
|
|
240
|
-
return
|
|
241
|
+
return manualLink == nullptr
|
|
242
|
+
? automaticLink == nullptr ? NSMakeRange(0, 0) : automaticLinkRange
|
|
243
|
+
: manualLinkRange;
|
|
241
244
|
}
|
|
242
245
|
|
|
243
246
|
- (void)manageLinkTypingAttributes {
|
|
@@ -279,6 +282,7 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
|
|
|
279
282
|
- (void)handleAutomaticLinks:(NSString *)word inRange:(NSRange)wordRange {
|
|
280
283
|
InlineCodeStyle *inlineCodeStyle = [_input->stylesDict objectForKey:@([InlineCodeStyle getStyleType])];
|
|
281
284
|
MentionStyle *mentionStyle = [_input->stylesDict objectForKey:@([MentionStyle getStyleType])];
|
|
285
|
+
CodeBlockStyle *codeBlockStyle = [_input->stylesDict objectForKey:@([CodeBlockStyle getStyleType])];
|
|
282
286
|
|
|
283
287
|
if (inlineCodeStyle == nullptr || mentionStyle == nullptr) {
|
|
284
288
|
return;
|
|
@@ -294,6 +298,11 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
|
|
|
294
298
|
return;
|
|
295
299
|
}
|
|
296
300
|
|
|
301
|
+
// we don't recognize links in codeblocks
|
|
302
|
+
if ([codeBlockStyle anyOccurence:wordRange]) {
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
297
306
|
// remove connected different links
|
|
298
307
|
[self removeConnectedLinksIfNeeded:word range:wordRange];
|
|
299
308
|
|
|
@@ -349,10 +358,9 @@ static NSString *const AutomaticLinkAttributeName = @"AutomaticLinkAttributeName
|
|
|
349
358
|
}
|
|
350
359
|
if(addStyle) {
|
|
351
360
|
[self addLink:word url:regexPassedUrl range:wordRange manual:NO];
|
|
361
|
+
// emit onLinkDetected if style was added
|
|
362
|
+
[_input emitOnLinkDetectedEvent:word url:regexPassedUrl range:wordRange];
|
|
352
363
|
}
|
|
353
|
-
|
|
354
|
-
// emit onLinkDetected
|
|
355
|
-
[_input emitOnLinkDetectedEvent:word url:regexPassedUrl range:wordRange];
|
|
356
364
|
}
|
|
357
365
|
}
|
|
358
366
|
|