react-native-simple-rich-text-editor 0.2.0-beta.0 → 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 +1 -1
- package/lib/commonjs/index.js +45 -12
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/index.js +47 -13
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/commonjs/src/index.d.ts +2 -1
- package/lib/typescript/commonjs/src/index.d.ts.map +1 -1
- package/lib/typescript/module/src/index.d.ts +2 -1
- package/lib/typescript/module/src/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/index.tsx +73 -12
package/README.md
CHANGED
package/lib/commonjs/index.js
CHANGED
@@ -41,10 +41,27 @@ var _jsxRuntime = require("react/jsx-runtime");
|
|
41
41
|
* ```
|
42
42
|
*/
|
43
43
|
|
44
|
+
// Debounce utility function
|
45
|
+
const debounce = (func, delay) => {
|
46
|
+
let timeoutId = null;
|
47
|
+
return (...args) => {
|
48
|
+
if (timeoutId) {
|
49
|
+
clearTimeout(timeoutId);
|
50
|
+
}
|
51
|
+
timeoutId = setTimeout(() => {
|
52
|
+
func(...args);
|
53
|
+
timeoutId = null;
|
54
|
+
}, delay);
|
55
|
+
};
|
56
|
+
};
|
44
57
|
const RichTextEditor = ({
|
45
|
-
text,
|
46
|
-
onEmitText
|
58
|
+
text: externalText,
|
59
|
+
onEmitText,
|
60
|
+
emitTextAfterMillisecondsOfInactivity = 500
|
47
61
|
}) => {
|
62
|
+
// Internal state to track the current text value
|
63
|
+
const [internalText, setInternalText] = (0, _react.useState)(externalText);
|
64
|
+
|
48
65
|
// Track the current selection (cursor position) in the TextInput.
|
49
66
|
const [selection, setSelection] = (0, _react.useState)({
|
50
67
|
start: 0,
|
@@ -52,21 +69,37 @@ const RichTextEditor = ({
|
|
52
69
|
});
|
53
70
|
const [needsBoldEndMarker, setNeedsBoldEndMarker] = (0, _react.useState)(false); // we must count the BOLD_START and BOLD_END markers in text prop => inital state
|
54
71
|
const [needsItalicEndMarker, setNeedsItalicEndMarker] = (0, _react.useState)(false);
|
72
|
+
|
73
|
+
// Create a debounced version of onEmitText
|
74
|
+
const debouncedEmitText = (0, _react.useRef)(debounce(newText => {
|
75
|
+
onEmitText(newText);
|
76
|
+
}, emitTextAfterMillisecondsOfInactivity)).current;
|
77
|
+
|
78
|
+
// Update internal text when external text changes
|
79
|
+
(0, _react.useEffect)(() => {
|
80
|
+
setInternalText(externalText);
|
81
|
+
}, [externalText]);
|
82
|
+
|
83
|
+
// Handle text changes
|
84
|
+
const handleTextChange = (0, _react.useCallback)(newText => {
|
85
|
+
setInternalText(newText);
|
86
|
+
debouncedEmitText(newText);
|
87
|
+
}, [debouncedEmitText]);
|
55
88
|
const handleFontStyleMarkerInsertion = style => () => {
|
56
89
|
const isBold = style === 'bold';
|
57
90
|
if (isBold ? needsBoldEndMarker : needsItalicEndMarker) {
|
58
91
|
const {
|
59
92
|
text: newText
|
60
|
-
} = (0, _textManipulation.addFontStyleEndMarker)(
|
93
|
+
} = (0, _textManipulation.addFontStyleEndMarker)(internalText, selection, style);
|
61
94
|
isBold ? setNeedsBoldEndMarker(false) : setNeedsItalicEndMarker(false);
|
62
|
-
|
95
|
+
handleTextChange(newText);
|
63
96
|
} else {
|
64
97
|
const {
|
65
98
|
text: newText
|
66
|
-
} = (0, _textManipulation.addFontStyleMarkers)(
|
99
|
+
} = (0, _textManipulation.addFontStyleMarkers)(internalText, selection, style);
|
67
100
|
// this is a reaction to an implementation detail of addBoldMarkers - TODO: solve it cleanly
|
68
101
|
if (selection.start - selection.end === 0) isBold ? setNeedsBoldEndMarker(true) : setNeedsItalicEndMarker(true);
|
69
|
-
|
102
|
+
handleTextChange(newText);
|
70
103
|
}
|
71
104
|
};
|
72
105
|
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_reactNative.View, {
|
@@ -75,28 +108,28 @@ const RichTextEditor = ({
|
|
75
108
|
style: styles.row,
|
76
109
|
children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
77
110
|
style: styles.button,
|
78
|
-
onPress: () =>
|
111
|
+
onPress: () => handleTextChange((0, _textManipulation.addHeadlineMarker)(_markers.Markers.H1, internalText, selection)),
|
79
112
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
80
113
|
style: styles.buttonText,
|
81
114
|
children: "H1"
|
82
115
|
})
|
83
116
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
84
117
|
style: styles.button,
|
85
|
-
onPress: () =>
|
118
|
+
onPress: () => handleTextChange((0, _textManipulation.addHeadlineMarker)(_markers.Markers.H2, internalText, selection)),
|
86
119
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
87
120
|
style: styles.buttonText,
|
88
121
|
children: "H2"
|
89
122
|
})
|
90
123
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
91
124
|
style: styles.button,
|
92
|
-
onPress: () =>
|
125
|
+
onPress: () => handleTextChange((0, _textManipulation.addHeadlineMarker)(_markers.Markers.H3, internalText, selection)),
|
93
126
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
94
127
|
style: styles.buttonText,
|
95
128
|
children: "H3"
|
96
129
|
})
|
97
130
|
}), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.TouchableOpacity, {
|
98
131
|
style: styles.button,
|
99
|
-
onPress: () =>
|
132
|
+
onPress: () => handleTextChange((0, _textManipulation.addBulletPointMarker)(internalText, selection)),
|
100
133
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactNative.Text, {
|
101
134
|
style: styles.buttonText,
|
102
135
|
children: "List"
|
@@ -122,9 +155,9 @@ const RichTextEditor = ({
|
|
122
155
|
autoCorrect: false,
|
123
156
|
style: styles.input,
|
124
157
|
onSelectionChange: e => setSelection(e.nativeEvent.selection),
|
125
|
-
onChangeText:
|
158
|
+
onChangeText: handleTextChange,
|
126
159
|
children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_Render.Render, {
|
127
|
-
encodedText:
|
160
|
+
encodedText: internalText
|
128
161
|
})
|
129
162
|
})]
|
130
163
|
});
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"names":["_react","require","_reactNative","_Render","_textManipulation","_markers","_jsxRuntime","RichTextEditor","text","onEmitText","
|
1
|
+
{"version":3,"names":["_react","require","_reactNative","_Render","_textManipulation","_markers","_jsxRuntime","debounce","func","delay","timeoutId","args","clearTimeout","setTimeout","RichTextEditor","text","externalText","onEmitText","emitTextAfterMillisecondsOfInactivity","internalText","setInternalText","useState","selection","setSelection","start","end","needsBoldEndMarker","setNeedsBoldEndMarker","needsItalicEndMarker","setNeedsItalicEndMarker","debouncedEmitText","useRef","newText","current","useEffect","handleTextChange","useCallback","handleFontStyleMarkerInsertion","style","isBold","addFontStyleEndMarker","addFontStyleMarkers","jsxs","View","styles","container","children","row","jsx","TouchableOpacity","button","onPress","addHeadlineMarker","Markers","H1","Text","buttonText","H2","H3","addBulletPointMarker","fixedWidth","TextInput","multiline","autoFocus","autoCorrect","input","onSelectionChange","e","nativeEvent","onChangeText","Render","encodedText","exports","electricBlueHex","StyleSheet","create","flex","margin","flexDirection","justifyContent","flexWrap","textAlignVertical","paddingVertical","paddingHorizontal","borderRadius","borderWidth","borderColor","backgroundColor","flexShrink","textAlign","color","width","_default","default"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
package/lib/module/index.js
CHANGED
@@ -31,16 +31,34 @@
|
|
31
31
|
* ```
|
32
32
|
*/
|
33
33
|
|
34
|
-
import { useState } from 'react';
|
34
|
+
import { useState, useEffect, useRef, useCallback } from 'react';
|
35
35
|
import { StyleSheet, TextInput, TouchableOpacity, View, Text } from 'react-native';
|
36
36
|
import { Render } from "./internal/rendering/components/Render.js";
|
37
37
|
import { addFontStyleMarkers, addFontStyleEndMarker, addHeadlineMarker, addBulletPointMarker } from "./internal/text-formats/unicode-markers-format/text-manipulation.js";
|
38
38
|
import { Markers } from "./internal/text-formats/unicode-markers-format/markers.js";
|
39
|
+
|
40
|
+
// Debounce utility function
|
39
41
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
42
|
+
const debounce = (func, delay) => {
|
43
|
+
let timeoutId = null;
|
44
|
+
return (...args) => {
|
45
|
+
if (timeoutId) {
|
46
|
+
clearTimeout(timeoutId);
|
47
|
+
}
|
48
|
+
timeoutId = setTimeout(() => {
|
49
|
+
func(...args);
|
50
|
+
timeoutId = null;
|
51
|
+
}, delay);
|
52
|
+
};
|
53
|
+
};
|
40
54
|
const RichTextEditor = ({
|
41
|
-
text,
|
42
|
-
onEmitText
|
55
|
+
text: externalText,
|
56
|
+
onEmitText,
|
57
|
+
emitTextAfterMillisecondsOfInactivity = 500
|
43
58
|
}) => {
|
59
|
+
// Internal state to track the current text value
|
60
|
+
const [internalText, setInternalText] = useState(externalText);
|
61
|
+
|
44
62
|
// Track the current selection (cursor position) in the TextInput.
|
45
63
|
const [selection, setSelection] = useState({
|
46
64
|
start: 0,
|
@@ -48,21 +66,37 @@ const RichTextEditor = ({
|
|
48
66
|
});
|
49
67
|
const [needsBoldEndMarker, setNeedsBoldEndMarker] = useState(false); // we must count the BOLD_START and BOLD_END markers in text prop => inital state
|
50
68
|
const [needsItalicEndMarker, setNeedsItalicEndMarker] = useState(false);
|
69
|
+
|
70
|
+
// Create a debounced version of onEmitText
|
71
|
+
const debouncedEmitText = useRef(debounce(newText => {
|
72
|
+
onEmitText(newText);
|
73
|
+
}, emitTextAfterMillisecondsOfInactivity)).current;
|
74
|
+
|
75
|
+
// Update internal text when external text changes
|
76
|
+
useEffect(() => {
|
77
|
+
setInternalText(externalText);
|
78
|
+
}, [externalText]);
|
79
|
+
|
80
|
+
// Handle text changes
|
81
|
+
const handleTextChange = useCallback(newText => {
|
82
|
+
setInternalText(newText);
|
83
|
+
debouncedEmitText(newText);
|
84
|
+
}, [debouncedEmitText]);
|
51
85
|
const handleFontStyleMarkerInsertion = style => () => {
|
52
86
|
const isBold = style === 'bold';
|
53
87
|
if (isBold ? needsBoldEndMarker : needsItalicEndMarker) {
|
54
88
|
const {
|
55
89
|
text: newText
|
56
|
-
} = addFontStyleEndMarker(
|
90
|
+
} = addFontStyleEndMarker(internalText, selection, style);
|
57
91
|
isBold ? setNeedsBoldEndMarker(false) : setNeedsItalicEndMarker(false);
|
58
|
-
|
92
|
+
handleTextChange(newText);
|
59
93
|
} else {
|
60
94
|
const {
|
61
95
|
text: newText
|
62
|
-
} = addFontStyleMarkers(
|
96
|
+
} = addFontStyleMarkers(internalText, selection, style);
|
63
97
|
// this is a reaction to an implementation detail of addBoldMarkers - TODO: solve it cleanly
|
64
98
|
if (selection.start - selection.end === 0) isBold ? setNeedsBoldEndMarker(true) : setNeedsItalicEndMarker(true);
|
65
|
-
|
99
|
+
handleTextChange(newText);
|
66
100
|
}
|
67
101
|
};
|
68
102
|
return /*#__PURE__*/_jsxs(View, {
|
@@ -71,28 +105,28 @@ const RichTextEditor = ({
|
|
71
105
|
style: styles.row,
|
72
106
|
children: [/*#__PURE__*/_jsx(TouchableOpacity, {
|
73
107
|
style: styles.button,
|
74
|
-
onPress: () =>
|
108
|
+
onPress: () => handleTextChange(addHeadlineMarker(Markers.H1, internalText, selection)),
|
75
109
|
children: /*#__PURE__*/_jsx(Text, {
|
76
110
|
style: styles.buttonText,
|
77
111
|
children: "H1"
|
78
112
|
})
|
79
113
|
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
80
114
|
style: styles.button,
|
81
|
-
onPress: () =>
|
115
|
+
onPress: () => handleTextChange(addHeadlineMarker(Markers.H2, internalText, selection)),
|
82
116
|
children: /*#__PURE__*/_jsx(Text, {
|
83
117
|
style: styles.buttonText,
|
84
118
|
children: "H2"
|
85
119
|
})
|
86
120
|
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
87
121
|
style: styles.button,
|
88
|
-
onPress: () =>
|
122
|
+
onPress: () => handleTextChange(addHeadlineMarker(Markers.H3, internalText, selection)),
|
89
123
|
children: /*#__PURE__*/_jsx(Text, {
|
90
124
|
style: styles.buttonText,
|
91
125
|
children: "H3"
|
92
126
|
})
|
93
127
|
}), /*#__PURE__*/_jsx(TouchableOpacity, {
|
94
128
|
style: styles.button,
|
95
|
-
onPress: () =>
|
129
|
+
onPress: () => handleTextChange(addBulletPointMarker(internalText, selection)),
|
96
130
|
children: /*#__PURE__*/_jsx(Text, {
|
97
131
|
style: styles.buttonText,
|
98
132
|
children: "List"
|
@@ -118,9 +152,9 @@ const RichTextEditor = ({
|
|
118
152
|
autoCorrect: false,
|
119
153
|
style: styles.input,
|
120
154
|
onSelectionChange: e => setSelection(e.nativeEvent.selection),
|
121
|
-
onChangeText:
|
155
|
+
onChangeText: handleTextChange,
|
122
156
|
children: /*#__PURE__*/_jsx(Render, {
|
123
|
-
encodedText:
|
157
|
+
encodedText: internalText
|
124
158
|
})
|
125
159
|
})]
|
126
160
|
});
|
package/lib/module/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"names":["useState","StyleSheet","TextInput","TouchableOpacity","View","Text","Render","addFontStyleMarkers","addFontStyleEndMarker","addHeadlineMarker","addBulletPointMarker","Markers","jsx","_jsx","jsxs","_jsxs","RichTextEditor","text","onEmitText","selection","setSelection","start","end","needsBoldEndMarker","setNeedsBoldEndMarker","needsItalicEndMarker","setNeedsItalicEndMarker","handleFontStyleMarkerInsertion","style","isBold","
|
1
|
+
{"version":3,"names":["useState","useEffect","useRef","useCallback","StyleSheet","TextInput","TouchableOpacity","View","Text","Render","addFontStyleMarkers","addFontStyleEndMarker","addHeadlineMarker","addBulletPointMarker","Markers","jsx","_jsx","jsxs","_jsxs","debounce","func","delay","timeoutId","args","clearTimeout","setTimeout","RichTextEditor","text","externalText","onEmitText","emitTextAfterMillisecondsOfInactivity","internalText","setInternalText","selection","setSelection","start","end","needsBoldEndMarker","setNeedsBoldEndMarker","needsItalicEndMarker","setNeedsItalicEndMarker","debouncedEmitText","newText","current","handleTextChange","handleFontStyleMarkerInsertion","style","isBold","styles","container","children","row","button","onPress","H1","buttonText","H2","H3","fixedWidth","multiline","autoFocus","autoCorrect","input","onSelectionChange","e","nativeEvent","onChangeText","encodedText","electricBlueHex","create","flex","margin","flexDirection","justifyContent","flexWrap","textAlignVertical","paddingVertical","paddingHorizontal","borderRadius","borderWidth","borderColor","backgroundColor","flexShrink","textAlign","color","width"],"sourceRoot":"../../src","sources":["index.tsx"],"mappings":"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","ignoreList":[]}
|
@@ -29,9 +29,10 @@
|
|
29
29
|
* ```
|
30
30
|
*/
|
31
31
|
import type { Dispatch, SetStateAction } from 'react';
|
32
|
-
declare const RichTextEditor: ({ text, onEmitText, }: {
|
32
|
+
declare const RichTextEditor: ({ text: externalText, onEmitText, emitTextAfterMillisecondsOfInactivity, }: {
|
33
33
|
text: string;
|
34
34
|
onEmitText: Dispatch<SetStateAction<string>>;
|
35
|
+
emitTextAfterMillisecondsOfInactivity?: number;
|
35
36
|
}) => import("react/jsx-runtime").JSX.Element;
|
36
37
|
export default RichTextEditor;
|
37
38
|
export { RichTextEditor };
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAuCtD,QAAA,MAAM,cAAc,GAAI,4EAIrB;IACD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7C,qCAAqC,CAAC,EAAE,MAAM,CAAC;CAChD,4CAgIA,CAAC;AAgCF,eAAe,cAAc,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
@@ -29,9 +29,10 @@
|
|
29
29
|
* ```
|
30
30
|
*/
|
31
31
|
import type { Dispatch, SetStateAction } from 'react';
|
32
|
-
declare const RichTextEditor: ({ text, onEmitText, }: {
|
32
|
+
declare const RichTextEditor: ({ text: externalText, onEmitText, emitTextAfterMillisecondsOfInactivity, }: {
|
33
33
|
text: string;
|
34
34
|
onEmitText: Dispatch<SetStateAction<string>>;
|
35
|
+
emitTextAfterMillisecondsOfInactivity?: number;
|
35
36
|
}) => import("react/jsx-runtime").JSX.Element;
|
36
37
|
export default RichTextEditor;
|
37
38
|
export { RichTextEditor };
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/index.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAuCtD,QAAA,MAAM,cAAc,GAAI,4EAIrB;IACD,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAC7C,qCAAqC,CAAC,EAAE,MAAM,CAAC;CAChD,4CAgIA,CAAC;AAgCF,eAAe,cAAc,CAAC;AAC9B,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "react-native-simple-rich-text-editor",
|
3
|
-
"version": "0.2.0
|
3
|
+
"version": "0.2.0",
|
4
4
|
"description": "A lean Rich Text Editor for React Native built upon TextInput outputting Markdown",
|
5
5
|
"source": "./src/index.tsx",
|
6
6
|
"main": "./lib/commonjs/index.js",
|
@@ -38,7 +38,7 @@
|
|
38
38
|
"!**/.*"
|
39
39
|
],
|
40
40
|
"scripts": {
|
41
|
-
"example": "yarn workspace react-native-simple-rich-text-editor-example",
|
41
|
+
"example": "yarn workspace react-native-simple-rich-text-editor-example start",
|
42
42
|
"test": "jest",
|
43
43
|
"typecheck": "tsc",
|
44
44
|
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
@@ -84,7 +84,7 @@
|
|
84
84
|
"react": "18.3.1",
|
85
85
|
"react-native": "0.76.7",
|
86
86
|
"react-native-builder-bob": "^0.37.0",
|
87
|
-
"release-it": "^
|
87
|
+
"release-it": "^18",
|
88
88
|
"typescript": "^5.2.2"
|
89
89
|
},
|
90
90
|
"resolutions": {
|
package/src/index.tsx
CHANGED
@@ -29,7 +29,7 @@
|
|
29
29
|
* ```
|
30
30
|
*/
|
31
31
|
import type { Dispatch, SetStateAction } from 'react';
|
32
|
-
import { useState } from 'react';
|
32
|
+
import { useState, useEffect, useRef, useCallback } from 'react';
|
33
33
|
import {
|
34
34
|
StyleSheet,
|
35
35
|
TextInput,
|
@@ -48,13 +48,37 @@ import {
|
|
48
48
|
} from './internal/text-formats/unicode-markers-format/text-manipulation.ts';
|
49
49
|
import { Markers } from './internal/text-formats/unicode-markers-format/markers.ts';
|
50
50
|
|
51
|
+
// Debounce utility function
|
52
|
+
const debounce = <T extends (...args: any[]) => any>(
|
53
|
+
func: T,
|
54
|
+
delay: number
|
55
|
+
): ((...args: Parameters<T>) => void) => {
|
56
|
+
let timeoutId: NodeJS.Timeout | null = null;
|
57
|
+
|
58
|
+
return (...args: Parameters<T>) => {
|
59
|
+
if (timeoutId) {
|
60
|
+
clearTimeout(timeoutId);
|
61
|
+
}
|
62
|
+
|
63
|
+
timeoutId = setTimeout(() => {
|
64
|
+
func(...args);
|
65
|
+
timeoutId = null;
|
66
|
+
}, delay);
|
67
|
+
};
|
68
|
+
};
|
69
|
+
|
51
70
|
const RichTextEditor = ({
|
52
|
-
text,
|
71
|
+
text: externalText,
|
53
72
|
onEmitText,
|
73
|
+
emitTextAfterMillisecondsOfInactivity = 500,
|
54
74
|
}: {
|
55
75
|
text: string;
|
56
76
|
onEmitText: Dispatch<SetStateAction<string>>;
|
77
|
+
emitTextAfterMillisecondsOfInactivity?: number;
|
57
78
|
}) => {
|
79
|
+
// Internal state to track the current text value
|
80
|
+
const [internalText, setInternalText] = useState(externalText);
|
81
|
+
|
58
82
|
// Track the current selection (cursor position) in the TextInput.
|
59
83
|
const [selection, setSelection] = useState<{ start: number; end: number }>({
|
60
84
|
start: 0,
|
@@ -63,18 +87,47 @@ const RichTextEditor = ({
|
|
63
87
|
const [needsBoldEndMarker, setNeedsBoldEndMarker] = useState(false); // we must count the BOLD_START and BOLD_END markers in text prop => inital state
|
64
88
|
const [needsItalicEndMarker, setNeedsItalicEndMarker] = useState(false);
|
65
89
|
|
90
|
+
// Create a debounced version of onEmitText
|
91
|
+
const debouncedEmitText = useRef(
|
92
|
+
debounce((newText: string) => {
|
93
|
+
onEmitText(newText);
|
94
|
+
}, emitTextAfterMillisecondsOfInactivity)
|
95
|
+
).current;
|
96
|
+
|
97
|
+
// Update internal text when external text changes
|
98
|
+
useEffect(() => {
|
99
|
+
setInternalText(externalText);
|
100
|
+
}, [externalText]);
|
101
|
+
|
102
|
+
// Handle text changes
|
103
|
+
const handleTextChange = useCallback(
|
104
|
+
(newText: string) => {
|
105
|
+
setInternalText(newText);
|
106
|
+
debouncedEmitText(newText);
|
107
|
+
},
|
108
|
+
[debouncedEmitText]
|
109
|
+
);
|
110
|
+
|
66
111
|
const handleFontStyleMarkerInsertion = (style: 'bold' | 'italic') => () => {
|
67
112
|
const isBold = style === 'bold';
|
68
113
|
if (isBold ? needsBoldEndMarker : needsItalicEndMarker) {
|
69
|
-
const { text: newText } = addFontStyleEndMarker(
|
114
|
+
const { text: newText } = addFontStyleEndMarker(
|
115
|
+
internalText,
|
116
|
+
selection,
|
117
|
+
style
|
118
|
+
);
|
70
119
|
isBold ? setNeedsBoldEndMarker(false) : setNeedsItalicEndMarker(false);
|
71
|
-
|
120
|
+
handleTextChange(newText);
|
72
121
|
} else {
|
73
|
-
const { text: newText } = addFontStyleMarkers(
|
122
|
+
const { text: newText } = addFontStyleMarkers(
|
123
|
+
internalText,
|
124
|
+
selection,
|
125
|
+
style
|
126
|
+
);
|
74
127
|
// this is a reaction to an implementation detail of addBoldMarkers - TODO: solve it cleanly
|
75
128
|
if (selection.start - selection.end === 0)
|
76
129
|
isBold ? setNeedsBoldEndMarker(true) : setNeedsItalicEndMarker(true);
|
77
|
-
|
130
|
+
handleTextChange(newText);
|
78
131
|
}
|
79
132
|
};
|
80
133
|
|
@@ -84,7 +137,9 @@ const RichTextEditor = ({
|
|
84
137
|
<TouchableOpacity
|
85
138
|
style={styles.button}
|
86
139
|
onPress={() =>
|
87
|
-
|
140
|
+
handleTextChange(
|
141
|
+
addHeadlineMarker(Markers.H1, internalText, selection)
|
142
|
+
)
|
88
143
|
}
|
89
144
|
>
|
90
145
|
<Text style={styles.buttonText}>H1</Text>
|
@@ -92,7 +147,9 @@ const RichTextEditor = ({
|
|
92
147
|
<TouchableOpacity
|
93
148
|
style={styles.button}
|
94
149
|
onPress={() =>
|
95
|
-
|
150
|
+
handleTextChange(
|
151
|
+
addHeadlineMarker(Markers.H2, internalText, selection)
|
152
|
+
)
|
96
153
|
}
|
97
154
|
>
|
98
155
|
<Text style={styles.buttonText}>H2</Text>
|
@@ -100,14 +157,18 @@ const RichTextEditor = ({
|
|
100
157
|
<TouchableOpacity
|
101
158
|
style={styles.button}
|
102
159
|
onPress={() =>
|
103
|
-
|
160
|
+
handleTextChange(
|
161
|
+
addHeadlineMarker(Markers.H3, internalText, selection)
|
162
|
+
)
|
104
163
|
}
|
105
164
|
>
|
106
165
|
<Text style={styles.buttonText}>H3</Text>
|
107
166
|
</TouchableOpacity>
|
108
167
|
<TouchableOpacity
|
109
168
|
style={styles.button}
|
110
|
-
onPress={() =>
|
169
|
+
onPress={() =>
|
170
|
+
handleTextChange(addBulletPointMarker(internalText, selection))
|
171
|
+
}
|
111
172
|
>
|
112
173
|
<Text style={styles.buttonText}>List</Text>
|
113
174
|
</TouchableOpacity>
|
@@ -136,9 +197,9 @@ const RichTextEditor = ({
|
|
136
197
|
onSelectionChange={(
|
137
198
|
e: NativeSyntheticEvent<TextInputSelectionChangeEventData>
|
138
199
|
) => setSelection(e.nativeEvent.selection)}
|
139
|
-
onChangeText={
|
200
|
+
onChangeText={handleTextChange}
|
140
201
|
>
|
141
|
-
<Render encodedText={
|
202
|
+
<Render encodedText={internalText} />
|
142
203
|
</TextInput>
|
143
204
|
</View>
|
144
205
|
);
|