@openeditor/react-native-prose-editor 0.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 (91) hide show
  1. package/LICENSE +160 -0
  2. package/README.md +180 -0
  3. package/android/build.gradle +69 -0
  4. package/android/consumer-rules.pro +18 -0
  5. package/android/src/androidTest/AndroidManifest.xml +8 -0
  6. package/android/src/androidTest/java/com/apollohg/editor/MeasureHeightInstrumentedTest.kt +53 -0
  7. package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceCollaborationInitialSyncTest.kt +241 -0
  8. package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceImeRegressionTest.kt +338 -0
  9. package/android/src/androidTest/java/com/apollohg/editor/NativeDeviceOutsideTapTest.kt +789 -0
  10. package/android/src/androidTest/java/com/apollohg/editor/NativeDevicePerformanceTest.kt +350 -0
  11. package/android/src/androidTest/java/com/apollohg/editor/NativeEditorOutsideTapActivity.kt +5 -0
  12. package/android/src/main/assets/editor-icons/MaterialIcons.json +2236 -0
  13. package/android/src/main/assets/editor-icons/MaterialIcons.ttf +0 -0
  14. package/android/src/main/java/com/apollohg/editor/CaretGeometry.kt +50 -0
  15. package/android/src/main/java/com/apollohg/editor/EditorAddons.kt +135 -0
  16. package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +4527 -0
  17. package/android/src/main/java/com/apollohg/editor/EditorHeightBehavior.kt +14 -0
  18. package/android/src/main/java/com/apollohg/editor/EditorInputConnection.kt +999 -0
  19. package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +474 -0
  20. package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +199 -0
  21. package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +3137 -0
  22. package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +561 -0
  23. package/android/src/main/java/com/apollohg/editor/NativeProseViewerExpoView.kt +315 -0
  24. package/android/src/main/java/com/apollohg/editor/NativeToolbar.kt +1413 -0
  25. package/android/src/main/java/com/apollohg/editor/PositionBridge.kt +133 -0
  26. package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +315 -0
  27. package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +2172 -0
  28. package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +399 -0
  29. package/android/src/sharedTest/java/com/apollohg/editor/NativePerformanceSupport.kt +482 -0
  30. package/android/src/test/java/com/apollohg/editor/CaretGeometryTest.kt +137 -0
  31. package/android/src/test/java/com/apollohg/editor/EditorEditTextHardwareKeyTest.kt +321 -0
  32. package/android/src/test/java/com/apollohg/editor/EditorInputConnectionTest.kt +4202 -0
  33. package/android/src/test/java/com/apollohg/editor/NativeEditorExpoViewTest.kt +2581 -0
  34. package/android/src/test/java/com/apollohg/editor/NativeEditorModuleTest.kt +27 -0
  35. package/android/src/test/java/com/apollohg/editor/NativePerformanceTest.kt +197 -0
  36. package/android/src/test/java/com/apollohg/editor/NativeProseViewerExpoViewTest.kt +84 -0
  37. package/android/src/test/java/com/apollohg/editor/NativeToolbarTest.kt +544 -0
  38. package/android/src/test/java/com/apollohg/editor/PositionBridgeTest.kt +461 -0
  39. package/android/src/test/java/com/apollohg/editor/RenderBridgeTest.kt +2054 -0
  40. package/android/src/test/java/com/apollohg/editor/RichTextEditorViewTest.kt +1367 -0
  41. package/app.plugin.js +62 -0
  42. package/dist/EditorTheme.d.ts +113 -0
  43. package/dist/EditorTheme.js +29 -0
  44. package/dist/EditorToolbar.d.ts +191 -0
  45. package/dist/EditorToolbar.js +1095 -0
  46. package/dist/NativeEditorBridge.d.ts +325 -0
  47. package/dist/NativeEditorBridge.js +933 -0
  48. package/dist/NativeProseViewer.d.ts +53 -0
  49. package/dist/NativeProseViewer.js +480 -0
  50. package/dist/NativeRichTextEditor.d.ts +183 -0
  51. package/dist/NativeRichTextEditor.js +2574 -0
  52. package/dist/YjsCollaboration.d.ts +90 -0
  53. package/dist/YjsCollaboration.js +743 -0
  54. package/dist/addons.d.ts +100 -0
  55. package/dist/addons.js +82 -0
  56. package/dist/heightCache.d.ts +6 -0
  57. package/dist/heightCache.js +45 -0
  58. package/dist/index.d.ts +10 -0
  59. package/dist/index.js +30 -0
  60. package/dist/schemas.d.ts +37 -0
  61. package/dist/schemas.js +263 -0
  62. package/dist/useNativeEditor.d.ts +42 -0
  63. package/dist/useNativeEditor.js +124 -0
  64. package/expo-module.config.json +9 -0
  65. package/ios/EditorAddons.swift +327 -0
  66. package/ios/EditorCore.xcframework/Info.plist +44 -0
  67. package/ios/EditorCore.xcframework/ios-arm64/libeditor_core.a +0 -0
  68. package/ios/EditorCore.xcframework/ios-arm64_x86_64-simulator/libeditor_core.a +0 -0
  69. package/ios/EditorLayoutManager.swift +774 -0
  70. package/ios/EditorTheme.swift +515 -0
  71. package/ios/Generated_editor_core.swift +1513 -0
  72. package/ios/NativeEditorExpoView.swift +3593 -0
  73. package/ios/NativeEditorModule.swift +624 -0
  74. package/ios/NativeProseViewerExpoView.swift +276 -0
  75. package/ios/PositionBridge.swift +558 -0
  76. package/ios/ReactNativeProseEditor.podspec +49 -0
  77. package/ios/RenderBridge.swift +1708 -0
  78. package/ios/RichTextEditorView.swift +6150 -0
  79. package/ios/Tests/NativePerformanceTests.swift +687 -0
  80. package/ios/Tests/PositionBridgeTests.swift +706 -0
  81. package/ios/Tests/RenderBridgeTests.swift +2236 -0
  82. package/ios/Tests/RichTextEditorViewTests.swift +6548 -0
  83. package/ios/editor_coreFFI/editor_coreFFI.h +1289 -0
  84. package/ios/editor_coreFFI/module.modulemap +7 -0
  85. package/ios/editor_coreFFI.h +904 -0
  86. package/ios/editor_coreFFI.modulemap +7 -0
  87. package/package.json +74 -0
  88. package/rust/android/arm64-v8a/libeditor_core.so +0 -0
  89. package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
  90. package/rust/android/x86_64/libeditor_core.so +0 -0
  91. package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +2563 -0
