@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
package/src/useViewerAtoms.tsx
CHANGED
|
@@ -56,9 +56,14 @@ function parsePositions(
|
|
|
56
56
|
snapshot: string
|
|
57
57
|
): AtomPosition[] {
|
|
58
58
|
const values: unknown = JSON.parse(json);
|
|
59
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
137
|
+
const identifiers = JSON.stringify((atoms ?? []).map(atom => [ atom.name, atom.idAttribute ]));
|
|
138
|
+
|
|
120
139
|
const generation = useMemo(
|
|
121
|
-
() =>
|
|
122
|
-
|
|
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(
|
|
126
|
-
[atoms]
|
|
147
|
+
() => new Map((atoms ?? []).map(atom => [ atom.name, atom ])),
|
|
148
|
+
[ atoms ]
|
|
127
149
|
);
|
|
150
|
+
|
|
128
151
|
const enabled = definitions.size > 0;
|
|
129
|
-
|
|
152
|
+
|
|
153
|
+
const [ measurementState, setMeasurementState ] = useState<{ generation: string; revision: number; values: Record<string, Measurement> }>({
|
|
130
154
|
generation,
|
|
131
155
|
revision: 0,
|
|
132
|
-
values: {}
|
|
156
|
+
values: {},
|
|
133
157
|
});
|
|
134
|
-
|
|
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
|
-
|
|
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))
|
|
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
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
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,
|
|
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))
|
|
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(
|
|
283
|
+
setMeasurementState(previous =>
|
|
231
284
|
previous.generation === value.generation
|
|
232
285
|
? previous
|
|
233
286
|
: {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
setLayout(
|
|
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
|
-
|
|
259
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
368
|
-
|
|
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
|
-
|
|
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(
|
|
474
|
+
const children = renderedPositions.map(atom => {
|
|
387
475
|
const Component = definitions.get(atom.nodeType)?.component;
|
|
388
|
-
|
|
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={
|
|
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={
|
|
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:
|
|
510
|
+
updateAttrs: partial => updateAttrs(layout!.generation, atom, partial),
|
|
418
511
|
}}
|
|
419
512
|
/>
|
|
420
513
|
</View>
|
|
421
514
|
);
|
|
422
515
|
});
|
|
423
|
-
|
|
516
|
+
|
|
517
|
+
return {
|
|
518
|
+
enabled, themeJson: configuredThemeJson, onAtomLayout, onContainerLayout, children,
|
|
519
|
+
};
|
|
424
520
|
}
|
|
425
521
|
|
|
426
522
|
/** @deprecated Use RichTextViewerAtomAttrsUpdateEvent instead. */
|