@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
@@ -56,9 +56,14 @@ function parsePositions(
56
56
  snapshot: string
57
57
  ): AtomPosition[] {
58
58
  const values: unknown = JSON.parse(json);
59
- if (!Array.isArray(values)) throw new Error('Atom positions must be an array.');
59
+
60
+ if (!Array.isArray(values)) {
61
+ throw new Error('Atom positions must be an array.');
62
+ }
63
+
60
64
  const seen = new Set<number>();
61
65
  const identities = new Set<string>();
66
+
62
67
  return values.map((value: unknown) => {
63
68
  if (
64
69
  !isRecord(value) ||
@@ -68,8 +73,8 @@ function parsePositions(
68
73
  Number(value.docPos) < 0 ||
69
74
  Number(value.docPos) > 0xffffffff ||
70
75
  typeof value.attrsJson !== 'string' ||
71
- !['x', 'y', 'width', 'height'].every(
72
- (key) => typeof value[key] === 'number' && Number.isFinite(value[key])
76
+ ![ 'x', 'y', 'width', 'height' ].every(
77
+ key => typeof value[key] === 'number' && Number.isFinite(value[key])
73
78
  ) ||
74
79
  Number(value.width) <= 0 ||
75
80
  Number(value.height) < 0 ||
@@ -77,20 +82,33 @@ function parsePositions(
77
82
  ) {
78
83
  throw new Error('Invalid prepared atom position.');
79
84
  }
85
+
80
86
  const attrs: unknown = JSON.parse(value.attrsJson);
81
- if (!isRecord(attrs)) throw new Error('Atom attributes must be a JSON object.');
87
+
88
+ if (!isRecord(attrs)) {
89
+ throw new Error('Atom attributes must be a JSON object.');
90
+ }
91
+
82
92
  const idAttribute = definitions.get(value.nodeType)?.idAttribute;
83
93
  const atomId = idAttribute == null ? undefined : attrs[idAttribute];
84
- if (idAttribute != null && (typeof atomId !== 'string' || atomId.length === 0))
94
+
95
+ if (idAttribute != null && (typeof atomId !== 'string' || atomId.length === 0)) {
85
96
  throw new Error('Atom identity must be a non-empty string.');
97
+ }
98
+
86
99
  const key =
87
100
  typeof atomId === 'string'
88
- ? JSON.stringify([value.nodeType, atomId])
89
- : `${snapshot}:${value.docPos}:${value.nodeType}`;
90
- if (identities.has(key)) throw new Error('Duplicate atom identity.');
101
+ ? JSON.stringify([ value.nodeType, atomId ])
102
+ : `${snapshot}:${String(value.docPos)}:${value.nodeType}`;
103
+
104
+ if (identities.has(key)) {
105
+ throw new Error('Duplicate atom identity.');
106
+ }
107
+
91
108
  identities.add(key);
92
109
  seen.add(Number(value.docPos));
93
- return { ...value, attrs, key, atomId } as unknown as AtomPosition;
110
+
111
+ return { ...value, attrs, key, atomId } as AtomPosition;
94
112
  });
95
113
  }
96
114
 
@@ -113,49 +131,69 @@ export function useViewerAtoms({
113
131
  onUpdateAtomAttrs?: UpdateHandler;
114
132
  onError?: (event: RichTextViewerErrorEvent) => void;
115
133
  }) {
116
- const [width, setWidth] = useState<number | null>(null);
117
- const snapshot = useMemo(() => String(++nextGeneration), [identity]);
134
+ const [ width, setWidth ] = useState<number | null>(null);
135
+ const snapshot = useMemo(() => ({ identity, value: String(++nextGeneration) }), [ identity ]).value;
118
136
  const serializedAtoms = serializeEditorAtoms(atoms);
119
- const identifiers = JSON.stringify((atoms ?? []).map((atom) => [atom.name, atom.idAttribute]));
137
+ const identifiers = JSON.stringify((atoms ?? []).map(atom => [ atom.name, atom.idAttribute ]));
138
+
120
139
  const generation = useMemo(
121
- () => String(++nextGeneration),
122
- [identity, identifiers, serializedAtoms, width]
123
- );
140
+ () => ({
141
+ identity, identifiers, serializedAtoms, width, value: String(++nextGeneration),
142
+ }),
143
+ [ identity, identifiers, serializedAtoms, width ]
144
+ ).value;
145
+
124
146
  const definitions = useMemo(
125
- () => new Map((atoms ?? []).map((atom) => [atom.name, atom])),
126
- [atoms]
147
+ () => new Map((atoms ?? []).map(atom => [ atom.name, atom ])),
148
+ [ atoms ]
127
149
  );
150
+
128
151
  const enabled = definitions.size > 0;
129
- const [measurementState, setMeasurementState] = useState({
152
+
153
+ const [ measurementState, setMeasurementState ] = useState<{ generation: string; revision: number; values: Record<string, Measurement> }>({
130
154
  generation,
131
155
  revision: 0,
132
- values: {} as Measurements,
156
+ values: {},
133
157
  });
134
- const measurements = measurementState.generation === generation ? measurementState.values : {};
158
+
159
+ const measurements = useMemo(
160
+ () => measurementState.generation === generation ? measurementState.values : {},
161
+ [ measurementState, generation ]
162
+ );
163
+
135
164
  const revision = String(
136
165
  measurementState.generation === generation ? measurementState.revision : 0
137
166
  );
138
- const [layout, setLayout] = useState<{
167
+
168
+ const [ layout, setLayout ] = useState<{
139
169
  generation: string;
140
170
  json: string;
141
171
  positions: AtomPosition[];
142
172
  } | null>(null);
173
+
143
174
  const pendingLayout = layout?.generation !== generation;
144
175
  const renderedPositions = layout?.positions ?? [];
145
176
  const positions = pendingLayout ? [] : renderedPositions;
146
177
  const mountedMeasurements = useRef(new Map<string, { nodeType: string; size: Measurement }>());
178
+
147
179
  useLayoutEffect(() => {
148
- for (const [key, measured] of mountedMeasurements.current) {
149
- if (!definitions.has(measured.nodeType)) mountedMeasurements.current.delete(key);
180
+ for (const [ key, measured ] of mountedMeasurements.current) {
181
+ if (!definitions.has(measured.nodeType)) {
182
+ mountedMeasurements.current.delete(key);
183
+ }
150
184
  }
151
- }, [definitions]);
185
+ }, [ definitions ]);
186
+
152
187
  const mounted = useRef(false);
188
+
153
189
  useLayoutEffect(() => {
154
190
  mounted.current = true;
191
+
155
192
  return () => {
156
193
  mounted.current = false;
157
194
  };
158
195
  }, []);
196
+
159
197
  const current = useRef({
160
198
  generation,
161
199
  snapshot,
@@ -169,6 +207,7 @@ export function useViewerAtoms({
169
207
  onError,
170
208
  width,
171
209
  });
210
+
172
211
  current.current = {
173
212
  generation,
174
213
  snapshot,
@@ -187,21 +226,27 @@ export function useViewerAtoms({
187
226
  () =>
188
227
  enabled
189
228
  ? JSON.stringify({
190
- ...(themeJson ? JSON.parse(themeJson) : {}),
191
- viewerAtoms: {
192
- ...JSON.parse(serializedAtoms!),
193
- generation,
194
- revision,
195
- measurements,
196
- },
197
- })
229
+ ...(themeJson ? JSON.parse(themeJson) : {}),
230
+ viewerAtoms: {
231
+ ...JSON.parse(serializedAtoms!),
232
+ generation,
233
+ revision,
234
+ measurements,
235
+ },
236
+ })
198
237
  : themeJson,
199
- [enabled, themeJson, serializedAtoms, generation, revision, measurements]
238
+ [ enabled,
239
+ themeJson,
240
+ serializedAtoms,
241
+ generation,
242
+ revision,
243
+ measurements ]
200
244
  );
201
245
 
202
246
  const onAtomLayout = useCallback((event: NativeSyntheticEvent<ViewerAtomLayoutEvent>) => {
203
247
  const value = event.nativeEvent;
204
248
  const latest = current.current;
249
+
205
250
  if (
206
251
  !mounted.current ||
207
252
  value.generation !== latest.generation ||
@@ -209,41 +254,49 @@ export function useViewerAtoms({
209
254
  !Number.isFinite(value.layoutWidth) ||
210
255
  value.layoutWidth <= 0 ||
211
256
  (latest.width != null && Math.abs(latest.width - value.layoutWidth) > 1)
212
- )
257
+ ) {
213
258
  return;
259
+ }
260
+
214
261
  try {
215
262
  const next = parsePositions(value.atomsJson, latest.definitions, latest.snapshot);
216
263
  const retainedMeasurements: Measurements = {};
217
264
  const retainedKeys = new Set<string>();
265
+
218
266
  for (const atom of next) {
219
267
  const key = atom.key;
220
268
  retainedKeys.add(key);
221
269
  const measured = mountedMeasurements.current.get(key);
270
+
222
271
  if (measured?.size.width === atom.width) {
223
272
  retainedMeasurements[String(atom.docPos)] = measured.size;
224
273
  }
225
274
  }
275
+
226
276
  for (const key of mountedMeasurements.current.keys()) {
227
- if (!retainedKeys.has(key)) mountedMeasurements.current.delete(key);
277
+ if (!retainedKeys.has(key)) {
278
+ mountedMeasurements.current.delete(key);
279
+ }
228
280
  }
281
+
229
282
  // Unchanged mounted sizes do not emit another Yoga onLayout event.
230
- setMeasurementState((previous) =>
283
+ setMeasurementState(previous =>
231
284
  previous.generation === value.generation
232
285
  ? previous
233
286
  : {
234
- generation: value.generation,
235
- revision: Object.keys(retainedMeasurements).length > 0 ? 1 : 0,
236
- values: retainedMeasurements,
237
- }
238
- );
239
- setLayout((previous) =>
287
+ generation: value.generation,
288
+ revision: Object.keys(retainedMeasurements).length > 0 ? 1 : 0,
289
+ values: retainedMeasurements,
290
+ });
291
+
292
+ setLayout(previous =>
240
293
  previous?.generation === value.generation && previous.json === value.atomsJson
241
294
  ? previous
242
- : { generation: value.generation, json: value.atomsJson, positions: next }
243
- );
295
+ : { generation: value.generation, json: value.atomsJson, positions: next });
244
296
  } catch {
245
297
  mountedMeasurements.current.clear();
246
298
  setLayout(null);
299
+
247
300
  latest.onError?.({
248
301
  domain: 'viewer',
249
302
  code: 'INVALID_ATOM_LAYOUT',
@@ -255,27 +308,35 @@ export function useViewerAtoms({
255
308
 
256
309
  const onContainerLayout = useCallback((event: LayoutChangeEvent) => {
257
310
  const next = event.nativeEvent.layout.width;
258
- if (Number.isFinite(next) && next > 0)
259
- setWidth((previous) => (previous === next ? previous : next));
311
+
312
+ if (Number.isFinite(next) && next > 0) {
313
+ setWidth(previous => (previous === next ? previous : next));
314
+ }
260
315
  }, []);
261
316
 
262
317
  const updateQueues = useRef(new Map<string, Promise<void>>());
318
+
263
319
  const acknowledged = useRef(
264
320
  new Map<string, { generation: string; attrs: Record<string, unknown> }>()
265
321
  );
322
+
266
323
  useLayoutEffect(() => {
267
324
  acknowledged.current.clear();
268
- }, [generation]);
325
+ }, [ generation ]);
326
+
269
327
  const updateAttrs = useCallback(
270
328
  (owner: string, atom: AtomPosition, update: AtomAttrsUpdate): Promise<void> => {
271
- const execute = async () => {
329
+ const execute = async() => {
272
330
  const latest = current.current;
273
- if (!mounted.current)
331
+
332
+ if (!mounted.current) {
274
333
  throw new AtomUpdateAttrsError('not-ready', 'The viewer is unmounted.');
334
+ }
335
+
275
336
  if (
276
337
  owner !== latest.generation ||
277
338
  !latest.positions.some(
278
- (position) =>
339
+ position =>
279
340
  position.key === atom.key && position.attrsJson === atom.attrsJson
280
341
  )
281
342
  ) {
@@ -284,29 +345,36 @@ export function useViewerAtoms({
284
345
  'The viewer content has changed.'
285
346
  );
286
347
  }
287
- if (latest.readOnly || !latest.onUpdateAtomAttrs)
348
+
349
+ if (latest.readOnly || !latest.onUpdateAtomAttrs) {
288
350
  throw new AtomUpdateAttrsError(
289
351
  'not-applicable',
290
352
  'Viewer updates require readOnly={false} and onUpdateAtomAttrs.'
291
353
  );
354
+ }
355
+
292
356
  const accepted = acknowledged.current.get(atom.key);
293
357
  const attrs = accepted?.generation === owner ? accepted.attrs : atom.attrs;
294
358
  const partial = resolveAtomAttrsUpdate(attrs, update);
295
359
  const definition = latest.definitions.get(atom.nodeType)!;
360
+
296
361
  try {
297
362
  validateAttributes({ ...attrs, ...partial }, definition.attrs ?? {});
363
+
298
364
  if (
299
365
  definition.idAttribute &&
300
366
  Object.prototype.hasOwnProperty.call(partial, definition.idAttribute) &&
301
367
  partial[definition.idAttribute] !== attrs[definition.idAttribute]
302
- )
368
+ ) {
303
369
  throw new Error('An atom identity cannot be changed.');
370
+ }
304
371
  } catch (error) {
305
372
  throw new AtomUpdateAttrsError(
306
373
  'not-applicable',
307
374
  error instanceof Error ? error.message : String(error)
308
375
  );
309
376
  }
377
+
310
378
  await latest.onUpdateAtomAttrs({
311
379
  nodeType: atom.nodeType,
312
380
  atomId: atom.atomId,
@@ -314,20 +382,30 @@ export function useViewerAtoms({
314
382
  attrs,
315
383
  partial,
316
384
  });
317
- if (mounted.current && current.current.generation === owner)
385
+
386
+ if (mounted.current && current.current.generation === owner) {
318
387
  acknowledged.current.set(atom.key, {
319
388
  generation: owner,
320
389
  attrs: { ...attrs, ...partial },
321
390
  });
391
+ }
322
392
  };
393
+
323
394
  const previous = updateQueues.current.get(atom.key);
324
- const result = previous ? previous.catch(() => {}).then(execute) : execute();
395
+
396
+ const result = previous ? previous.catch(() => {
397
+ }).then(execute) : execute();
398
+
325
399
  updateQueues.current.set(atom.key, result);
400
+
326
401
  const clean = () => {
327
- if (updateQueues.current.get(atom.key) === result)
402
+ if (updateQueues.current.get(atom.key) === result) {
328
403
  updateQueues.current.delete(atom.key);
404
+ }
329
405
  };
406
+
330
407
  void result.then(clean, clean);
408
+
331
409
  return result;
332
410
  },
333
411
  []
@@ -342,6 +420,7 @@ export function useViewerAtoms({
342
420
  ) => {
343
421
  const latest = current.current;
344
422
  const measured = event.nativeEvent.layout;
423
+
345
424
  if (
346
425
  !mounted.current ||
347
426
  owner !== latest.generation ||
@@ -352,24 +431,33 @@ export function useViewerAtoms({
352
431
  measured.height < 0 ||
353
432
  Math.abs(measured.width - atom.width) > 1 ||
354
433
  !latest.renderedPositions.some(
355
- (position) =>
434
+ position =>
356
435
  position.docPos === atom.docPos &&
357
436
  position.nodeType === atom.nodeType &&
358
437
  position.width === atom.width &&
359
438
  position.attrsJson === atom.attrsJson
360
439
  )
361
- )
440
+ ) {
362
441
  return;
442
+ }
443
+
363
444
  mountedMeasurements.current.set(atom.key, {
364
445
  nodeType: atom.nodeType,
365
446
  size: { width: atom.width, height: measured.height },
366
447
  });
367
- if (latest.pendingLayout) return;
368
- setMeasurementState((previous) => {
448
+
449
+ if (latest.pendingLayout) {
450
+ return;
451
+ }
452
+
453
+ setMeasurementState(previous => {
369
454
  const values = previous.generation === owner ? previous.values : {};
370
455
  const existing = values[String(atom.docPos)];
371
- if (existing?.width === atom.width && existing.height === measured.height)
456
+
457
+ if (existing?.width === atom.width && existing.height === measured.height) {
372
458
  return previous;
459
+ }
460
+
373
461
  return {
374
462
  generation: owner,
375
463
  revision: previous.generation === owner ? previous.revision + 1 : 1,
@@ -383,9 +471,13 @@ export function useViewerAtoms({
383
471
  []
384
472
  );
385
473
 
386
- const children = renderedPositions.map((atom) => {
474
+ const children = renderedPositions.map(atom => {
387
475
  const Component = definitions.get(atom.nodeType)?.component;
388
- if (!Component) return null;
476
+
477
+ if (!Component) {
478
+ return null;
479
+ }
480
+
389
481
  return (
390
482
  <View
391
483
  key={atom.key}
@@ -400,13 +492,14 @@ export function useViewerAtoms({
400
492
  width: atom.width,
401
493
  opacity: pendingLayout ? 0 : 1,
402
494
  }}
403
- onLayout={(event) => measure(generation, atom, Component, event)}>
495
+ onLayout={event => measure(generation, atom, Component, event)}
496
+ >
404
497
  <AtomHost
405
498
  component={Component}
406
499
  width={atom.width}
407
500
  estimatedHeight={atom.height}
408
501
  visible={atomIsVisible(atom.y, atom.height, viewport)}
409
- onMeasure={(event) => measure(generation, atom, Component, event)}
502
+ onMeasure={event => measure(generation, atom, Component, event)}
410
503
  atomProps={{
411
504
  attrs: atom.attrs,
412
505
  nodeType: atom.nodeType,
@@ -414,13 +507,16 @@ export function useViewerAtoms({
414
507
  readOnly,
415
508
  interactive: interactive && !pendingLayout,
416
509
  isViewer: true,
417
- updateAttrs: (partial) => updateAttrs(layout!.generation, atom, partial),
510
+ updateAttrs: partial => updateAttrs(layout!.generation, atom, partial),
418
511
  }}
419
512
  />
420
513
  </View>
421
514
  );
422
515
  });
423
- return { enabled, themeJson: configuredThemeJson, onAtomLayout, onContainerLayout, children };
516
+
517
+ return {
518
+ enabled, themeJson: configuredThemeJson, onAtomLayout, onContainerLayout, children,
519
+ };
424
520
  }
425
521
 
426
522
  /** @deprecated Use RichTextViewerAtomAttrsUpdateEvent instead. */