@apollohg/react-native-rich-text-editor 2.0.0 → 2.0.2
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 +8 -116
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/android/build.gradle +3 -0
- package/android/src/main/java/com/apollohg/editor/EditorEditTextImages.kt +25 -0
- package/android/src/main/java/com/apollohg/editor/EditorV2RenderValidation.kt +1 -0
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +101 -33
- package/android/src/main/java/com/apollohg/editor/RenderMediaSpans.kt +15 -3
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +14 -0
- package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +1 -1
- package/app.plugin.js +42 -39
- package/dist/AtomHost.js +18 -11
- package/dist/EditorAddon.js +4 -2
- package/dist/EditorAddonNormalization.js +17 -9
- package/dist/EditorMentionThemeNormalization.js +6 -3
- package/dist/EditorMentionThemeValidation.js +28 -11
- package/dist/EditorStyleSheetNormalization.js +85 -38
- package/dist/EditorStyleSheetTypes.d.ts +1 -1
- package/dist/EditorTheme.d.ts +4 -0
- package/dist/EditorTheme.js +5 -3
- package/dist/EditorToolbarComponent.d.ts +1 -1
- package/dist/EditorToolbarComponent.js +1 -1
- package/dist/EditorToolbarRegistry.js +5 -5
- package/dist/EditorToolbarTypes.d.ts +12 -12
- package/dist/EditorToolbarVisuals.js +1 -1
- package/dist/ExternalTextComposition.js +89 -43
- package/dist/ImageLoadingPolicy.d.ts +1 -1
- package/dist/NativeEditorBoundaryError.d.ts +7 -7
- package/dist/NativeEditorBoundaryError.js +9 -5
- package/dist/NativeEditorCollaborationTransport.d.ts +7 -7
- package/dist/NativeEditorCollaborationTransport.js +28 -15
- package/dist/NativeEditorCreateConfig.js +11 -6
- package/dist/NativeEditorCreateJson.js +51 -25
- package/dist/NativeEditorCreateValidation.js +11 -6
- package/dist/NativeEditorDocumentBridge.d.ts +1 -1
- package/dist/NativeEditorDocumentBridge.js +32 -18
- package/dist/NativeEditorDocumentHandle.d.ts +4 -4
- package/dist/NativeEditorDocumentHandle.js +1 -1
- package/dist/NativeEditorLocalAwareness.js +25 -13
- package/dist/NativeEditorNativeModule.d.ts +8 -1
- package/dist/NativeEditorNativeModule.js +7 -5
- package/dist/NativeEditorRenderNormalization.js +66 -25
- package/dist/NativeEditorResultNormalization.js +53 -29
- package/dist/NativeEditorTypes.d.ts +10 -10
- package/dist/NativeEditorV2Decimal.js +6 -3
- package/dist/NativeProseViewer.js +11 -3
- package/dist/ResourceLimits.d.ts +2 -2
- package/dist/ResourceLimits.js +7 -2
- package/dist/RichTextEditor.js +1 -1
- package/dist/RichTextEditorNativeTypes.d.ts +1 -1
- package/dist/RichTextEditorNativeView.d.ts +1 -3
- package/dist/RichTextEditorSerialization.js +24 -14
- package/dist/RichTextEditorTypes.d.ts +1 -1
- package/dist/YjsCollaboration.d.ts +7 -7
- package/dist/YjsCollaboration.js +42 -26
- package/dist/addons.d.ts +4 -4
- package/dist/addons.js +3 -3
- package/dist/atomInstances.js +6 -3
- package/dist/atomUpdates.js +7 -4
- package/dist/atoms.js +20 -12
- package/dist/attributeValidation.js +55 -24
- package/dist/contentExpression.js +80 -42
- package/dist/listMarkers.d.ts +1 -1
- package/dist/listMarkers.js +1 -1
- package/dist/schemaDefinition.d.ts +1 -1
- package/dist/schemaDefinition.js +23 -14
- package/dist/schemaDocument.js +1 -1
- package/dist/schemaDocumentConstruction.js +25 -14
- package/dist/schemaNormalization.js +51 -29
- package/dist/schemaPresets.d.ts +1 -1
- package/dist/schemaPresets.js +9 -4
- package/dist/schemaResolution.js +46 -25
- package/dist/specs/PreparedProseViewerNativeComponent.d.ts +4 -0
- package/dist/useEditorToolbarInteractions.d.ts +2 -2
- package/dist/useEditorToolbarInteractions.js +45 -23
- package/dist/useEditorToolbarItems.d.ts +1 -1
- package/dist/useEditorToolbarItems.js +4 -3
- package/dist/useEditorToolbarPresentation.d.ts +3 -3
- package/dist/useEditorToolbarPresentation.js +4 -4
- package/dist/useEditorToolbarState.d.ts +1 -1
- package/dist/useEditorToolbarState.js +1 -1
- package/dist/useFocusPreservingFrames.js +11 -8
- package/dist/useNativeEditor.d.ts +1 -1
- package/dist/useNativeEditor.js +21 -11
- package/dist/useRichTextEditorCommands.d.ts +2 -2
- package/dist/useRichTextEditorCommands.js +59 -29
- package/dist/useRichTextEditorEvents.d.ts +3 -3
- package/dist/useRichTextEditorEvents.js +75 -32
- package/dist/useRichTextEditorMentions.d.ts +4 -4
- package/dist/useRichTextEditorMentions.js +48 -29
- package/dist/useRichTextEditorPresentation.d.ts +4 -4
- package/dist/useRichTextEditorPresentation.js +25 -23
- package/dist/useRichTextEditorState.d.ts +3 -3
- package/dist/useRichTextEditorState.js +20 -15
- package/dist/useRichTextEditorUpdates.d.ts +1 -1
- package/dist/useRichTextEditorUpdates.js +99 -41
- package/dist/useViewerAtoms.js +63 -36
- package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
- package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
- package/ios/EditorInteractionOverlays.swift +114 -122
- package/ios/EditorLayoutManager.swift +13 -3
- package/ios/EditorStyleSheet.swift +12 -1
- package/ios/EditorTextView+Images.swift +32 -10
- package/ios/EditorTextView.swift +1 -1
- package/ios/EditorV2Adapter+RenderValidation.swift +2 -2
- package/ios/NativeToolbar+ButtonStyle.swift +17 -5
- package/ios/NativeToolbar+Diagnostics.swift +15 -0
- package/ios/RenderBridge+Atoms.swift +0 -1
- package/ios/RenderBridge+Attachments.swift +0 -29
- package/ios/RichTextEditorView.swift +4 -12
- package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +3 -2
- package/package.json +11 -3
- package/react-native.config.js +1 -1
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- package/rust/android/x86/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/src/AtomHost.tsx +58 -28
- package/src/EditorAddon.ts +18 -8
- package/src/EditorAddonNormalization.ts +40 -11
- package/src/EditorMentionThemeNormalization.ts +15 -4
- package/src/EditorMentionThemeValidation.ts +51 -14
- package/src/EditorStyleSheet.ts +1 -0
- package/src/EditorStyleSheetNormalization.ts +195 -64
- package/src/EditorStyleSheetTypes.ts +1 -1
- package/src/EditorTheme.ts +13 -5
- package/src/EditorToolbarComponent.tsx +2 -1
- package/src/EditorToolbarRegistry.tsx +23 -13
- package/src/EditorToolbarTypes.tsx +12 -12
- package/src/EditorToolbarVisuals.tsx +6 -3
- package/src/EditorUpdateRevision.ts +1 -0
- package/src/ExternalTextComposition.ts +259 -49
- package/src/ImageLoadingPolicy.ts +3 -1
- package/src/NativeEditorBoundaryError.ts +38 -11
- package/src/NativeEditorCollaborationTransport.ts +108 -27
- package/src/NativeEditorCreateConfig.ts +92 -19
- package/src/NativeEditorCreateJson.ts +163 -26
- package/src/NativeEditorCreateValidation.ts +33 -12
- package/src/NativeEditorDocumentBridge.ts +111 -19
- package/src/NativeEditorDocumentHandle.ts +9 -4
- package/src/NativeEditorLocalAwareness.ts +70 -15
- package/src/NativeEditorNativeModule.ts +13 -9
- package/src/NativeEditorRenderNormalization.ts +128 -29
- package/src/NativeEditorResultNormalization.ts +143 -32
- package/src/NativeEditorTypes.ts +12 -11
- package/src/NativeEditorV2Decimal.ts +12 -3
- package/src/NativeProseViewer.tsx +46 -16
- package/src/ResourceLimits.ts +15 -8
- package/src/RichTextEditor.tsx +3 -1
- package/src/RichTextEditorNativeTypes.tsx +1 -1
- package/src/RichTextEditorNativeView.tsx +2 -8
- package/src/RichTextEditorSerialization.tsx +70 -24
- package/src/RichTextEditorTypes.tsx +1 -1
- package/src/YjsCollaboration.ts +143 -47
- package/src/addons.ts +12 -8
- package/src/atomInstances.ts +25 -5
- package/src/atomUpdates.ts +23 -6
- package/src/atoms.ts +85 -18
- package/src/attributeValidation.ts +97 -33
- package/src/contentExpression.ts +218 -63
- package/src/listMarkers.ts +1 -1
- package/src/schemaDefinition.ts +82 -20
- package/src/schemaDocument.ts +15 -1
- package/src/schemaDocumentConstruction.ts +80 -16
- package/src/schemaNormalization.ts +155 -41
- package/src/schemaPresets.ts +37 -31
- package/src/schemaResolution.ts +126 -27
- package/src/specs/PreparedProseViewerNativeComponent.ts +4 -2
- package/src/useEditorToolbarInteractions.tsx +85 -36
- package/src/useEditorToolbarItems.tsx +50 -22
- package/src/useEditorToolbarPresentation.tsx +81 -58
- package/src/useEditorToolbarState.tsx +8 -6
- package/src/useFocusPreservingFrames.ts +43 -15
- package/src/useNativeEditor.ts +111 -51
- package/src/useRichTextEditorCommands.tsx +127 -54
- package/src/useRichTextEditorEvents.tsx +144 -42
- package/src/useRichTextEditorMentions.tsx +118 -37
- package/src/useRichTextEditorPresentation.tsx +128 -114
- package/src/useRichTextEditorState.tsx +76 -40
- package/src/useRichTextEditorUpdates.tsx +207 -56
- package/src/useViewerAtoms.tsx +162 -66
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useCallback, useMemo } from 'react';
|
|
2
|
-
import { useEditorToolbarState } from './useEditorToolbarState';
|
|
2
|
+
import { type useEditorToolbarState } from './useEditorToolbarState';
|
|
3
3
|
import {
|
|
4
4
|
type EditorToolbarLeafItem,
|
|
5
5
|
type EditorToolbarItemPlacement,
|
|
@@ -88,6 +88,7 @@ export function useEditorToolbarItems(
|
|
|
88
88
|
if (onToggleMark) {
|
|
89
89
|
return () => onToggleMark(item.mark);
|
|
90
90
|
}
|
|
91
|
+
|
|
91
92
|
switch (item.mark) {
|
|
92
93
|
case 'bold':
|
|
93
94
|
return onToggleBold;
|
|
@@ -100,10 +101,12 @@ export function useEditorToolbarItems(
|
|
|
100
101
|
default:
|
|
101
102
|
return null;
|
|
102
103
|
}
|
|
104
|
+
|
|
103
105
|
case 'list':
|
|
104
106
|
if (onToggleListType) {
|
|
105
107
|
return () => onToggleListType(item.listType);
|
|
106
108
|
}
|
|
109
|
+
|
|
107
110
|
return item.listType === 'bulletList' || item.listType === 'bullet_list'
|
|
108
111
|
? (onToggleBulletList ?? null)
|
|
109
112
|
: (onToggleOrderedList ?? null);
|
|
@@ -115,10 +118,12 @@ export function useEditorToolbarItems(
|
|
|
115
118
|
return onToggleHeading ? () => onToggleHeading(item.level) : null;
|
|
116
119
|
case 'blockquote':
|
|
117
120
|
return onToggleBlockquote ?? null;
|
|
121
|
+
|
|
118
122
|
case 'node':
|
|
119
123
|
if (onInsertNodeType) {
|
|
120
124
|
return () => onInsertNodeType(item.nodeType);
|
|
121
125
|
}
|
|
126
|
+
|
|
122
127
|
switch (item.nodeType) {
|
|
123
128
|
case 'hardBreak':
|
|
124
129
|
case 'hard_break':
|
|
@@ -129,10 +134,12 @@ export function useEditorToolbarItems(
|
|
|
129
134
|
default:
|
|
130
135
|
return null;
|
|
131
136
|
}
|
|
137
|
+
|
|
132
138
|
case 'command':
|
|
133
139
|
if (onRunCommand) {
|
|
134
140
|
return () => onRunCommand(item.command);
|
|
135
141
|
}
|
|
142
|
+
|
|
136
143
|
switch (item.command) {
|
|
137
144
|
case 'indentList':
|
|
138
145
|
return onIndentList ?? null;
|
|
@@ -143,6 +150,8 @@ export function useEditorToolbarItems(
|
|
|
143
150
|
case 'redo':
|
|
144
151
|
return onRedo;
|
|
145
152
|
}
|
|
153
|
+
|
|
154
|
+
// Falls through for unsupported commands.
|
|
146
155
|
case 'action':
|
|
147
156
|
return onToolbarAction ? () => onToolbarAction(item.key) : null;
|
|
148
157
|
}
|
|
@@ -177,22 +186,22 @@ export function useEditorToolbarItems(
|
|
|
177
186
|
item.key != null
|
|
178
187
|
? `${prefix}${item.key}`
|
|
179
188
|
: item.type === 'mark'
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
189
|
+
? `${prefix}mark:${item.mark}:${index}`
|
|
190
|
+
: item.type === 'link'
|
|
191
|
+
? `${prefix}link:${index}`
|
|
192
|
+
: item.type === 'image'
|
|
193
|
+
? `${prefix}image:${index}`
|
|
194
|
+
: item.type === 'heading'
|
|
195
|
+
? `${prefix}heading:${item.level}:${index}`
|
|
196
|
+
: item.type === 'blockquote'
|
|
197
|
+
? `${prefix}blockquote:${index}`
|
|
198
|
+
: item.type === 'list'
|
|
199
|
+
? `${prefix}list:${item.listType}:${index}`
|
|
200
|
+
: item.type === 'command'
|
|
201
|
+
? `${prefix}command:${item.command}:${index}`
|
|
202
|
+
: item.type === 'node'
|
|
203
|
+
? `${prefix}node:${item.nodeType}:${index}`
|
|
204
|
+
: `${prefix}action:${String(item.key)}:${index}`,
|
|
196
205
|
[]
|
|
197
206
|
);
|
|
198
207
|
|
|
@@ -206,12 +215,14 @@ export function useEditorToolbarItems(
|
|
|
206
215
|
): ToolbarButton | null => {
|
|
207
216
|
const resolvedPlacement = resolveToolbarItemPlacement(item.placement ?? placement);
|
|
208
217
|
const action = getActionForItem(item);
|
|
218
|
+
|
|
209
219
|
if (!action) {
|
|
210
220
|
return null;
|
|
211
221
|
}
|
|
212
222
|
|
|
213
223
|
let isActive = false;
|
|
214
224
|
let isDisabled = false;
|
|
225
|
+
|
|
215
226
|
switch (item.type) {
|
|
216
227
|
case 'mark':
|
|
217
228
|
isActive = isMarkActive(item.mark);
|
|
@@ -224,24 +235,28 @@ export function useEditorToolbarItems(
|
|
|
224
235
|
case 'image':
|
|
225
236
|
isDisabled = !insertableNodes.includes('image') || !onRequestImage;
|
|
226
237
|
break;
|
|
238
|
+
|
|
227
239
|
case 'heading': {
|
|
228
240
|
const headingNodeType = `h${item.level}`;
|
|
229
241
|
isActive = !!nodes[headingNodeType];
|
|
230
242
|
isDisabled = !commands[`toggleHeading${item.level}`];
|
|
231
243
|
break;
|
|
232
244
|
}
|
|
245
|
+
|
|
233
246
|
case 'blockquote':
|
|
234
247
|
isActive = !!nodes['blockquote'];
|
|
235
248
|
isDisabled = !commands['toggleBlockquote'];
|
|
236
249
|
break;
|
|
237
250
|
case 'list':
|
|
238
251
|
isActive = !!nodes[item.listType];
|
|
252
|
+
|
|
239
253
|
isDisabled =
|
|
240
254
|
!commands[
|
|
241
255
|
item.listType === 'bulletList' || item.listType === 'bullet_list'
|
|
242
256
|
? 'wrapBulletList'
|
|
243
257
|
: 'wrapOrderedList'
|
|
244
258
|
];
|
|
259
|
+
|
|
245
260
|
break;
|
|
246
261
|
case 'command':
|
|
247
262
|
switch (item.command) {
|
|
@@ -258,6 +273,7 @@ export function useEditorToolbarItems(
|
|
|
258
273
|
isDisabled = !historyState.canRedo;
|
|
259
274
|
break;
|
|
260
275
|
}
|
|
276
|
+
|
|
261
277
|
break;
|
|
262
278
|
case 'action':
|
|
263
279
|
isActive = !!item.isActive;
|
|
@@ -304,8 +320,10 @@ export function useEditorToolbarItems(
|
|
|
304
320
|
if (entry.type !== 'separator') {
|
|
305
321
|
return true;
|
|
306
322
|
}
|
|
323
|
+
|
|
307
324
|
const previous = list[index - 1];
|
|
308
325
|
const next = list[index + 1];
|
|
326
|
+
|
|
309
327
|
return (
|
|
310
328
|
previous != null &&
|
|
311
329
|
previous.type !== 'separator' &&
|
|
@@ -319,6 +337,7 @@ export function useEditorToolbarItems(
|
|
|
319
337
|
const scrollEntries: ToolbarRenderedItem[] = [];
|
|
320
338
|
const endEntries: ToolbarRenderedItem[] = [];
|
|
321
339
|
const nextGroups = new Map<string, ToolbarGroupButton>();
|
|
340
|
+
|
|
322
341
|
const entriesForPlacement = (placement: EditorToolbarItemPlacement) =>
|
|
323
342
|
placement === 'start' ? startEntries : placement === 'end' ? endEntries : scrollEntries;
|
|
324
343
|
|
|
@@ -326,27 +345,31 @@ export function useEditorToolbarItems(
|
|
|
326
345
|
const item = toolbarItems[index];
|
|
327
346
|
const placement = resolveToolbarItemPlacement(item.placement);
|
|
328
347
|
const targetEntries = entriesForPlacement(placement);
|
|
348
|
+
|
|
329
349
|
if (item.type === 'separator') {
|
|
330
350
|
targetEntries.push({
|
|
331
351
|
type: 'separator',
|
|
332
352
|
key: item.key ?? `separator:${index}`,
|
|
333
353
|
placement,
|
|
334
354
|
});
|
|
355
|
+
|
|
335
356
|
continue;
|
|
336
357
|
}
|
|
337
358
|
|
|
338
359
|
if (item.type === 'group') {
|
|
339
360
|
const children = item.items
|
|
340
361
|
.map((child, childIndex) =>
|
|
341
|
-
resolveButton(child, childIndex, `${item.key}:`, item.key, placement)
|
|
342
|
-
)
|
|
362
|
+
resolveButton(child, childIndex, `${item.key}:`, item.key, placement))
|
|
343
363
|
.filter((child): child is ToolbarButton => child != null);
|
|
364
|
+
|
|
344
365
|
if (children.length === 0) {
|
|
345
366
|
continue;
|
|
346
367
|
}
|
|
368
|
+
|
|
347
369
|
const presentation = item.presentation ?? 'expand';
|
|
348
370
|
const isExpanded = presentation === 'expand' && expandedGroupKey === item.key;
|
|
349
371
|
const isMenuOpen = presentation === 'menu' && menuState?.groupKey === item.key;
|
|
372
|
+
|
|
350
373
|
const group: ToolbarGroupButton = {
|
|
351
374
|
key: item.key,
|
|
352
375
|
label: item.label,
|
|
@@ -355,13 +378,15 @@ export function useEditorToolbarItems(
|
|
|
355
378
|
presentation,
|
|
356
379
|
placement,
|
|
357
380
|
children,
|
|
358
|
-
isActive: children.some(
|
|
359
|
-
isDisabled: children.every(
|
|
381
|
+
isActive: children.some(child => child.isActive) || isExpanded || isMenuOpen,
|
|
382
|
+
isDisabled: children.every(child => child.isDisabled),
|
|
360
383
|
isExpanded,
|
|
361
384
|
isOpen: isExpanded || isMenuOpen,
|
|
362
385
|
};
|
|
386
|
+
|
|
363
387
|
nextGroups.set(group.key, group);
|
|
364
388
|
targetEntries.push({ type: 'group', group });
|
|
389
|
+
|
|
365
390
|
if (group.isExpanded) {
|
|
366
391
|
for (const child of children) {
|
|
367
392
|
entriesForPlacement(child.placement).push({
|
|
@@ -370,10 +395,12 @@ export function useEditorToolbarItems(
|
|
|
370
395
|
});
|
|
371
396
|
}
|
|
372
397
|
}
|
|
398
|
+
|
|
373
399
|
continue;
|
|
374
400
|
}
|
|
375
401
|
|
|
376
402
|
const button = resolveButton(item, index, '', undefined, placement);
|
|
403
|
+
|
|
377
404
|
if (button) {
|
|
378
405
|
targetEntries.push({ type: 'button', button });
|
|
379
406
|
}
|
|
@@ -385,6 +412,7 @@ export function useEditorToolbarItems(
|
|
|
385
412
|
endItems: compactRenderedItems(endEntries),
|
|
386
413
|
groupsByKey: nextGroups,
|
|
387
414
|
};
|
|
388
|
-
}, [expandedGroupKey, menuState?.groupKey, resolveButton, toolbarItems]);
|
|
415
|
+
}, [ expandedGroupKey, menuState?.groupKey, resolveButton, toolbarItems ]);
|
|
416
|
+
|
|
389
417
|
return { startItems, scrollItems, endItems, groupsByKey };
|
|
390
418
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Modal, Pressable, ScrollView, Text, TouchableOpacity, View } from 'react-native';
|
|
2
|
-
import { useEditorToolbarState } from './useEditorToolbarState';
|
|
3
|
-
import { useEditorToolbarItems } from './useEditorToolbarItems';
|
|
4
|
-
import { useEditorToolbarInteractions } from './useEditorToolbarInteractions';
|
|
2
|
+
import { type useEditorToolbarState } from './useEditorToolbarState';
|
|
3
|
+
import { type useEditorToolbarItems } from './useEditorToolbarItems';
|
|
4
|
+
import { type useEditorToolbarInteractions } from './useEditorToolbarInteractions';
|
|
5
5
|
import {
|
|
6
6
|
BUTTON_VISIBLE,
|
|
7
7
|
TOOLBAR_PADDING_V,
|
|
@@ -106,50 +106,57 @@ export function useEditorToolbarPresentation(
|
|
|
106
106
|
menuState == null
|
|
107
107
|
? 0
|
|
108
108
|
: Math.max(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
MENU_MARGIN,
|
|
110
|
+
Math.min(
|
|
111
|
+
menuState.y + menuState.height + 8,
|
|
112
|
+
windowHeight - menuHeight - MENU_MARGIN
|
|
113
|
+
)
|
|
114
|
+
);
|
|
115
115
|
|
|
116
116
|
const menuLeft =
|
|
117
117
|
menuState == null
|
|
118
118
|
? 0
|
|
119
119
|
: Math.max(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
120
|
+
MENU_MARGIN,
|
|
121
|
+
Math.min(
|
|
122
|
+
menuState.x + menuState.width - MENU_WIDTH,
|
|
123
|
+
windowWidth - MENU_WIDTH - MENU_MARGIN
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
126
|
|
|
127
127
|
const resolveButtonVisuals = (
|
|
128
128
|
button: Pick<ToolbarButton, 'buttonStyle' | 'isActive' | 'isDisabled'>
|
|
129
129
|
) => {
|
|
130
130
|
const activeColor =
|
|
131
131
|
button.buttonStyle?.activeColor ?? theme?.buttonActiveColor ?? ACTIVE_COLOR;
|
|
132
|
+
|
|
132
133
|
const defaultColor = button.buttonStyle?.color ?? theme?.buttonColor ?? DEFAULT_COLOR;
|
|
134
|
+
|
|
133
135
|
const disabledColor =
|
|
134
136
|
button.buttonStyle?.disabledColor ?? theme?.buttonDisabledColor ?? DISABLED_COLOR;
|
|
137
|
+
|
|
135
138
|
const backgroundColor =
|
|
136
139
|
button.buttonStyle?.backgroundColor ?? theme?.buttonBackgroundColor ?? 'transparent';
|
|
140
|
+
|
|
137
141
|
const activeBackgroundColor =
|
|
138
142
|
button.buttonStyle?.activeBackgroundColor ??
|
|
139
143
|
theme?.buttonActiveBackgroundColor ??
|
|
140
144
|
ACTIVE_BG;
|
|
145
|
+
|
|
141
146
|
const disabledBackgroundColor =
|
|
142
147
|
button.buttonStyle?.disabledBackgroundColor ??
|
|
143
148
|
theme?.buttonDisabledBackgroundColor ??
|
|
144
149
|
(button.isActive ? activeBackgroundColor : backgroundColor);
|
|
150
|
+
|
|
145
151
|
const requestedIconSize = button.buttonStyle?.iconSize ?? theme?.buttonIconSize;
|
|
152
|
+
|
|
146
153
|
return {
|
|
147
154
|
color: button.isDisabled ? disabledColor : button.isActive ? activeColor : defaultColor,
|
|
148
155
|
backgroundColor: button.isDisabled
|
|
149
156
|
? disabledBackgroundColor
|
|
150
157
|
: button.isActive
|
|
151
|
-
|
|
152
|
-
|
|
158
|
+
? activeBackgroundColor
|
|
159
|
+
: backgroundColor,
|
|
153
160
|
iconSize:
|
|
154
161
|
requestedIconSize != null &&
|
|
155
162
|
Number.isFinite(requestedIconSize) &&
|
|
@@ -184,16 +191,17 @@ export function useEditorToolbarPresentation(
|
|
|
184
191
|
ref={
|
|
185
192
|
anchorGroupKey == null
|
|
186
193
|
? undefined
|
|
187
|
-
:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
+
: node => {
|
|
195
|
+
if (node) {
|
|
196
|
+
groupButtonRefs.current.set(anchorGroupKey, node);
|
|
197
|
+
} else {
|
|
198
|
+
groupButtonRefs.current.delete(anchorGroupKey);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
194
201
|
}
|
|
195
202
|
collapsable={false}
|
|
196
|
-
style={styles.buttonAnchor}
|
|
203
|
+
style={styles.buttonAnchor}
|
|
204
|
+
>
|
|
197
205
|
<TouchableOpacity
|
|
198
206
|
onPressIn={handleToolbarPressIn}
|
|
199
207
|
onPressOut={handleToolbarPressOut}
|
|
@@ -208,13 +216,14 @@ export function useEditorToolbarPresentation(
|
|
|
208
216
|
},
|
|
209
217
|
]}
|
|
210
218
|
activeOpacity={0.5}
|
|
211
|
-
accessibilityRole='button'
|
|
219
|
+
accessibilityRole={'button'}
|
|
212
220
|
accessibilityLabel={button.label}
|
|
213
221
|
accessibilityState={{
|
|
214
222
|
selected: button.isActive,
|
|
215
223
|
disabled: button.isDisabled,
|
|
216
224
|
expanded: options?.showsDisclosure ? options.expanded : undefined,
|
|
217
|
-
}}
|
|
225
|
+
}}
|
|
226
|
+
>
|
|
218
227
|
<View>
|
|
219
228
|
<ToolbarIcon
|
|
220
229
|
icon={button.icon}
|
|
@@ -224,7 +233,7 @@ export function useEditorToolbarPresentation(
|
|
|
224
233
|
</View>
|
|
225
234
|
</TouchableOpacity>
|
|
226
235
|
{options?.showsDisclosure ? (
|
|
227
|
-
<Text style={[styles.groupDisclosure, { color: visuals.color }]}>
|
|
236
|
+
<Text style={[ styles.groupDisclosure, { color: visuals.color } ]}>
|
|
228
237
|
{'\u25BE'}
|
|
229
238
|
</Text>
|
|
230
239
|
) : null}
|
|
@@ -244,10 +253,11 @@ export function useEditorToolbarPresentation(
|
|
|
244
253
|
);
|
|
245
254
|
|
|
246
255
|
const renderToolbarItems = (items: ToolbarRenderedItem[]) =>
|
|
247
|
-
items.map(
|
|
256
|
+
items.map(item => {
|
|
248
257
|
if (item.type === 'separator') {
|
|
249
258
|
return renderSeparator(item.key);
|
|
250
259
|
}
|
|
260
|
+
|
|
251
261
|
if (item.type === 'group') {
|
|
252
262
|
return renderButton(
|
|
253
263
|
{
|
|
@@ -266,13 +276,14 @@ export function useEditorToolbarPresentation(
|
|
|
266
276
|
}
|
|
267
277
|
);
|
|
268
278
|
}
|
|
279
|
+
|
|
269
280
|
return renderButton(item.button, () => handleButtonPress(item.button));
|
|
270
281
|
});
|
|
271
282
|
|
|
272
283
|
return (
|
|
273
284
|
<View
|
|
274
285
|
ref={rootRef}
|
|
275
|
-
testID='editor-toolbar-root'
|
|
286
|
+
testID={'editor-toolbar-root'}
|
|
276
287
|
collapsable={false}
|
|
277
288
|
onLayout={handleToolbarLayout}
|
|
278
289
|
style={[
|
|
@@ -296,16 +307,17 @@ export function useEditorToolbarPresentation(
|
|
|
296
307
|
{
|
|
297
308
|
borderRadius: theme?.borderRadius ?? TOOLBAR_RADIUS,
|
|
298
309
|
},
|
|
299
|
-
]}
|
|
310
|
+
]}
|
|
311
|
+
>
|
|
300
312
|
<View style={styles.toolbarRow}>
|
|
301
313
|
{startItems.length > 0 ? (
|
|
302
|
-
<View style={[styles.fixedSection, styles.startFixedSection]}>
|
|
314
|
+
<View style={[ styles.fixedSection, styles.startFixedSection ]}>
|
|
303
315
|
{renderToolbarItems(startItems)}
|
|
304
316
|
</View>
|
|
305
317
|
) : null}
|
|
306
318
|
{shouldRenderMentionSuggestions && mentionState != null ? (
|
|
307
319
|
<ScrollView
|
|
308
|
-
testID='editor-toolbar-mention-suggestions'
|
|
320
|
+
testID={'editor-toolbar-mention-suggestions'}
|
|
309
321
|
horizontal
|
|
310
322
|
showsHorizontalScrollIndicator={false}
|
|
311
323
|
style={[
|
|
@@ -322,30 +334,33 @@ export function useEditorToolbarPresentation(
|
|
|
322
334
|
},
|
|
323
335
|
mentionState.theme?.suggestions?.shadowColor != null
|
|
324
336
|
? {
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
337
|
+
shadowColor: mentionState.theme.suggestions.shadowColor,
|
|
338
|
+
shadowOpacity: 0.14,
|
|
339
|
+
shadowRadius: 12,
|
|
340
|
+
shadowOffset: { width: 0, height: 4 },
|
|
341
|
+
elevation: 8,
|
|
342
|
+
}
|
|
331
343
|
: null,
|
|
332
344
|
]}
|
|
333
345
|
contentContainerStyle={styles.mentionSuggestionsContent}
|
|
334
|
-
keyboardShouldPersistTaps='always'
|
|
335
|
-
|
|
346
|
+
keyboardShouldPersistTaps={'always'}
|
|
347
|
+
>
|
|
348
|
+
{mentionState.suggestions.map(suggestion => {
|
|
336
349
|
const label = resolveMentionSuggestionDisplayLabel(
|
|
337
350
|
suggestion,
|
|
338
351
|
mentionState.trigger
|
|
339
352
|
);
|
|
353
|
+
|
|
340
354
|
const optionTheme = (
|
|
341
355
|
mentionState.suggestionThemes?.[suggestion.key] ??
|
|
342
356
|
mentionState.theme
|
|
343
357
|
)?.suggestions?.option;
|
|
358
|
+
|
|
344
359
|
return (
|
|
345
360
|
<Pressable
|
|
346
361
|
key={suggestion.key}
|
|
347
362
|
testID={`editor-toolbar-mention-suggestion-${suggestion.key}`}
|
|
348
|
-
accessibilityRole='button'
|
|
363
|
+
accessibilityRole={'button'}
|
|
349
364
|
accessibilityLabel={label}
|
|
350
365
|
onPressIn={handleToolbarPressIn}
|
|
351
366
|
onPressOut={handleToolbarPressOut}
|
|
@@ -361,7 +376,8 @@ export function useEditorToolbarPresentation(
|
|
|
361
376
|
borderWidth: optionTheme?.borderWidth ?? 0,
|
|
362
377
|
borderRadius: optionTheme?.borderRadius ?? 12,
|
|
363
378
|
},
|
|
364
|
-
]}
|
|
379
|
+
]}
|
|
380
|
+
>
|
|
365
381
|
{({ pressed }) => (
|
|
366
382
|
<>
|
|
367
383
|
<Text
|
|
@@ -377,7 +393,8 @@ export function useEditorToolbarPresentation(
|
|
|
377
393
|
'#000000')
|
|
378
394
|
: (optionTheme?.textColor ?? '#000000'),
|
|
379
395
|
},
|
|
380
|
-
]}
|
|
396
|
+
]}
|
|
397
|
+
>
|
|
381
398
|
{label}
|
|
382
399
|
</Text>
|
|
383
400
|
{suggestion.subtitle ? (
|
|
@@ -390,7 +407,8 @@ export function useEditorToolbarPresentation(
|
|
|
390
407
|
optionTheme?.secondaryTextColor ??
|
|
391
408
|
'#8E8E93',
|
|
392
409
|
},
|
|
393
|
-
]}
|
|
410
|
+
]}
|
|
411
|
+
>
|
|
394
412
|
{suggestion.subtitle}
|
|
395
413
|
</Text>
|
|
396
414
|
) : null}
|
|
@@ -406,13 +424,14 @@ export function useEditorToolbarPresentation(
|
|
|
406
424
|
showsHorizontalScrollIndicator={false}
|
|
407
425
|
style={styles.scrollSection}
|
|
408
426
|
contentContainerStyle={styles.scrollContent}
|
|
409
|
-
keyboardShouldPersistTaps='always'
|
|
410
|
-
onScrollBeginDrag={() => setMenuState(null)}
|
|
427
|
+
keyboardShouldPersistTaps={'always'}
|
|
428
|
+
onScrollBeginDrag={() => setMenuState(null)}
|
|
429
|
+
>
|
|
411
430
|
{renderToolbarItems(scrollItems)}
|
|
412
431
|
</ScrollView>
|
|
413
432
|
)}
|
|
414
433
|
{endItems.length > 0 ? (
|
|
415
|
-
<View style={[styles.fixedSection, styles.endFixedSection]}>
|
|
434
|
+
<View style={[ styles.fixedSection, styles.endFixedSection ]}>
|
|
416
435
|
{renderToolbarItems(endItems)}
|
|
417
436
|
</View>
|
|
418
437
|
) : null}
|
|
@@ -421,12 +440,13 @@ export function useEditorToolbarPresentation(
|
|
|
421
440
|
<Modal
|
|
422
441
|
transparent
|
|
423
442
|
visible
|
|
424
|
-
animationType='fade'
|
|
425
|
-
onRequestClose={() => setMenuState(null)}
|
|
443
|
+
animationType={'fade'}
|
|
444
|
+
onRequestClose={() => setMenuState(null)}
|
|
445
|
+
>
|
|
426
446
|
<Pressable style={styles.menuBackdrop} onPress={() => setMenuState(null)}>
|
|
427
447
|
<View
|
|
428
448
|
ref={menuCardRef}
|
|
429
|
-
testID='editor-toolbar-menu-card'
|
|
449
|
+
testID={'editor-toolbar-menu-card'}
|
|
430
450
|
collapsable={false}
|
|
431
451
|
onLayout={handleMenuLayout}
|
|
432
452
|
style={[
|
|
@@ -437,9 +457,11 @@ export function useEditorToolbarPresentation(
|
|
|
437
457
|
backgroundColor: theme?.backgroundColor ?? TOOLBAR_BG,
|
|
438
458
|
borderColor: theme?.borderColor ?? MENU_BORDER,
|
|
439
459
|
},
|
|
440
|
-
]}
|
|
441
|
-
|
|
460
|
+
]}
|
|
461
|
+
>
|
|
462
|
+
{menuGroup.children.map(button => {
|
|
442
463
|
const visuals = resolveButtonVisuals(button);
|
|
464
|
+
|
|
443
465
|
return (
|
|
444
466
|
<Pressable
|
|
445
467
|
key={button.key}
|
|
@@ -454,21 +476,22 @@ export function useEditorToolbarPresentation(
|
|
|
454
476
|
},
|
|
455
477
|
pressed &&
|
|
456
478
|
!button.isDisabled && {
|
|
457
|
-
|
|
458
|
-
|
|
479
|
+
opacity: 0.75,
|
|
480
|
+
},
|
|
459
481
|
]}
|
|
460
|
-
accessibilityRole='button'
|
|
482
|
+
accessibilityRole={'button'}
|
|
461
483
|
accessibilityLabel={button.label}
|
|
462
484
|
accessibilityState={{
|
|
463
485
|
selected: button.isActive,
|
|
464
486
|
disabled: button.isDisabled,
|
|
465
|
-
}}
|
|
487
|
+
}}
|
|
488
|
+
>
|
|
466
489
|
<ToolbarIcon
|
|
467
490
|
icon={button.icon}
|
|
468
491
|
color={visuals.color}
|
|
469
492
|
size={visuals.iconSize}
|
|
470
493
|
/>
|
|
471
|
-
<Text style={[styles.menuLabel, { color: visuals.color }]}>
|
|
494
|
+
<Text style={[ styles.menuLabel, { color: visuals.color } ]}>
|
|
472
495
|
{button.label}
|
|
473
496
|
</Text>
|
|
474
497
|
</Pressable>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { useCallback, useContext, useEffect, useRef, useState } from 'react';
|
|
2
|
-
import { View, useWindowDimensions } from 'react-native';
|
|
1
|
+
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { type View, useWindowDimensions } from 'react-native';
|
|
3
3
|
import { DEFAULT_EDITOR_TOOLBAR_ITEMS } from './EditorToolbarItems';
|
|
4
4
|
import { type EditorToolbarProps, type ToolbarMenuState } from './EditorToolbarTypes';
|
|
5
5
|
import {
|
|
@@ -37,7 +37,7 @@ export function useEditorToolbarState({
|
|
|
37
37
|
showTopBorder,
|
|
38
38
|
preserveEditorFocus = true,
|
|
39
39
|
}: EditorToolbarProps) {
|
|
40
|
-
const marks = activeState.marks ?? {};
|
|
40
|
+
const marks = useMemo(() => activeState.marks ?? {}, [ activeState.marks ]);
|
|
41
41
|
|
|
42
42
|
const nodes = activeState.nodes ?? {};
|
|
43
43
|
|
|
@@ -59,9 +59,9 @@ export function useEditorToolbarState({
|
|
|
59
59
|
|
|
60
60
|
const { width: windowWidth, height: windowHeight } = useWindowDimensions();
|
|
61
61
|
|
|
62
|
-
const [expandedGroupKey, setExpandedGroupKey] = useState<string | null>(null);
|
|
62
|
+
const [ expandedGroupKey, setExpandedGroupKey ] = useState<string | null>(null);
|
|
63
63
|
|
|
64
|
-
const [menuState, setMenuState] = useState<ToolbarMenuState | null>(null);
|
|
64
|
+
const [ menuState, setMenuState ] = useState<ToolbarMenuState | null>(null);
|
|
65
65
|
|
|
66
66
|
const frameOwnerIdRef = useRef(frameOwnerId);
|
|
67
67
|
|
|
@@ -99,12 +99,13 @@ export function useEditorToolbarState({
|
|
|
99
99
|
|
|
100
100
|
useEffect(() => {
|
|
101
101
|
framePublisherMountedRef.current = true;
|
|
102
|
+
|
|
102
103
|
return () => {
|
|
103
104
|
framePublisherMountedRef.current = false;
|
|
104
105
|
};
|
|
105
106
|
}, []);
|
|
106
107
|
|
|
107
|
-
const isMarkActive = useCallback((mark: string) => !!marks[mark], [marks]);
|
|
108
|
+
const isMarkActive = useCallback((mark: string) => !!marks[mark], [ marks ]);
|
|
108
109
|
|
|
109
110
|
const canIndentList = !!commands['indentList'];
|
|
110
111
|
|
|
@@ -112,6 +113,7 @@ export function useEditorToolbarState({
|
|
|
112
113
|
|
|
113
114
|
const shouldRenderMentionSuggestions =
|
|
114
115
|
publishesFocusFrames && mentionState != null && mentionState.suggestions.length > 0;
|
|
116
|
+
|
|
115
117
|
return {
|
|
116
118
|
onToggleMark,
|
|
117
119
|
onToggleBold,
|