@@ -0,0 +1,53 @@
1
+ import React from 'react';
2
+ import { type StyleProp, type ViewStyle } from 'react-native';
3
+ import { type EditorMentionTheme, type EditorTheme } from './EditorTheme';
4
+ import type { DocumentJSON } from './NativeEditorBridge';
5
+ import { type SchemaDefinition } from './schemas';
6
+ export interface NativeProseViewerMentionRenderContext {
7
+ docPos: number;
8
+ label: string;
9
+ attrs: Record<string, unknown>;
10
+ }
11
+ export interface NativeProseViewerMentionPressEvent extends NativeProseViewerMentionRenderContext {
12
+ }
13
+ export interface NativeProseViewerLinkPressEvent {
14
+ href: string;
15
+ text: string;
16
+ }
17
+ type NativeProseViewerContent = DocumentJSON | string;
18
+ export type NativeProseViewerMentionPrefix = string | ((mention: NativeProseViewerMentionRenderContext) => string | null | undefined);
19
+ export interface NativeProseViewerMentionsAddonConfig {
20
+ trigger?: string;
21
+ prefix?: NativeProseViewerMentionPrefix;
22
+ theme?: EditorMentionTheme;
23
+ resolveTheme?: (mention: NativeProseViewerMentionRenderContext) => EditorMentionTheme | null | undefined;
24
+ onPress?: (event: NativeProseViewerMentionPressEvent) => void;
25
+ }
26
+ export interface NativeProseViewerAddons {
27
+ mentions?: NativeProseViewerMentionsAddonConfig;
28
+ }
29
+ interface NativeProseViewerBaseProps {
30
+ contentRevision?: string | number;
31
+ contentJSONRevision?: string | number;
32
+ schema?: SchemaDefinition;
33
+ theme?: EditorTheme;
34
+ style?: StyleProp<ViewStyle>;
35
+ allowBase64Images?: boolean;
36
+ collapseTrailingEmptyParagraphs?: boolean;
37
+ enableLinkTaps?: boolean;
38
+ addons?: NativeProseViewerAddons;
39
+ onPressLink?: (event: NativeProseViewerLinkPressEvent) => void;
40
+ contentId?: string;
41
+ containerWidth?: number;
42
+ }
43
+ interface NativeProseViewerJsonProps extends NativeProseViewerBaseProps {
44
+ contentJSON: NativeProseViewerContent;
45
+ contentHTML?: never;
46
+ }
47
+ interface NativeProseViewerHtmlProps extends NativeProseViewerBaseProps {
48
+ contentHTML: string;
49
+ contentJSON?: never;
50
+ }
51
+ export type NativeProseViewerProps = NativeProseViewerJsonProps | NativeProseViewerHtmlProps;
52
+ export declare function NativeProseViewer({ ...props }: NativeProseViewerProps): React.JSX.Element;
53
+ export {};
@@ -0,0 +1,480 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NativeProseViewer = NativeProseViewer;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const expo_modules_core_1 = require("expo-modules-core");
7
+ const react_native_1 = require("react-native");
8
+ const addons_1 = require("./addons");
9
+ const EditorTheme_1 = require("./EditorTheme");
10
+ const heightCache_1 = require("./heightCache");
11
+ const schemas_1 = require("./schemas");
12
+ const NativeProseViewerView = (0, expo_modules_core_1.requireNativeViewManager)('NativeEditor', 'NativeProseViewer');
13
+ let nativeProseViewerModule = null;
14
+ function getNativeProseViewerModule() {
15
+ if (!nativeProseViewerModule) {
16
+ nativeProseViewerModule =
17
+ (0, expo_modules_core_1.requireNativeModule)('NativeEditor');
18
+ }
19
+ return nativeProseViewerModule;
20
+ }
21
+ const serializedJsonCache = new WeakMap();
22
+ const EMPTY_TEXT_BLOCK_PLACEHOLDER = '\u200B';
23
+ function stringifyCachedJson(value) {
24
+ if (value != null && typeof value === 'object') {
25
+ const cached = serializedJsonCache.get(value);
26
+ if (cached != null) {
27
+ return cached;
28
+ }
29
+ const serialized = JSON.stringify(value);
30
+ serializedJsonCache.set(value, serialized);
31
+ return serialized;
32
+ }
33
+ return JSON.stringify(value);
34
+ }
35
+ function looksLikeRenderElementsJson(json) {
36
+ for (let index = 0; index < json.length; index += 1) {
37
+ const char = json[index];
38
+ if (char === ' ' || char === '\n' || char === '\r' || char === '\t') {
39
+ continue;
40
+ }
41
+ return char === '[';
42
+ }
43
+ return false;
44
+ }
45
+ function unicodeScalarLength(text) {
46
+ let length = 0;
47
+ for (const _char of text) {
48
+ length += 1;
49
+ }
50
+ return length;
51
+ }
52
+ function normalizeMentionAttrs(node) {
53
+ if (node == null || typeof node !== 'object') {
54
+ return {};
55
+ }
56
+ const attrs = node.attrs;
57
+ if (attrs == null || typeof attrs !== 'object' || Array.isArray(attrs)) {
58
+ return {};
59
+ }
60
+ return attrs;
61
+ }
62
+ function baseMentionLabelFromAttrs(attrs) {
63
+ const label = attrs.label;
64
+ return typeof label === 'string' && label.length > 0 ? label : 'mention';
65
+ }
66
+ function resolveConfiguredMentionPrefix(prefix, mention) {
67
+ const rawPrefix = typeof prefix === 'function' ? prefix(mention) : prefix;
68
+ return typeof rawPrefix === 'string' && rawPrefix.length > 0 ? rawPrefix : undefined;
69
+ }
70
+ function mentionTriggerFromAttrs(attrs) {
71
+ const trigger = attrs.mentionSuggestionChar;
72
+ return typeof trigger === 'string' && trigger.length > 0 ? trigger : undefined;
73
+ }
74
+ function applyMentionPrefix(label, prefix) {
75
+ if (!prefix || label.startsWith(prefix)) {
76
+ return label;
77
+ }
78
+ return `${prefix}${label}`;
79
+ }
80
+ function resolveMentionRenderedLabel(mentionContext, prefix, trigger) {
81
+ if (prefix !== undefined) {
82
+ return applyMentionPrefix(mentionContext.label, resolveConfiguredMentionPrefix(prefix, mentionContext));
83
+ }
84
+ return applyMentionPrefix(mentionContext.label, trigger ?? mentionTriggerFromAttrs(mentionContext.attrs));
85
+ }
86
+ function collectMentionPayloadsByDocPos(document, mentionsAddon) {
87
+ const mentions = new Map();
88
+ const effectiveMentionPrefix = mentionsAddon?.prefix;
89
+ const effectiveResolveMentionTheme = mentionsAddon?.resolveTheme;
90
+ const defaultMentionTheme = mentionsAddon?.theme;
91
+ const trigger = mentionsAddon?.trigger?.trim() || undefined;
92
+ const visit = (node, pos, isRoot = false) => {
93
+ if (node == null || typeof node !== 'object') {
94
+ return pos;
95
+ }
96
+ const nodeRecord = node;
97
+ const nodeType = typeof nodeRecord.type === 'string' ? nodeRecord.type : '';
98
+ const content = Array.isArray(nodeRecord.content) ? nodeRecord.content : [];
99
+ if (nodeType === 'text') {
100
+ const text = typeof nodeRecord.text === 'string' ? nodeRecord.text : '';
101
+ return pos + unicodeScalarLength(text);
102
+ }
103
+ if (nodeType === 'mention') {
104
+ const attrs = normalizeMentionAttrs(nodeRecord);
105
+ const label = baseMentionLabelFromAttrs(attrs);
106
+ const mentionContext = { docPos: pos, label, attrs };
107
+ const renderedLabel = resolveMentionRenderedLabel(mentionContext, effectiveMentionPrefix, trigger);
108
+ const resolvedMentionTheme = effectiveResolveMentionTheme?.(mentionContext) ?? undefined;
109
+ const mentionTheme = defaultMentionTheme || resolvedMentionTheme
110
+ ? {
111
+ ...(defaultMentionTheme ?? {}),
112
+ ...(resolvedMentionTheme ?? {}),
113
+ }
114
+ : undefined;
115
+ mentions.set(pos, {
116
+ ...mentionContext,
117
+ renderedLabel,
118
+ mentionTheme,
119
+ });
120
+ }
121
+ if (isRoot && nodeType === 'doc') {
122
+ let nextPos = pos;
123
+ for (const child of content) {
124
+ nextPos = visit(child, nextPos);
125
+ }
126
+ return nextPos;
127
+ }
128
+ if (content.length === 0) {
129
+ return pos + 1;
130
+ }
131
+ let nextPos = pos + 1;
132
+ for (const child of content) {
133
+ nextPos = visit(child, nextPos);
134
+ }
135
+ return nextPos + 1;
136
+ };
137
+ visit(document, 0, true);
138
+ return mentions;
139
+ }
140
+ function applyResolvedMentionRendering(renderJson, mentionPayloadsByDocPos) {
141
+ if (mentionPayloadsByDocPos.size === 0) {
142
+ return renderJson;
143
+ }
144
+ let parsedElements;
145
+ try {
146
+ parsedElements = JSON.parse(renderJson);
147
+ }
148
+ catch {
149
+ return renderJson;
150
+ }
151
+ if (!Array.isArray(parsedElements)) {
152
+ return renderJson;
153
+ }
154
+ let didChange = false;
155
+ const nextElements = parsedElements.map((element) => {
156
+ if (element == null || typeof element !== 'object' || Array.isArray(element)) {
157
+ return element;
158
+ }
159
+ const renderElement = element;
160
+ if (renderElement.type !== 'opaqueInlineAtom' ||
161
+ renderElement.nodeType !== 'mention' ||
162
+ typeof renderElement.docPos !== 'number') {
163
+ return element;
164
+ }
165
+ const mention = mentionPayloadsByDocPos.get(renderElement.docPos);
166
+ if (!mention) {
167
+ return element;
168
+ }
169
+ let nextElement = renderElement;
170
+ if (renderElement.label !== mention.renderedLabel) {
171
+ nextElement = { ...nextElement, label: mention.renderedLabel };
172
+ didChange = true;
173
+ }
174
+ if (mention.mentionTheme && Object.keys(mention.mentionTheme).length > 0) {
175
+ nextElement =
176
+ nextElement === renderElement ? { ...nextElement } : nextElement;
177
+ nextElement.mentionTheme = mention.mentionTheme;
178
+ didChange = true;
179
+ }
180
+ return nextElement;
181
+ });
182
+ return didChange ? JSON.stringify(nextElements) : renderJson;
183
+ }
184
+ function isTopLevelSingleElementBlock(element) {
185
+ return element.type === 'voidBlock' || element.type === 'opaqueBlockAtom';
186
+ }
187
+ function isEmptyParagraphPlaceholderText(text) {
188
+ if (text.length === 0) {
189
+ return false;
190
+ }
191
+ return Array.from(text).every((char) => char === EMPTY_TEXT_BLOCK_PLACEHOLDER);
192
+ }
193
+ function isCollapsibleEmptyParagraphText(text) {
194
+ return Array.from(text).every((char) => char === EMPTY_TEXT_BLOCK_PLACEHOLDER);
195
+ }
196
+ function renderElementsJsonContainsOnlyEmptyParagraphs(renderJson) {
197
+ let parsedElements;
198
+ try {
199
+ parsedElements = JSON.parse(renderJson);
200
+ }
201
+ catch {
202
+ return false;
203
+ }
204
+ if (!Array.isArray(parsedElements)) {
205
+ return false;
206
+ }
207
+ if (parsedElements.length === 0) {
208
+ return true;
209
+ }
210
+ let hasParagraph = false;
211
+ let paragraphIsOpen = false;
212
+ for (const element of parsedElements) {
213
+ if (element == null || typeof element !== 'object' || Array.isArray(element)) {
214
+ return false;
215
+ }
216
+ const renderElement = element;
217
+ switch (renderElement.type) {
218
+ case 'blockStart':
219
+ if (paragraphIsOpen ||
220
+ renderElement.nodeType !== 'paragraph' ||
221
+ renderElement.depth !== 0) {
222
+ return false;
223
+ }
224
+ paragraphIsOpen = true;
225
+ hasParagraph = true;
226
+ break;
227
+ case 'textRun':
228
+ if (!paragraphIsOpen ||
229
+ typeof renderElement.text !== 'string' ||
230
+ !isCollapsibleEmptyParagraphText(renderElement.text)) {
231
+ return false;
232
+ }
233
+ break;
234
+ case 'blockEnd':
235
+ if (!paragraphIsOpen) {
236
+ return false;
237
+ }
238
+ paragraphIsOpen = false;
239
+ break;
240
+ default:
241
+ return false;
242
+ }
243
+ }
244
+ return hasParagraph && !paragraphIsOpen;
245
+ }
246
+ function isTrailingEmptyParagraphRange(elements, start, endExclusive) {
247
+ const startElement = elements[start];
248
+ const endElement = elements[endExclusive - 1];
249
+ if (startElement?.type !== 'blockStart' ||
250
+ startElement.nodeType !== 'paragraph' ||
251
+ startElement.depth !== 0 ||
252
+ endElement?.type !== 'blockEnd') {
253
+ return false;
254
+ }
255
+ const innerElements = elements.slice(start + 1, endExclusive - 1);
256
+ return (innerElements.length > 0 &&
257
+ innerElements.every((element) => element.type === 'textRun' &&
258
+ typeof element.text === 'string' &&
259
+ isEmptyParagraphPlaceholderText(element.text)));
260
+ }
261
+ function collapseTrailingEmptyParagraphRenderElements(renderJson) {
262
+ let parsedElements;
263
+ try {
264
+ parsedElements = JSON.parse(renderJson);
265
+ }
266
+ catch {
267
+ return renderJson;
268
+ }
269
+ if (!Array.isArray(parsedElements)) {
270
+ return renderJson;
271
+ }
272
+ const elements = parsedElements;
273
+ const topLevelRanges = [];
274
+ for (let index = 0; index < elements.length; index += 1) {
275
+ const element = elements[index];
276
+ if (!element || typeof element !== 'object' || Array.isArray(element)) {
277
+ continue;
278
+ }
279
+ if (element.type === 'blockStart' && element.depth === 0) {
280
+ let nestingDepth = 1;
281
+ let cursor = index + 1;
282
+ while (cursor < elements.length && nestingDepth > 0) {
283
+ const current = elements[cursor];
284
+ if (current?.type === 'blockStart') {
285
+ nestingDepth += 1;
286
+ }
287
+ else if (current?.type === 'blockEnd') {
288
+ nestingDepth -= 1;
289
+ }
290
+ cursor += 1;
291
+ }
292
+ if (nestingDepth !== 0) {
293
+ return renderJson;
294
+ }
295
+ topLevelRanges.push({ start: index, endExclusive: cursor });
296
+ index = cursor - 1;
297
+ continue;
298
+ }
299
+ if (isTopLevelSingleElementBlock(element)) {
300
+ topLevelRanges.push({ start: index, endExclusive: index + 1 });
301
+ }
302
+ }
303
+ if (topLevelRanges.length <= 1) {
304
+ return renderJson;
305
+ }
306
+ let trimStart = null;
307
+ for (let rangeIndex = topLevelRanges.length - 1; rangeIndex >= 1; rangeIndex -= 1) {
308
+ const range = topLevelRanges[rangeIndex];
309
+ if (!isTrailingEmptyParagraphRange(elements, range.start, range.endExclusive)) {
310
+ break;
311
+ }
312
+ trimStart = range.start;
313
+ }
314
+ if (trimStart == null) {
315
+ return renderJson;
316
+ }
317
+ return JSON.stringify(elements.slice(0, trimStart));
318
+ }
319
+ function serializeDocumentInput(document, schema) {
320
+ if (typeof document === 'string') {
321
+ try {
322
+ const parsed = JSON.parse(document);
323
+ const normalizedDocument = (0, schemas_1.normalizeDocumentJson)(parsed, schema);
324
+ return {
325
+ normalizedDocument,
326
+ serializedContentJson: stringifyCachedJson(normalizedDocument),
327
+ };
328
+ }
329
+ catch {
330
+ return {
331
+ normalizedDocument: null,
332
+ serializedContentJson: document,
333
+ };
334
+ }
335
+ }
336
+ const normalizedDocument = (0, schemas_1.normalizeDocumentJson)(document, schema);
337
+ return {
338
+ normalizedDocument,
339
+ serializedContentJson: stringifyCachedJson(normalizedDocument),
340
+ };
341
+ }
342
+ function extractRenderError(json) {
343
+ try {
344
+ const parsed = JSON.parse(json);
345
+ if (parsed == null || typeof parsed !== 'object' || Array.isArray(parsed)) {
346
+ return null;
347
+ }
348
+ const error = parsed.error;
349
+ return typeof error === 'string' ? error : null;
350
+ }
351
+ catch {
352
+ return null;
353
+ }
354
+ }
355
+ function NativeProseViewer({ ...props }) {
356
+ const { contentRevision, contentJSONRevision, schema, theme, style, allowBase64Images = false, collapseTrailingEmptyParagraphs = true, enableLinkTaps = true, addons, onPressLink, contentId, containerWidth, } = props;
357
+ const mentionPressHandler = addons?.mentions?.onPress;
358
+ const contentJSON = 'contentJSON' in props ? props.contentJSON : undefined;
359
+ const contentHTML = 'contentHTML' in props ? props.contentHTML : undefined;
360
+ const resolvedContentRevision = contentRevision ?? contentJSONRevision;
361
+ const documentSchema = (0, react_1.useMemo)(() => (0, addons_1.withMentionsSchema)(schema ?? schemas_1.tiptapSchema), [schema]);
362
+ const { normalizedDocument, serializedContentJson } = (0, react_1.useMemo)(() => {
363
+ if (contentJSON === undefined) {
364
+ return {
365
+ normalizedDocument: null,
366
+ serializedContentJson: null,
367
+ };
368
+ }
369
+ return serializeDocumentInput(contentJSON, documentSchema);
370
+ }, [contentJSON, resolvedContentRevision, documentSchema]);
371
+ const themeJson = (0, react_1.useMemo)(() => (0, EditorTheme_1.serializeEditorTheme)(theme), [theme]);
372
+ const mentionPayloadsByDocPos = (0, react_1.useMemo)(() => normalizedDocument == null
373
+ ? new Map()
374
+ : collectMentionPayloadsByDocPos(normalizedDocument, addons?.mentions), [addons?.mentions, normalizedDocument]);
375
+ const renderJson = (0, react_1.useMemo)(() => {
376
+ const configJson = JSON.stringify({
377
+ schema: documentSchema,
378
+ ...(allowBase64Images ? { allowBase64Images } : {}),
379
+ });
380
+ const nextRenderJson = serializedContentJson != null
381
+ ? getNativeProseViewerModule().renderDocumentJson(configJson, serializedContentJson)
382
+ : getNativeProseViewerModule().renderDocumentHtml(configJson, contentHTML ?? '');
383
+ const renderError = extractRenderError(nextRenderJson);
384
+ if (renderError != null) {
385
+ console.error(`NativeProseViewer: ${renderError}`);
386
+ return '[]';
387
+ }
388
+ if (looksLikeRenderElementsJson(nextRenderJson)) {
389
+ const collapsedRenderJson = collapseTrailingEmptyParagraphs
390
+ ? collapseTrailingEmptyParagraphRenderElements(nextRenderJson)
391
+ : nextRenderJson;
392
+ return applyResolvedMentionRendering(collapsedRenderJson, mentionPayloadsByDocPos);
393
+ }
394
+ console.error('NativeProseViewer: native renderDocumentJson returned an invalid payload.');
395
+ return '[]';
396
+ }, [
397
+ allowBase64Images,
398
+ collapseTrailingEmptyParagraphs,
399
+ contentHTML,
400
+ documentSchema,
401
+ mentionPayloadsByDocPos,
402
+ serializedContentJson,
403
+ ]);
404
+ const renderJsonIsCollapsedEmpty = (0, react_1.useMemo)(() => collapseTrailingEmptyParagraphs &&
405
+ renderElementsJsonContainsOnlyEmptyParagraphs(renderJson), [collapseTrailingEmptyParagraphs, renderJson]);
406
+ const [contentHeight, setContentHeight] = (0, react_1.useState)(null);
407
+ (0, react_1.useEffect)(() => {
408
+ setContentHeight(null);
409
+ }, [contentId]);
410
+ const renderJsonHash = (0, react_1.useMemo)(() => (0, heightCache_1.computeRenderJsonHash)(renderJson), [renderJson]);
411
+ const layoutContextKey = (0, react_1.useMemo)(() => containerWidth != null ? (0, heightCache_1.computeLayoutContextKey)(themeJson, containerWidth) : null, [themeJson, containerWidth]);
412
+ const preMeasuredHeight = (0, react_1.useMemo)(() => {
413
+ if (!contentId || layoutContextKey == null || containerWidth == null) {
414
+ return null;
415
+ }
416
+ const cached = (0, heightCache_1.getHeightCache)(contentId, layoutContextKey, renderJsonHash);
417
+ if (cached != null)
418
+ return cached;
419
+ const measured = getNativeProseViewerModule().measureContentHeight(renderJson, themeJson, containerWidth);
420
+ if (measured > 0) {
421
+ (0, heightCache_1.setHeightCache)(contentId, layoutContextKey, renderJsonHash, measured);
422
+ }
423
+ return measured > 0 ? measured : null;
424
+ }, [contentId, containerWidth, renderJson, themeJson, layoutContextKey, renderJsonHash]);
425
+ const handleContentHeightChange = (0, react_1.useCallback)((event) => {
426
+ const density = react_native_1.Platform.OS === 'android' ? react_native_1.PixelRatio.get() : 1;
427
+ const nextHeight = Math.ceil(event.nativeEvent.contentHeight / density);
428
+ if (nextHeight < 0)
429
+ return;
430
+ if (nextHeight === 0 && !renderJsonIsCollapsedEmpty)
431
+ return;
432
+ if (nextHeight === 0) {
433
+ setContentHeight((currentHeight) => currentHeight === 0 ? currentHeight : 0);
434
+ return;
435
+ }
436
+ setContentHeight((currentHeight) => currentHeight === nextHeight ? currentHeight : nextHeight);
437
+ if (contentId && layoutContextKey != null) {
438
+ (0, heightCache_1.setHeightCache)(contentId, layoutContextKey, renderJsonHash, nextHeight);
439
+ }
440
+ }, [renderJsonIsCollapsedEmpty, contentId, layoutContextKey, renderJsonHash]);
441
+ const handlePressMention = (0, react_1.useCallback)((event) => {
442
+ if (!mentionPressHandler)
443
+ return;
444
+ const { docPos, label } = event.nativeEvent;
445
+ const resolvedMention = mentionPayloadsByDocPos.get(docPos);
446
+ mentionPressHandler({
447
+ docPos,
448
+ label: resolvedMention?.renderedLabel ?? label,
449
+ attrs: resolvedMention?.attrs ?? {},
450
+ });
451
+ }, [mentionPayloadsByDocPos, mentionPressHandler]);
452
+ const handlePressLink = (0, react_1.useCallback)((event) => {
453
+ if (!onPressLink)
454
+ return;
455
+ onPressLink({
456
+ href: event.nativeEvent.href,
457
+ text: event.nativeEvent.text,
458
+ });
459
+ }, [onPressLink]);
460
+ const nativeStyle = (0, react_1.useMemo)(() => {
461
+ let measuredStyle = null;
462
+ if (renderJsonIsCollapsedEmpty) {
463
+ measuredStyle = { height: 0, minHeight: 0 };
464
+ }
465
+ else if (contentHeight != null && contentHeight > 0) {
466
+ measuredStyle = { minHeight: contentHeight };
467
+ }
468
+ else if (preMeasuredHeight != null && preMeasuredHeight > 0) {
469
+ measuredStyle = { minHeight: preMeasuredHeight };
470
+ }
471
+ return [
472
+ { minHeight: renderJsonIsCollapsedEmpty ? 0 : 1 },
473
+ style,
474
+ measuredStyle,
475
+ ];
476
+ }, [contentHeight, preMeasuredHeight, renderJsonIsCollapsedEmpty, style]);
477
+ return ((0, jsx_runtime_1.jsx)(NativeProseViewerView, { style: nativeStyle, renderJson: renderJson, themeJson: themeJson, collapsesWhenEmpty: collapseTrailingEmptyParagraphs, enableLinkTaps: enableLinkTaps, interceptLinkTaps: typeof onPressLink === 'function', onContentHeightChange: handleContentHeightChange, onPressLink: typeof onPressLink === 'function' ? handlePressLink : undefined, onPressMention: typeof mentionPressHandler === 'function'
478
+ ? handlePressMention
479
+ : undefined }));
480
+ }