@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.
Files changed (180) hide show
  1. package/README.md +8 -116
  2. package/THIRD_PARTY_NOTICES.md +1 -1
  3. package/android/build.gradle +3 -0
  4. package/android/src/main/java/com/apollohg/editor/EditorEditTextImages.kt +25 -0
  5. package/android/src/main/java/com/apollohg/editor/EditorV2RenderValidation.kt +1 -0
  6. package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +101 -33
  7. package/android/src/main/java/com/apollohg/editor/RenderMediaSpans.kt +15 -3
  8. package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +14 -0
  9. package/android/src/main/java/com/apollohg/editor/viewer/AndroidProseLayoutEngine.kt +1 -1
  10. package/app.plugin.js +42 -39
  11. package/dist/AtomHost.js +18 -11
  12. package/dist/EditorAddon.js +4 -2
  13. package/dist/EditorAddonNormalization.js +17 -9
  14. package/dist/EditorMentionThemeNormalization.js +6 -3
  15. package/dist/EditorMentionThemeValidation.js +28 -11
  16. package/dist/EditorStyleSheetNormalization.js +85 -38
  17. package/dist/EditorStyleSheetTypes.d.ts +1 -1
  18. package/dist/EditorTheme.d.ts +4 -0
  19. package/dist/EditorTheme.js +5 -3
  20. package/dist/EditorToolbarComponent.d.ts +1 -1
  21. package/dist/EditorToolbarComponent.js +1 -1
  22. package/dist/EditorToolbarRegistry.js +5 -5
  23. package/dist/EditorToolbarTypes.d.ts +12 -12
  24. package/dist/EditorToolbarVisuals.js +1 -1
  25. package/dist/ExternalTextComposition.js +89 -43
  26. package/dist/ImageLoadingPolicy.d.ts +1 -1
  27. package/dist/NativeEditorBoundaryError.d.ts +7 -7
  28. package/dist/NativeEditorBoundaryError.js +9 -5
  29. package/dist/NativeEditorCollaborationTransport.d.ts +7 -7
  30. package/dist/NativeEditorCollaborationTransport.js +28 -15
  31. package/dist/NativeEditorCreateConfig.js +11 -6
  32. package/dist/NativeEditorCreateJson.js +51 -25
  33. package/dist/NativeEditorCreateValidation.js +11 -6
  34. package/dist/NativeEditorDocumentBridge.d.ts +1 -1
  35. package/dist/NativeEditorDocumentBridge.js +32 -18
  36. package/dist/NativeEditorDocumentHandle.d.ts +4 -4
  37. package/dist/NativeEditorDocumentHandle.js +1 -1
  38. package/dist/NativeEditorLocalAwareness.js +25 -13
  39. package/dist/NativeEditorNativeModule.d.ts +8 -1
  40. package/dist/NativeEditorNativeModule.js +7 -5
  41. package/dist/NativeEditorRenderNormalization.js +66 -25
  42. package/dist/NativeEditorResultNormalization.js +53 -29
  43. package/dist/NativeEditorTypes.d.ts +10 -10
  44. package/dist/NativeEditorV2Decimal.js +6 -3
  45. package/dist/NativeProseViewer.js +11 -3
  46. package/dist/ResourceLimits.d.ts +2 -2
  47. package/dist/ResourceLimits.js +7 -2
  48. package/dist/RichTextEditor.js +1 -1
  49. package/dist/RichTextEditorNativeTypes.d.ts +1 -1
  50. package/dist/RichTextEditorNativeView.d.ts +1 -3
  51. package/dist/RichTextEditorSerialization.js +24 -14
  52. package/dist/RichTextEditorTypes.d.ts +1 -1
  53. package/dist/YjsCollaboration.d.ts +7 -7
  54. package/dist/YjsCollaboration.js +42 -26
  55. package/dist/addons.d.ts +4 -4
  56. package/dist/addons.js +3 -3
  57. package/dist/atomInstances.js +6 -3
  58. package/dist/atomUpdates.js +7 -4
  59. package/dist/atoms.js +20 -12
  60. package/dist/attributeValidation.js +55 -24
  61. package/dist/contentExpression.js +80 -42
  62. package/dist/listMarkers.d.ts +1 -1
  63. package/dist/listMarkers.js +1 -1
  64. package/dist/schemaDefinition.d.ts +1 -1
  65. package/dist/schemaDefinition.js +23 -14
  66. package/dist/schemaDocument.js +1 -1
  67. package/dist/schemaDocumentConstruction.js +25 -14
  68. package/dist/schemaNormalization.js +51 -29
  69. package/dist/schemaPresets.d.ts +1 -1
  70. package/dist/schemaPresets.js +9 -4
  71. package/dist/schemaResolution.js +46 -25
  72. package/dist/specs/PreparedProseViewerNativeComponent.d.ts +4 -0
  73. package/dist/useEditorToolbarInteractions.d.ts +2 -2
  74. package/dist/useEditorToolbarInteractions.js +45 -23
  75. package/dist/useEditorToolbarItems.d.ts +1 -1
  76. package/dist/useEditorToolbarItems.js +4 -3
  77. package/dist/useEditorToolbarPresentation.d.ts +3 -3
  78. package/dist/useEditorToolbarPresentation.js +4 -4
  79. package/dist/useEditorToolbarState.d.ts +1 -1
  80. package/dist/useEditorToolbarState.js +1 -1
  81. package/dist/useFocusPreservingFrames.js +11 -8
  82. package/dist/useNativeEditor.d.ts +1 -1
  83. package/dist/useNativeEditor.js +21 -11
  84. package/dist/useRichTextEditorCommands.d.ts +2 -2
  85. package/dist/useRichTextEditorCommands.js +59 -29
  86. package/dist/useRichTextEditorEvents.d.ts +3 -3
  87. package/dist/useRichTextEditorEvents.js +75 -32
  88. package/dist/useRichTextEditorMentions.d.ts +4 -4
  89. package/dist/useRichTextEditorMentions.js +48 -29
  90. package/dist/useRichTextEditorPresentation.d.ts +4 -4
  91. package/dist/useRichTextEditorPresentation.js +25 -23
  92. package/dist/useRichTextEditorState.d.ts +3 -3
  93. package/dist/useRichTextEditorState.js +20 -15
  94. package/dist/useRichTextEditorUpdates.d.ts +1 -1
  95. package/dist/useRichTextEditorUpdates.js +99 -41
  96. package/dist/useViewerAtoms.js +63 -36
  97. package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
  98. package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
  99. package/ios/EditorInteractionOverlays.swift +114 -122
  100. package/ios/EditorLayoutManager.swift +13 -3
  101. package/ios/EditorStyleSheet.swift +12 -1
  102. package/ios/EditorTextView+Images.swift +32 -10
  103. package/ios/EditorTextView.swift +1 -1
  104. package/ios/EditorV2Adapter+RenderValidation.swift +2 -2
  105. package/ios/NativeToolbar+ButtonStyle.swift +17 -5
  106. package/ios/NativeToolbar+Diagnostics.swift +15 -0
  107. package/ios/RenderBridge+Atoms.swift +0 -1
  108. package/ios/RenderBridge+Attachments.swift +0 -29
  109. package/ios/RichTextEditorView.swift +4 -12
  110. package/ios/Viewer/CoreTextProseLayoutEngine+AttributedText.swift +3 -2
  111. package/package.json +11 -3
  112. package/react-native.config.js +1 -1
  113. package/rust/android/arm64-v8a/libeditor_core.so +0 -0
  114. package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
  115. package/rust/android/x86/libeditor_core.so +0 -0
  116. package/rust/android/x86_64/libeditor_core.so +0 -0
  117. package/src/AtomHost.tsx +58 -28
  118. package/src/EditorAddon.ts +18 -8
  119. package/src/EditorAddonNormalization.ts +40 -11
  120. package/src/EditorMentionThemeNormalization.ts +15 -4
  121. package/src/EditorMentionThemeValidation.ts +51 -14
  122. package/src/EditorStyleSheet.ts +1 -0
  123. package/src/EditorStyleSheetNormalization.ts +195 -64
  124. package/src/EditorStyleSheetTypes.ts +1 -1
  125. package/src/EditorTheme.ts +13 -5
  126. package/src/EditorToolbarComponent.tsx +2 -1
  127. package/src/EditorToolbarRegistry.tsx +23 -13
  128. package/src/EditorToolbarTypes.tsx +12 -12
  129. package/src/EditorToolbarVisuals.tsx +6 -3
  130. package/src/EditorUpdateRevision.ts +1 -0
  131. package/src/ExternalTextComposition.ts +259 -49
  132. package/src/ImageLoadingPolicy.ts +3 -1
  133. package/src/NativeEditorBoundaryError.ts +38 -11
  134. package/src/NativeEditorCollaborationTransport.ts +108 -27
  135. package/src/NativeEditorCreateConfig.ts +92 -19
  136. package/src/NativeEditorCreateJson.ts +163 -26
  137. package/src/NativeEditorCreateValidation.ts +33 -12
  138. package/src/NativeEditorDocumentBridge.ts +111 -19
  139. package/src/NativeEditorDocumentHandle.ts +9 -4
  140. package/src/NativeEditorLocalAwareness.ts +70 -15
  141. package/src/NativeEditorNativeModule.ts +13 -9
  142. package/src/NativeEditorRenderNormalization.ts +128 -29
  143. package/src/NativeEditorResultNormalization.ts +143 -32
  144. package/src/NativeEditorTypes.ts +12 -11
  145. package/src/NativeEditorV2Decimal.ts +12 -3
  146. package/src/NativeProseViewer.tsx +46 -16
  147. package/src/ResourceLimits.ts +15 -8
  148. package/src/RichTextEditor.tsx +3 -1
  149. package/src/RichTextEditorNativeTypes.tsx +1 -1
  150. package/src/RichTextEditorNativeView.tsx +2 -8
  151. package/src/RichTextEditorSerialization.tsx +70 -24
  152. package/src/RichTextEditorTypes.tsx +1 -1
  153. package/src/YjsCollaboration.ts +143 -47
  154. package/src/addons.ts +12 -8
  155. package/src/atomInstances.ts +25 -5
  156. package/src/atomUpdates.ts +23 -6
  157. package/src/atoms.ts +85 -18
  158. package/src/attributeValidation.ts +97 -33
  159. package/src/contentExpression.ts +218 -63
  160. package/src/listMarkers.ts +1 -1
  161. package/src/schemaDefinition.ts +82 -20
  162. package/src/schemaDocument.ts +15 -1
  163. package/src/schemaDocumentConstruction.ts +80 -16
  164. package/src/schemaNormalization.ts +155 -41
  165. package/src/schemaPresets.ts +37 -31
  166. package/src/schemaResolution.ts +126 -27
  167. package/src/specs/PreparedProseViewerNativeComponent.ts +4 -2
  168. package/src/useEditorToolbarInteractions.tsx +85 -36
  169. package/src/useEditorToolbarItems.tsx +50 -22
  170. package/src/useEditorToolbarPresentation.tsx +81 -58
  171. package/src/useEditorToolbarState.tsx +8 -6
  172. package/src/useFocusPreservingFrames.ts +43 -15
  173. package/src/useNativeEditor.ts +111 -51
  174. package/src/useRichTextEditorCommands.tsx +127 -54
  175. package/src/useRichTextEditorEvents.tsx +144 -42
  176. package/src/useRichTextEditorMentions.tsx +118 -37
  177. package/src/useRichTextEditorPresentation.tsx +128 -114
  178. package/src/useRichTextEditorState.tsx +76 -40
  179. package/src/useRichTextEditorUpdates.tsx +207 -56
  180. 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
