@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
@@ -39,13 +39,18 @@ export function admitSchemaCollections(
39
39
 
40
40
  for (let nodeIndex = 0; nodeIndex < schema.nodes.length; nodeIndex += 1) {
41
41
  const node = schema.nodes[nodeIndex];
42
- if (node == null || typeof node !== 'object') return null;
42
+
43
+ if (node == null || typeof node !== 'object') {
44
+ return null;
45
+ }
46
+
43
47
  const groups: string[] = [];
48
+
44
49
  if (typeof node.group === 'string') {
45
50
  if (
46
51
  !forEachGroupToken(
47
52
  node.group,
48
- (group) => groups.push(group),
53
+ group => groups.push(group),
49
54
  () => consumeSchemaWork(budget),
50
55
  () => consumeSchemaWork(budget)
51
56
  )
@@ -53,16 +58,26 @@ export function admitSchemaCollections(
53
58
  throw schemaBoundaryError(budget.limit, budget.limit + 1);
54
59
  }
55
60
  }
61
+
56
62
  const attrs = collectOwnAttrs(node.attrs, budget);
57
- if (attrs == null) return null;
63
+
64
+ if (attrs == null) {
65
+ return null;
66
+ }
67
+
58
68
  const projectionAttrs = collectOwnAttrs(node.json?.attrs, budget);
59
- if (projectionAttrs == null) return null;
69
+
70
+ if (projectionAttrs == null) {
71
+ return null;
72
+ }
73
+
60
74
  if (
61
75
  node.json != null &&
62
76
  ((typeof node.json.type === 'string' &&
63
77
  !consumeSchemaStringWork(budget, node.json.type)) ||
64
- projectionAttrs.some(([name, rawValue]) => {
78
+ projectionAttrs.some(([ name, rawValue ]) => {
65
79
  const value: unknown = rawValue;
80
+
66
81
  return (
67
82
  !consumeSchemaStringWork(budget, name) ||
68
83
  (typeof value === 'string'
@@ -73,21 +88,32 @@ export function admitSchemaCollections(
73
88
  ) {
74
89
  throw schemaBoundaryError(budget.limit, budget.limit + 1);
75
90
  }
91
+
76
92
  if (budget.exhausted) {
77
93
  throw schemaBoundaryError(budget.limit, budget.limit + 1);
78
94
  }
95
+
79
96
  groupsByNode.set(node, groups);
80
97
  attrsByNode.set(node, attrs);
81
98
  }
82
99
 
83
100
  for (let markIndex = 0; markIndex < schema.marks.length; markIndex += 1) {
84
101
  const mark = schema.marks[markIndex];
102
+
85
103
  if (!consumeSchemaWork(budget)) {
86
104
  throw schemaBoundaryError(budget.limit, budget.limit + 1);
87
105
  }
88
- if (mark == null || typeof mark !== 'object') return null;
106
+
107
+ if (mark == null || typeof mark !== 'object') {
108
+ return null;
109
+ }
110
+
89
111
  const attrs = collectOwnAttrs(mark.attrs, budget);
90
- if (attrs == null) return null;
112
+
113
+ if (attrs == null) {
114
+ return null;
115
+ }
116
+
91
117
  if (budget.exhausted) {
92
118
  throw schemaBoundaryError(budget.limit, budget.limit + 1);
93
119
  }
@@ -101,45 +127,70 @@ export function resolveDocumentSchema(
101
127
  schema?: SchemaDefinition,
102
128
  limits?: DocumentDescriptorLimits
103
129
  ): SchemaDefinition {
104
- if (schema == null) return defaultSchema;
105
- if (!Array.isArray(schema.nodes)) return defaultSchema;
130
+ if (schema == null) {
131
+ return defaultSchema;
132
+ }
133
+
134
+ if (!Array.isArray(schema.nodes)) {
135
+ return defaultSchema;
136
+ }
137
+
106
138
  if (!Array.isArray(schema.marks)) {
107
139
  schema = { ...schema, marks: [] };
108
140
  }
109
141
 
110
142
  const resolvedLimits = resolveDescriptorLimits(limits);
143
+
111
144
  if (schema.nodes.length > resolvedLimits.maxSchemaNodes) {
112
145
  throw schemaBoundaryError(resolvedLimits.maxSchemaNodes, schema.nodes.length);
113
146
  }
147
+
114
148
  let expressionBytes = 0;
149
+
115
150
  for (let nodeIndex = 0; nodeIndex < schema.nodes.length; nodeIndex += 1) {
116
151
  const node = schema.nodes[nodeIndex];
152
+
117
153
  if (node != null && typeof node.content === 'string') {
118
154
  expressionBytes += utf8ByteLengthUpTo(
119
155
  node.content,
120
156
  resolvedLimits.maxSchemaExpressionBytes - expressionBytes
121
157
  );
158
+
122
159
  if (expressionBytes > resolvedLimits.maxSchemaExpressionBytes) {
123
160
  throw schemaBoundaryError(resolvedLimits.maxSchemaExpressionBytes, expressionBytes);
124
161
  }
125
162
  }
126
163
  }
164
+
127
165
  const schemaBudget = createSchemaWorkBudget(resolvedLimits);
128
- if (admitSchemaCollections(schema, schemaBudget) == null) return defaultSchema;
166
+
167
+ if (admitSchemaCollections(schema, schemaBudget) == null) {
168
+ return defaultSchema;
169
+ }
170
+
129
171
  const normalizedSchema = normalizeSchemaDefinition(schema, schemaBudget);
130
- if (normalizedSchema == null) return defaultSchema;
172
+
173
+ if (normalizedSchema == null) {
174
+ return defaultSchema;
175
+ }
176
+
131
177
  schema = normalizedSchema;
178
+
132
179
  const admittedCollections = admitSchemaCollections(schema, {
133
180
  limit: Number.MAX_SAFE_INTEGER,
134
181
  work: 0,
135
182
  exhausted: false,
136
183
  });
137
- if (admittedCollections == null) return defaultSchema;
184
+
185
+ if (admittedCollections == null) {
186
+ return defaultSchema;
187
+ }
138
188
 
139
189
  const nodeNames = new Set<string>();
140
190
  const markNames = new Set<string>();
141
191
  let docRoles = 0;
142
192
  let textRoles = 0;
193
+
143
194
  for (const node of schema.nodes) {
144
195
  if (
145
196
  node == null ||
@@ -151,10 +202,18 @@ export function resolveDocumentSchema(
151
202
  ) {
152
203
  return defaultSchema;
153
204
  }
205
+
154
206
  nodeNames.add(node.name);
155
- if (node.role === 'doc') docRoles += 1;
156
- if (node.role === 'text') textRoles += 1;
207
+
208
+ if (node.role === 'doc') {
209
+ docRoles += 1;
210
+ }
211
+
212
+ if (node.role === 'text') {
213
+ textRoles += 1;
214
+ }
157
215
  }
216
+
158
217
  for (const mark of schema.marks) {
159
218
  if (
160
219
  mark == null ||
@@ -164,29 +223,42 @@ export function resolveDocumentSchema(
164
223
  ) {
165
224
  return defaultSchema;
166
225
  }
226
+
167
227
  markNames.add(mark.name);
168
228
  }
169
- if (docRoles !== 1 || textRoles !== 1) return defaultSchema;
229
+
230
+ if (docRoles !== 1 || textRoles !== 1) {
231
+ return defaultSchema;
232
+ }
170
233
 
171
234
  const groups = new Set<string>();
172
235
  const nodesBySymbol = new Map<string, NodeSpec[]>();
236
+
173
237
  const addCandidate = (symbol: string, node: NodeSpec): void => {
174
238
  const candidates = nodesBySymbol.get(symbol);
175
- if (candidates) candidates.push(node);
176
- else nodesBySymbol.set(symbol, [node]);
239
+
240
+ if (candidates) {
241
+ candidates.push(node);
242
+ } else {
243
+ nodesBySymbol.set(symbol, [ node ]);
244
+ }
177
245
  };
246
+
178
247
  for (const node of schema.nodes) {
179
248
  addCandidate(node.name, node);
249
+
180
250
  for (const group of admittedCollections.groupsByNode.get(node) ?? []) {
181
251
  groups.add(group);
182
252
  addCandidate(group, node);
183
253
  }
184
254
  }
255
+
185
256
  for (const node of schema.nodes) {
186
257
  const symbols = contentExpressionSymbols(node.content);
258
+
187
259
  if (
188
260
  symbols == null ||
189
- symbols.some((symbol) => !nodeNames.has(symbol) && !groups.has(symbol))
261
+ symbols.some(symbol => !nodeNames.has(symbol) && !groups.has(symbol))
190
262
  ) {
191
263
  return defaultSchema;
192
264
  }
@@ -194,49 +266,76 @@ export function resolveDocumentSchema(
194
266
 
195
267
  const generatable = new Set<string>();
196
268
  const consumeWork = (): boolean => consumeSchemaWork(schemaBudget);
269
+
197
270
  const contentIsConstructible = (node: NodeSpec): boolean => {
198
- if (!consumeWork()) return false;
271
+ if (!consumeWork()) {
272
+ return false;
273
+ }
274
+
199
275
  return (
200
276
  minimalContentMatch(
201
277
  node.content,
202
- (symbol) => {
278
+ symbol => {
203
279
  const candidates = nodesBySymbol.get(symbol) ?? [];
280
+
204
281
  for (const candidate of candidates) {
205
- if (!consumeWork()) return undefined;
206
- if (generatable.has(candidate.name)) return { type: candidate.name };
282
+ if (!consumeWork()) {
283
+ return undefined;
284
+ }
285
+
286
+ if (generatable.has(candidate.name)) {
287
+ return { type: candidate.name };
288
+ }
207
289
  }
290
+
208
291
  return undefined;
209
292
  },
210
293
  consumeWork
211
294
  ) != null
212
295
  );
213
296
  };
297
+
214
298
  while (true) {
215
299
  const before = generatable.size;
300
+
216
301
  for (const node of schema.nodes) {
217
302
  const hasRequiredAttrs = (admittedCollections.attrsByNode.get(node) ?? []).some(
218
- ([, attr]) => attr.default === undefined
303
+ ([ , attr ]) => attr.default === undefined
219
304
  );
305
+
220
306
  if (node.role !== 'text' && !hasRequiredAttrs && contentIsConstructible(node)) {
221
307
  generatable.add(node.name);
222
308
  }
223
309
  }
224
- if (generatable.size === before) break;
310
+
311
+ if (generatable.size === before) {
312
+ break;
313
+ }
225
314
  }
315
+
226
316
  if (schemaBudget.exhausted) {
227
317
  throw schemaBoundaryError(schemaBudget.limit, schemaBudget.limit + 1);
228
318
  }
229
- const hasUnconstructibleNode = schema.nodes.some((node) => !contentIsConstructible(node));
319
+
320
+ const hasUnconstructibleNode = schema.nodes.some(node => !contentIsConstructible(node));
321
+
230
322
  if (schemaBudget.exhausted) {
231
323
  throw schemaBoundaryError(schemaBudget.limit, schemaBudget.limit + 1);
232
324
  }
233
- if (hasUnconstructibleNode) return defaultSchema;
325
+
326
+ if (hasUnconstructibleNode) {
327
+ return defaultSchema;
328
+ }
234
329
 
235
330
  try {
236
331
  constructDefaultEmptyDocument(schema, resolvedLimits, admittedCollections);
332
+
237
333
  return schema;
238
334
  } catch (error) {
239
- if (error instanceof NativeEditorBoundaryError) throw error;
335
+ if (error instanceof NativeEditorBoundaryError) {
336
+ throw error;
337
+ }
338
+
240
339
  return defaultSchema;
241
340
  }
242
341
  }
@@ -18,8 +18,10 @@ export interface NativeProps extends ViewProps {
18
18
  mentionInteractionsEnabled?: WithDefault<boolean, false>;
19
19
  fontEnvironmentRevision: Int32;
20
20
  onPressLink?: DirectEventHandler<{ href: string; text: string }>;
21
- // React Native numbers are IEEE-754 doubles, which exactly represent every
22
- // UInt32 document position. Int32 would corrupt positions above 2^31 - 1.
21
+ /**
22
+ * React Native numbers are IEEE-754 doubles, which exactly represent every
23
+ * UInt32 document position. Int32 would corrupt positions above 2^31 - 1.
24
+ */
23
25
  onPressMention?: DirectEventHandler<{ docPos: Double; label: string; attrsJson: string }>;
24
26
  onAtomLayout?: DirectEventHandler<{
25
27
  generation: string;
@@ -1,7 +1,7 @@
1
1
  import { useCallback, useEffect } from 'react';
2
2
  import { Keyboard } from 'react-native';
3
- import { useEditorToolbarState } from './useEditorToolbarState';
4
- import { useEditorToolbarItems } from './useEditorToolbarItems';
3
+ import { type useEditorToolbarState } from './useEditorToolbarState';
4
+ import { type useEditorToolbarItems } from './useEditorToolbarItems';
5
5
  import {
6
6
  unregisterEditorToolbarFrame,
7
7
  registerEditorToolbarFrame,
@@ -80,10 +80,12 @@ export function useEditorToolbarInteractions(
80
80
  const publishToolbarFrame = useCallback(() => {
81
81
  const registrationId = registrationIdRef.current;
82
82
  const toolbar = rootRef.current;
83
+
83
84
  if (!publishesFocusFrames || registrationId == null || !toolbar) {
84
85
  if (registrationId != null) {
85
86
  unregisterEditorToolbarFrame(registrationId);
86
87
  }
88
+
87
89
  return;
88
90
  }
89
91
 
@@ -92,6 +94,7 @@ export function useEditorToolbarInteractions(
92
94
  }
93
95
 
94
96
  const measuredFrameOwnerId = frameOwnerId;
97
+
95
98
  toolbar.measureInWindow((x, y, width, height) => {
96
99
  if (
97
100
  !framePublisherMountedRef.current ||
@@ -100,21 +103,30 @@ export function useEditorToolbarInteractions(
100
103
  ) {
101
104
  return;
102
105
  }
106
+
103
107
  registerEditorToolbarFrame(
104
108
  registrationId,
105
109
  { x, y, width, height },
106
110
  measuredFrameOwnerId
107
111
  );
108
112
  });
109
- }, [frameOwnerId, publishesFocusFrames]);
113
+ }, [ frameOwnerId,
114
+ frameOwnerIdRef,
115
+ framePublisherMountedRef,
116
+ publishesFocusFrames,
117
+ publishesFocusFramesRef,
118
+ registrationIdRef,
119
+ rootRef ]);
110
120
 
111
121
  const publishMenuFrame = useCallback(() => {
112
122
  const registrationId = menuRegistrationIdRef.current;
113
123
  const menuCard = menuCardRef.current;
124
+
114
125
  if (!publishesFocusFrames || menuState == null || registrationId == null || !menuCard) {
115
126
  if (registrationId != null) {
116
127
  unregisterEditorToolbarFrame(registrationId);
117
128
  }
129
+
118
130
  return;
119
131
  }
120
132
 
@@ -124,6 +136,7 @@ export function useEditorToolbarInteractions(
124
136
 
125
137
  const measuredFrameOwnerId = frameOwnerId;
126
138
  const measuredMenuState = menuState;
139
+
127
140
  menuCard.measureInWindow((x, y, width, height) => {
128
141
  if (
129
142
  !framePublisherMountedRef.current ||
@@ -133,25 +146,34 @@ export function useEditorToolbarInteractions(
133
146
  ) {
134
147
  return;
135
148
  }
149
+
136
150
  registerEditorToolbarFrame(
137
151
  registrationId,
138
152
  { x, y, width, height },
139
153
  measuredFrameOwnerId
140
154
  );
141
155
  });
142
- }, [frameOwnerId, menuState, publishesFocusFrames]);
156
+ }, [ frameOwnerId,
157
+ frameOwnerIdRef,
158
+ framePublisherMountedRef,
159
+ menuCardRef,
160
+ menuRegistrationIdRef,
161
+ menuState,
162
+ menuStateRef,
163
+ publishesFocusFrames,
164
+ publishesFocusFramesRef ]);
143
165
 
144
166
  const publishToolbarFrames = useCallback(() => {
145
167
  publishToolbarFrame();
146
168
  publishMenuFrame();
147
- }, [publishMenuFrame, publishToolbarFrame]);
169
+ }, [ publishMenuFrame, publishToolbarFrame ]);
148
170
 
149
171
  const cancelScheduledFramePublishes = useCallback(() => {
150
- framePublishAnimationFramesRef.current.forEach((frame) => cancelAnimationFrame(frame));
172
+ framePublishAnimationFramesRef.current.forEach(frame => cancelAnimationFrame(frame));
151
173
  framePublishAnimationFramesRef.current = [];
152
- framePublishTimeoutsRef.current.forEach((timeout) => clearTimeout(timeout));
174
+ framePublishTimeoutsRef.current.forEach(timeout => clearTimeout(timeout));
153
175
  framePublishTimeoutsRef.current = [];
154
- }, []);
176
+ }, [ framePublishAnimationFramesRef, framePublishTimeoutsRef ]);
155
177
 
156
178
  const scheduleToolbarFramePublish = useCallback(() => {
157
179
  if (!publishesFocusFrames) {
@@ -162,32 +184,39 @@ export function useEditorToolbarInteractions(
162
184
  publishToolbarFrames();
163
185
 
164
186
  framePublishAnimationFramesRef.current.push(requestAnimationFrame(publishToolbarFrames));
165
- KEYBOARD_FRAME_REMEASURE_DELAYS_MS.forEach((delay) => {
187
+
188
+ KEYBOARD_FRAME_REMEASURE_DELAYS_MS.forEach(delay => {
166
189
  framePublishTimeoutsRef.current.push(setTimeout(publishToolbarFrames, delay));
167
190
  });
168
- }, [cancelScheduledFramePublishes, publishesFocusFrames, publishToolbarFrames]);
191
+ }, [ publishesFocusFrames,
192
+ cancelScheduledFramePublishes,
193
+ publishToolbarFrames,
194
+ framePublishAnimationFramesRef,
195
+ framePublishTimeoutsRef ]);
169
196
 
170
197
  const handleToolbarLayout = useCallback(() => {
171
198
  requestAnimationFrame(publishToolbarFrame);
172
- }, [publishToolbarFrame]);
199
+ }, [ publishToolbarFrame ]);
173
200
 
174
201
  const handleMenuLayout = useCallback(() => {
175
202
  requestAnimationFrame(publishMenuFrame);
176
- }, [publishMenuFrame]);
203
+ }, [ publishMenuFrame ]);
177
204
 
178
205
  useEffect(() => {
179
206
  if (!publishesFocusFrames) {
180
207
  const registrationId = registrationIdRef.current;
208
+
181
209
  if (registrationId != null) {
182
210
  unregisterEditorToolbarFrame(registrationId);
183
211
  }
212
+
184
213
  return;
185
214
  }
186
215
 
187
216
  const frame = requestAnimationFrame(publishToolbarFrame);
217
+
188
218
  return () => cancelAnimationFrame(frame);
189
- }, [
190
- expandedGroupKey,
219
+ }, [ expandedGroupKey,
191
220
  menuState?.groupKey,
192
221
  publishesFocusFrames,
193
222
  publishToolbarFrame,
@@ -196,42 +225,55 @@ export function useEditorToolbarInteractions(
196
225
  endItems.length,
197
226
  windowHeight,
198
227
  windowWidth,
199
- ]);
228
+ registrationIdRef ]);
200
229
 
201
230
  useEffect(() => {
202
231
  if (!publishesFocusFrames || menuState == null) {
203
232
  const registrationId = menuRegistrationIdRef.current;
233
+
204
234
  if (registrationId != null) {
205
235
  unregisterEditorToolbarFrame(registrationId);
206
236
  }
237
+
207
238
  return;
208
239
  }
209
240
 
210
241
  const frame = requestAnimationFrame(publishMenuFrame);
242
+
211
243
  return () => cancelAnimationFrame(frame);
212
- }, [menuState, publishesFocusFrames, publishMenuFrame, windowHeight, windowWidth]);
244
+ }, [ menuState,
245
+ publishesFocusFrames,
246
+ publishMenuFrame,
247
+ windowHeight,
248
+ windowWidth,
249
+ menuRegistrationIdRef ]);
213
250
 
214
251
  useEffect(() => {
215
252
  const registrationId = registrationIdRef.current;
216
253
  const menuRegistrationId = menuRegistrationIdRef.current;
254
+
217
255
  return () => {
218
256
  cancelScheduledFramePublishes();
257
+
219
258
  if (toolbarInteractionActiveRef.current) {
220
259
  toolbarInteractionActiveRef.current = false;
221
260
  endEditorToolbarInteraction();
222
261
  }
262
+
223
263
  if (registrationId != null) {
224
264
  unregisterEditorToolbarFrame(registrationId);
225
265
  }
266
+
226
267
  if (menuRegistrationId != null) {
227
268
  unregisterEditorToolbarFrame(menuRegistrationId);
228
269
  }
229
270
  };
230
- }, [cancelScheduledFramePublishes]);
271
+ }, [ cancelScheduledFramePublishes, menuRegistrationIdRef, registrationIdRef, toolbarInteractionActiveRef ]);
231
272
 
232
273
  useEffect(() => {
233
274
  if (!publishesFocusFrames) {
234
275
  cancelScheduledFramePublishes();
276
+
235
277
  return;
236
278
  }
237
279
 
@@ -242,81 +284,88 @@ export function useEditorToolbarInteractions(
242
284
  ];
243
285
 
244
286
  return () => {
245
- subscriptions.forEach((subscription) => subscription.remove());
287
+ subscriptions.forEach(subscription => subscription.remove());
246
288
  cancelScheduledFramePublishes();
247
289
  };
248
- }, [cancelScheduledFramePublishes, publishesFocusFrames, scheduleToolbarFramePublish]);
290
+ }, [ cancelScheduledFramePublishes, publishesFocusFrames, scheduleToolbarFramePublish ]);
249
291
 
250
292
  useEffect(() => {
251
293
  if (expandedGroupKey != null && !groupsByKey.has(expandedGroupKey)) {
252
294
  setExpandedGroupKey(null);
253
295
  }
254
- }, [expandedGroupKey, groupsByKey]);
296
+ }, [ expandedGroupKey, groupsByKey, setExpandedGroupKey ]);
255
297
 
256
298
  useEffect(() => {
257
299
  if (menuState != null && !groupsByKey.has(menuState.groupKey)) {
258
300
  setMenuState(null);
259
301
  }
260
- }, [groupsByKey, menuState]);
302
+ }, [ groupsByKey, menuState, setMenuState ]);
261
303
 
262
304
  useEffect(() => {
263
305
  if (shouldRenderMentionSuggestions) {
264
306
  setExpandedGroupKey(null);
265
307
  setMenuState(null);
266
308
  }
267
- }, [shouldRenderMentionSuggestions]);
309
+ }, [ setExpandedGroupKey, setMenuState, shouldRenderMentionSuggestions ]);
268
310
 
