@apollohg/react-native-prose-editor 0.1.1 → 0.3.0
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 +12 -7
- package/android/build.gradle +7 -2
- package/android/src/main/java/com/apollohg/editor/EditorEditText.kt +289 -2
- package/android/src/main/java/com/apollohg/editor/EditorTheme.kt +51 -1
- package/android/src/main/java/com/apollohg/editor/ImageResizeOverlayView.kt +199 -0
- package/android/src/main/java/com/apollohg/editor/NativeEditorExpoView.kt +16 -3
- package/android/src/main/java/com/apollohg/editor/NativeEditorModule.kt +82 -1
- package/android/src/main/java/com/apollohg/editor/NativeToolbar.kt +403 -45
- package/android/src/main/java/com/apollohg/editor/RemoteSelectionOverlayView.kt +246 -0
- package/android/src/main/java/com/apollohg/editor/RenderBridge.kt +841 -155
- package/android/src/main/java/com/apollohg/editor/RichTextEditorView.kt +125 -8
- package/{src/EditorTheme.ts → dist/EditorTheme.d.ts} +12 -52
- package/dist/EditorTheme.js +29 -0
- package/dist/EditorToolbar.d.ts +129 -0
- package/dist/EditorToolbar.js +394 -0
- package/dist/NativeEditorBridge.d.ts +242 -0
- package/dist/NativeEditorBridge.js +647 -0
- package/dist/NativeRichTextEditor.d.ts +142 -0
- package/dist/NativeRichTextEditor.js +649 -0
- package/dist/YjsCollaboration.d.ts +83 -0
- package/dist/YjsCollaboration.js +585 -0
- package/dist/addons.d.ts +70 -0
- package/dist/addons.js +77 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +26 -0
- package/dist/schemas.d.ts +35 -0
- package/{src/schemas.ts → dist/schemas.js} +62 -27
- package/dist/useNativeEditor.d.ts +40 -0
- package/dist/useNativeEditor.js +117 -0
- package/ios/EditorAddons.swift +26 -3
- 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/ios/EditorLayoutManager.swift +236 -0
- package/ios/EditorTheme.swift +51 -1
- package/ios/Generated_editor_core.swift +270 -2
- package/ios/NativeEditorExpoView.swift +612 -45
- package/ios/NativeEditorModule.swift +81 -0
- package/ios/PositionBridge.swift +22 -0
- package/ios/RenderBridge.swift +427 -39
- package/ios/RichTextEditorView.swift +1342 -18
- package/ios/editor_coreFFI/editor_coreFFI.h +209 -0
- package/package.json +80 -64
- package/rust/android/arm64-v8a/libeditor_core.so +0 -0
- package/rust/android/armeabi-v7a/libeditor_core.so +0 -0
- package/rust/android/x86_64/libeditor_core.so +0 -0
- package/rust/bindings/kotlin/uniffi/editor_core/editor_core.kt +404 -4
- package/src/EditorToolbar.tsx +0 -620
- package/src/NativeEditorBridge.ts +0 -607
- package/src/NativeRichTextEditor.tsx +0 -951
- package/src/addons.ts +0 -158
- package/src/index.ts +0 -63
- package/src/useNativeEditor.ts +0 -173
|
@@ -2,6 +2,7 @@ package com.apollohg.editor
|
|
|
2
2
|
|
|
3
3
|
import android.content.Context
|
|
4
4
|
import android.graphics.Color
|
|
5
|
+
import android.graphics.RectF
|
|
5
6
|
import android.graphics.drawable.GradientDrawable
|
|
6
7
|
import android.util.AttributeSet
|
|
7
8
|
import android.view.MotionEvent
|
|
@@ -17,6 +18,7 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
17
18
|
attrs: AttributeSet? = null,
|
|
18
19
|
defStyleAttr: Int = 0
|
|
19
20
|
) : LinearLayout(context, attrs, defStyleAttr) {
|
|
21
|
+
val editorViewport: FrameLayout
|
|
20
22
|
|
|
21
23
|
private class EditorScrollView(context: Context) : ScrollView(context) {
|
|
22
24
|
private fun updateParentIntercept(action: Int) {
|
|
@@ -43,8 +45,11 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
43
45
|
|
|
44
46
|
val editorEditText: EditorEditText
|
|
45
47
|
val editorScrollView: ScrollView
|
|
48
|
+
private val remoteSelectionOverlayView: RemoteSelectionOverlayView
|
|
49
|
+
private val imageResizeOverlayView: ImageResizeOverlayView
|
|
46
50
|
|
|
47
51
|
private var heightBehavior: EditorHeightBehavior = EditorHeightBehavior.FIXED
|
|
52
|
+
private var imageResizingEnabled = true
|
|
48
53
|
private var theme: EditorTheme? = null
|
|
49
54
|
private var baseBackgroundColor: Int = Color.WHITE
|
|
50
55
|
private var viewportBottomInsetPx: Int = 0
|
|
@@ -59,6 +64,7 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
59
64
|
} else {
|
|
60
65
|
editorEditText.unbindEditor()
|
|
61
66
|
}
|
|
67
|
+
refreshOverlays()
|
|
62
68
|
}
|
|
63
69
|
|
|
64
70
|
init {
|
|
@@ -69,9 +75,39 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
69
75
|
clipToPadding = false
|
|
70
76
|
isFillViewport = false
|
|
71
77
|
}
|
|
78
|
+
editorViewport = FrameLayout(context)
|
|
79
|
+
remoteSelectionOverlayView = RemoteSelectionOverlayView(context)
|
|
80
|
+
imageResizeOverlayView = ImageResizeOverlayView(context)
|
|
72
81
|
editorScrollView.addView(editorEditText, createEditorLayoutParams())
|
|
82
|
+
editorViewport.addView(
|
|
83
|
+
editorScrollView,
|
|
84
|
+
FrameLayout.LayoutParams(
|
|
85
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
86
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
editorViewport.addView(
|
|
90
|
+
remoteSelectionOverlayView,
|
|
91
|
+
FrameLayout.LayoutParams(
|
|
92
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
93
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
94
|
+
)
|
|
95
|
+
)
|
|
96
|
+
editorViewport.addView(
|
|
97
|
+
imageResizeOverlayView,
|
|
98
|
+
FrameLayout.LayoutParams(
|
|
99
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
100
|
+
ViewGroup.LayoutParams.MATCH_PARENT
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
remoteSelectionOverlayView.bind(this)
|
|
104
|
+
imageResizeOverlayView.bind(this)
|
|
105
|
+
editorScrollView.setOnScrollChangeListener { _, _, _, _, _ ->
|
|
106
|
+
refreshOverlays()
|
|
107
|
+
}
|
|
108
|
+
editorEditText.onSelectionOrContentMayChange = { refreshOverlays() }
|
|
73
109
|
|
|
74
|
-
addView(
|
|
110
|
+
addView(editorViewport, createContainerLayoutParams())
|
|
75
111
|
updateScrollContainerAppearance()
|
|
76
112
|
updateScrollContainerInsets()
|
|
77
113
|
}
|
|
@@ -84,6 +120,7 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
84
120
|
baseBackgroundColor = backgroundColor
|
|
85
121
|
editorEditText.setBaseStyle(textSizePx, textColor, backgroundColor)
|
|
86
122
|
updateScrollContainerAppearance()
|
|
123
|
+
refreshOverlays()
|
|
87
124
|
}
|
|
88
125
|
|
|
89
126
|
fun applyTheme(theme: EditorTheme?) {
|
|
@@ -100,8 +137,10 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
100
137
|
childHeight + editorScrollView.paddingTop + editorScrollView.paddingBottom - editorScrollView.height
|
|
101
138
|
)
|
|
102
139
|
editorScrollView.scrollTo(0, previousScrollY.coerceIn(0, maxScrollY))
|
|
140
|
+
refreshOverlays()
|
|
103
141
|
}
|
|
104
142
|
}
|
|
143
|
+
refreshOverlays()
|
|
105
144
|
}
|
|
106
145
|
|
|
107
146
|
fun setHeightBehavior(heightBehavior: EditorHeightBehavior) {
|
|
@@ -109,7 +148,7 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
109
148
|
this.heightBehavior = heightBehavior
|
|
110
149
|
editorEditText.setHeightBehavior(heightBehavior)
|
|
111
150
|
editorEditText.layoutParams = createEditorLayoutParams()
|
|
112
|
-
|
|
151
|
+
editorViewport.layoutParams = createContainerLayoutParams()
|
|
113
152
|
editorScrollView.isVerticalScrollBarEnabled = heightBehavior == EditorHeightBehavior.FIXED
|
|
114
153
|
editorScrollView.overScrollMode = if (heightBehavior == EditorHeightBehavior.FIXED) {
|
|
115
154
|
OVER_SCROLL_IF_CONTENT_SCROLLS
|
|
@@ -117,18 +156,53 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
117
156
|
OVER_SCROLL_NEVER
|
|
118
157
|
}
|
|
119
158
|
updateScrollContainerInsets()
|
|
159
|
+
refreshOverlays()
|
|
120
160
|
requestLayout()
|
|
121
161
|
}
|
|
122
162
|
|
|
163
|
+
fun setImageResizingEnabled(enabled: Boolean) {
|
|
164
|
+
if (imageResizingEnabled == enabled) return
|
|
165
|
+
imageResizingEnabled = enabled
|
|
166
|
+
editorEditText.setImageResizingEnabled(enabled)
|
|
167
|
+
refreshOverlays()
|
|
168
|
+
}
|
|
169
|
+
|
|
123
170
|
fun setViewportBottomInsetPx(bottomInsetPx: Int) {
|
|
124
171
|
val clampedInset = bottomInsetPx.coerceAtLeast(0)
|
|
125
172
|
if (viewportBottomInsetPx == clampedInset) return
|
|
126
173
|
viewportBottomInsetPx = clampedInset
|
|
127
174
|
updateScrollContainerInsets()
|
|
128
175
|
editorEditText.setViewportBottomInsetPx(clampedInset)
|
|
176
|
+
refreshOverlays()
|
|
129
177
|
requestLayout()
|
|
130
178
|
}
|
|
131
179
|
|
|
180
|
+
fun setRemoteSelections(selections: List<RemoteSelectionDecoration>) {
|
|
181
|
+
remoteSelectionOverlayView.setRemoteSelections(selections)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
fun refreshRemoteSelections() {
|
|
185
|
+
remoteSelectionOverlayView.invalidate()
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
fun imageResizeOverlayRectForTesting(): android.graphics.RectF? =
|
|
189
|
+
imageResizeOverlayView.visibleRectForTesting()
|
|
190
|
+
|
|
191
|
+
fun resizeSelectedImageForTesting(widthPx: Float, heightPx: Float) {
|
|
192
|
+
imageResizeOverlayView.simulateResizeForTesting(widthPx, heightPx)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
fun remoteSelectionDebugSnapshotsForTesting(): List<RemoteSelectionDebugSnapshot> =
|
|
196
|
+
remoteSelectionOverlayView.debugSnapshotsForTesting()
|
|
197
|
+
|
|
198
|
+
fun setRemoteSelectionScalarResolverForTesting(resolver: (Long, Int) -> Int) {
|
|
199
|
+
remoteSelectionOverlayView.docToScalarResolver = resolver
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fun setRemoteSelectionEditorIdForTesting(editorId: Long) {
|
|
203
|
+
remoteSelectionOverlayView.editorIdOverrideForTesting = editorId
|
|
204
|
+
}
|
|
205
|
+
|
|
132
206
|
fun setContent(html: String) {
|
|
133
207
|
if (editorId == 0L) return
|
|
134
208
|
val renderJSON = editorSetHtml(editorId.toULong(), html)
|
|
@@ -157,16 +231,16 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
157
231
|
val childWidthSpec = getChildMeasureSpec(
|
|
158
232
|
widthMeasureSpec,
|
|
159
233
|
paddingLeft + paddingRight,
|
|
160
|
-
|
|
234
|
+
editorViewport.layoutParams.width
|
|
161
235
|
)
|
|
162
236
|
val childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
|
|
163
|
-
|
|
237
|
+
editorViewport.measure(childWidthSpec, childHeightSpec)
|
|
164
238
|
|
|
165
239
|
val measuredWidth = resolveSize(
|
|
166
|
-
|
|
240
|
+
editorViewport.measuredWidth + paddingLeft + paddingRight,
|
|
167
241
|
widthMeasureSpec
|
|
168
242
|
)
|
|
169
|
-
val desiredHeight =
|
|
243
|
+
val desiredHeight = editorViewport.measuredHeight + paddingTop + paddingBottom
|
|
170
244
|
val measuredHeight = when (MeasureSpec.getMode(heightMeasureSpec)) {
|
|
171
245
|
MeasureSpec.AT_MOST -> desiredHeight.coerceAtMost(MeasureSpec.getSize(heightMeasureSpec))
|
|
172
246
|
else -> desiredHeight
|
|
@@ -176,11 +250,12 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
176
250
|
|
|
177
251
|
private fun updateScrollContainerAppearance() {
|
|
178
252
|
val cornerRadiusPx = (theme?.borderRadius ?: 0f) * resources.displayMetrics.density
|
|
179
|
-
|
|
253
|
+
editorViewport.background = GradientDrawable().apply {
|
|
180
254
|
cornerRadius = cornerRadiusPx
|
|
181
255
|
setColor(theme?.backgroundColor ?: baseBackgroundColor)
|
|
182
256
|
}
|
|
183
|
-
|
|
257
|
+
editorViewport.clipToOutline = cornerRadiusPx > 0f
|
|
258
|
+
editorScrollView.setBackgroundColor(Color.TRANSPARENT)
|
|
184
259
|
appliedCornerRadiusPx = cornerRadiusPx
|
|
185
260
|
}
|
|
186
261
|
|
|
@@ -208,4 +283,46 @@ class RichTextEditorView @JvmOverloads constructor(
|
|
|
208
283
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
209
284
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
210
285
|
)
|
|
286
|
+
|
|
287
|
+
internal fun selectedImageGeometry(): EditorEditText.SelectedImageGeometry? {
|
|
288
|
+
val geometry = editorEditText.selectedImageGeometry() ?: return null
|
|
289
|
+
return EditorEditText.SelectedImageGeometry(
|
|
290
|
+
docPos = geometry.docPos,
|
|
291
|
+
rect = RectF(
|
|
292
|
+
editorViewport.left + editorScrollView.left + editorEditText.left + geometry.rect.left,
|
|
293
|
+
editorViewport.top + editorScrollView.top + editorEditText.top + geometry.rect.top - editorScrollView.scrollY,
|
|
294
|
+
editorViewport.left + editorScrollView.left + editorEditText.left + geometry.rect.right,
|
|
295
|
+
editorViewport.top + editorScrollView.top + editorEditText.top + geometry.rect.bottom - editorScrollView.scrollY
|
|
296
|
+
)
|
|
297
|
+
)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
internal fun maximumImageWidthPx(): Float {
|
|
301
|
+
val availableWidth =
|
|
302
|
+
maxOf(editorEditText.width, editorEditText.measuredWidth) -
|
|
303
|
+
editorEditText.compoundPaddingLeft -
|
|
304
|
+
editorEditText.compoundPaddingRight
|
|
305
|
+
return availableWidth.coerceAtLeast(48).toFloat()
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
internal fun clampImageSize(
|
|
309
|
+
widthPx: Float,
|
|
310
|
+
heightPx: Float,
|
|
311
|
+
maximumWidthPx: Float = maximumImageWidthPx()
|
|
312
|
+
): Pair<Float, Float> {
|
|
313
|
+
val aspectRatio = maxOf(widthPx / maxOf(heightPx, 1f), 0.1f)
|
|
314
|
+
val clampedWidth = minOf(maxOf(48f, maximumWidthPx), maxOf(48f, widthPx))
|
|
315
|
+
val clampedHeight = maxOf(48f, clampedWidth / aspectRatio)
|
|
316
|
+
return clampedWidth to clampedHeight
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
internal fun resizeImage(docPos: Int, widthPx: Float, heightPx: Float) {
|
|
320
|
+
val (clampedWidth, clampedHeight) = clampImageSize(widthPx, heightPx)
|
|
321
|
+
editorEditText.resizeImageAtDocPos(docPos, clampedWidth, clampedHeight)
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
private fun refreshOverlays() {
|
|
325
|
+
remoteSelectionOverlayView.invalidate()
|
|
326
|
+
imageResizeOverlayView.refresh()
|
|
327
|
+
}
|
|
211
328
|
}
|
|
@@ -1,18 +1,5 @@
|
|
|
1
|
-
export type EditorFontWeight =
|
|
2
|
-
| 'normal'
|
|
3
|
-
| 'bold'
|
|
4
|
-
| '100'
|
|
5
|
-
| '200'
|
|
6
|
-
| '300'
|
|
7
|
-
| '400'
|
|
8
|
-
| '500'
|
|
9
|
-
| '600'
|
|
10
|
-
| '700'
|
|
11
|
-
| '800'
|
|
12
|
-
| '900';
|
|
13
|
-
|
|
1
|
+
export type EditorFontWeight = 'normal' | 'bold' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900';
|
|
14
2
|
export type EditorFontStyle = 'normal' | 'italic';
|
|
15
|
-
|
|
16
3
|
export interface EditorMentionTheme {
|
|
17
4
|
textColor?: string;
|
|
18
5
|
backgroundColor?: string;
|
|
@@ -30,7 +17,6 @@ export interface EditorMentionTheme {
|
|
|
30
17
|
optionHighlightedBackgroundColor?: string;
|
|
31
18
|
optionHighlightedTextColor?: string;
|
|
32
19
|
}
|
|
33
|
-
|
|
34
20
|
export interface EditorTextStyle {
|
|
35
21
|
fontFamily?: string;
|
|
36
22
|
fontSize?: number;
|
|
@@ -40,7 +26,6 @@ export interface EditorTextStyle {
|
|
|
40
26
|
lineHeight?: number;
|
|
41
27
|
spacingAfter?: number;
|
|
42
28
|
}
|
|
43
|
-
|
|
44
29
|
export interface EditorHeadingTheme {
|
|
45
30
|
h1?: EditorTextStyle;
|
|
46
31
|
h2?: EditorTextStyle;
|
|
@@ -49,21 +34,27 @@ export interface EditorHeadingTheme {
|
|
|
49
34
|
h5?: EditorTextStyle;
|
|
50
35
|
h6?: EditorTextStyle;
|
|
51
36
|
}
|
|
52
|
-
|
|
53
37
|
export interface EditorListTheme {
|
|
54
38
|
indent?: number;
|
|
55
39
|
itemSpacing?: number;
|
|
56
40
|
markerColor?: string;
|
|
57
41
|
markerScale?: number;
|
|
58
42
|
}
|
|
59
|
-
|
|
60
43
|
export interface EditorHorizontalRuleTheme {
|
|
61
44
|
color?: string;
|
|
62
45
|
thickness?: number;
|
|
63
46
|
verticalMargin?: number;
|
|
64
47
|
}
|
|
65
|
-
|
|
48
|
+
export interface EditorBlockquoteTheme {
|
|
49
|
+
text?: EditorTextStyle;
|
|
50
|
+
indent?: number;
|
|
51
|
+
borderColor?: string;
|
|
52
|
+
borderWidth?: number;
|
|
53
|
+
markerGap?: number;
|
|
54
|
+
}
|
|
55
|
+
export type EditorToolbarAppearance = 'custom' | 'native';
|
|
66
56
|
export interface EditorToolbarTheme {
|
|
57
|
+
appearance?: EditorToolbarAppearance;
|
|
67
58
|
backgroundColor?: string;
|
|
68
59
|
borderColor?: string;
|
|
69
60
|
borderWidth?: number;
|
|
@@ -77,17 +68,16 @@ export interface EditorToolbarTheme {
|
|
|
77
68
|
buttonActiveBackgroundColor?: string;
|
|
78
69
|
buttonBorderRadius?: number;
|
|
79
70
|
}
|
|
80
|
-
|
|
81
71
|
export interface EditorContentInsets {
|
|
82
72
|
top?: number;
|
|
83
73
|
right?: number;
|
|
84
74
|
bottom?: number;
|
|
85
75
|
left?: number;
|
|
86
76
|
}
|
|
87
|
-
|
|
88
77
|
export interface EditorTheme {
|
|
89
78
|
text?: EditorTextStyle;
|
|
90
79
|
paragraph?: EditorTextStyle;
|
|
80
|
+
blockquote?: EditorBlockquoteTheme;
|
|
91
81
|
headings?: EditorHeadingTheme;
|
|
92
82
|
list?: EditorListTheme;
|
|
93
83
|
horizontalRule?: EditorHorizontalRuleTheme;
|
|
@@ -97,34 +87,4 @@ export interface EditorTheme {
|
|
|
97
87
|
borderRadius?: number;
|
|
98
88
|
contentInsets?: EditorContentInsets;
|
|
99
89
|
}
|
|
100
|
-
|
|
101
|
-
function stripUndefined(value: unknown): unknown {
|
|
102
|
-
if (Array.isArray(value)) {
|
|
103
|
-
return value
|
|
104
|
-
.map((item) => stripUndefined(item))
|
|
105
|
-
.filter((item) => item !== undefined);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (value != null && typeof value === 'object') {
|
|
109
|
-
const entries = Object.entries(value as Record<string, unknown>)
|
|
110
|
-
.map(([key, entryValue]) => [key, stripUndefined(entryValue)] as const)
|
|
111
|
-
.filter(([, entryValue]) => entryValue !== undefined);
|
|
112
|
-
if (entries.length === 0) {
|
|
113
|
-
return undefined;
|
|
114
|
-
}
|
|
115
|
-
return Object.fromEntries(entries);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (typeof value === 'number' && !Number.isFinite(value)) {
|
|
119
|
-
return undefined;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return value;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export function serializeEditorTheme(theme?: EditorTheme): string | undefined {
|
|
126
|
-
if (!theme) return undefined;
|
|
127
|
-
const cleaned = stripUndefined(theme);
|
|
128
|
-
if (!cleaned || typeof cleaned !== 'object') return undefined;
|
|
129
|
-
return JSON.stringify(cleaned);
|
|
130
|
-
}
|
|
90
|
+
export declare function serializeEditorTheme(theme?: EditorTheme): string | undefined;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.serializeEditorTheme = serializeEditorTheme;
|
|
4
|
+
function stripUndefined(value) {
|
|
5
|
+
if (Array.isArray(value)) {
|
|
6
|
+
return value.map((item) => stripUndefined(item)).filter((item) => item !== undefined);
|
|
7
|
+
}
|
|
8
|
+
if (value != null && typeof value === 'object') {
|
|
9
|
+
const entries = Object.entries(value)
|
|
10
|
+
.map(([key, entryValue]) => [key, stripUndefined(entryValue)])
|
|
11
|
+
.filter(([, entryValue]) => entryValue !== undefined);
|
|
12
|
+
if (entries.length === 0) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
return Object.fromEntries(entries);
|
|
16
|
+
}
|
|
17
|
+
if (typeof value === 'number' && !Number.isFinite(value)) {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
function serializeEditorTheme(theme) {
|
|
23
|
+
if (!theme)
|
|
24
|
+
return undefined;
|
|
25
|
+
const cleaned = stripUndefined(theme);
|
|
26
|
+
if (!cleaned || typeof cleaned !== 'object')
|
|
27
|
+
return undefined;
|
|
28
|
+
return JSON.stringify(cleaned);
|
|
29
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import type { ActiveState, HistoryState } from './NativeEditorBridge';
|
|
2
|
+
import type { EditorToolbarTheme } from './EditorTheme';
|
|
3
|
+
export type EditorToolbarListType = 'bulletList' | 'orderedList';
|
|
4
|
+
export type EditorToolbarCommand = 'indentList' | 'outdentList' | 'undo' | 'redo';
|
|
5
|
+
export type EditorToolbarDefaultIconId = 'bold' | 'italic' | 'underline' | 'strike' | 'link' | 'image' | 'blockquote' | 'bulletList' | 'orderedList' | 'indentList' | 'outdentList' | 'lineBreak' | 'horizontalRule' | 'undo' | 'redo';
|
|
6
|
+
export interface EditorToolbarSFSymbolIcon {
|
|
7
|
+
type: 'sfSymbol';
|
|
8
|
+
name: string;
|
|
9
|
+
}
|
|
10
|
+
export interface EditorToolbarMaterialIcon {
|
|
11
|
+
type: 'material';
|
|
12
|
+
name: string;
|
|
13
|
+
}
|
|
14
|
+
export type EditorToolbarIcon = {
|
|
15
|
+
type: 'default';
|
|
16
|
+
id: EditorToolbarDefaultIconId;
|
|
17
|
+
} | {
|
|
18
|
+
type: 'glyph';
|
|
19
|
+
text: string;
|
|
20
|
+
} | {
|
|
21
|
+
type: 'platform';
|
|
22
|
+
ios?: EditorToolbarSFSymbolIcon;
|
|
23
|
+
android?: EditorToolbarMaterialIcon;
|
|
24
|
+
fallbackText?: string;
|
|
25
|
+
};
|
|
26
|
+
export type EditorToolbarItem = {
|
|
27
|
+
type: 'mark';
|
|
28
|
+
mark: string;
|
|
29
|
+
label: string;
|
|
30
|
+
icon: EditorToolbarIcon;
|
|
31
|
+
key?: string;
|
|
32
|
+
} | {
|
|
33
|
+
type: 'link';
|
|
34
|
+
label: string;
|
|
35
|
+
icon: EditorToolbarIcon;
|
|
36
|
+
key?: string;
|
|
37
|
+
} | {
|
|
38
|
+
type: 'image';
|
|
39
|
+
label: string;
|
|
40
|
+
icon: EditorToolbarIcon;
|
|
41
|
+
key?: string;
|
|
42
|
+
} | {
|
|
43
|
+
type: 'blockquote';
|
|
44
|
+
label: string;
|
|
45
|
+
icon: EditorToolbarIcon;
|
|
46
|
+
key?: string;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'list';
|
|
49
|
+
listType: EditorToolbarListType;
|
|
50
|
+
label: string;
|
|
51
|
+
icon: EditorToolbarIcon;
|
|
52
|
+
key?: string;
|
|
53
|
+
} | {
|
|
54
|
+
type: 'command';
|
|
55
|
+
command: EditorToolbarCommand;
|
|
56
|
+
label: string;
|
|
57
|
+
icon: EditorToolbarIcon;
|
|
58
|
+
key?: string;
|
|
59
|
+
} | {
|
|
60
|
+
type: 'node';
|
|
61
|
+
nodeType: string;
|
|
62
|
+
label: string;
|
|
63
|
+
icon: EditorToolbarIcon;
|
|
64
|
+
key?: string;
|
|
65
|
+
} | {
|
|
66
|
+
type: 'separator';
|
|
67
|
+
key?: string;
|
|
68
|
+
} | {
|
|
69
|
+
type: 'action';
|
|
70
|
+
key: string;
|
|
71
|
+
label: string;
|
|
72
|
+
icon: EditorToolbarIcon;
|
|
73
|
+
isActive?: boolean;
|
|
74
|
+
isDisabled?: boolean;
|
|
75
|
+
};
|
|
76
|
+
export declare const DEFAULT_EDITOR_TOOLBAR_ITEMS: readonly EditorToolbarItem[];
|
|
77
|
+
export interface EditorToolbarProps {
|
|
78
|
+
/** Currently active marks and nodes from the Rust engine. */
|
|
79
|
+
activeState: ActiveState;
|
|
80
|
+
/** Current undo/redo availability. */
|
|
81
|
+
historyState: HistoryState;
|
|
82
|
+
/** Toggle bold mark. */
|
|
83
|
+
onToggleBold: () => void;
|
|
84
|
+
/** Toggle italic mark. */
|
|
85
|
+
onToggleItalic: () => void;
|
|
86
|
+
/** Toggle underline mark. */
|
|
87
|
+
onToggleUnderline: () => void;
|
|
88
|
+
/** Toggle strikethrough mark. */
|
|
89
|
+
onToggleStrike: () => void;
|
|
90
|
+
/** Toggle bullet list. */
|
|
91
|
+
onToggleBulletList?: () => void;
|
|
92
|
+
/** Toggle blockquote wrapping. */
|
|
93
|
+
onToggleBlockquote?: () => void;
|
|
94
|
+
/** Toggle ordered list. */
|
|
95
|
+
onToggleOrderedList?: () => void;
|
|
96
|
+
/** Indent the current list item. */
|
|
97
|
+
onIndentList?: () => void;
|
|
98
|
+
/** Outdent the current list item. */
|
|
99
|
+
onOutdentList?: () => void;
|
|
100
|
+
/** Insert horizontal rule. */
|
|
101
|
+
onInsertHorizontalRule?: () => void;
|
|
102
|
+
/** Insert inline hard break. */
|
|
103
|
+
onInsertLineBreak?: () => void;
|
|
104
|
+
/** Undo the last operation. */
|
|
105
|
+
onUndo: () => void;
|
|
106
|
+
/** Redo the last undone operation. */
|
|
107
|
+
onRedo: () => void;
|
|
108
|
+
/** Generic mark toggle handler used by configurable mark buttons. */
|
|
109
|
+
onToggleMark?: (mark: string) => void;
|
|
110
|
+
/** Generic list toggle handler used by configurable list buttons. */
|
|
111
|
+
onToggleListType?: (listType: EditorToolbarListType) => void;
|
|
112
|
+
/** Generic node insertion handler used by configurable node buttons. */
|
|
113
|
+
onInsertNodeType?: (nodeType: string) => void;
|
|
114
|
+
/** Generic command handler used by configurable command buttons. */
|
|
115
|
+
onRunCommand?: (command: EditorToolbarCommand) => void;
|
|
116
|
+
/** Generic action handler for arbitrary JS-defined toolbar buttons. */
|
|
117
|
+
onToolbarAction?: (key: string) => void;
|
|
118
|
+
/** Link button handler used by first-class link toolbar items. */
|
|
119
|
+
onRequestLink?: () => void;
|
|
120
|
+
/** Image button handler used by first-class image toolbar items. */
|
|
121
|
+
onRequestImage?: () => void;
|
|
122
|
+
/** Displayed toolbar items, in order. Defaults to the built-in toolbar. */
|
|
123
|
+
toolbarItems?: readonly EditorToolbarItem[];
|
|
124
|
+
/** Optional theme overrides for toolbar chrome and button colors. */
|
|
125
|
+
theme?: EditorToolbarTheme;
|
|
126
|
+
/** Whether to render the built-in top separator line. */
|
|
127
|
+
showTopBorder?: boolean;
|
|
128
|
+
}
|
|
129
|
+
export declare function EditorToolbar({ activeState, historyState, onToggleBold, onToggleItalic, onToggleUnderline, onToggleStrike, onToggleBulletList, onToggleBlockquote, onToggleOrderedList, onIndentList, onOutdentList, onInsertHorizontalRule, onInsertLineBreak, onUndo, onRedo, onToggleMark, onToggleListType, onInsertNodeType, onRunCommand, onToolbarAction, onRequestLink, onRequestImage, toolbarItems, theme, showTopBorder, }: EditorToolbarProps): import("react/jsx-runtime").JSX.Element;
|