- ? `${prefix}mark:${item.mark}:${index}`
181
- : item.type === 'link'
182
- ? `${prefix}link:${index}`
183
- : item.type === 'image'
184
- ? `${prefix}image:${index}`
185
- : item.type === 'heading'
186
- ? `${prefix}heading:${item.level}:${index}`
187
- : item.type === 'blockquote'
188
- ? `${prefix}blockquote:${index}`
189
- : item.type === 'list'
190
- ? `${prefix}list:${item.listType}:${index}`
191
- : item.type === 'command'
192
- ? `${prefix}command:${item.command}:${index}`
193
- : item.type === 'node'
194
- ? `${prefix}node:${item.nodeType}:${index}`
195
- : `${prefix}action:${item.key}:${index}`,
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((child) => child.isActive) || isExpanded || isMenuOpen,
359
- isDisabled: children.every((child) => child.isDisabled),
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
- MENU_MARGIN,
110
- Math.min(
111
- menuState.y + menuState.height + 8,
112
- windowHeight - menuHeight - MENU_MARGIN
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
- MENU_MARGIN,
121
- Math.min(
122
- menuState.x + menuState.width - MENU_WIDTH,
123
- windowWidth - MENU_WIDTH - MENU_MARGIN
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
- ? activeBackgroundColor
152
- : backgroundColor,
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
- : (node) => {
188
- if (node) {
189
- groupButtonRefs.current.set(anchorGroupKey, node);
190
- } else {
191
- groupButtonRefs.current.delete(anchorGroupKey);
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((item) => {
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
- shadowColor: mentionState.theme.suggestions.shadowColor,
326
- shadowOpacity: 0.14,
327
- shadowRadius: 12,
328
- shadowOffset: { width: 0, height: 4 },
329
- elevation: 8,
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
- {mentionState.suggestions.map((suggestion) => {
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
- {menuGroup.children.map((button) => {
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
- opacity: 0.75,
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,