269
311
  const handleButtonPress = useCallback((button: ToolbarButton) => {
270
312
  button.action();
313
+
271
314
  if (button.groupKey) {
272
- setExpandedGroupKey((current) => (current === button.groupKey ? null : current));
315
+ setExpandedGroupKey(current => (current === button.groupKey ? null : current));
273
316
  }
317
+
274
318
  setMenuState(null);
275
- }, []);
319
+ }, [ setExpandedGroupKey, setMenuState ]);
276
320
 
277
321
  const handleToolbarPressIn = useCallback(() => {
278
322
  if (preserveEditorFocus && !toolbarInteractionActiveRef.current) {
279
323
  toolbarInteractionActiveRef.current = true;
280
324
  beginEditorToolbarInteraction();
281
325
  }
282
- }, [preserveEditorFocus]);
326
+ }, [ preserveEditorFocus, toolbarInteractionActiveRef ]);
283
327
 
284
328
  const handleToolbarPressOut = useCallback(() => {
285
329
  if (preserveEditorFocus && toolbarInteractionActiveRef.current) {
286
330
  toolbarInteractionActiveRef.current = false;
287
331
  endEditorToolbarInteraction();
288
332
  }
289
- }, [preserveEditorFocus]);
333
+ }, [ preserveEditorFocus, toolbarInteractionActiveRef ]);
290
334
 
