@hero-design/rn 8.108.2 → 8.109.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/.turbo/turbo-build.log +3 -9
- package/CHANGELOG.md +6 -0
- package/es/index.js +182 -95
- package/lib/index.js +182 -95
- package/package.json +1 -1
- package/src/components/RichTextEditor/BaseRichTextEditor.tsx +301 -0
- package/src/components/RichTextEditor/EditorToolbar.tsx +11 -20
- package/src/components/RichTextEditor/MentionList.tsx +2 -2
- package/src/components/RichTextEditor/RichTextEditor.tsx +42 -261
- package/src/components/RichTextEditor/StyledRichTextEditor.ts +0 -1
- package/src/components/RichTextEditor/__tests__/RichTextEditor.spec.tsx +8 -8
- package/src/components/RichTextEditor/__tests__/__snapshots__/RichTextEditor.spec.tsx.snap +7 -9
- package/src/components/RichTextEditor/constants.ts +5 -0
- package/src/components/RichTextEditor/hooks/useRichTextEditorEvents.ts +60 -0
- package/src/components/RichTextEditor/index.tsx +10 -2
- package/stats/8.108.2/rn-stats.html +3 -1
- package/stats/8.109.0/rn-stats.html +4844 -0
- package/types/components/RichTextEditor/BaseRichTextEditor.d.ts +69 -0
- package/types/components/RichTextEditor/RichTextEditor.d.ts +2 -45
- package/types/components/RichTextEditor/constants.d.ts +4 -0
- package/types/components/RichTextEditor/hooks/useRichTextEditorEvents.d.ts +21 -0
- package/types/components/RichTextEditor/index.d.ts +14 -2
|
@@ -1,21 +1,17 @@
|
|
|
1
|
-
import { useTheme } from '@emotion/react';
|
|
2
1
|
import { isEmptyContent } from 'hero-editor';
|
|
3
2
|
import React, {
|
|
4
3
|
forwardRef,
|
|
5
4
|
useCallback,
|
|
6
5
|
useEffect,
|
|
7
|
-
useImperativeHandle,
|
|
8
6
|
useMemo,
|
|
9
7
|
useRef,
|
|
10
8
|
useState,
|
|
11
9
|
} from 'react';
|
|
12
10
|
|
|
13
|
-
import { Animated,
|
|
14
|
-
import type {
|
|
15
|
-
import type {
|
|
16
|
-
import
|
|
17
|
-
import heroEditorApp from './heroEditorApp';
|
|
18
|
-
import { isAndroid } from '../../utils/helpers';
|
|
11
|
+
import { Animated, Easing } from 'react-native';
|
|
12
|
+
import type { ReactElement, Ref } from 'react';
|
|
13
|
+
import type { LayoutChangeEvent } from 'react-native';
|
|
14
|
+
import { useTheme } from '../../theme';
|
|
19
15
|
import Icon from '../Icon';
|
|
20
16
|
import {
|
|
21
17
|
StyledAsteriskLabelInsideTextInput,
|
|
@@ -31,61 +27,21 @@ import {
|
|
|
31
27
|
StyledTextInputAndLabelContainer,
|
|
32
28
|
StyledTextInputContainer,
|
|
33
29
|
} from '../TextInput/StyledTextInput';
|
|
34
|
-
import { ToolbarEvents } from './constants';
|
|
35
|
-
import { emitter } from './EditorEvent';
|
|
36
|
-
import { StyledWebView } from './StyledRichTextEditor';
|
|
37
|
-
import * as Events from './utils/events';
|
|
38
|
-
import { postMessage, requestBlurEditor } from './utils/rnWebView';
|
|
39
|
-
import type { WebViewEventMessage } from './utils/rnWebView';
|
|
40
30
|
import { LABEL_ANIMATION_DURATION } from '../TextInput';
|
|
41
|
-
import type { TextUnit } from './types';
|
|
42
31
|
import Typography from '../Typography';
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
setReadOnly: (readOnly: boolean) => void;
|
|
49
|
-
}
|
|
32
|
+
import BaseRichTextEditor from './BaseRichTextEditor';
|
|
33
|
+
import type {
|
|
34
|
+
BaseRichTextEditorProps,
|
|
35
|
+
RichTextEditorRef,
|
|
36
|
+
} from './BaseRichTextEditor';
|
|
50
37
|
|
|
51
38
|
export type EditorValue = {
|
|
52
39
|
type: string;
|
|
53
40
|
children: any;
|
|
54
41
|
}[];
|
|
55
42
|
|
|
56
|
-
export interface RichTextEditorProps
|
|
57
|
-
|
|
58
|
-
* If true, the editor will be focused when the user enters the screen
|
|
59
|
-
*/
|
|
60
|
-
autoFocus?: boolean;
|
|
61
|
-
/**
|
|
62
|
-
* Error message
|
|
63
|
-
*/
|
|
64
|
-
error?: string;
|
|
65
|
-
/**
|
|
66
|
-
* Field value
|
|
67
|
-
*/
|
|
68
|
-
value?: EditorValue;
|
|
69
|
-
/**
|
|
70
|
-
* Unique name used to communicate with webview
|
|
71
|
-
*/
|
|
72
|
-
name: string;
|
|
73
|
-
/**
|
|
74
|
-
* Callback function called when the field value changed
|
|
75
|
-
*/
|
|
76
|
-
onChange: (data: EditorValue) => void;
|
|
77
|
-
/**
|
|
78
|
-
* Callback function called when the cursor position changed
|
|
79
|
-
*/
|
|
80
|
-
onCursorChange?: (params: { position: { top: number } }) => void;
|
|
81
|
-
/**
|
|
82
|
-
* Field placeholder
|
|
83
|
-
*/
|
|
84
|
-
placeholder?: string;
|
|
85
|
-
/**
|
|
86
|
-
* Additional styles
|
|
87
|
-
*/
|
|
88
|
-
style?: StyleProp<ViewStyle>;
|
|
43
|
+
export interface RichTextEditorProps
|
|
44
|
+
extends Omit<BaseRichTextEditorProps, 'editorRef'> {
|
|
89
45
|
/**
|
|
90
46
|
* Field label
|
|
91
47
|
*/
|
|
@@ -115,7 +71,7 @@ const defaultValue: EditorValue = [
|
|
|
115
71
|
},
|
|
116
72
|
];
|
|
117
73
|
|
|
118
|
-
const RichTextEditor
|
|
74
|
+
const RichTextEditor = ({
|
|
119
75
|
autoFocus = true,
|
|
120
76
|
name,
|
|
121
77
|
placeholder = '',
|
|
@@ -127,14 +83,13 @@ const RichTextEditor: ComponentType<RichTextEditorProps> = ({
|
|
|
127
83
|
helpText,
|
|
128
84
|
required,
|
|
129
85
|
testID,
|
|
86
|
+
onFocus,
|
|
87
|
+
onBlur,
|
|
130
88
|
forwardedRef,
|
|
131
89
|
value = defaultValue,
|
|
132
90
|
}: RichTextEditorProps): ReactElement => {
|
|
133
91
|
const theme = useTheme();
|
|
134
92
|
|
|
135
|
-
const webview = useRef<WebView | null>(null);
|
|
136
|
-
const [webviewHeight, setWebviewHeight] = useState<number | undefined>();
|
|
137
|
-
|
|
138
93
|
const [isFocused, setIsFocused] = useState(false);
|
|
139
94
|
const isEmptyValue = useMemo(() => isEmptyContent(value), [value]);
|
|
140
95
|
const state = useMemo(() => {
|
|
@@ -147,12 +102,6 @@ const RichTextEditor: ComponentType<RichTextEditorProps> = ({
|
|
|
147
102
|
return 'default';
|
|
148
103
|
}, [isFocused, error, isEmptyValue]);
|
|
149
104
|
|
|
150
|
-
const normalizeEventName = (event: string) => `${name}/${event}`;
|
|
151
|
-
const postMessageToWebview = (message: WebViewEventMessage) => {
|
|
152
|
-
if (webview && webview.current) {
|
|
153
|
-
postMessage(webview.current, message);
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
105
|
const [inputSize, setInputSize] = React.useState<{
|
|
157
106
|
height: number;
|
|
158
107
|
width: number;
|
|
@@ -174,184 +123,15 @@ const RichTextEditor: ComponentType<RichTextEditorProps> = ({
|
|
|
174
123
|
setInputSize((prev) => ({ ...prev, height, width }));
|
|
175
124
|
}, []);
|
|
176
125
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
() => setIsFocused(true)
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
const removeBlurListener = Events.on(
|
|
185
|
-
emitter,
|
|
186
|
-
normalizeEventName('editor-blur'),
|
|
187
|
-
() => setIsFocused(false)
|
|
188
|
-
);
|
|
189
|
-
|
|
190
|
-
return () => {
|
|
191
|
-
removeFocusListener();
|
|
192
|
-
removeBlurListener();
|
|
193
|
-
};
|
|
194
|
-
}, []);
|
|
195
|
-
|
|
196
|
-
const html = useMemo(() => {
|
|
197
|
-
const initialValueString = JSON.stringify(value);
|
|
198
|
-
|
|
199
|
-
return `
|
|
200
|
-
<!DOCTYPE html>
|
|
201
|
-
<html>
|
|
202
|
-
<head>
|
|
203
|
-
<meta charset="utf-8">
|
|
204
|
-
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=0">
|
|
205
|
-
<style>
|
|
206
|
-
body {
|
|
207
|
-
margin: 0;
|
|
208
|
-
}
|
|
209
|
-
</style>
|
|
210
|
-
</head>
|
|
211
|
-
<body>
|
|
212
|
-
<div id="root"></div>
|
|
213
|
-
<script>
|
|
214
|
-
window.__editorConfigs = {
|
|
215
|
-
placeholder: "${placeholder}",
|
|
216
|
-
initialValue: ${initialValueString},
|
|
217
|
-
isAndroid: ${isAndroid ? 'true' : 'false'},
|
|
218
|
-
autoFocus: ${autoFocus},
|
|
219
|
-
style: {
|
|
220
|
-
padding: '0 !important',
|
|
221
|
-
fontSize: ${theme.__hd__.richTextEditor.fontSizes.editor},
|
|
222
|
-
color: '${theme.__hd__.richTextEditor.colors.text}'
|
|
223
|
-
}
|
|
224
|
-
};
|
|
225
|
-
${heroEditorApp}
|
|
226
|
-
</script>
|
|
227
|
-
</body>
|
|
228
|
-
</html>
|
|
229
|
-
`;
|
|
230
|
-
}, []);
|
|
231
|
-
|
|
232
|
-
const requestBlur = useCallback(() => {
|
|
233
|
-
if (webview.current && isFocused) {
|
|
234
|
-
requestBlurEditor(webview.current);
|
|
235
|
-
}
|
|
236
|
-
}, [isFocused]);
|
|
237
|
-
|
|
238
|
-
const insertNodes = useCallback((nodes: Record<string, unknown>[]) => {
|
|
239
|
-
postMessageToWebview({
|
|
240
|
-
type: '@hero-editor/webview/editor-insert-nodes',
|
|
241
|
-
data: { nodes },
|
|
242
|
-
});
|
|
243
|
-
}, []);
|
|
244
|
-
|
|
245
|
-
const deleteBackward = useCallback((unit?: TextUnit) => {
|
|
246
|
-
postMessageToWebview({
|
|
247
|
-
type: '@hero-editor/webview/editor-delete-backward',
|
|
248
|
-
data: { unit },
|
|
249
|
-
});
|
|
250
|
-
}, []);
|
|
251
|
-
|
|
252
|
-
const setReadOnly = useCallback((readOnly: boolean) => {
|
|
253
|
-
postMessageToWebview({
|
|
254
|
-
type: '@hero-editor/webview/editor-read-only',
|
|
255
|
-
data: { readOnly },
|
|
256
|
-
});
|
|
257
|
-
}, []);
|
|
258
|
-
|
|
259
|
-
useImperativeHandle(
|
|
260
|
-
forwardedRef,
|
|
261
|
-
() => ({ requestBlur, insertNodes, deleteBackward, setReadOnly }),
|
|
262
|
-
[requestBlur, deleteBackward, insertNodes, setReadOnly]
|
|
263
|
-
);
|
|
264
|
-
|
|
265
|
-
/* Forward events from Toolbar and MentionList to webview */
|
|
266
|
-
useEffect(() => {
|
|
267
|
-
const toolbarEventListenerRemovers = Object.values(ToolbarEvents).map(
|
|
268
|
-
(eventName) =>
|
|
269
|
-
Events.on(emitter, normalizeEventName(eventName), (data) => {
|
|
270
|
-
postMessageToWebview({
|
|
271
|
-
type: `@hero-editor/webview/${eventName}`,
|
|
272
|
-
data,
|
|
273
|
-
});
|
|
274
|
-
})
|
|
275
|
-
);
|
|
276
|
-
|
|
277
|
-
const removeMentionApplyListener = Events.on(
|
|
278
|
-
emitter,
|
|
279
|
-
normalizeEventName('mention-apply'),
|
|
280
|
-
(data) =>
|
|
281
|
-
postMessageToWebview({
|
|
282
|
-
type: '@hero-editor/webview/mention-apply',
|
|
283
|
-
data,
|
|
284
|
-
})
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
return () => {
|
|
288
|
-
removeMentionApplyListener();
|
|
289
|
-
toolbarEventListenerRemovers.forEach((remover) => remover());
|
|
290
|
-
};
|
|
291
|
-
}, []);
|
|
292
|
-
|
|
293
|
-
const handleEditorLayoutEvent = useCallback((messageData: any) => {
|
|
294
|
-
const editorLayout = messageData
|
|
295
|
-
? {
|
|
296
|
-
width: Number(messageData.width),
|
|
297
|
-
height: Number(messageData.height),
|
|
298
|
-
}
|
|
299
|
-
: undefined;
|
|
300
|
-
|
|
301
|
-
if (editorLayout) {
|
|
302
|
-
setWebviewHeight(editorLayout.height);
|
|
303
|
-
}
|
|
304
|
-
}, []);
|
|
305
|
-
|
|
306
|
-
/* Handle events from webview */
|
|
307
|
-
const onMessage = useCallback(
|
|
308
|
-
(event?: { nativeEvent?: { data?: string } }) => {
|
|
309
|
-
const message = event?.nativeEvent?.data
|
|
310
|
-
? JSON.parse(event?.nativeEvent?.data)
|
|
311
|
-
: undefined;
|
|
312
|
-
|
|
313
|
-
const messageType = message?.type;
|
|
314
|
-
|
|
315
|
-
const messageData = message?.data;
|
|
316
|
-
|
|
317
|
-
switch (messageType) {
|
|
318
|
-
case '@hero-editor/webview/editor-focus':
|
|
319
|
-
Events.emit(emitter, normalizeEventName('editor-focus'), undefined);
|
|
320
|
-
|
|
321
|
-
break;
|
|
322
|
-
case '@hero-editor/webview/editor-blur':
|
|
323
|
-
Events.emit(emitter, normalizeEventName('editor-blur'), undefined);
|
|
324
|
-
|
|
325
|
-
break;
|
|
326
|
-
case '@hero-editor/webview/mention-search':
|
|
327
|
-
Events.emit(
|
|
328
|
-
emitter,
|
|
329
|
-
normalizeEventName('mention-search'),
|
|
330
|
-
messageData
|
|
331
|
-
);
|
|
126
|
+
const handleEditorFocus = useCallback(() => {
|
|
127
|
+
onFocus?.();
|
|
128
|
+
setIsFocused(true);
|
|
129
|
+
}, [onFocus]);
|
|
332
130
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
break;
|
|
340
|
-
case '@hero-editor/webview/cursor-change':
|
|
341
|
-
onCursorChange?.(messageData);
|
|
342
|
-
|
|
343
|
-
break;
|
|
344
|
-
|
|
345
|
-
case '@hero-editor/webview/editor-layout':
|
|
346
|
-
handleEditorLayoutEvent(messageData);
|
|
347
|
-
break;
|
|
348
|
-
|
|
349
|
-
default:
|
|
350
|
-
break;
|
|
351
|
-
}
|
|
352
|
-
},
|
|
353
|
-
[]
|
|
354
|
-
);
|
|
131
|
+
const handleEditorBlur = useCallback(() => {
|
|
132
|
+
onBlur?.();
|
|
133
|
+
setIsFocused(false);
|
|
134
|
+
}, [onBlur]);
|
|
355
135
|
|
|
356
136
|
return (
|
|
357
137
|
<StyledContainer testID={testID}>
|
|
@@ -408,25 +188,26 @@ const RichTextEditor: ComponentType<RichTextEditorProps> = ({
|
|
|
408
188
|
<StyledTextInputContainer onLayout={onLayout}>
|
|
409
189
|
<StyledBorderBackDrop themeState={state} themeFocused={isFocused} />
|
|
410
190
|
|
|
411
|
-
<StyledTextInputAndLabelContainer
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
191
|
+
<StyledTextInputAndLabelContainer testID="webViewWrapper">
|
|
192
|
+
<BaseRichTextEditor
|
|
193
|
+
name={name}
|
|
194
|
+
value={value}
|
|
195
|
+
style={[
|
|
196
|
+
style,
|
|
197
|
+
{
|
|
198
|
+
marginHorizontal:
|
|
199
|
+
theme.__hd__.textInput.space.inputHorizontalMargin,
|
|
200
|
+
},
|
|
201
|
+
]}
|
|
202
|
+
testID="webview"
|
|
203
|
+
onChange={onChange}
|
|
204
|
+
autoFocus={autoFocus}
|
|
205
|
+
editorRef={forwardedRef}
|
|
206
|
+
placeholder={placeholder}
|
|
207
|
+
onBlur={handleEditorBlur}
|
|
208
|
+
onFocus={handleEditorFocus}
|
|
209
|
+
onCursorChange={onCursorChange}
|
|
210
|
+
/>
|
|
430
211
|
</StyledTextInputAndLabelContainer>
|
|
431
212
|
</StyledTextInputContainer>
|
|
432
213
|
<StyledErrorAndHelpTextContainer>
|
|
@@ -10,6 +10,7 @@ import RichTextEditor from '../RichTextEditor';
|
|
|
10
10
|
import renderWithTheme from '../../../testHelpers/renderWithTheme';
|
|
11
11
|
import { theme } from '../../../index';
|
|
12
12
|
import HeroDesignProvider from '../../HeroDesignProvider';
|
|
13
|
+
import type { RichTextEditorRef } from '../BaseRichTextEditor';
|
|
13
14
|
|
|
14
15
|
type OnMessageCallback = (event: {
|
|
15
16
|
nativeEvent: { data: string };
|
|
@@ -58,7 +59,6 @@ jest.mock('../utils/rnWebView', () => {
|
|
|
58
59
|
/* eslint-disable */
|
|
59
60
|
/// @ts-ignore
|
|
60
61
|
import { postMessageMock } from '../utils/rnWebView';
|
|
61
|
-
import { RichTextEditorRef } from '../../../../types';
|
|
62
62
|
/* eslint-enable */
|
|
63
63
|
|
|
64
64
|
describe('RichTextEditor', () => {
|
|
@@ -100,7 +100,7 @@ describe('RichTextEditor', () => {
|
|
|
100
100
|
);
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
-
describe('
|
|
103
|
+
describe('received event editor-focus', () => {
|
|
104
104
|
it('should emit correct data', () => {
|
|
105
105
|
const emittedEvents: string[] = [];
|
|
106
106
|
Events.on(EditorEventEmitter, 'rich-text-editor/editor-focus', () => {
|
|
@@ -118,7 +118,7 @@ describe('RichTextEditor', () => {
|
|
|
118
118
|
});
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
-
describe('
|
|
121
|
+
describe('received event editor-blur', () => {
|
|
122
122
|
it('should emit correct data', () => {
|
|
123
123
|
const emittedEvents: string[] = [];
|
|
124
124
|
Events.on(EditorEventEmitter, 'rich-text-editor/editor-blur', () => {
|
|
@@ -133,7 +133,7 @@ describe('RichTextEditor', () => {
|
|
|
133
133
|
});
|
|
134
134
|
});
|
|
135
135
|
|
|
136
|
-
describe('
|
|
136
|
+
describe('received event mention-search', () => {
|
|
137
137
|
it('should emit correct data', () => {
|
|
138
138
|
const emittedEvents: string[] = [];
|
|
139
139
|
Events.on(EditorEventEmitter, 'rich-text-editor/mention-search', () => {
|
|
@@ -148,7 +148,7 @@ describe('RichTextEditor', () => {
|
|
|
148
148
|
});
|
|
149
149
|
});
|
|
150
150
|
|
|
151
|
-
describe('
|
|
151
|
+
describe('received event editor-change', () => {
|
|
152
152
|
beforeEach(() => {
|
|
153
153
|
onChangeMock.mockReset();
|
|
154
154
|
});
|
|
@@ -163,7 +163,7 @@ describe('RichTextEditor', () => {
|
|
|
163
163
|
});
|
|
164
164
|
});
|
|
165
165
|
|
|
166
|
-
describe('
|
|
166
|
+
describe('received event cursor-change', () => {
|
|
167
167
|
beforeEach(() => {
|
|
168
168
|
onCursorChangeMock.mockReset();
|
|
169
169
|
});
|
|
@@ -178,7 +178,7 @@ describe('RichTextEditor', () => {
|
|
|
178
178
|
});
|
|
179
179
|
});
|
|
180
180
|
|
|
181
|
-
describe('
|
|
181
|
+
describe('received event editor-layout', () => {
|
|
182
182
|
it('should update height', () => {
|
|
183
183
|
act(() => {
|
|
184
184
|
onMessageOfLatestRendering({
|
|
@@ -201,7 +201,7 @@ describe('RichTextEditor', () => {
|
|
|
201
201
|
);
|
|
202
202
|
|
|
203
203
|
expect(wrapper.toJSON()).toMatchSnapshot();
|
|
204
|
-
expect(wrapper.getByTestId('
|
|
204
|
+
expect(wrapper.getByTestId('webview')).toHaveStyle({
|
|
205
205
|
height: 480,
|
|
206
206
|
});
|
|
207
207
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
2
2
|
|
|
3
|
-
exports[`RichTextEditor onMessage
|
|
3
|
+
exports[`RichTextEditor onMessage received event editor-layout should update height 1`] = `
|
|
4
4
|
<View
|
|
5
5
|
style={
|
|
6
6
|
{
|
|
@@ -132,9 +132,7 @@ exports[`RichTextEditor onMessage recevied event editor-layout should update hei
|
|
|
132
132
|
"flexGrow": 2,
|
|
133
133
|
"flexShrink": 1,
|
|
134
134
|
},
|
|
135
|
-
|
|
136
|
-
"height": 480,
|
|
137
|
-
},
|
|
135
|
+
undefined,
|
|
138
136
|
]
|
|
139
137
|
}
|
|
140
138
|
testID="webViewWrapper"
|
|
@@ -206,12 +204,13 @@ exports[`RichTextEditor onMessage recevied event editor-layout should update hei
|
|
|
206
204
|
{
|
|
207
205
|
"backgroundColor": "transparent",
|
|
208
206
|
"fontSize": 16,
|
|
209
|
-
"marginHorizontal": 8,
|
|
210
207
|
"minHeight": 24,
|
|
211
208
|
"textAlignVertical": "center",
|
|
212
209
|
},
|
|
213
210
|
{
|
|
214
211
|
"backgroundColor": "yellow",
|
|
212
|
+
"height": 480,
|
|
213
|
+
"marginHorizontal": 8,
|
|
215
214
|
},
|
|
216
215
|
]
|
|
217
216
|
}
|
|
@@ -458,9 +457,7 @@ exports[`RichTextEditor should render correctly 1`] = `
|
|
|
458
457
|
"flexGrow": 2,
|
|
459
458
|
"flexShrink": 1,
|
|
460
459
|
},
|
|
461
|
-
|
|
462
|
-
"height": undefined,
|
|
463
|
-
},
|
|
460
|
+
undefined,
|
|
464
461
|
]
|
|
465
462
|
}
|
|
466
463
|
testID="webViewWrapper"
|
|
@@ -532,12 +529,13 @@ exports[`RichTextEditor should render correctly 1`] = `
|
|
|
532
529
|
{
|
|
533
530
|
"backgroundColor": "transparent",
|
|
534
531
|
"fontSize": 16,
|
|
535
|
-
"marginHorizontal": 8,
|
|
536
532
|
"minHeight": 24,
|
|
537
533
|
"textAlignVertical": "center",
|
|
538
534
|
},
|
|
539
535
|
{
|
|
540
536
|
"backgroundColor": "yellow",
|
|
537
|
+
"height": undefined,
|
|
538
|
+
"marginHorizontal": 8,
|
|
541
539
|
},
|
|
542
540
|
]
|
|
543
541
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import * as Events from '../utils/events';
|
|
4
|
+
import { emitter } from '../EditorEvent';
|
|
5
|
+
import type { EditorEvents, ToolbarEvents } from '../constants';
|
|
6
|
+
|
|
7
|
+
type SubscribableEvent = ToolbarEvents | EditorEvents;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Hook to subscribe to events for a rich text editor
|
|
11
|
+
* @param editorName - The name of the editor to subscribe to events for
|
|
12
|
+
* @returns An object with two functions: emitEvent and subscribeToEvents
|
|
13
|
+
* @example
|
|
14
|
+
* const { emitEvent, subscribeToEvents } = useRichTextEditorEvents('editorName');
|
|
15
|
+
* subscribeToEvents(EditorEvents.EditorFocus, () => {
|
|
16
|
+
* console.log('Editor focused');
|
|
17
|
+
* });
|
|
18
|
+
* emitEvent({ type: EditorEvents.EditorFocus, data: null });
|
|
19
|
+
*/
|
|
20
|
+
const useRichTextEditorEvents = (editorName: string) => {
|
|
21
|
+
const normalizeEventName = useCallback(
|
|
22
|
+
(event: string) => `${editorName}/${event}`,
|
|
23
|
+
[editorName]
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
const subscribeToEvents = useCallback(
|
|
27
|
+
<TEventName extends SubscribableEvent>(
|
|
28
|
+
eventName: TEventName,
|
|
29
|
+
onEvent: (data: unknown) => void
|
|
30
|
+
) =>
|
|
31
|
+
Events.on(
|
|
32
|
+
emitter,
|
|
33
|
+
normalizeEventName(eventName),
|
|
34
|
+
(eventData: unknown) => {
|
|
35
|
+
onEvent(eventData);
|
|
36
|
+
}
|
|
37
|
+
),
|
|
38
|
+
[normalizeEventName]
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const emitEvent = useCallback(
|
|
42
|
+
<TEventName extends ToolbarEvents>({
|
|
43
|
+
type,
|
|
44
|
+
data,
|
|
45
|
+
}: {
|
|
46
|
+
type: TEventName;
|
|
47
|
+
data: unknown;
|
|
48
|
+
}) => {
|
|
49
|
+
Events.emit(emitter, normalizeEventName(type), data);
|
|
50
|
+
},
|
|
51
|
+
[normalizeEventName]
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
emitEvent,
|
|
56
|
+
subscribeToEvents,
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export default useRichTextEditorEvents;
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import Toolbar from './EditorToolbar';
|
|
2
2
|
import MentionList from './MentionList';
|
|
3
3
|
import RichTextEditor from './RichTextEditor';
|
|
4
|
-
import
|
|
4
|
+
import { EditorEvents, ToolbarEvents } from './constants';
|
|
5
|
+
import useRichTextEditorEvents from './hooks/useRichTextEditorEvents';
|
|
6
|
+
import type { RichTextEditorProps } from './RichTextEditor';
|
|
7
|
+
import BaseRichTextEditor from './BaseRichTextEditor';
|
|
8
|
+
import type { RichTextEditorRef } from './BaseRichTextEditor';
|
|
5
9
|
|
|
6
10
|
export type { RichTextEditorProps, RichTextEditorRef };
|
|
7
11
|
|
|
8
12
|
export default Object.assign(RichTextEditor, {
|
|
9
|
-
MentionList,
|
|
10
13
|
Toolbar,
|
|
14
|
+
MentionList,
|
|
15
|
+
EditorEvents,
|
|
16
|
+
ToolbarEvents,
|
|
17
|
+
useRichTextEditorEvents,
|
|
18
|
+
Base: BaseRichTextEditor,
|
|
11
19
|
});
|