react-native-enriched 0.1.2 → 0.1.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/README.md CHANGED
@@ -22,7 +22,7 @@ Built by [Software Mansion](https://swmansion.com/) and sponsored by [Filament](
22
22
  <img width="80" height="80" alt="Filament Logo" src="https://github.com/user-attachments/assets/4103ab79-da34-4164-aa5f-dcf08815bf65" />
23
23
 
24
24
  \
25
- Since 2012 [Software Mansion](https://swmansion.com) is a software agency with experience in building web and mobile apps. We are Core React Native Contributors and experts in dealing with all kinds of React Native issues.
25
+ Since 2012 [Software Mansion](https://swmansion.com) is a software agency with experience in building web and mobile apps. We are Core React Native Contributors and experts in dealing with all kinds of React Native issues.
26
26
  We can help you build your next dream product –
27
27
  [Hire us](https://swmansion.com/contact/projects?utm_source=react-native-enriched&utm_medium=readme).
28
28
 
@@ -139,27 +139,27 @@ const styles = StyleSheet.create({
139
139
  Summary of what happens here:
140
140
 
141
141
  1. Any methods imperatively called on the input to e.g. toggle some style must be used through a `ref` of `EnrichedTextInputInstance` type. Here, `toggleBold` method that is called on the button press calls `ref.current?.toggleBold()`, which toggles the bold styling within the current selection.
142
- 2. All the active styles info is emitted by `onChangeState` event. Set up a proper callback that accepts a `NativeSyntheticEvent<OnChangeStateEvent>` argument and you can access an object with boolean properties indicating which styles are active, such as `isBold` in the example. Here, this info is stored in a react state and used to change colors on the button.
142
+ 2. All the active styles info is emitted by `onChangeState` event. Set up a proper callback that accepts a `NativeSyntheticEvent<OnChangeStateEvent>` argument, and you can access an object with boolean properties indicating which styles are active, such as `isBold` in the example. Here, this info is stored in a React state and used to change colors on the button.
143
143
 
144
144
  ## Non Parametrized Styles
145
145
 
146
146
  Supported styles:
147
147
 
148
- - **bold**
149
- - *italic*
150
- - <ins>underline</ins>
151
- - ~~strikethrough~~
152
- - `inline code`
148
+ - bold
149
+ - italic
150
+ - underline
151
+ - strikethrough
152
+ - inline code
153
153
  - H1 heading
154
154
  - H2 heading
155
155
  - H3 heading
156
- - `codeblock`
157
- - > blockquote
156
+ - codeblock
157
+ - blockquote
158
158
  - ordered list
159
159
  - unordered list
160
160
 
161
161
  > [!NOTE]
162
- > The iOS doesn't support codeblocks just yet but it's planned in the near future!
162
+ > The iOS doesn't support codeblocks just yet, but it's planned in the near future!
163
163
 
164
164
  Each of the styles can be toggled the same way as in the example from [usage section](#usage); call a proper `toggle` function on the component ref.
165
165
 
@@ -176,7 +176,7 @@ The links are here, just like in any other editor, a piece of text with a URL at
176
176
 
177
177
  ### Automatic links detection
178
178
 
179
- `react-native-enriched` automatically detects words that appear to be some URLs and makes them links. Currently we are using pretty naive approach to detect whether text can be treated as a link or not. On iOS it's a pretty simple regex, on Android we are using URL regex provided by the system.
179
+ `react-native-enriched` automatically detects words that appear to be some URLs and makes them links. Currently, we are using pretty naive approach to detect whether text can be treated as a link or not. On iOS it's a pretty simple regex, on Android we are using URL regex provided by the system.
180
180
 
181
181
  ### Applying links manually
182
182
 
@@ -194,7 +194,7 @@ Mentions are meant to be a customisable style that lets you put mentioning phras
194
194
 
195
195
  ### Mention Indicators
196
196
 
197
- There is a [mentionIndicators](#mentionindicators) prop that lets you define what characters can start a mention. By default it is set to `[ @ ]`, meaning that typing a `@` character in the input will start the creation of a mention.
197
+ There is a [mentionIndicators](#mentionindicators) prop that lets you define what characters can start a mention. By default, it is set to `[ @ ]`, meaning that typing a `@` character in the input will start the creation of a mention.
198
198
 
199
199
  ### Starting a mention
200
200
 
@@ -216,10 +216,10 @@ Whenever you feel ready with the currently edited mention (so most likely user c
216
216
 
217
217
  You can insert an image into the input using [setImage](#setimage) ref method.
218
218
 
219
- The image will be put into a single line in the input and will affects the line's height as well as input's height. Keep in mind, that image will replace currently selected text or insert into the cursor position if there is no text selection.
219
+ The image will be put into a single line in the input and will affect the line's height as well as input's height. Keep in mind, that image will replace currently selected text or insert into the cursor position if there is no text selection.
220
220
 
221
221
  > [!NOTE]
222
- > The iOS doesn't support inline images just yet but it's planned in the near future!
222
+ > The iOS doesn't support inline images just yet, but it's planned in the near future!
223
223
 
224
224
  ## Style Detection
225
225
 
@@ -294,7 +294,7 @@ If `false`, text is not editable.
294
294
 
295
295
  #### `htmlStyle`
296
296
 
297
- A prop for customizing styles' appearances.
297
+ A prop for customizing styles appearances.
298
298
 
299
299
  | Type | Default Value | Platform |
300
300
  |--------------------------------|----------------------------------------------------|----------|
@@ -330,9 +330,9 @@ interface OnChangeHtmlEvent {
330
330
 
331
331
  - `value` is the new HTML.
332
332
 
333
- | Type | Default Value | Platform |
334
- |------------------------------------------------------|---------------|----------|
335
- | `(NativeSyntheticEvent\<OnChangeHtmlEvent>) => void` | - | Both |
333
+ | Type | Default Value | Platform |
334
+ |------------------------------------------------------------|---------------|----------|
335
+ | `(event: NativeSyntheticEvent<OnChangeHtmlEvent>) => void` | - | Both |
336
336
 
337
337
  #### `onChangeMention`
338
338
 
@@ -350,9 +350,9 @@ interface OnChangeMentionEvent {
350
350
  - `indicator` is the indicator of the currently edited mention.
351
351
  - `text` contains whole text that has been typed after the indicator.
352
352
 
353
- | Type | Default Value | Platform |
354
- |----------------------------------|---------------|----------|
355
- | `(OnChangeMentionEvent) => void` | - | Both |
353
+ | Type | Default Value | Platform |
354
+ |-----------------------------------------|---------------|----------|
355
+ | `(event: OnChangeMentionEvent) => void` | - | Both |
356
356
 
357
357
  #### `onChangeSelection`
358
358
 
@@ -361,7 +361,7 @@ Callback that is called each time user changes selection or moves the cursor in
361
361
  Payload interface:
362
362
 
363
363
  ```ts
364
- OnChangeSelectionEvent {
364
+ interface OnChangeSelectionEvent {
365
365
  start: Int32;
366
366
  end: Int32;
367
367
  text: string;
@@ -372,9 +372,9 @@ OnChangeSelectionEvent {
372
372
  - `end` is the first index after the selection's ending. For just a cursor in place (no selection), `start` equals `end`.
373
373
  - `text` is the input's text in the current selection.
374
374
 
375
- | Type | Default Value | Platform |
376
- |-----------------------------------------------------------|---------------|----------|
377
- | `(NativeSyntheticEvent\<OnChangeSelectionEvent>) => void` | - | Both |
375
+ | Type | Default Value | Platform |
376
+ |-----------------------------------------------------------------|---------------|----------|
377
+ | `(event: NativeSyntheticEvent<OnChangeSelectionEvent>) => void` | - | Both |
378
378
 
379
379
  #### `onChangeState`
380
380
 
@@ -402,9 +402,9 @@ interface OnChangeStateEvent {
402
402
  }
403
403
  ```
404
404
 
405
- | Type | Default Value | Platform |
406
- |-------------------------------------------------------|---------------|----------|
407
- | `(NativeSyntheticEvent\<OnChangeStateEvent>) => void` | - | Both |
405
+ | Type | Default Value | Platform |
406
+ |-------------------------------------------------------------|---------------|----------|
407
+ | `(event: NativeSyntheticEvent<OnChangeStateEvent>) => void` | - | Both |
408
408
 
409
409
  #### `onChangeText`
410
410
 
@@ -420,9 +420,9 @@ interface OnChangeTextEvent {
420
420
 
421
421
  - `value` is the new text value of the input.
422
422
 
423
- | Type | Default Value | Platform |
424
- |------------------------------------------------------|---------------|----------|
425
- | `(NativeSyntheticEvent\<OnChangeTextEvent>) => void` | - | Both |
423
+ | Type | Default Value | Platform |
424
+ |------------------------------------------------------------|---------------|----------|
425
+ | `(event: NativeSyntheticEvent<OnChangeTextEvent>) => void` | - | Both |
426
426
 
427
427
  #### `onEndMention`
428
428
 
@@ -462,9 +462,9 @@ interface OnLinkDetected {
462
462
  - `start` is the starting index of the link.
463
463
  - `end` is the first index after the ending index of the link.
464
464
 
465
- | Type | Default Value | Platform |
466
- |----------------------------|---------------|----------|
467
- | `(OnLinkDetected) => void` | - | Both |
465
+ | Type | Default Value | Platform |
466
+ |-----------------------------------|---------------|----------|
467
+ | `(event: OnLinkDetected) => void` | - | Both |
468
468
 
469
469
  #### `onMentionDetected`
470
470
 
@@ -473,7 +473,7 @@ Callback called when mention has been detected - either a new mention has been a
473
473
  Payload interface contains all the useful mention data:
474
474
 
475
475
  ```ts
476
- OnMentionDetected {
476
+ interface OnMentionDetected {
477
477
  text: string;
478
478
  indicator: string;
479
479
  attributes: Record<string, string>;
@@ -484,9 +484,9 @@ OnMentionDetected {
484
484
  - `indicator` is the indicator of the mention.
485
485
  - `attributes` are the additional user-defined attributes that are being stored with the mention.
486
486
 
487
- | Type | Default Value | Platform |
488
- |-------------------------------|---------------|----------|
489
- | `(OnMentionDetected) => void` | - | Both |
487
+ | Type | Default Value | Platform |
488
+ |--------------------------------------|---------------|----------|
489
+ | `(event: OnMentionDetected) => void` | - | Both |
490
490
 
491
491
  #### `onStartMention`
492
492
 
@@ -560,12 +560,12 @@ If true, Android will use experimental synchronous events. This will prevent fro
560
560
 
561
561
  ### Ref Methods
562
562
 
563
- All of the methods should be called on the input's [ref](#ref).
563
+ All the methods should be called on the input's [ref](#ref).
564
564
 
565
565
  #### `.blur()`
566
566
 
567
567
  ```ts
568
- blur: () => void
568
+ blur: () => void;
569
569
  ```
570
570
 
571
571
  Blurs the input.
@@ -812,6 +812,9 @@ interface MentionStyleProperties {
812
812
  - `fontSize` is the size of the heading's font, defaults to `32`/`24`/`20` for h1/h2/h3.
813
813
  - `bold` defines whether the heading should be bolded, defaults to `false`.
814
814
 
815
+ > [!NOTE]
816
+ > On iOS, the headings cannot have same `fontSize` as the component's `fontSize`. Doing so results in unexpected behavior.
817
+
815
818
  #### blockquote
816
819
 
817
820
  - `borderColor` defines the color of the rectangular border drawn to the left of blockquote text. Takes [color](https://reactnative.dev/docs/colors) value, defaults to `darkgray`.
@@ -204,7 +204,9 @@ class EnrichedTextInputView : AppCompatEditText {
204
204
  }
205
205
  }
206
206
 
207
- val finalText = currentText.mergeSpannables(start, end, item?.text.toString())
207
+ // Currently, we do not support pasting images
208
+ if (item?.text == null) return
209
+ val finalText = currentText.mergeSpannables(start, end, item.text.toString())
208
210
  setValue(finalText)
209
211
  parametrizedStyles?.detectAllLinks()
210
212
  }
@@ -154,6 +154,8 @@ class HtmlStyle {
154
154
  }
155
155
 
156
156
  private fun withOpacity(color: Int, alpha: Int): Int {
157
+ // Do not apply opacity to transparent color
158
+ if (Color.alpha(color) == 0) return color
157
159
  val a = alpha.coerceIn(0, 255)
158
160
  return (color and 0x00FFFFFF) or (a shl 24)
159
161
  }
@@ -3,6 +3,7 @@
3
3
  #import "FontExtension.h"
4
4
  #import "OccurenceUtils.h"
5
5
  #import "ParagraphsUtils.h"
6
+ #import "ColorExtension.h"
6
7
 
7
8
  @implementation InlineCodeStyle {
8
9
  EnrichedTextInputView *_input;
@@ -33,7 +34,7 @@
33
34
  NSRange currentRange = [value rangeValue];
34
35
  [_input->textView.textStorage beginEditing];
35
36
 
36
- [_input->textView.textStorage addAttribute:NSBackgroundColorAttributeName value:[[_input->config inlineCodeBgColor] colorWithAlphaComponent:0.4] range:currentRange];
37
+ [_input->textView.textStorage addAttribute:NSBackgroundColorAttributeName value:[[_input->config inlineCodeBgColor] colorWithAlphaIfNotTransparent:0.4] range:currentRange];
37
38
  [_input->textView.textStorage addAttribute:NSForegroundColorAttributeName value:[_input->config inlineCodeFgColor] range:currentRange];
38
39
  [_input->textView.textStorage addAttribute:NSUnderlineColorAttributeName value:[_input->config inlineCodeFgColor] range:currentRange];
39
40
  [_input->textView.textStorage addAttribute:NSStrikethroughColorAttributeName value:[_input->config inlineCodeFgColor] range:currentRange];
@@ -41,7 +42,7 @@
41
42
  usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
42
43
  UIFont *font = (UIFont *)value;
43
44
  if(font != nullptr) {
44
- UIFont *newFont = [[_input->config monospacedFont] withFontTraits:font];
45
+ UIFont *newFont = [[[_input->config monospacedFont] withFontTraits:font] setSize:font.pointSize];
45
46
  [_input->textView.textStorage addAttribute:NSFontAttributeName value:newFont range:range];
46
47
  }
47
48
  }
@@ -53,13 +54,13 @@
53
54
 
54
55
  - (void)addTypingAttributes {
55
56
  NSMutableDictionary *newTypingAttrs = [_input->textView.typingAttributes mutableCopy];
56
- newTypingAttrs[NSBackgroundColorAttributeName] = [[_input->config inlineCodeBgColor] colorWithAlphaComponent:0.4];
57
+ newTypingAttrs[NSBackgroundColorAttributeName] = [[_input->config inlineCodeBgColor] colorWithAlphaIfNotTransparent:0.4];
57
58
  newTypingAttrs[NSForegroundColorAttributeName] = [_input->config inlineCodeFgColor];
58
59
  newTypingAttrs[NSUnderlineColorAttributeName] = [_input->config inlineCodeFgColor];
59
60
  newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config inlineCodeFgColor];
60
61
  UIFont* currentFont = (UIFont *)newTypingAttrs[NSFontAttributeName];
61
62
  if(currentFont != nullptr) {
62
- newTypingAttrs[NSFontAttributeName] = [[_input->config monospacedFont] withFontTraits:currentFont];
63
+ newTypingAttrs[NSFontAttributeName] = [[[_input->config monospacedFont] withFontTraits:currentFont] setSize:currentFont.pointSize];
63
64
  }
64
65
  _input->textView.typingAttributes = newTypingAttrs;
65
66
  }
@@ -75,7 +76,7 @@
75
76
  usingBlock:^(id _Nullable value, NSRange range, BOOL * _Nonnull stop) {
76
77
  UIFont *font = (UIFont *)value;
77
78
  if(font != nullptr) {
78
- UIFont *newFont = [[_input->config primaryFont] withFontTraits:font];
79
+ UIFont *newFont = [[[_input->config primaryFont] withFontTraits:font] setSize:font.pointSize];
79
80
  [_input->textView.textStorage addAttribute:NSFontAttributeName value:newFont range:range];
80
81
  }
81
82
  }
@@ -92,7 +93,7 @@
92
93
  newTypingAttrs[NSStrikethroughColorAttributeName] = [_input->config primaryColor];
93
94
  UIFont* currentFont = (UIFont *)newTypingAttrs[NSFontAttributeName];
94
95
  if(currentFont != nullptr) {
95
- newTypingAttrs[NSFontAttributeName] = [[_input->config primaryFont] withFontTraits:currentFont];
96
+ newTypingAttrs[NSFontAttributeName] = [[[_input->config primaryFont] withFontTraits:currentFont] setSize:currentFont.pointSize];
96
97
  }
97
98
  _input->textView.typingAttributes = newTypingAttrs;
98
99
  }
@@ -4,6 +4,7 @@
4
4
  #import "TextInsertionUtils.h"
5
5
  #import "WordsUtils.h"
6
6
  #import "UIView+React.h"
7
+ #import "ColorExtension.h"
7
8
 
8
9
  // custom NSAttributedStringKey to differentiate from links
9
10
  static NSString *const MentionAttributeName = @"MentionAttributeName";
@@ -154,7 +155,7 @@ static NSString *const MentionAttributeName = @"MentionAttributeName";
154
155
  NSForegroundColorAttributeName: styleProps.color,
155
156
  NSUnderlineColorAttributeName: styleProps.color,
156
157
  NSStrikethroughColorAttributeName: styleProps.color,
157
- NSBackgroundColorAttributeName: [styleProps.backgroundColor colorWithAlphaComponent:0.4],
158
+ NSBackgroundColorAttributeName: [styleProps.backgroundColor colorWithAlphaIfNotTransparent:0.4],
158
159
  } mutableCopy];
159
160
 
160
161
  if(styleProps.decorationLine == DecorationUnderline) {
@@ -186,7 +187,7 @@ static NSString *const MentionAttributeName = @"MentionAttributeName";
186
187
  NSForegroundColorAttributeName: styleProps.color,
187
188
  NSUnderlineColorAttributeName: styleProps.color,
188
189
  NSStrikethroughColorAttributeName: styleProps.color,
189
- NSBackgroundColorAttributeName: [styleProps.backgroundColor colorWithAlphaComponent:0.4],
190
+ NSBackgroundColorAttributeName: [styleProps.backgroundColor colorWithAlphaIfNotTransparent:0.4],
190
191
  } mutableCopy];
191
192
 
192
193
  if(styleProps.decorationLine == DecorationUnderline) {
@@ -3,4 +3,5 @@
3
3
 
4
4
  @interface UIColor (ColorExtension)
5
5
  - (BOOL)isEqualToColor:(UIColor *)otherColor;
6
+ - (UIColor *)colorWithAlphaIfNotTransparent:(CGFloat)newAlpha;
6
7
  @end
@@ -24,4 +24,13 @@
24
24
 
25
25
  return [selfColor isEqual:otherColor];
26
26
  }
27
+
28
+ - (UIColor *)colorWithAlphaIfNotTransparent:(CGFloat)newAlpha {
29
+ CGFloat alpha = 0.0;
30
+ [self getRed:nil green:nil blue:nil alpha:&alpha];
31
+ if (alpha > 0.0) {
32
+ return [self colorWithAlphaComponent:newAlpha];
33
+ }
34
+ return self;
35
+ }
27
36
  @end
@@ -18,7 +18,7 @@
18
18
  [textView.textStorage insertAttributedString:newAttrStr atIndex:index];
19
19
 
20
20
  if(withSelection) {
21
- if(!textView.focused) {
21
+ if(![textView isFirstResponder]) {
22
22
  [textView reactFocus];
23
23
  }
24
24
  textView.selectedRange = NSMakeRange(index + text.length, 0);
@@ -38,7 +38,7 @@
38
38
  }
39
39
 
40
40
  if(withSelection) {
41
- if(!textView.focused) {
41
+ if(![textView isFirstResponder]) {
42
42
  [textView reactFocus];
43
43
  }
44
44
  textView.selectedRange = NSMakeRange(range.location + text.length, 0);
@@ -63,8 +63,7 @@
63
63
  }
64
64
 
65
65
  // fix the selection if needed
66
- if(input->textView.focused) {
67
- [input->textView reactFocus];
66
+ if([input->textView isFirstResponder]) {
68
67
  input->textView.selectedRange = NSMakeRange(preRemoveSelection.location + postRemoveOffset, preRemoveSelection.length);
69
68
  }
70
69
  }
@@ -112,8 +111,7 @@
112
111
  }
113
112
 
114
113
  // fix the selection if needed
115
- if(input->textView.focused) {
116
- [input->textView reactFocus];
114
+ if([input->textView isFirstResponder]) {
117
115
  input->textView.selectedRange = NSMakeRange(preAddSelection.location + postAddOffset, preAddSelection.length);
118
116
  }
119
117
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-enriched",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Rich Text Editor component for React Native",
5
5
  "source": "./src/index.tsx",
6
6
  "main": "./lib/module/index.js",