291
335
  const handleGroupPress = useCallback((group: ToolbarGroupButton) => {
292
336
  if (group.isDisabled) {
293
337
  return;
294
338
  }
339
+
295
340
  if (group.presentation === 'expand') {
296
341
  setMenuState(null);
297
- setExpandedGroupKey((current) => (current === group.key ? null : group.key));
342
+ setExpandedGroupKey(current => (current === group.key ? null : group.key));
343
+
298
344
  return;
299
345
  }
300
346
 
301
347
  const anchor = groupButtonRefs.current.get(group.key);
348
+
302
349
  if (!anchor) {
303
350
  return;
304
351
  }
352
+
305
353
  anchor.measureInWindow((x, y, width, height) => {
306
354
  setExpandedGroupKey(null);
307
- setMenuState((current) =>
355
+
356
+ setMenuState(current =>
308
357
  current?.groupKey === group.key
309
358
  ? null
310
359
  : {
311
- groupKey: group.key,
312
- x,
313
- y,
314
- width,
315
- height,
316
- }
317
- );
360
+ groupKey: group.key,
361
+ x,
362
+ y,
363
+ width,
364
+ height,
365
+ });
318
366
  });
319
- }, []);
367
+ }, [ groupButtonRefs, setExpandedGroupKey, setMenuState ]);
368
+
320
369
  return {
321
370
  handleToolbarPressIn,
322
371
  handleToolbarPressOut,