@apollohg/react-native-rich-text-editor 2.0.0 → 2.0.1
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 +1 -1
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/android/build.gradle +3 -0
- 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 +84 -37
- 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/Info.plist +5 -5
- 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/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/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/README.md
CHANGED
|
@@ -147,7 +147,7 @@ Addons are a readonly array. Conditional `false`, `null`, and `undefined` entrie
|
|
|
147
147
|
|
|
148
148
|
```tsx
|
|
149
149
|
import { createMentionsAddon } from '@apollohg/react-native-rich-text-editor';
|
|
150
|
-
import { createCodeHighlightingAddon } from '@react-native-rich-text-editor
|
|
150
|
+
import { createCodeHighlightingAddon } from '@apollohg/react-native-rich-text-editor-code-highlighting';
|
|
151
151
|
|
|
152
152
|
<RichTextEditor
|
|
153
153
|
documentHandle={documentHandle}
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -6,7 +6,7 @@ Include the applicable notices and source links with distributed applications or
|
|
|
6
6
|
|
|
7
7
|
## Package scope
|
|
8
8
|
|
|
9
|
-
`@apollohg/react-native-rich-text-editor` — core Rust engine, generated UniFFI bindings, native dependencies, and copied Material Icons assets. The optional `@react-native-rich-text-editor
|
|
9
|
+
`@apollohg/react-native-rich-text-editor` — core Rust engine, generated UniFFI bindings, native dependencies, and copied Material Icons assets. The optional `@apollohg/react-native-rich-text-editor-code-highlighting` package has its own notices file.
|
|
10
10
|
|
|
11
11
|
## Rust dependencies
|
|
12
12
|
|
package/android/build.gradle
CHANGED
|
@@ -86,6 +86,9 @@ tasks.register('buildHostEditorCore', Exec) {
|
|
|
86
86
|
// by a transformed Android artifact.
|
|
87
87
|
def hostTestJnaClasspath = files(configurations.hostTestJna)
|
|
88
88
|
tasks.withType(Test).configureEach {
|
|
89
|
+
// Security fixtures can be selected through the process environment.
|
|
90
|
+
outputs.upToDateWhen { false }
|
|
91
|
+
outputs.cacheIf { false }
|
|
89
92
|
dependsOn tasks.named('buildHostEditorCore')
|
|
90
93
|
inputs.files(hostTestJnaClasspath).withPropertyName('hostTestJnaClasspath')
|
|
91
94
|
systemProperty 'uniffi.component.editor_core.libraryOverride', hostEditorCoreLibrary.absolutePath
|
package/app.plugin.js
CHANGED
|
@@ -4,59 +4,62 @@ const { createRequire } = require('module');
|
|
|
4
4
|
const pkg = require('./package.json');
|
|
5
5
|
|
|
6
6
|
const PACKAGING_EXCLUDES_PROPERTY = 'android.packagingOptions.excludes';
|
|
7
|
+
|
|
7
8
|
const LEGACY_JNA_ABI_EXCLUDES = [
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
'**/armeabi/libjnidispatch.so',
|
|
10
|
+
'**/mips/libjnidispatch.so',
|
|
11
|
+
'**/mips64/libjnidispatch.so',
|
|
11
12
|
];
|
|
12
13
|
|
|
13
14
|
function requireExpoConfigPlugins() {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
try {
|
|
16
|
+
return require('expo/config-plugins');
|
|
17
|
+
} catch (error) {
|
|
18
|
+
if (error.code !== 'MODULE_NOT_FOUND') {
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
const appRequire = createRequire(path.join(process.cwd(), 'package.json'));
|
|
23
|
+
|
|
24
|
+
return appRequire('expo/config-plugins');
|
|
25
|
+
}
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
function mergeCommaList(value, additions) {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
const values = (value || '')
|
|
30
|
+
.split(',')
|
|
31
|
+
.map(item => item.trim())
|
|
32
|
+
.filter(Boolean);
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
return Array.from(new Set([ ...values, ...additions ])).join(',');
|
|
33
35
|
}
|
|
34
36
|
|
|
35
37
|
function withReactNativeProseEditor(config) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
38
|
+
const { withGradleProperties } = requireExpoConfigPlugins();
|
|
39
|
+
|
|
40
|
+
return withGradleProperties(config, config => {
|
|
41
|
+
const existing = config.modResults.find(
|
|
42
|
+
property => property.type === 'property' && property.key === PACKAGING_EXCLUDES_PROPERTY
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
const value = mergeCommaList(existing && existing.value, LEGACY_JNA_ABI_EXCLUDES);
|
|
46
|
+
|
|
47
|
+
if (existing) {
|
|
48
|
+
existing.value = value;
|
|
49
|
+
} else {
|
|
50
|
+
config.modResults.push({
|
|
51
|
+
type: 'property',
|
|
52
|
+
key: PACKAGING_EXCLUDES_PROPERTY,
|
|
53
|
+
value,
|
|
54
|
+
});
|
|
55
|
+
}
|
|
53
56
|
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
return config;
|
|
58
|
+
});
|
|
56
59
|
}
|
|
57
60
|
|
|
58
61
|
module.exports = requireExpoConfigPlugins().createRunOncePlugin(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
withReactNativeProseEditor,
|
|
63
|
+
pkg.name,
|
|
64
|
+
pkg.version
|
|
62
65
|
);
|
package/dist/AtomHost.js
CHANGED
|
@@ -40,8 +40,9 @@ const react_1 = __importStar(require("react"));
|
|
|
40
40
|
const react_native_1 = require("react-native");
|
|
41
41
|
const atomInstances_1 = require("./atomInstances");
|
|
42
42
|
function atomIsVisible(y, height, viewport) {
|
|
43
|
-
if (!viewport)
|
|
43
|
+
if (!viewport) {
|
|
44
44
|
return true;
|
|
45
|
+
}
|
|
45
46
|
if (![viewport.y, viewport.height, viewport.overscan ?? 200].every(Number.isFinite) ||
|
|
46
47
|
viewport.height < 0 ||
|
|
47
48
|
(viewport.overscan ?? 200) < 0) {
|
|
@@ -64,8 +65,9 @@ class AtomErrorBoundary extends react_1.default.Component {
|
|
|
64
65
|
: { component: props.component, error: false };
|
|
65
66
|
}
|
|
66
67
|
render() {
|
|
67
|
-
if (!this.state.error)
|
|
68
|
+
if (!this.state.error) {
|
|
68
69
|
return this.props.children;
|
|
70
|
+
}
|
|
69
71
|
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { accessibilityRole: 'alert', style: { minHeight: 32, padding: 8 }, children: [(0, jsx_runtime_1.jsxs)(react_native_1.Text, { children: ["Unable to display ", this.props.nodeType] }), (0, jsx_runtime_1.jsx)(react_native_1.Pressable, { accessibilityRole: 'button', onPress: () => this.setState({ error: false }), children: (0, jsx_runtime_1.jsx)(react_native_1.Text, { children: "Retry" }) })] }));
|
|
70
72
|
}
|
|
71
73
|
}
|
|
@@ -85,30 +87,35 @@ function AtomHost({ component: Component, atomProps, width, estimatedHeight, vis
|
|
|
85
87
|
};
|
|
86
88
|
}, []);
|
|
87
89
|
const updateAttrs = (0, react_1.useCallback)(async (update) => {
|
|
88
|
-
if (!mounted.current)
|
|
90
|
+
if (!mounted.current) {
|
|
89
91
|
throw new atomInstances_1.AtomUpdateAttrsError('not-ready', 'The atom is unmounted.');
|
|
92
|
+
}
|
|
90
93
|
const request = ++latestRequest.current;
|
|
91
|
-
setPending(
|
|
94
|
+
setPending(count => count + 1);
|
|
92
95
|
setUpdateError(null);
|
|
93
96
|
try {
|
|
94
97
|
await applyUpdate(update);
|
|
95
98
|
}
|
|
96
99
|
catch (error) {
|
|
97
|
-
if (mounted.current && latestRequest.current === request)
|
|
100
|
+
if (mounted.current && latestRequest.current === request) {
|
|
98
101
|
setUpdateError(error instanceof Error ? error : new Error(String(error)));
|
|
102
|
+
}
|
|
99
103
|
throw error;
|
|
100
104
|
}
|
|
101
105
|
finally {
|
|
102
|
-
if (mounted.current)
|
|
103
|
-
setPending(
|
|
106
|
+
if (mounted.current) {
|
|
107
|
+
setPending(count => count - 1);
|
|
108
|
+
}
|
|
104
109
|
}
|
|
105
110
|
}, [applyUpdate]);
|
|
106
111
|
const editor = (0, react_1.useMemo)(() => {
|
|
107
|
-
if (!atomProps.editor)
|
|
112
|
+
if (!atomProps.editor) {
|
|
108
113
|
return undefined;
|
|
114
|
+
}
|
|
109
115
|
const guard = (action) => async () => {
|
|
110
|
-
if (!mounted.current)
|
|
116
|
+
if (!mounted.current) {
|
|
111
117
|
throw new atomInstances_1.AtomUpdateAttrsError('not-ready', 'The atom is unmounted.');
|
|
118
|
+
}
|
|
112
119
|
return action();
|
|
113
120
|
};
|
|
114
121
|
return {
|
|
@@ -120,14 +127,14 @@ function AtomHost({ component: Component, atomProps, width, estimatedHeight, vis
|
|
|
120
127
|
}, [atomProps.editor]);
|
|
121
128
|
const retainedHeight = measurement.width === width ? measurement.height : estimatedHeight;
|
|
122
129
|
const show = visible || focused || active || pending > 0 || atomProps.selected;
|
|
123
|
-
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { testID: 'atom-host', nativeID: nativeID, collapsable: false, pointerEvents: atomProps.interactive === false ? 'none' : 'box-none', accessibilityElementsHidden: !show, importantForAccessibility: !show ? 'no-hide-descendants' : 'auto', onFocus: () => setFocused(true), onBlur: () => setFocused(false), onLayout:
|
|
130
|
+
return ((0, jsx_runtime_1.jsx)(react_native_1.View, { testID: 'atom-host', nativeID: nativeID, collapsable: false, pointerEvents: atomProps.interactive === false ? 'none' : 'box-none', accessibilityElementsHidden: !show, importantForAccessibility: !show ? 'no-hide-descendants' : 'auto', onFocus: () => setFocused(true), onBlur: () => setFocused(false), onLayout: event => {
|
|
124
131
|
onMeasure?.(event);
|
|
125
132
|
const size = event.nativeEvent.layout;
|
|
126
133
|
if (show &&
|
|
127
134
|
Number.isFinite(size.height) &&
|
|
128
135
|
size.height >= 0 &&
|
|
129
136
|
Math.abs(size.width - width) <= 1) {
|
|
130
|
-
setMeasurement(
|
|
137
|
+
setMeasurement(previous => previous.width === width && previous.height === size.height
|
|
131
138
|
? previous
|
|
132
139
|
: { width, height: size.height });
|
|
133
140
|
}
|
package/dist/EditorAddon.js
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createMentionsAddon = createMentionsAddon;
|
|
4
4
|
function copyConfiguration(value, ancestors = new Set()) {
|
|
5
|
-
if (value === null || typeof value !== 'object')
|
|
5
|
+
if (value === null || typeof value !== 'object') {
|
|
6
6
|
return value;
|
|
7
|
-
|
|
7
|
+
}
|
|
8
|
+
if (ancestors.has(value)) {
|
|
8
9
|
throw new Error('Addon configuration must not contain cycles.');
|
|
10
|
+
}
|
|
9
11
|
if (!Array.isArray(value) && Object.getPrototypeOf(value) !== Object.prototype) {
|
|
10
12
|
throw new Error('Addon configuration must contain only plain objects and arrays.');
|
|
11
13
|
}
|
|
@@ -5,30 +5,38 @@ function isRecord(value) {
|
|
|
5
5
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
6
6
|
}
|
|
7
7
|
function normalizeEditorAddons(addons) {
|
|
8
|
-
if (addons === undefined)
|
|
8
|
+
if (addons === undefined) {
|
|
9
9
|
return {};
|
|
10
|
-
|
|
10
|
+
}
|
|
11
|
+
if (!Array.isArray(addons)) {
|
|
11
12
|
throw new Error('addons must be a flat readonly array.');
|
|
13
|
+
}
|
|
12
14
|
const normalized = {};
|
|
13
15
|
const ids = new Set();
|
|
14
16
|
for (const [index, addon] of addons.entries()) {
|
|
15
|
-
if (addon === false || addon === null || addon === undefined)
|
|
17
|
+
if (addon === false || addon === null || addon === undefined) {
|
|
16
18
|
continue;
|
|
19
|
+
}
|
|
17
20
|
const path = `addons[${index}]`;
|
|
18
|
-
if (!isRecord(addon))
|
|
21
|
+
if (!isRecord(addon)) {
|
|
19
22
|
throw new Error(`${path} must be an addon descriptor.`);
|
|
20
|
-
|
|
23
|
+
}
|
|
24
|
+
if (addon.version !== 1) {
|
|
21
25
|
throw new Error(`${path}.version must be 1.`);
|
|
26
|
+
}
|
|
22
27
|
if (addon.capability !== 'mentions' && addon.capability !== 'code-highlighting') {
|
|
23
28
|
throw new Error(`${path}.capability is unsupported.`);
|
|
24
29
|
}
|
|
25
|
-
if (addon.id !== addon.capability)
|
|
30
|
+
if (addon.id !== addon.capability) {
|
|
26
31
|
throw new Error(`${path}.id must match its capability.`);
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
}
|
|
33
|
+
if (ids.has(addon.capability)) {
|
|
34
|
+
throw new Error(`${path}: duplicate addon id '${String(addon.id)}'.`);
|
|
35
|
+
}
|
|
29
36
|
ids.add(addon.capability);
|
|
30
|
-
if (!isRecord(addon.options))
|
|
37
|
+
if (!isRecord(addon.options)) {
|
|
31
38
|
throw new Error(`${path}.options must be an object.`);
|
|
39
|
+
}
|
|
32
40
|
if (addon.capability === 'mentions') {
|
|
33
41
|
normalized.mentions = addon.options;
|
|
34
42
|
}
|
|
@@ -4,12 +4,15 @@ exports.normalizeEditorMentionTheme = normalizeEditorMentionTheme;
|
|
|
4
4
|
const EditorStyleSheetNormalization_1 = require("./EditorStyleSheetNormalization");
|
|
5
5
|
const EditorMentionThemeValidation_1 = require("./EditorMentionThemeValidation");
|
|
6
6
|
function normalizeEditorMentionTheme(theme) {
|
|
7
|
-
if (theme === undefined)
|
|
7
|
+
if (theme === undefined) {
|
|
8
8
|
return undefined;
|
|
9
|
-
|
|
9
|
+
}
|
|
10
|
+
if (!(0, EditorMentionThemeValidation_1.validEditorMentionTheme)(theme)) {
|
|
10
11
|
throw new TypeError('Invalid mention theme');
|
|
11
|
-
|
|
12
|
+
}
|
|
13
|
+
if (!theme.node) {
|
|
12
14
|
return theme;
|
|
15
|
+
}
|
|
13
16
|
const node = theme.node;
|
|
14
17
|
const { textColor, style: existingStyle, ...appearance } = node;
|
|
15
18
|
const style = (0, EditorStyleSheetNormalization_1.normalizeEditorTheme)({
|
|
@@ -9,7 +9,7 @@ function isPlainRecord(value) {
|
|
|
9
9
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
10
10
|
}
|
|
11
11
|
function hasOnlyOwnKeys(value, fields) {
|
|
12
|
-
return Reflect.ownKeys(value).every(
|
|
12
|
+
return Reflect.ownKeys(value).every(key => typeof key === 'string' && fields.includes(key));
|
|
13
13
|
}
|
|
14
14
|
exports.MENTION_NODE_STRING_FIELDS = [
|
|
15
15
|
'textColor',
|
|
@@ -41,7 +41,17 @@ exports.MENTION_SUGGESTIONS_NUMBER_FIELDS = [
|
|
|
41
41
|
'borderWidth',
|
|
42
42
|
'borderRadius',
|
|
43
43
|
];
|
|
44
|
-
exports.EDITOR_MENTION_THEME_FONT_WEIGHTS = new Set(['normal',
|
|
44
|
+
exports.EDITOR_MENTION_THEME_FONT_WEIGHTS = new Set(['normal',
|
|
45
|
+
'bold',
|
|
46
|
+
'100',
|
|
47
|
+
'200',
|
|
48
|
+
'300',
|
|
49
|
+
'400',
|
|
50
|
+
'500',
|
|
51
|
+
'600',
|
|
52
|
+
'700',
|
|
53
|
+
'800',
|
|
54
|
+
'900']);
|
|
45
55
|
exports.EDITOR_MENTION_THEME_FIELDS = [
|
|
46
56
|
'node',
|
|
47
57
|
'suggestions',
|
|
@@ -52,12 +62,14 @@ function validMentionFontWeight(value) {
|
|
|
52
62
|
exports.EDITOR_MENTION_THEME_FONT_WEIGHTS.has(value)));
|
|
53
63
|
}
|
|
54
64
|
function validMentionThemeSection(value, stringFields, numberFields, extraFields) {
|
|
55
|
-
if (!isPlainRecord(value))
|
|
65
|
+
if (!isPlainRecord(value)) {
|
|
56
66
|
return false;
|
|
57
|
-
|
|
67
|
+
}
|
|
68
|
+
if (!hasOnlyOwnKeys(value, [...stringFields, ...numberFields, ...extraFields])) {
|
|
58
69
|
return false;
|
|
59
|
-
|
|
60
|
-
|
|
70
|
+
}
|
|
71
|
+
return (stringFields.every(field => value[field] === undefined || typeof value[field] === 'string') &&
|
|
72
|
+
numberFields.every(field => value[field] === undefined ||
|
|
61
73
|
(typeof value[field] === 'number' && Number.isFinite(value[field]))));
|
|
62
74
|
}
|
|
63
75
|
function validEditorMentionTheme(value) {
|
|
@@ -66,16 +78,19 @@ function validEditorMentionTheme(value) {
|
|
|
66
78
|
}
|
|
67
79
|
const { node, suggestions } = value;
|
|
68
80
|
if (node !== undefined) {
|
|
69
|
-
if (!isPlainRecord(node))
|
|
81
|
+
if (!isPlainRecord(node)) {
|
|
70
82
|
return false;
|
|
83
|
+
}
|
|
71
84
|
const { textColor, style, ...appearance } = node;
|
|
72
|
-
if (textColor !== undefined && typeof textColor !== 'string')
|
|
85
|
+
if (textColor !== undefined && typeof textColor !== 'string') {
|
|
73
86
|
return false;
|
|
87
|
+
}
|
|
74
88
|
try {
|
|
75
89
|
(0, EditorStyleSheetNormalization_1.normalizeEditorTheme)({ mention: [{ color: textColor }, appearance] });
|
|
76
90
|
if (style !== undefined) {
|
|
77
|
-
if (!isPlainRecord(style))
|
|
91
|
+
if (!isPlainRecord(style)) {
|
|
78
92
|
return false;
|
|
93
|
+
}
|
|
79
94
|
(0, EditorStyleSheetNormalization_1.normalizeEditorTheme)({ mention: style });
|
|
80
95
|
}
|
|
81
96
|
}
|
|
@@ -83,13 +98,15 @@ function validEditorMentionTheme(value) {
|
|
|
83
98
|
return false;
|
|
84
99
|
}
|
|
85
100
|
}
|
|
86
|
-
if (suggestions === undefined)
|
|
101
|
+
if (suggestions === undefined) {
|
|
87
102
|
return true;
|
|
103
|
+
}
|
|
88
104
|
if (!validMentionThemeSection(suggestions, exports.MENTION_SUGGESTIONS_STRING_FIELDS, exports.MENTION_SUGGESTIONS_NUMBER_FIELDS, ['option'])) {
|
|
89
105
|
return false;
|
|
90
106
|
}
|
|
91
107
|
const option = suggestions.option;
|
|
92
|
-
if (option === undefined)
|
|
108
|
+
if (option === undefined) {
|
|
93
109
|
return true;
|
|
110
|
+
}
|
|
94
111
|
return (validMentionThemeSection(option, exports.MENTION_OPTION_STRING_FIELDS, exports.MENTION_OPTION_NUMBER_FIELDS, ['fontWeight']) && validMentionFontWeight(option.fontWeight));
|
|
95
112
|
}
|
|
@@ -22,14 +22,14 @@ const border = [
|
|
|
22
22
|
'borderColor',
|
|
23
23
|
'borderStyle',
|
|
24
24
|
'borderRadius',
|
|
25
|
-
...sides.flatMap(
|
|
26
|
-
...corners.map(
|
|
25
|
+
...sides.flatMap(side => [`border${side}Width`, `border${side}Color`]),
|
|
26
|
+
...corners.map(corner => `border${corner}Radius`),
|
|
27
27
|
];
|
|
28
28
|
const spacing = (prefix) => [
|
|
29
29
|
prefix,
|
|
30
30
|
`${prefix}Horizontal`,
|
|
31
31
|
`${prefix}Vertical`,
|
|
32
|
-
...sides.map(
|
|
32
|
+
...sides.map(side => `${prefix}${side}`),
|
|
33
33
|
];
|
|
34
34
|
const surface = ['backgroundColor', ...border, ...spacing('padding')];
|
|
35
35
|
const box = [...surface, ...spacing('margin')];
|
|
@@ -68,8 +68,22 @@ const fields = {
|
|
|
68
68
|
};
|
|
69
69
|
const enums = {
|
|
70
70
|
fontStyle: ['normal', 'italic'],
|
|
71
|
-
fontWeight: ['normal',
|
|
72
|
-
|
|
71
|
+
fontWeight: ['normal',
|
|
72
|
+
'bold',
|
|
73
|
+
'100',
|
|
74
|
+
'200',
|
|
75
|
+
'300',
|
|
76
|
+
'400',
|
|
77
|
+
'500',
|
|
78
|
+
'600',
|
|
79
|
+
'700',
|
|
80
|
+
'800',
|
|
81
|
+
'900'],
|
|
82
|
+
textAlign: ['auto',
|
|
83
|
+
'left',
|
|
84
|
+
'right',
|
|
85
|
+
'center',
|
|
86
|
+
'justify'],
|
|
73
87
|
textDecorationLine: ['none', 'underline', 'line-through', 'underline line-through'],
|
|
74
88
|
textDecorationStyle: ['solid', 'double', 'dotted', 'dashed'],
|
|
75
89
|
borderStyle: ['solid', 'dashed', 'dotted'],
|
|
@@ -79,39 +93,50 @@ function invalid(path, reason) {
|
|
|
79
93
|
throw new TypeError(`Invalid editor style ${path}: ${reason}`);
|
|
80
94
|
}
|
|
81
95
|
function record(value) {
|
|
82
|
-
if (value == null || typeof value !== 'object' || Array.isArray(value))
|
|
96
|
+
if (value == null || typeof value !== 'object' || Array.isArray(value)) {
|
|
83
97
|
return false;
|
|
98
|
+
}
|
|
84
99
|
const prototype = Object.getPrototypeOf(value);
|
|
85
100
|
return prototype === Object.prototype || prototype === null;
|
|
86
101
|
}
|
|
87
102
|
function normalizeEditorColor(value, path) {
|
|
88
|
-
if (typeof value !== 'string')
|
|
103
|
+
if (typeof value !== 'string') {
|
|
89
104
|
return invalid(path, 'expected a color string');
|
|
105
|
+
}
|
|
90
106
|
const argb = (0, react_native_1.processColor)(value);
|
|
91
|
-
if (typeof argb !== 'number')
|
|
107
|
+
if (typeof argb !== 'number') {
|
|
92
108
|
return invalid(path, 'unrecognized color');
|
|
109
|
+
}
|
|
93
110
|
const rgba = ((argb << 8) | (argb >>> 24)) >>> 0;
|
|
94
111
|
return `#${rgba.toString(16).padStart(8, '0')}`;
|
|
95
112
|
}
|
|
96
113
|
function orderedMarker(value, path) {
|
|
97
|
-
if (!record(value))
|
|
114
|
+
if (!record(value)) {
|
|
98
115
|
return invalid(path, 'expected an ordered marker object');
|
|
116
|
+
}
|
|
99
117
|
const result = {};
|
|
100
118
|
for (const [key, entry] of Object.entries(value)) {
|
|
101
|
-
if (key !== 'schemes' && key !== 'suffix')
|
|
119
|
+
if (key !== 'schemes' && key !== 'suffix') {
|
|
102
120
|
invalid(`${path}.${key}`, 'unsupported property');
|
|
103
|
-
|
|
121
|
+
}
|
|
122
|
+
if (entry === undefined) {
|
|
104
123
|
continue;
|
|
124
|
+
}
|
|
105
125
|
if (key === 'suffix') {
|
|
106
|
-
if (entry !== '.' && entry !== ')')
|
|
126
|
+
if (entry !== '.' && entry !== ')') {
|
|
107
127
|
invalid(`${path}.${key}`, 'expected . or )');
|
|
128
|
+
}
|
|
108
129
|
result[key] = entry;
|
|
109
130
|
}
|
|
110
131
|
else {
|
|
111
|
-
const schemes = ['decimal',
|
|
132
|
+
const schemes = ['decimal',
|
|
133
|
+
'lowerAlpha',
|
|
134
|
+
'upperAlpha',
|
|
135
|
+
'lowerRoman',
|
|
136
|
+
'upperRoman'];
|
|
112
137
|
if (!Array.isArray(entry) ||
|
|
113
138
|
entry.length === 0 ||
|
|
114
|
-
!entry.every(
|
|
139
|
+
!entry.every(item => typeof item === 'string' && schemes.includes(item))) {
|
|
115
140
|
invalid(`${path}.${key}`, 'expected a nonempty array of numbering schemes');
|
|
116
141
|
}
|
|
117
142
|
result[key] = [...entry];
|
|
@@ -120,54 +145,68 @@ function orderedMarker(value, path) {
|
|
|
120
145
|
return result;
|
|
121
146
|
}
|
|
122
147
|
function property(key, value, path) {
|
|
123
|
-
if (value === undefined)
|
|
148
|
+
if (value === undefined) {
|
|
124
149
|
return undefined;
|
|
125
|
-
|
|
150
|
+
}
|
|
151
|
+
if (key === 'ordered') {
|
|
126
152
|
return orderedMarker(value, path);
|
|
127
|
-
|
|
153
|
+
}
|
|
154
|
+
if (key === 'checked') {
|
|
128
155
|
return normalizeStyle(value, checkbox, path);
|
|
129
|
-
|
|
156
|
+
}
|
|
157
|
+
if (key === 'color' || key.endsWith('Color')) {
|
|
130
158
|
return normalizeEditorColor(value, path);
|
|
159
|
+
}
|
|
131
160
|
if (key === 'fontFamily') {
|
|
132
|
-
if (typeof value !== 'string' || value.length === 0)
|
|
161
|
+
if (typeof value !== 'string' || value.length === 0) {
|
|
133
162
|
invalid(path, 'expected a nonempty font family');
|
|
163
|
+
}
|
|
134
164
|
return value;
|
|
135
165
|
}
|
|
136
166
|
if (enums[key]) {
|
|
137
167
|
const candidate = key === 'fontWeight' && typeof value === 'number' ? String(value) : value;
|
|
138
|
-
if (typeof candidate !== 'string' || !enums[key].includes(candidate))
|
|
168
|
+
if (typeof candidate !== 'string' || !enums[key].includes(candidate)) {
|
|
139
169
|
invalid(path, 'unsupported value');
|
|
170
|
+
}
|
|
140
171
|
return candidate;
|
|
141
172
|
}
|
|
142
|
-
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
173
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
143
174
|
invalid(path, 'expected a finite number');
|
|
144
|
-
|
|
175
|
+
}
|
|
176
|
+
if (['fontSize', 'lineHeight', 'size', 'scale'].includes(key) && value <= 0) {
|
|
145
177
|
invalid(path, 'expected a positive number');
|
|
146
|
-
|
|
178
|
+
}
|
|
179
|
+
if (!key.startsWith('margin') && key !== 'letterSpacing' && value < 0) {
|
|
147
180
|
invalid(path, 'expected a nonnegative number');
|
|
181
|
+
}
|
|
148
182
|
return value;
|
|
149
183
|
}
|
|
150
184
|
function normalizeStyle(value, allowed, path) {
|
|
151
185
|
const raw = {};
|
|
152
186
|
const ancestors = new Set();
|
|
153
187
|
function append(entry, depth) {
|
|
154
|
-
if (entry === false || entry == null)
|
|
188
|
+
if (entry === false || entry == null) {
|
|
155
189
|
return;
|
|
156
|
-
|
|
190
|
+
}
|
|
191
|
+
if (depth > 64) {
|
|
157
192
|
invalid(path, 'style arrays are nested too deeply');
|
|
193
|
+
}
|
|
158
194
|
if (Array.isArray(entry)) {
|
|
159
|
-
if (ancestors.has(entry))
|
|
195
|
+
if (ancestors.has(entry)) {
|
|
160
196
|
invalid(path, 'cyclic style array');
|
|
197
|
+
}
|
|
161
198
|
ancestors.add(entry);
|
|
162
|
-
entry.forEach(
|
|
199
|
+
entry.forEach(item => append(item, depth + 1));
|
|
163
200
|
ancestors.delete(entry);
|
|
164
201
|
return;
|
|
165
202
|
}
|
|
166
|
-
if (!record(entry))
|
|
203
|
+
if (!record(entry)) {
|
|
167
204
|
invalid(path, 'expected a style object or array');
|
|
205
|
+
}
|
|
168
206
|
for (const [key, field] of Object.entries(entry)) {
|
|
169
|
-
if (!allowed.includes(key))
|
|
207
|
+
if (!allowed.includes(key)) {
|
|
170
208
|
invalid(`${path}.${key}`, 'unsupported property');
|
|
209
|
+
}
|
|
171
210
|
raw[key] = property(key, field, `${path}.${key}`);
|
|
172
211
|
}
|
|
173
212
|
}
|
|
@@ -177,8 +216,9 @@ function normalizeStyle(value, allowed, path) {
|
|
|
177
216
|
for (const side of sides) {
|
|
178
217
|
const axis = side === 'Top' || side === 'Bottom' ? 'Vertical' : 'Horizontal';
|
|
179
218
|
const resolved = raw[`${prefix}${side}`] ?? raw[`${prefix}${axis}`] ?? raw[prefix];
|
|
180
|
-
if (resolved !== undefined)
|
|
219
|
+
if (resolved !== undefined) {
|
|
181
220
|
result[`${prefix}${side}`] = resolved;
|
|
221
|
+
}
|
|
182
222
|
}
|
|
183
223
|
delete result[prefix];
|
|
184
224
|
delete result[`${prefix}Horizontal`];
|
|
@@ -187,40 +227,47 @@ function normalizeStyle(value, allowed, path) {
|
|
|
187
227
|
for (const suffix of ['Width', 'Color']) {
|
|
188
228
|
for (const side of sides) {
|
|
189
229
|
const resolved = raw[`border${side}${suffix}`] ?? raw[`border${suffix}`];
|
|
190
|
-
if (resolved !== undefined)
|
|
230
|
+
if (resolved !== undefined) {
|
|
191
231
|
result[`border${side}${suffix}`] = resolved;
|
|
232
|
+
}
|
|
192
233
|
}
|
|
193
234
|
delete result[`border${suffix}`];
|
|
194
235
|
}
|
|
195
236
|
for (const corner of corners) {
|
|
196
237
|
const resolved = raw[`border${corner}Radius`] ?? raw.borderRadius;
|
|
197
|
-
if (resolved !== undefined)
|
|
238
|
+
if (resolved !== undefined) {
|
|
198
239
|
result[`border${corner}Radius`] = resolved;
|
|
240
|
+
}
|
|
199
241
|
}
|
|
200
242
|
delete result.borderRadius;
|
|
201
243
|
return result;
|
|
202
244
|
}
|
|
203
245
|
function normalizeEditorTheme(theme) {
|
|
204
|
-
if (!record(theme))
|
|
246
|
+
if (!record(theme)) {
|
|
205
247
|
invalid('theme', 'expected a named style map');
|
|
248
|
+
}
|
|
206
249
|
const styles = {};
|
|
207
250
|
const result = { version: 1 };
|
|
208
251
|
for (const [key, value] of Object.entries(theme)) {
|
|
209
252
|
if (key === 'toolbar') {
|
|
210
253
|
if (value !== undefined) {
|
|
211
|
-
if (!record(value))
|
|
254
|
+
if (!record(value)) {
|
|
212
255
|
invalid('toolbar', 'expected toolbar configuration');
|
|
256
|
+
}
|
|
213
257
|
result.toolbar = { ...value };
|
|
214
258
|
}
|
|
215
259
|
continue;
|
|
216
260
|
}
|
|
217
|
-
if (!Object.prototype.hasOwnProperty.call(fields, key))
|
|
261
|
+
if (!Object.prototype.hasOwnProperty.call(fields, key)) {
|
|
218
262
|
invalid(key, 'unsupported element');
|
|
263
|
+
}
|
|
219
264
|
const style = normalizeStyle(value, fields[key], key);
|
|
220
|
-
if (Object.keys(style).length)
|
|
265
|
+
if (Object.keys(style).length) {
|
|
221
266
|
styles[key] = style;
|
|
267
|
+
}
|
|
222
268
|
}
|
|
223
|
-
if (Object.keys(styles).length)
|
|
269
|
+
if (Object.keys(styles).length) {
|
|
224
270
|
result.styles = styles;
|
|
271
|
+
}
|
|
225
272
|
return result;
|
|
226
273
|
}
|
package/dist/EditorTheme.d.ts
CHANGED
|
@@ -114,4 +114,8 @@ export interface EditorContentInsets {
|
|
|
114
114
|
bottom?: number;
|
|
115
115
|
left?: number;
|
|
116
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Mentions are configured on the mentions addon, not on EditorTheme; native
|
|
119
|
+
* still reads them from the theme payload.
|
|
120
|
+
*/
|
|
117
121
|
export declare function serializeEditorTheme(theme?: EditorTheme, mentionTheme?: EditorMentionTheme): string | undefined;
|