@chaitrabhairappa/react-native-rich-text-editor 1.0.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/LICENSE +21 -0
- package/README.md +220 -0
- package/android/build.gradle +61 -0
- package/android/src/main/AndroidManifest.xml +3 -0
- package/android/src/main/java/com/richtext/editor/FloatingToolbar.kt +350 -0
- package/android/src/main/java/com/richtext/editor/RichTextEditorPackage.kt +16 -0
- package/android/src/main/java/com/richtext/editor/RichTextEditorView.kt +1292 -0
- package/android/src/main/java/com/richtext/editor/RichTextEditorViewManager.kt +236 -0
- package/ios/RichTextEditorView.swift +1574 -0
- package/ios/RichTextEditorViewManager.m +45 -0
- package/ios/RichTextEditorViewManager.swift +235 -0
- package/lib/commonjs/index.js +156 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/types.js +8 -0
- package/lib/commonjs/types.js.map +1 -0
- package/lib/module/index.js +143 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/src/index.d.ts +7 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +76 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/package.json +78 -0
- package/react-native-richtext-editor.podspec +21 -0
- package/src/index.tsx +199 -0
- package/src/types.ts +125 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
export interface StyleRange {
|
|
3
|
+
style: 'bold' | 'italic' | 'underline' | 'strikethrough' | 'link' | 'code' | 'highlight';
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
url?: string;
|
|
7
|
+
highlightColor?: string;
|
|
8
|
+
}
|
|
9
|
+
export type BlockType = 'paragraph' | 'bullet' | 'numbered' | 'heading' | 'quote' | 'checklist';
|
|
10
|
+
export type TextAlignment = 'left' | 'center' | 'right';
|
|
11
|
+
export type EditorVariant = 'outlined' | 'flat';
|
|
12
|
+
export interface Block {
|
|
13
|
+
type: BlockType;
|
|
14
|
+
text: string;
|
|
15
|
+
styles: StyleRange[];
|
|
16
|
+
alignment?: TextAlignment;
|
|
17
|
+
checked?: boolean;
|
|
18
|
+
indentLevel?: number;
|
|
19
|
+
}
|
|
20
|
+
export interface ContentChangeEvent {
|
|
21
|
+
nativeEvent: {
|
|
22
|
+
text: string;
|
|
23
|
+
blocks: Block[];
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export interface SelectionChangeEvent {
|
|
27
|
+
nativeEvent: {
|
|
28
|
+
start: number;
|
|
29
|
+
end: number;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
export type ToolbarOption = 'bold' | 'italic' | 'strikethrough' | 'underline' | 'code' | 'highlight' | 'heading' | 'bullet' | 'numbered' | 'quote' | 'checklist' | 'link' | 'undo' | 'redo' | 'clearFormatting' | 'indent' | 'outdent' | 'alignLeft' | 'alignCenter' | 'alignRight';
|
|
33
|
+
export declare const DEFAULT_TOOLBAR_OPTIONS: ToolbarOption[];
|
|
34
|
+
export interface RichTextEditorProps {
|
|
35
|
+
style?: StyleProp<ViewStyle>;
|
|
36
|
+
placeholder?: string;
|
|
37
|
+
initialContent?: Block[];
|
|
38
|
+
readOnly?: boolean;
|
|
39
|
+
maxHeight?: number;
|
|
40
|
+
showToolbar?: boolean;
|
|
41
|
+
toolbarOptions?: ToolbarOption[];
|
|
42
|
+
variant?: EditorVariant;
|
|
43
|
+
onContentChange?: (event: ContentChangeEvent) => void;
|
|
44
|
+
onSelectionChange?: (event: SelectionChangeEvent) => void;
|
|
45
|
+
onFocus?: () => void;
|
|
46
|
+
onBlur?: () => void;
|
|
47
|
+
}
|
|
48
|
+
export interface RichTextEditorRef {
|
|
49
|
+
setContent: (blocks: Block[]) => void;
|
|
50
|
+
getText: () => Promise<string>;
|
|
51
|
+
getBlocks: () => Promise<Block[]>;
|
|
52
|
+
clear: () => void;
|
|
53
|
+
focus: () => void;
|
|
54
|
+
blur: () => void;
|
|
55
|
+
toggleBold: () => void;
|
|
56
|
+
toggleItalic: () => void;
|
|
57
|
+
toggleUnderline: () => void;
|
|
58
|
+
toggleStrikethrough: () => void;
|
|
59
|
+
toggleCode: () => void;
|
|
60
|
+
toggleHighlight: (color?: string) => void;
|
|
61
|
+
setHeading: () => void;
|
|
62
|
+
setBulletList: () => void;
|
|
63
|
+
setNumberedList: () => void;
|
|
64
|
+
setQuote: () => void;
|
|
65
|
+
setChecklist: () => void;
|
|
66
|
+
setParagraph: () => void;
|
|
67
|
+
insertLink: (url: string, text: string) => void;
|
|
68
|
+
undo: () => void;
|
|
69
|
+
redo: () => void;
|
|
70
|
+
clearFormatting: () => void;
|
|
71
|
+
indent: () => void;
|
|
72
|
+
outdent: () => void;
|
|
73
|
+
setAlignment: (alignment: TextAlignment) => void;
|
|
74
|
+
toggleChecklistItem: () => void;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@chaitrabhairappa/react-native-rich-text-editor",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A powerful native rich text editor for React Native with support for text formatting, lists, and more",
|
|
5
|
+
"main": "lib/commonjs/index.js",
|
|
6
|
+
"module": "lib/module/index.js",
|
|
7
|
+
"types": "lib/typescript/src/index.d.ts",
|
|
8
|
+
"react-native": "src/index.tsx",
|
|
9
|
+
"source": "src/index.tsx",
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"lib",
|
|
13
|
+
"android",
|
|
14
|
+
"!android/build",
|
|
15
|
+
"ios",
|
|
16
|
+
"!ios/build",
|
|
17
|
+
"react-native-richtext-editor.podspec",
|
|
18
|
+
"!**/__tests__",
|
|
19
|
+
"!**/__fixtures__",
|
|
20
|
+
"!**/__mocks__",
|
|
21
|
+
"!lib/typescript/example"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"typescript": "tsc --noEmit",
|
|
25
|
+
"lint": "eslint \"**/*.{js,ts,tsx}\"",
|
|
26
|
+
"prepare": "bob build",
|
|
27
|
+
"clean": "del-cli lib"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"react-native",
|
|
31
|
+
"rich-text",
|
|
32
|
+
"editor",
|
|
33
|
+
"wysiwyg",
|
|
34
|
+
"text-formatting",
|
|
35
|
+
"ios",
|
|
36
|
+
"android"
|
|
37
|
+
],
|
|
38
|
+
"repository": {
|
|
39
|
+
"type": "git",
|
|
40
|
+
"url": "git+https://github.com/Chaitra9225/react-native-richtext-editor.git"
|
|
41
|
+
},
|
|
42
|
+
"author": "Chaitra Bhairappa <chaithrachinnu.cbg@gmail.com>",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/Chaitra9225/react-native-richtext-editor/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/Chaitra9225/react-native-richtext-editor#readme",
|
|
48
|
+
"publishConfig": {
|
|
49
|
+
"registry": "https://registry.npmjs.org/"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"@types/react": "^18.2.0",
|
|
53
|
+
"@types/react-native": "^0.72.0",
|
|
54
|
+
"del-cli": "^5.0.0",
|
|
55
|
+
"react": "18.2.0",
|
|
56
|
+
"react-native": "0.72.0",
|
|
57
|
+
"react-native-builder-bob": "^0.23.0",
|
|
58
|
+
"typescript": "^5.0.0"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"react": "*",
|
|
62
|
+
"react-native": "*"
|
|
63
|
+
},
|
|
64
|
+
"react-native-builder-bob": {
|
|
65
|
+
"source": "src",
|
|
66
|
+
"output": "lib",
|
|
67
|
+
"targets": [
|
|
68
|
+
"commonjs",
|
|
69
|
+
"module",
|
|
70
|
+
[
|
|
71
|
+
"typescript",
|
|
72
|
+
{
|
|
73
|
+
"project": "tsconfig.build.json"
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = "react-native-richtext-editor"
|
|
7
|
+
s.version = package["version"]
|
|
8
|
+
s.summary = package["description"]
|
|
9
|
+
s.homepage = package["homepage"]
|
|
10
|
+
s.license = package["license"]
|
|
11
|
+
s.authors = package["author"]
|
|
12
|
+
|
|
13
|
+
s.platforms = { :ios => "13.0" }
|
|
14
|
+
s.source = { :git => "https://github.com/yourusername/react-native-richtext-editor.git", :tag => "#{s.version}" }
|
|
15
|
+
|
|
16
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
17
|
+
|
|
18
|
+
s.dependency "React-Core"
|
|
19
|
+
|
|
20
|
+
s.swift_version = "5.0"
|
|
21
|
+
end
|
package/src/index.tsx
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import React, { forwardRef, useImperativeHandle, useRef, useCallback, useState } from 'react';
|
|
2
|
+
import { requireNativeComponent, UIManager, findNodeHandle, StyleSheet } from 'react-native';
|
|
3
|
+
import type {
|
|
4
|
+
Block,
|
|
5
|
+
TextAlignment,
|
|
6
|
+
ContentChangeEvent,
|
|
7
|
+
SelectionChangeEvent,
|
|
8
|
+
RichTextEditorProps,
|
|
9
|
+
RichTextEditorRef,
|
|
10
|
+
} from './types';
|
|
11
|
+
|
|
12
|
+
const COMPONENT_NAME = 'RichTextEditorView';
|
|
13
|
+
|
|
14
|
+
interface SizeChangeEvent {
|
|
15
|
+
nativeEvent: {
|
|
16
|
+
height: number;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface NativeComponentProps extends Omit<RichTextEditorProps, 'onFocus' | 'onBlur' | 'readOnly' | 'initialContent' | 'toolbarOptions'> {
|
|
21
|
+
editable?: boolean;
|
|
22
|
+
initialContent?: Block[];
|
|
23
|
+
toolbarOptions?: string[];
|
|
24
|
+
onEditorFocus?: () => void;
|
|
25
|
+
onEditorBlur?: () => void;
|
|
26
|
+
onSizeChange?: (event: SizeChangeEvent) => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const RichTextEditorViewNative = requireNativeComponent<NativeComponentProps>(COMPONENT_NAME);
|
|
30
|
+
|
|
31
|
+
const RichTextEditor = forwardRef<RichTextEditorRef, RichTextEditorProps>(
|
|
32
|
+
(props, ref) => {
|
|
33
|
+
const nativeRef = useRef(null);
|
|
34
|
+
const [height, setHeight] = useState<number>(44);
|
|
35
|
+
|
|
36
|
+
const handleSizeChange = useCallback((event: SizeChangeEvent) => {
|
|
37
|
+
const newHeight = event.nativeEvent?.height;
|
|
38
|
+
if (newHeight && newHeight > 0) {
|
|
39
|
+
setHeight(newHeight);
|
|
40
|
+
}
|
|
41
|
+
}, []);
|
|
42
|
+
|
|
43
|
+
const dispatchCommand = useCallback((command: string, args: unknown[] = []) => {
|
|
44
|
+
const handle = findNodeHandle(nativeRef.current);
|
|
45
|
+
if (handle) {
|
|
46
|
+
const commands = UIManager.getViewManagerConfig(COMPONENT_NAME)?.Commands;
|
|
47
|
+
const commandId = commands?.[command];
|
|
48
|
+
if (commandId !== undefined) {
|
|
49
|
+
UIManager.dispatchViewManagerCommand(handle, commandId, args);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}, []);
|
|
53
|
+
|
|
54
|
+
useImperativeHandle(ref, () => ({
|
|
55
|
+
setContent: (blocks: Block[]) => {
|
|
56
|
+
dispatchCommand('setContent', [blocks]);
|
|
57
|
+
},
|
|
58
|
+
getText: async (): Promise<string> => {
|
|
59
|
+
return new Promise((resolve) => {
|
|
60
|
+
resolve('');
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
getBlocks: async (): Promise<Block[]> => {
|
|
64
|
+
return new Promise((resolve) => {
|
|
65
|
+
resolve([]);
|
|
66
|
+
});
|
|
67
|
+
},
|
|
68
|
+
clear: () => {
|
|
69
|
+
dispatchCommand('clear');
|
|
70
|
+
},
|
|
71
|
+
focus: () => {
|
|
72
|
+
dispatchCommand('focus');
|
|
73
|
+
},
|
|
74
|
+
blur: () => {
|
|
75
|
+
dispatchCommand('blur');
|
|
76
|
+
},
|
|
77
|
+
toggleBold: () => {
|
|
78
|
+
dispatchCommand('toggleBold');
|
|
79
|
+
},
|
|
80
|
+
toggleItalic: () => {
|
|
81
|
+
dispatchCommand('toggleItalic');
|
|
82
|
+
},
|
|
83
|
+
toggleUnderline: () => {
|
|
84
|
+
dispatchCommand('toggleUnderline');
|
|
85
|
+
},
|
|
86
|
+
toggleStrikethrough: () => {
|
|
87
|
+
dispatchCommand('toggleStrikethrough');
|
|
88
|
+
},
|
|
89
|
+
toggleCode: () => {
|
|
90
|
+
dispatchCommand('toggleCode');
|
|
91
|
+
},
|
|
92
|
+
toggleHighlight: (color?: string) => {
|
|
93
|
+
dispatchCommand('toggleHighlight', color ? [color] : []);
|
|
94
|
+
},
|
|
95
|
+
setHeading: () => {
|
|
96
|
+
dispatchCommand('setHeading');
|
|
97
|
+
},
|
|
98
|
+
setBulletList: () => {
|
|
99
|
+
dispatchCommand('setBulletList');
|
|
100
|
+
},
|
|
101
|
+
setNumberedList: () => {
|
|
102
|
+
dispatchCommand('setNumberedList');
|
|
103
|
+
},
|
|
104
|
+
setQuote: () => {
|
|
105
|
+
dispatchCommand('setQuote');
|
|
106
|
+
},
|
|
107
|
+
setChecklist: () => {
|
|
108
|
+
dispatchCommand('setChecklist');
|
|
109
|
+
},
|
|
110
|
+
setParagraph: () => {
|
|
111
|
+
dispatchCommand('setParagraph');
|
|
112
|
+
},
|
|
113
|
+
insertLink: (url: string, text: string) => {
|
|
114
|
+
dispatchCommand('insertLink', [url, text]);
|
|
115
|
+
},
|
|
116
|
+
undo: () => {
|
|
117
|
+
dispatchCommand('undo');
|
|
118
|
+
},
|
|
119
|
+
redo: () => {
|
|
120
|
+
dispatchCommand('redo');
|
|
121
|
+
},
|
|
122
|
+
clearFormatting: () => {
|
|
123
|
+
dispatchCommand('clearFormatting');
|
|
124
|
+
},
|
|
125
|
+
indent: () => {
|
|
126
|
+
dispatchCommand('indent');
|
|
127
|
+
},
|
|
128
|
+
outdent: () => {
|
|
129
|
+
dispatchCommand('outdent');
|
|
130
|
+
},
|
|
131
|
+
setAlignment: (alignment: TextAlignment) => {
|
|
132
|
+
dispatchCommand('setAlignment', [alignment]);
|
|
133
|
+
},
|
|
134
|
+
toggleChecklistItem: () => {
|
|
135
|
+
dispatchCommand('toggleChecklistItem');
|
|
136
|
+
},
|
|
137
|
+
}));
|
|
138
|
+
|
|
139
|
+
const handleContentChange = useCallback(
|
|
140
|
+
(event: ContentChangeEvent) => {
|
|
141
|
+
props.onContentChange?.(event);
|
|
142
|
+
},
|
|
143
|
+
[props.onContentChange]
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const handleSelectionChange = useCallback(
|
|
147
|
+
(event: SelectionChangeEvent) => {
|
|
148
|
+
props.onSelectionChange?.(event);
|
|
149
|
+
},
|
|
150
|
+
[props.onSelectionChange]
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const handleFocus = useCallback(() => {
|
|
154
|
+
props.onFocus?.();
|
|
155
|
+
}, [props.onFocus]);
|
|
156
|
+
|
|
157
|
+
const handleBlur = useCallback(() => {
|
|
158
|
+
props.onBlur?.();
|
|
159
|
+
}, [props.onBlur]);
|
|
160
|
+
|
|
161
|
+
const combinedStyle = StyleSheet.flatten([props.style, { height }]);
|
|
162
|
+
|
|
163
|
+
return (
|
|
164
|
+
<RichTextEditorViewNative
|
|
165
|
+
ref={nativeRef}
|
|
166
|
+
style={combinedStyle}
|
|
167
|
+
placeholder={props.placeholder}
|
|
168
|
+
initialContent={props.initialContent}
|
|
169
|
+
editable={props.readOnly !== undefined ? !props.readOnly : true}
|
|
170
|
+
maxHeight={props.maxHeight}
|
|
171
|
+
showToolbar={props.readOnly ? false : (props.showToolbar ?? true)}
|
|
172
|
+
toolbarOptions={props.toolbarOptions}
|
|
173
|
+
variant={props.variant ?? 'outlined'}
|
|
174
|
+
onContentChange={handleContentChange}
|
|
175
|
+
onSelectionChange={handleSelectionChange}
|
|
176
|
+
onEditorFocus={handleFocus}
|
|
177
|
+
onEditorBlur={handleBlur}
|
|
178
|
+
onSizeChange={handleSizeChange}
|
|
179
|
+
/>
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
RichTextEditor.displayName = 'RichTextEditor';
|
|
185
|
+
|
|
186
|
+
export default RichTextEditor;
|
|
187
|
+
export { DEFAULT_TOOLBAR_OPTIONS } from './types';
|
|
188
|
+
export type {
|
|
189
|
+
Block,
|
|
190
|
+
BlockType,
|
|
191
|
+
StyleRange,
|
|
192
|
+
TextAlignment,
|
|
193
|
+
EditorVariant,
|
|
194
|
+
ContentChangeEvent,
|
|
195
|
+
SelectionChangeEvent,
|
|
196
|
+
RichTextEditorRef,
|
|
197
|
+
RichTextEditorProps,
|
|
198
|
+
ToolbarOption,
|
|
199
|
+
} from './types';
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import type { StyleProp, ViewStyle } from 'react-native';
|
|
2
|
+
|
|
3
|
+
export interface StyleRange {
|
|
4
|
+
style: 'bold' | 'italic' | 'underline' | 'strikethrough' | 'link' | 'code' | 'highlight';
|
|
5
|
+
start: number;
|
|
6
|
+
end: number;
|
|
7
|
+
url?: string;
|
|
8
|
+
highlightColor?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export type BlockType = 'paragraph' | 'bullet' | 'numbered' | 'heading' | 'quote' | 'checklist';
|
|
12
|
+
export type TextAlignment = 'left' | 'center' | 'right';
|
|
13
|
+
export type EditorVariant = 'outlined' | 'flat';
|
|
14
|
+
|
|
15
|
+
export interface Block {
|
|
16
|
+
type: BlockType;
|
|
17
|
+
text: string;
|
|
18
|
+
styles: StyleRange[];
|
|
19
|
+
alignment?: TextAlignment;
|
|
20
|
+
checked?: boolean;
|
|
21
|
+
indentLevel?: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ContentChangeEvent {
|
|
25
|
+
nativeEvent: {
|
|
26
|
+
text: string;
|
|
27
|
+
blocks: Block[];
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface SelectionChangeEvent {
|
|
32
|
+
nativeEvent: {
|
|
33
|
+
start: number;
|
|
34
|
+
end: number;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type ToolbarOption =
|
|
39
|
+
| 'bold'
|
|
40
|
+
| 'italic'
|
|
41
|
+
| 'strikethrough'
|
|
42
|
+
| 'underline'
|
|
43
|
+
| 'code'
|
|
44
|
+
| 'highlight'
|
|
45
|
+
| 'heading'
|
|
46
|
+
| 'bullet'
|
|
47
|
+
| 'numbered'
|
|
48
|
+
| 'quote'
|
|
49
|
+
| 'checklist'
|
|
50
|
+
| 'link'
|
|
51
|
+
| 'undo'
|
|
52
|
+
| 'redo'
|
|
53
|
+
| 'clearFormatting'
|
|
54
|
+
| 'indent'
|
|
55
|
+
| 'outdent'
|
|
56
|
+
| 'alignLeft'
|
|
57
|
+
| 'alignCenter'
|
|
58
|
+
| 'alignRight';
|
|
59
|
+
|
|
60
|
+
export const DEFAULT_TOOLBAR_OPTIONS: ToolbarOption[] = [
|
|
61
|
+
'bold',
|
|
62
|
+
'italic',
|
|
63
|
+
'underline',
|
|
64
|
+
'strikethrough',
|
|
65
|
+
'code',
|
|
66
|
+
'highlight',
|
|
67
|
+
'heading',
|
|
68
|
+
'bullet',
|
|
69
|
+
'numbered',
|
|
70
|
+
'quote',
|
|
71
|
+
'checklist',
|
|
72
|
+
'link',
|
|
73
|
+
'undo',
|
|
74
|
+
'redo',
|
|
75
|
+
'clearFormatting',
|
|
76
|
+
'indent',
|
|
77
|
+
'outdent',
|
|
78
|
+
'alignLeft',
|
|
79
|
+
'alignCenter',
|
|
80
|
+
'alignRight',
|
|
81
|
+
];
|
|
82
|
+
|
|
83
|
+
export interface RichTextEditorProps {
|
|
84
|
+
style?: StyleProp<ViewStyle>;
|
|
85
|
+
placeholder?: string;
|
|
86
|
+
initialContent?: Block[];
|
|
87
|
+
readOnly?: boolean;
|
|
88
|
+
maxHeight?: number;
|
|
89
|
+
showToolbar?: boolean;
|
|
90
|
+
toolbarOptions?: ToolbarOption[];
|
|
91
|
+
variant?: EditorVariant;
|
|
92
|
+
onContentChange?: (event: ContentChangeEvent) => void;
|
|
93
|
+
onSelectionChange?: (event: SelectionChangeEvent) => void;
|
|
94
|
+
onFocus?: () => void;
|
|
95
|
+
onBlur?: () => void;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export interface RichTextEditorRef {
|
|
99
|
+
setContent: (blocks: Block[]) => void;
|
|
100
|
+
getText: () => Promise<string>;
|
|
101
|
+
getBlocks: () => Promise<Block[]>;
|
|
102
|
+
clear: () => void;
|
|
103
|
+
focus: () => void;
|
|
104
|
+
blur: () => void;
|
|
105
|
+
toggleBold: () => void;
|
|
106
|
+
toggleItalic: () => void;
|
|
107
|
+
toggleUnderline: () => void;
|
|
108
|
+
toggleStrikethrough: () => void;
|
|
109
|
+
toggleCode: () => void;
|
|
110
|
+
toggleHighlight: (color?: string) => void;
|
|
111
|
+
setHeading: () => void;
|
|
112
|
+
setBulletList: () => void;
|
|
113
|
+
setNumberedList: () => void;
|
|
114
|
+
setQuote: () => void;
|
|
115
|
+
setChecklist: () => void;
|
|
116
|
+
setParagraph: () => void;
|
|
117
|
+
insertLink: (url: string, text: string) => void;
|
|
118
|
+
undo: () => void;
|
|
119
|
+
redo: () => void;
|
|
120
|
+
clearFormatting: () => void;
|
|
121
|
+
indent: () => void;
|
|
122
|
+
outdent: () => void;
|
|
123
|
+
setAlignment: (alignment: TextAlignment) => void;
|
|
124
|
+
toggleChecklistItem: () => void;
|
|
125
|
+
}
|