enriched-text-input 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/API_REFERENCE.md CHANGED
@@ -1 +1,119 @@
1
- # API Reference
1
+ # API Reference
2
+
3
+ ## Props
4
+
5
+ ### `ref`
6
+
7
+ A React ref that lets you call any ref methods on the input.
8
+
9
+ ### `patterns`
10
+
11
+ An array of style patterns for the input. Use this prop to define which styles should be available for the input to use.
12
+
13
+ ```jsx
14
+ interface Pattern {
15
+ name: string;
16
+ render: React.Component;
17
+ opening: string | null;
18
+ closing: string | null;
19
+ }
20
+ ```
21
+
22
+ A pattern can have either both opening and closing enclosures defined or just the opening enclosure, but cannot have only the closing enclosure defined.
23
+
24
+ ### `defaultValue`
25
+
26
+ Provides an initial value for the input. Can be a string or an array of tokens. If it’s a string and it matches any style defined in the `patterns` prop, proper styles will be applied.
27
+
28
+ ### `onSelectionChange`
29
+
30
+ Callback that is called when the text input selection is changed.
31
+
32
+ ### `onValueChange`
33
+
34
+ Callback that is called when the text input's value changes. You can use this callback to call ref methods such as `.getRawValue()`, `.getRichTextValue()` or `.getTokenizedValue()` to get the text input’s value in your preferred
35
+
36
+ ### `onDebounceValueChange`
37
+
38
+ Same as `onValueChange` but with an applied debouncing effect.
39
+
40
+ ## Ref methods
41
+
42
+ ### `.setValue()`
43
+
44
+ ```jsx
45
+ setValue: (value: string | Tokens[]) => void;
46
+ ```
47
+
48
+ Sets the value of the input.
49
+
50
+ - `value: string | Tokens[]` - the value to set the input. If it’s a valid rich text string, the corresponding styling will be applied.
51
+
52
+ ### `.setSelection()`
53
+
54
+ ```jsx
55
+ setSelection: (start: number, end: number) => void;
56
+ ```
57
+
58
+ Sets the selection of the input.
59
+
60
+ - `start: number` - the start index of the input’s selection.
61
+ - `end: number` - the end index of the input’s selection.
62
+
63
+ ### `.focus()`
64
+
65
+ ```jsx
66
+ focus: () => void;
67
+ ```
68
+
69
+ Focuses the input;
70
+
71
+ ### `.blur()`
72
+
73
+ ```jsx
74
+ blur: () => void;
75
+ ```
76
+
77
+ Blurs the input.
78
+
79
+ ### `.toggleStyle()`
80
+
81
+ ```jsx
82
+ toggleStyle: (style: string) => void;
83
+ ```
84
+
85
+ Toggles a style at the cursor’s position.
86
+
87
+ - `style: string` - the name of a pattern to toggle.
88
+
89
+ ### `.getActiveStyle()`
90
+
91
+ ```jsx
92
+ getActiveStyle: () => string[] | [];
93
+ ```
94
+
95
+ Returns the active styles for the current selection.
96
+
97
+ ### `.getRawValue()`
98
+
99
+ ```jsx
100
+ getRawValue: () => string;
101
+ ```
102
+
103
+ Returns the input’s value as a raw string without rich text enclosures.
104
+
105
+ ### `.getRichTextValue()`
106
+
107
+ ```jsx
108
+ getRichTextValue: () => string;
109
+ ```
110
+
111
+ Returns the text input’s value as a rich text string matching the patterns for each style defined in the patterns prop. If a style does not define an opening and closing char, it is ignored.
112
+
113
+ ### `.getTokenizedValue()`
114
+
115
+ ```jsx
116
+ getTokenizedValue: () => Tokens[];
117
+ ```
118
+
119
+ Returns the text input's value as an array of tokens.
@@ -0,0 +1,33 @@
1
+ # Contributing
2
+
3
+ ## Clone this repo
4
+
5
+ 1. Fork and clone your Github froked repo:
6
+ ```
7
+ git clone https://github.com/<github_username>/enriched-text-input.git
8
+ ```
9
+
10
+ 2. Go to cloned repo directory:
11
+ ```
12
+ cd enriched-text-input
13
+ ```
14
+
15
+ ## Install dependencies
16
+
17
+ 1. Install the dependencies in the root of the repo:
18
+ ```
19
+ npm install
20
+ ```
21
+
22
+ 2. Go to the example project and install dependencies:
23
+ ```
24
+ cd example && npm install
25
+ ```
26
+
27
+ 3. After that you can start the project with:
28
+ ```
29
+ cd example && npm start
30
+ ```
31
+
32
+ ## Create a pull request
33
+ After making any changes, open a pull request. Once you submit your pull request, it will get reviewed.
package/README.md CHANGED
@@ -1,37 +1,36 @@
1
- [![plastic](https://dcbadge.limes.pink/api/server/https://discord.gg/DRmNp34bFE?bot=true&style=plastic)](https://discord.gg/DRmNp34bFE)
2
-
3
1
  # enriched-text-input
4
2
 
5
3
  > [!Note]
6
- > This library is still a work in progress. Expect breaking changes.
4
+ This library is still a work in progress. Expect breaking changes.
5
+ >
7
6
 
8
- Proof of concept for a JavaScript only rich-text TextInput component for React Native.
9
- The main idea is to render `<Text>` views as children of `<TextInput>`.
10
- It will only support text styling since it's not possible to render images inside `Text` views in React Native. [Try it on Expo Snack](https://snack.expo.dev/@patosala/enriched-text-input).
7
+ Proof of concept for a JavaScript only rich-text TextInput component for React Native. The main idea is to render `<Text>` views as children of `<TextInput>`. It will only support text styling since it's not possible to render images inside `Text` views in React Native. [Try it on Expo Snack](https://snack.expo.dev/@patosala/enriched-text-input).
11
8
 
12
9
  ## Motivation
10
+
13
11
  The field for rich-text in react native is still a bit green. Current libraries that add support for rich-text in react native applications are either WebViews wrapping libraries for the web, limiting customization, or require native code which drops support for Expo Go and react-native-web.
14
12
 
15
- In theory, by only using JavaScript we are able to provide better cross-platform compatibility and the possibility to style elements however you want as long as they follow react-native's `Text` supported styles.
13
+ In theory, by only using JavaScript we are able to provide better cross-platform compatibility and the possibility to style elements however you want as long as they follow react-native's `Text` supported styles.
16
14
 
17
15
  ## Installation
18
- ```
16
+
17
+ ```bash
19
18
  npm install enriched-text-input
20
19
  ```
21
20
 
22
21
  ## Usage
23
- ```js
22
+
23
+ ```jsx
24
24
  import { useRef } from 'react';
25
25
  import { StyleSheet, View } from 'react-native';
26
-
27
- import { RichTextInput, Toolbar } from 'enriched-text-input';
26
+ import { EnrichedTextInput, Toolbar } from 'enriched-text-input';
28
27
 
29
28
  export default function App() {
30
29
  const richTextInputRef = useRef(null);
31
30
 
32
31
  return (
33
32
  <View style={styles.container}>
34
- <RichTextInput ref={richTextInputRef}/>
33
+ <EnrichedTextInput ref={richTextInputRef}/>
35
34
  <Toolbar richTextInputRef={richTextInputRef}>
36
35
  <Toolbar.Bold />
37
36
  <Toolbar.Italic />
@@ -54,50 +53,58 @@ const styles = StyleSheet.create({
54
53
  ```
55
54
 
56
55
  ## Current state
57
- At the moment [1/1/2026] `enriched-text-input` works great for things such as small rich-text inputs (Eg. an input for a messaging app with rich-text support) but not for creating whole rich-text editors. This is because inline styles that do not break line are working as expected (Eg. bold, italic or underline work great but styles such as headings break line so they are currently not supported).
58
56
 
59
- Live parsing of rich text symbols (such as wrapping words in asterisks `*`) is still a work in progress an not working correctly but you can toggle styles through the ref api of the `EnrichedTextInput` (or use the provided `Toolbar` component as shown in the example usage).
57
+ At the moment [1/1/2026] `enriched-text-input` works great for things such as small rich-text inputs (Eg. an input for a messaging app with rich-text support) but not for creating whole rich-text editors. This is because inline styles that do not break line are working as expected (Eg. bold, italic or underline work great but styles such as headings break line so they are currently not yet supported).
58
+
59
+ Live parsing of rich text symbols (such as wrapping words in asterisks `*`) is still a work in progress an not working correctly but you can toggle styles through the ref api of the `EnrichedTextInput` (or use the provided `Toolbar`component as shown in the example usage).
60
60
 
61
61
  ## Features
62
62
 
63
- - [x] Basic text formatting (__bold__, _italic_, underline, ~~strikethrough~~ and `inline code`).
64
- - [ ] Rich text format parsing.
65
- - [ ] Links and mentions.
66
- - [x] Custom styling.
67
- - [x] Custom rich text patterns.
68
- - [ ] Exposed event handlers (onSubmit, onChange, onBlur, onFocus, etc).
69
- - [ ] Custom methods and event handlers (setValue, onStartMention, onStyleChange, etc).
70
- - [ ] Headings.
63
+ - [x] Inline markdown styles (**bold**, *italic*, underline, ~~strikethrough~~ and `inline code`).
64
+ - [ ] Paragraph styles (headings, lists, quotes, etc).
65
+ - [ ] Live rich-text parsing.
66
+ - [ ] Links and mentions.
67
+ - [x] Custom inline styles.
68
+ - [x] Custom methods and event handlers (setValue, onStartMention, onStyleChange, etc).
71
69
 
72
- ## Known limitations
73
- - Inline images.
74
- - Only `Text`component styles are supported.
70
+ ## API Reference
75
71
 
76
- ## Contributing
72
+ [API Reference](https://github.com/PatoSala/enriched-text-input/blob/main/API_REFERENCE.md)
77
73
 
78
- ### Clone this repo
74
+ ## Style patterns
79
75
 
80
- 1. Fork and clone your Github froked repo:
81
- ```
82
- git clone https://github.com/<github_username>/react-native-rich-text.git
83
- ```
76
+ Style patterns are the styles that you provide the input with for it to know how it should display certain portions of the text. You can check their structure in the [API reference](https://github.com/PatoSala/enriched-text-input/blob/main/API_REFERENCE.md#stylePatterns).
84
77
 
85
- 2. Go to cloned repo directory:
86
- ```
87
- cd react-native-rich-text
88
- ```
78
+ By default `enriched-text-input` uses a set of markdown styles (such as **bold**, *italic*, underline, ~~strikethrough~~ and `inline code`) for you to use out of the box, but you can provide your own custom styles through the `stylePatterns` prop. Keep in mind that when using this prop `enriched-text-input` will ignore any default style patterns so that only the ones you provided will be valid.
89
79
 
90
- ### Install dependencies
80
+ If you want you can provide `enriched-text-input` with both default style patterns and any additional style patterns you define:
91
81
 
92
- 1. Install the dependencies in the root of the repo:
93
- ```
94
- npm install
95
- ```
82
+ ```bash
83
+ import { EnrichedTextInput, markdownStyles } from "enriched-text-input";
96
84
 
97
- 3. After that you can start the project with:
98
- ```
99
- npm start
85
+ const customStyles = [
86
+ {
87
+ name: "comment",
88
+ opening: null,
89
+ closing: null,
90
+ render: Comment
91
+ }
92
+ ];
93
+
94
+ <EnrichedTextInput
95
+ stylePattrns={[...markdownStyles, ...customStyles]}
96
+ />
100
97
  ```
101
98
 
102
- ## Create a pull request
103
- After making any changes, open a pull request. Once you submit your pull request, it will get reviewed.
99
+ ## Applying styles
100
+
101
+ To apply styles you can either toggle them using the `ref` method `.toggleStyle()` which accepts as a parameter the name of a style pattern, or you can use rich-text enclosures while typing. This enclosures are defined within the `stylePatterns` prop and corresponding styles will get applied when a matching pattern is found inside the input.
102
+
103
+ ## Known limitations
104
+
105
+ - Inline images.
106
+ - Only `Text` component styles are supported.
107
+
108
+ ## Contributing
109
+
110
+ [Contributing guide](https://github.com/PatoSala/enriched-text-input/blob/main/CONTRIBUTING.md)
package/example/App.tsx CHANGED
@@ -1,21 +1,8 @@
1
1
  import { useRef, useState } from 'react';
2
- import { StyleSheet, View, KeyboardAvoidingView, Text, TouchableOpacity, Button, TextInput } from 'react-native';
3
- import { FontAwesome6 } from '@expo/vector-icons';
4
- import { RichTextInput, Toolbar, PATTERNS } from 'enriched-text-input';
2
+ import { StyleSheet, View, KeyboardAvoidingView, Text, Button, TextInput } from 'react-native';
3
+ import { EnrichedTextInput, Toolbar, markdownStyles } from 'enriched-text-input';
5
4
  import * as Clipboard from 'expo-clipboard';
6
5
 
7
- function Comment({ children }) {
8
- return (
9
- <Text style={{
10
- backgroundColor: "rgba(255, 203, 0, .12)",
11
- textDecorationLine: "underline",
12
- textDecorationColor: "rgba(255, 203, 0, .35)",
13
- }}>
14
- {children}
15
- </Text>
16
- )
17
- }
18
-
19
6
  export default function App() {
20
7
  const [rawValue, setRawValue] = useState("");
21
8
  const [richTextStringValue, setRichTextStringValue] = useState("");
@@ -23,17 +10,8 @@ export default function App() {
23
10
  console.log("ACTIVE STYLES:", activeStyles);
24
11
  const richTextInputRef = useRef(null);
25
12
 
26
- const customPatterns = [
27
- ...PATTERNS,
28
- { style: "comment", regex: null, render: Comment }
29
- ];
30
-
31
- const handleComment = () => {
32
- richTextInputRef.current?.toggleStyle("comment");
33
- }
34
-
35
13
  const handleGetRichText = () => {
36
- const richText = richTextInputRef.current?.getRichTextString();
14
+ const richText = richTextInputRef.current?.getRichTextValue();
37
15
 
38
16
  setRichTextStringValue(richText);
39
17
  }
@@ -57,11 +35,10 @@ export default function App() {
57
35
  onPress={() => richTextInputRef.current?.setValue(rawValue)}
58
36
  />
59
37
 
60
- <RichTextInput
38
+ <EnrichedTextInput
61
39
  ref={richTextInputRef}
62
- patterns={customPatterns}
63
- autoComplete="off"
64
40
  placeholder="Rich text"
41
+ onValueChange={() => console.log("VALUE CHANGE", richTextInputRef.current?.getRichTextValue())}
65
42
  multiline={true}
66
43
  />
67
44
  <Button
package/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- import RichTextInput, { PATTERNS } from "./src/RichTextInput";
1
+ import EnrichedTextInput from "./src/EnrichedTextInput";
2
+ import { markdownStyles } from "./src/markdownStyles";
2
3
  import Toolbar from "./src/Toolbar";
3
4
 
4
- export { RichTextInput, PATTERNS, Toolbar };
5
+ export { EnrichedTextInput, markdownStyles, Toolbar };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "enriched-text-input",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "JavaScript only rich text input component for React Native. Compatible with Expo Go.",
5
5
  "keywords": [
6
6
  "rich-text",
@@ -1,5 +1,6 @@
1
1
  import { useState, useImperativeHandle, useRef, useEffect, JSX } from "react";
2
2
  import { TextInput, Text, StyleSheet, View, TextInputProps } from "react-native";
3
+ import { markdownStyles } from "./markdownStyles";
3
4
 
4
5
  interface Token {
5
6
  text: string;
@@ -34,9 +35,14 @@ interface Pattern {
34
35
  closing?: string;
35
36
  }
36
37
 
37
- interface RichTextInputProps {
38
+ interface EnrichedTextInputProps {
38
39
  ref: any;
39
- patterns?: Pattern[]
40
+ stylePatterns?: Pattern[];
41
+ placeholder?: string;
42
+ multiline?: boolean;
43
+ defaultValue?: string | Token[];
44
+ onValueChange?: () => void;
45
+ onSelectionChange?: () => void;
40
46
  }
41
47
 
42
48
  /**
@@ -45,16 +51,7 @@ interface RichTextInputProps {
45
51
  * If just opening is defined, we look for a match that looks like {opening}{content}.
46
52
  * Closing can not be defined if opening is not defined.
47
53
  */
48
- export const PATTERNS : Pattern[] = [
49
- { style: "bold", regex: "\\*([^*]+)\\*", render: Bold, opening: "*", closing: "*" },
50
- { style: "italic", regex: "_([^_]+)_", render: Italic, opening: "_", closing: "_" },
51
- { style: "lineThrough", regex: "~([^~]+)~", render: Strikethrough, opening: "~", closing: "~" },
52
- { style: "code", regex: "`([^`]+)`", render: Code, opening: "`", closing: "`" },
53
- { style: "underline", regex: "__([^_]+)__", render: Underline, opening: "__", closing: "__" },
54
- { style: "heading", regex: null, render: Heading, opening: "#", closing: null },
55
- { style: "subHeading", regex: null, render: SubHeading, opening: "##", closing: null },
56
- { style: "subSubHeading", regex: null, render: SubSubHeading, opening: "###", closing: null }
57
- ];
54
+ export const defaultStylePatterns : Pattern[] = markdownStyles;
58
55
 
59
56
  function insertAt(str, index, substring) {
60
57
  // Clamp index into valid boundaries
@@ -82,7 +79,7 @@ function findTokens(
82
79
  ) {
83
80
 
84
81
  if (end) {
85
- // Search for all tokens between start and end
82
+ // To-do: search for all tokens between start and end
86
83
  return { result: null };
87
84
  }
88
85
 
@@ -143,6 +140,11 @@ function findMatchV2(str: string, patterns: Pattern[]) : RichTextMatch | null {
143
140
 
144
141
  /**
145
142
  * If prev token contains new annotation, negate prev. Else, use new annotation.
143
+ *
144
+ * @example
145
+ * prev: { bold: true }
146
+ * new: { bold: false }
147
+ * result: { bold: false }
146
148
  */
147
149
  function concileAnnotations(prevAnnotations, newAnnotations) {
148
150
  let updatedAnnotations = { ...prevAnnotations };
@@ -180,6 +182,7 @@ function diffStrings(prev, next) : Diff {
180
182
  }
181
183
 
182
184
  /**
185
+ * [Needs refactoring]
183
186
  * Parse rich text string into tokens.
184
187
  */
185
188
  const parseRichTextString = (richTextString: string, patterns: Pattern[], initialTokens?: Token[])
@@ -224,6 +227,13 @@ const parseRichTextString = (richTextString: string, patterns: Pattern[], initia
224
227
 
225
228
  /**
226
229
  * Parse tokens into rich text string.
230
+ * To-do: Find a way to group consequitive tokens with a same annotation inside one single
231
+ * wrapper.
232
+ *
233
+ * @example
234
+ * Tokens: [{ text: "Hello", annotations: { bold: true } }, { text: "World", annotations: { bold: true, italic: true } }]
235
+ * Current output: *Hello* *_World_*
236
+ * Desired output: *Hello _World_*
227
237
  */
228
238
  const parseTokens = (tokens: Token[], patterns: Pattern[]) => {
229
239
  return tokens.map(token => {
@@ -430,6 +440,7 @@ const updateTokens = (tokens: Token[], diff: Diff) => {
430
440
  * Remove:
431
441
  * - For more than two tokens, works.
432
442
  * - For two tokens, does not work properly.
443
+ * (right now remove for more than two tokens works properly. Anyway, it might need better testing).
433
444
  */
434
445
  if (diff.removed.length > 0) {
435
446
  const firstToken = selectedTokens[0];
@@ -658,71 +669,15 @@ function Token(props: TokenProps) : JSX.Element {
658
669
  );
659
670
  }
660
671
 
661
- function Code({ children }) {
662
- return (
663
- <Text style={styles.code}>{children}</Text>
664
- )
665
- }
666
-
667
- function Bold({ children }) {
668
- return (
669
- <Text style={styles.bold}>{children}</Text>
670
- )
671
- }
672
-
673
- function Italic({ children }) {
674
- return (
675
- <Text style={styles.italic}>{children}</Text>
676
- )
677
- }
678
-
679
- function Underline({ children }) {
680
- return (
681
- <Text style={styles.underline}>{children}</Text>
682
- )
683
- }
684
-
685
- function Strikethrough({ children }) {
686
- return (
687
- <Text style={styles.lineThrough}>{children}</Text>
688
- )
689
- }
690
-
691
- function UnderlineStrikethrough({ children }) {
692
- return (
693
- <Text style={styles.underlineLineThrough}>{children}</Text>
694
- )
695
- }
696
-
697
- function Heading({ children }) {
698
- return (
699
- <Text style={styles.heading}>{children}</Text>
700
- )
701
- }
702
-
703
- function SubHeading({ children }) {
704
- return (
705
- <Text style={styles.subHeading}>{children}</Text>
706
- )
707
- }
708
-
709
- function SubSubHeading({ children }) {
710
- return (
711
- <Text style={styles.subSubHeading}>{children}</Text>
712
- )
713
- }
714
-
715
- export default function RichTextInput(props: RichTextInputProps) {
672
+ export default function EnrichedTextInput(props: EnrichedTextInputProps) {
716
673
  const {
717
674
  ref,
718
- patterns = PATTERNS,
719
-
720
- /** TextInput props */
721
- value,
675
+ stylePatterns = defaultStylePatterns,
676
+ placeholder,
677
+ multiline = false,
722
678
  defaultValue,
723
- onChangeText,
724
679
  onSelectionChange,
725
- ...rest
680
+ onValueChange,
726
681
  } = props;
727
682
 
728
683
  const inputRef = useRef<TextInput>(null);
@@ -750,8 +705,24 @@ export default function RichTextInput(props: RichTextInputProps) {
750
705
  }
751
706
  }])
752
707
  }
708
+
709
+ onValueChange && onValueChange();
753
710
  }, [tokens]);
754
711
 
712
+ useEffect(( ) => {
713
+ if (defaultValue) {
714
+ if (Array.isArray(defaultValue)) {
715
+ // Maybe check if tokens structure is valid before setting.
716
+ setTokens(defaultValue);
717
+ return;
718
+ }
719
+ // To keep styles, parsing should be done before setting defaultValue
720
+ const { tokens, plain_text } = parseRichTextString(defaultValue, stylePatterns);
721
+ setTokens(tokens);
722
+ prevTextRef.current = plain_text;
723
+ }
724
+ }, []);
725
+
755
726
  /**
756
727
  * Prev text should not contain matching rich text formats.
757
728
  * Those should be spliced once the corresponding tokens are created.
@@ -780,7 +751,7 @@ export default function RichTextInput(props: RichTextInputProps) {
780
751
  const handleOnChangeText = (nextText: string) => {
781
752
  const diff = diffStrings(prevTextRef.current, nextText);
782
753
 
783
- const match = findMatchV2(nextText, patterns);
754
+ const match = findMatchV2(nextText, stylePatterns);
784
755
  /* console.log("MATCH:", match); */
785
756
 
786
757
  // Note: refactor to use new parseRichText function instead of regex
@@ -841,7 +812,7 @@ export default function RichTextInput(props: RichTextInputProps) {
841
812
  return;
842
813
  }
843
814
  // To keep styles, parsing should be done before setting value
844
- const { tokens, plain_text } = parseRichTextString(value, patterns);
815
+ const { tokens, plain_text } = parseRichTextString(value, stylePatterns);
845
816
  setTokens(tokens);
846
817
  prevTextRef.current = plain_text;
847
818
  },
@@ -865,13 +836,16 @@ export default function RichTextInput(props: RichTextInputProps) {
865
836
  * for each style defined in the patterns prop. If a style does not define an
866
837
  * opening and closing char, it is ignored.
867
838
  */
868
- getRichTextString() {
869
- return parseTokens(tokens, patterns);
839
+ getRawValue() {
840
+ return tokens.map(t => t.text).join("");
841
+ },
842
+ getRichTextValue() {
843
+ return parseTokens(tokens, stylePatterns);
870
844
  },
871
845
  /**
872
- * Returns the TextInput's value as an array of tokens with annotations.
846
+ * Returns the text input's value as an array of tokens.
873
847
  */
874
- getTokenizedString() : Token[] {
848
+ getTokenizedValue() : Token[] {
875
849
  return tokens;
876
850
  },
877
851
  /**
@@ -915,12 +889,13 @@ export default function RichTextInput(props: RichTextInputProps) {
915
889
  <TextInput
916
890
  ref={inputRef}
917
891
  style={styles.textInput}
892
+ placeholder={placeholder}
893
+ multiline={multiline}
918
894
  onSelectionChange={handleSelectionChange}
919
895
  onChangeText={handleOnChangeText}
920
- {...rest}
921
896
  >
922
897
  <Text style={styles.text}>
923
- {tokens.map((token, i) => <Token key={i} token={token} patterns={patterns}/>)}
898
+ {tokens.map((token, i) => <Token key={i} token={token} patterns={stylePatterns}/>)}
924
899
  </Text>
925
900
  </TextInput>
926
901
  </View>
@@ -936,53 +911,5 @@ const styles = StyleSheet.create({
936
911
  },
937
912
  text: {
938
913
  color: "black",
939
- },
940
- bold: {
941
- fontWeight: 'bold',
942
- },
943
- italic: {
944
- fontStyle: "italic"
945
- },
946
- lineThrough: {
947
- textDecorationLine: "line-through"
948
- },
949
- underline: {
950
- textDecorationLine: "underline",
951
- },
952
- underlineLineThrough: {
953
- textDecorationLine: "underline line-through"
954
- },
955
- codeContainer: {
956
- backgroundColor: "lightgray",
957
- paddingHorizontal: 4,
958
- borderRadius: 4,
959
- height: 24,
960
- position: "absolute",
961
- top: 10
962
- },
963
- code: {
964
- fontFamily: "ui-monospace",
965
- color: "#EB5757",
966
- fontSize: 20,
967
- backgroundColor: "rgba(135, 131, 120, .15)"
968
- },
969
- highlight: {
970
- width: "100%",
971
- position: "absolute",
972
- padding: 20,
973
- height: 24,
974
- backgroundColor: "blue"
975
- },
976
- heading: {
977
- fontSize: 32,
978
- fontWeight: "bold"
979
- },
980
- subHeading: {
981
- fontSize: 28,
982
- fontWeight: "bold"
983
- },
984
- subSubHeading: {
985
- fontSize: 24,
986
- fontWeight: "bold"
987
914
  }
988
915
  });
@@ -0,0 +1,106 @@
1
+ import { Text, StyleSheet } from "react-native";
2
+
3
+ export function Code({ children }) {
4
+ return (
5
+ <Text style={styles.code}>{children}</Text>
6
+ )
7
+ }
8
+
9
+ export function Bold({ children }) {
10
+ return (
11
+ <Text style={styles.bold}>{children}</Text>
12
+ )
13
+ }
14
+
15
+ export function Italic({ children }) {
16
+ return (
17
+ <Text style={styles.italic}>{children}</Text>
18
+ )
19
+ }
20
+
21
+ export function Underline({ children }) {
22
+ return (
23
+ <Text style={styles.underline}>{children}</Text>
24
+ )
25
+ }
26
+
27
+ export function Strikethrough({ children }) {
28
+ return (
29
+ <Text style={styles.lineThrough}>{children}</Text>
30
+ )
31
+ }
32
+
33
+ export function UnderlineStrikethrough({ children }) {
34
+ return (
35
+ <Text style={styles.underlineLineThrough}>{children}</Text>
36
+ )
37
+ }
38
+
39
+ export function Heading({ children }) {
40
+ return (
41
+ <Text style={styles.heading}>{children}</Text>
42
+ )
43
+ }
44
+
45
+ export function SubHeading({ children }) {
46
+ return (
47
+ <Text style={styles.subHeading}>{children}</Text>
48
+ )
49
+ }
50
+
51
+ export function SubSubHeading({ children }) {
52
+ return (
53
+ <Text style={styles.subSubHeading}>{children}</Text>
54
+ )
55
+ }
56
+
57
+ const styles = StyleSheet.create({
58
+ bold: {
59
+ fontWeight: 'bold',
60
+ },
61
+ italic: {
62
+ fontStyle: "italic"
63
+ },
64
+ lineThrough: {
65
+ textDecorationLine: "line-through"
66
+ },
67
+ underline: {
68
+ textDecorationLine: "underline",
69
+ },
70
+ underlineLineThrough: {
71
+ textDecorationLine: "underline line-through"
72
+ },
73
+ codeContainer: {
74
+ backgroundColor: "lightgray",
75
+ paddingHorizontal: 4,
76
+ borderRadius: 4,
77
+ height: 24,
78
+ position: "absolute",
79
+ top: 10
80
+ },
81
+ code: {
82
+ fontFamily: "ui-monospace",
83
+ color: "#EB5757",
84
+ fontSize: 20,
85
+ backgroundColor: "rgba(135, 131, 120, .15)"
86
+ },
87
+ highlight: {
88
+ width: "100%",
89
+ position: "absolute",
90
+ padding: 20,
91
+ height: 24,
92
+ backgroundColor: "blue"
93
+ },
94
+ heading: {
95
+ fontSize: 32,
96
+ fontWeight: "bold"
97
+ },
98
+ subHeading: {
99
+ fontSize: 28,
100
+ fontWeight: "bold"
101
+ },
102
+ subSubHeading: {
103
+ fontSize: 24,
104
+ fontWeight: "bold"
105
+ }
106
+ });
@@ -0,0 +1,12 @@
1
+ import { Bold, Code, Heading, Italic, SubHeading, SubSubHeading, Strikethrough, Underline } from "./components/StyledText";
2
+
3
+ export const markdownStyles = [
4
+ { style: "bold", regex: "\\*([^*]+)\\*", render: Bold, opening: "*", closing: "*" },
5
+ { style: "italic", regex: "_([^_]+)_", render: Italic, opening: "_", closing: "_" },
6
+ { style: "lineThrough", regex: "~([^~]+)~", render: Strikethrough, opening: "~", closing: "~" },
7
+ { style: "code", regex: "`([^`]+)`", render: Code, opening: "`", closing: "`" },
8
+ { style: "underline", regex: "__([^_]+)__", render: Underline, opening: "__", closing: "__" },
9
+ { style: "heading", regex: null, render: Heading, opening: "#", closing: null },
10
+ { style: "subHeading", regex: null, render: SubHeading, opening: "##", closing: null },
11
+ { style: "subSubHeading", regex: null, render: SubSubHeading, opening: "###", closing: null }
12
+ ];