@octane-xplat/richtext 0.10.0 → 0.11.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.
@@ -0,0 +1,357 @@
1
+ import Aztec
2
+ import Foundation
3
+ import UIKit
4
+
5
+ /// NativeScript-facing facade over AztecEditor-iOS `TextView`.
6
+ ///
7
+ /// Every member reachable from JavaScript is `@objc`-visible and uses only
8
+ /// ObjC-representable types (String, Bool, NSNumber, NSArray, blocks), so the
9
+ /// NativeScript metadata generator sees the whole surface. The leaf drives
10
+ /// this view directly; block properties carry the event callbacks.
11
+ ///
12
+ /// `placeholder` and sub/superscript toggles are wrapper-built: Aztec exposes
13
+ /// no native API for either. `taskList` and text alignment have no Aztec-iOS
14
+ /// equivalent at all — `applyFormat`/`isFormatActive` report them as
15
+ /// unsupported instead of faking a result.
16
+ @objcMembers
17
+ public final class XplatAztecEditorView: UIView, UITextViewDelegate {
18
+
19
+ /// FormattingIdentifier rawValue -> leaf format name. Aztec's identifier
20
+ /// map has no entries for `pre`, sub/superscript, task lists, or
21
+ /// alignment; those are checked at the attribute level below or honestly
22
+ /// reported as unsupported.
23
+ private static let identifierFormats: [String: String] = [
24
+ "bold": "bold",
25
+ "italic": "italic",
26
+ "underline": "underline",
27
+ "strikethrough": "strikethrough",
28
+ "code": "code",
29
+ "blockquote": "blockquote",
30
+ "orderedlist": "orderedList",
31
+ "unorderedlist": "bulletList",
32
+ "mark": "highlight",
33
+ "p": "paragraph",
34
+ "header1": "heading1",
35
+ "header2": "heading2",
36
+ "header3": "heading3",
37
+ "header4": "heading4",
38
+ "header5": "heading5",
39
+ "header6": "heading6",
40
+ "horizontalruler": "horizontalRule",
41
+ "link": "link",
42
+ ]
43
+
44
+ public let textView = TextView(
45
+ defaultFont: UIFont.preferredFont(forTextStyle: .body),
46
+ defaultMissingImage: UIImage())
47
+ private let placeholderLabel = UILabel()
48
+
49
+ // MARK: - JS callbacks
50
+
51
+ public var onTextChange: (() -> Void)?
52
+ public var onSelectionChange: ((NSNumber, NSNumber, NSArray) -> Void)?
53
+ public var onFocus: (() -> Void)?
54
+ public var onBlur: (() -> Void)?
55
+
56
+ public override init(frame: CGRect) {
57
+ super.init(frame: frame)
58
+ textView.delegate = self
59
+ textView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
60
+ addSubview(textView)
61
+
62
+ placeholderLabel.textColor = .secondaryLabel
63
+ placeholderLabel.font = textView.font
64
+ placeholderLabel.numberOfLines = 1
65
+ placeholderLabel.isUserInteractionEnabled = false
66
+ addSubview(placeholderLabel)
67
+ updatePlaceholderVisibility()
68
+ }
69
+
70
+ @available(*, unavailable)
71
+ public required init?(coder: NSCoder) {
72
+ fatalError("init(coder:) is not supported")
73
+ }
74
+
75
+ public override func layoutSubviews() {
76
+ super.layoutSubviews()
77
+ textView.frame = bounds
78
+ let inset = textView.textContainerInset
79
+ let padding = textView.textContainer.lineFragmentPadding
80
+ placeholderLabel.frame = CGRect(
81
+ x: inset.left + padding,
82
+ y: inset.top,
83
+ width: max(0, bounds.width - inset.left - inset.right - padding * 2),
84
+ height: placeholderLabel.font.lineHeight)
85
+ }
86
+
87
+ // MARK: - Props
88
+
89
+ public var placeholder: String? {
90
+ get { placeholderLabel.text }
91
+ set {
92
+ placeholderLabel.text = newValue
93
+ updatePlaceholderVisibility()
94
+ }
95
+ }
96
+
97
+ public var editable: Bool {
98
+ get { textView.isEditable }
99
+ set { textView.isEditable = newValue }
100
+ }
101
+
102
+ private func updatePlaceholderVisibility() {
103
+ let hasText = textView.textStorage.length > 0
104
+ placeholderLabel.isHidden = hasText || (placeholderLabel.text ?? "").isEmpty
105
+ }
106
+
107
+ // MARK: - Document
108
+
109
+ public func getHTML() -> String {
110
+ textView.getHTML(prettify: false)
111
+ }
112
+
113
+ public func setHTML(_ html: String) {
114
+ textView.setHTML(html)
115
+ updatePlaceholderVisibility()
116
+ }
117
+
118
+ // MARK: - Formats
119
+
120
+ /// Returns false for formats this engine cannot apply (`taskList`,
121
+ /// `align*`, `link` — the link API is `linkTo`) and unknown names.
122
+ public func applyFormat(_ format: String) -> Bool {
123
+ let range = textView.selectedRange
124
+ switch format {
125
+ case "bold": textView.toggleBold(range: range)
126
+ case "italic": textView.toggleItalic(range: range)
127
+ case "underline": textView.toggleUnderline(range: range)
128
+ case "strikethrough": textView.toggleStrikethrough(range: range)
129
+ case "code": textView.toggleCode(range: range)
130
+ case "blockquote": textView.toggleBlockquote(range: range)
131
+ case "bulletList": textView.toggleUnorderedList(range: range)
132
+ case "orderedList": textView.toggleOrderedList(range: range)
133
+ case "codeBlock": textView.togglePre(range: range)
134
+ case "paragraph": textView.toggleHeader(.none, range: range)
135
+ case "heading1": textView.toggleHeader(.h1, range: range)
136
+ case "heading2": textView.toggleHeader(.h2, range: range)
137
+ case "heading3": textView.toggleHeader(.h3, range: range)
138
+ case "heading4": textView.toggleHeader(.h4, range: range)
139
+ case "heading5": textView.toggleHeader(.h5, range: range)
140
+ case "heading6": textView.toggleHeader(.h6, range: range)
141
+ case "highlight": textView.toggleMark(range: range, color: nil, resetColor: true)
142
+ case "horizontalRule": textView.replaceWithHorizontalRuler(at: range)
143
+ case "subscript": toggleBaselineOffset(-4, in: range)
144
+ case "superscript": toggleBaselineOffset(4, in: range)
145
+ default: return false
146
+ }
147
+ return true
148
+ }
149
+
150
+ public func isFormatActive(_ format: String) -> Bool {
151
+ activeFormatSet().contains(format)
152
+ }
153
+
154
+ public func activeFormats() -> [String] {
155
+ activeFormatSet().sorted()
156
+ }
157
+
158
+ private func activeFormatSet() -> Set<String> {
159
+ var out = Set<String>()
160
+ for identifier in textView.formattingIdentifiersSpanningRange(textView.selectedRange) {
161
+ if let name = XplatAztecEditorView.identifierFormats[identifier.rawValue] {
162
+ out.insert(name)
163
+ }
164
+ }
165
+ let attributeSets = activeAttributeSets()
166
+ if !attributeSets.isEmpty {
167
+ if attributeSets.allSatisfy(hasPreProperty) { out.insert("codeBlock") }
168
+ if attributeSets.allSatisfy({ baselineOffset(of: $0) > 0 }) { out.insert("superscript") }
169
+ if attributeSets.allSatisfy({ baselineOffset(of: $0) < 0 }) { out.insert("subscript") }
170
+ }
171
+ return out
172
+ }
173
+
174
+ /// The attribute dictionaries the identifier check would consult:
175
+ /// typing attributes on an empty document, the attributes at the caret
176
+ /// for a collapsed selection, every run in the selection otherwise.
177
+ private func activeAttributeSets() -> [[NSAttributedString.Key: Any]] {
178
+ let storage = textView.textStorage
179
+ let range = textView.selectedRange
180
+ guard storage.length > 0 else { return [textView.typingAttributes] }
181
+ if range.length == 0 {
182
+ let index = min(range.location, storage.length - 1)
183
+ return [storage.attributes(at: index, effectiveRange: nil)]
184
+ }
185
+ var sets: [[NSAttributedString.Key: Any]] = []
186
+ storage.enumerateAttributes(in: range, options: []) { attributes, _, _ in
187
+ sets.append(attributes)
188
+ }
189
+ return sets
190
+ }
191
+
192
+ private func hasPreProperty(_ attributes: [NSAttributedString.Key: Any]) -> Bool {
193
+ guard let style = attributes[.paragraphStyle] as? ParagraphStyle else { return false }
194
+ // HTMLPre is module-internal to Aztec — match the property by type name.
195
+ return style.hasProperty { String(describing: Swift.type(of: $0)) == "HTMLPre" }
196
+ }
197
+
198
+ private func baselineOffset(of attributes: [NSAttributedString.Key: Any]) -> Int {
199
+ (attributes[.baselineOffset] as? NSNumber)?.intValue ?? 0
200
+ }
201
+
202
+ /// Aztec-iOS has no public sub/superscript toggle — apply or clear
203
+ /// `.baselineOffset` directly; the HTML converter serializes it back as
204
+ /// `<sub>`/`<sup>`. Collapsed selections toggle the typing attributes.
205
+ private func toggleBaselineOffset(_ offset: Int, in range: NSRange) {
206
+ let storage = textView.textStorage
207
+ if range.length == 0 || storage.length == 0 {
208
+ var attributes = textView.typingAttributes
209
+ let current = baselineOffset(of: attributes)
210
+ attributes[.baselineOffset] = current == offset ? nil : NSNumber(value: offset)
211
+ textView.typingAttributes = attributes
212
+ return
213
+ }
214
+ guard range.location + range.length <= storage.length else { return }
215
+
216
+ let original = storage.attributedSubstring(from: range)
217
+ let restoredRange = NSRange(location: range.location, length: range.length)
218
+ textView.undoManager?.registerUndo(withTarget: textView) { target in
219
+ target.textStorage.replaceCharacters(in: restoredRange, with: original)
220
+ target.delegate?.textViewDidChange?(target)
221
+ }
222
+
223
+ storage.beginEditing()
224
+ storage.enumerateAttribute(.baselineOffset, in: range, options: []) { value, subrange, _ in
225
+ let current = (value as? NSNumber)?.intValue ?? 0
226
+ if current == offset {
227
+ storage.removeAttribute(.baselineOffset, range: subrange)
228
+ } else {
229
+ storage.addAttribute(.baselineOffset, value: NSNumber(value: offset), range: subrange)
230
+ }
231
+ }
232
+ storage.endEditing()
233
+ textViewDidChange(textView)
234
+ }
235
+
236
+ // MARK: - Links
237
+
238
+ public func linkTo(_ urlString: String, anchor: String?) -> Bool {
239
+ guard let url = URL(string: urlString) else { return false }
240
+ textView.setLink(url, title: anchor ?? urlString, inRange: textView.selectedRange)
241
+ return true
242
+ }
243
+
244
+ public func removeLink() {
245
+ textView.removeLink(inRange: textView.selectedRange)
246
+ }
247
+
248
+ // MARK: - History
249
+
250
+ public func undo() {
251
+ textView.undoManager?.undo()
252
+ }
253
+
254
+ public func redo() {
255
+ textView.undoManager?.redo()
256
+ }
257
+
258
+ // MARK: - Structural commands
259
+
260
+ /// Block split at the caret — Aztec's `insertText` runs the same
261
+ /// paragraph/list handling as a real Enter keystroke. A non-collapsed
262
+ /// selection is deleted first, matching the leaf contract.
263
+ public func split() -> Bool {
264
+ let range = textView.selectedRange
265
+ guard range.location != NSNotFound,
266
+ range.location + range.length <= textView.textStorage.length
267
+ else { return false }
268
+ if range.length > 0 {
269
+ textView.deleteBackward()
270
+ }
271
+ textView.insertText("\n")
272
+ return true
273
+ }
274
+
275
+ /// Merge into the previous block — Backspace at a collapsed caret that
276
+ /// sits directly after a `\n` boundary, matching the leaf contract.
277
+ public func join() -> Bool {
278
+ let range = textView.selectedRange
279
+ guard range.length == 0, range.location > 0,
280
+ range.location <= textView.textStorage.length
281
+ else { return false }
282
+ let text = textView.textStorage.string as NSString
283
+ guard text.character(at: range.location - 1) == 0x000A else { return false }
284
+ textView.deleteBackward()
285
+ return true
286
+ }
287
+
288
+ /// `increaseIndent` nests a list item under its previous sibling or
289
+ /// deepens a blockquote; on plain blocks Aztec falls back to a `\t`
290
+ /// insert, so indent is available whenever the view can edit.
291
+ public func canIndent() -> Bool {
292
+ textView.isEditable
293
+ }
294
+
295
+ public func indent() -> Bool {
296
+ guard canIndent() else { return false }
297
+ textView.increaseIndent()
298
+ return true
299
+ }
300
+
301
+ /// `decreaseIndent` only acts on list/blockquote typing attributes —
302
+ /// there is no plain-block outdent, so report availability up front.
303
+ public func canOutdent() -> Bool {
304
+ guard textView.isEditable else { return false }
305
+ guard let style = textView.typingAttributes[.paragraphStyle] as? ParagraphStyle else {
306
+ return false
307
+ }
308
+ // TextList/Blockquote/HTMLLi are module-internal to Aztec.
309
+ return style.hasProperty {
310
+ let name = String(describing: Swift.type(of: $0))
311
+ return name == "TextList" || name == "Blockquote" || name == "HTMLLi"
312
+ }
313
+ }
314
+
315
+ public func outdent() -> Bool {
316
+ guard canOutdent() else { return false }
317
+ textView.decreaseIndent()
318
+ return true
319
+ }
320
+
321
+ // MARK: - Focus
322
+
323
+ public func focus() {
324
+ _ = textView.becomeFirstResponder()
325
+ }
326
+
327
+ public func blur() {
328
+ _ = textView.resignFirstResponder()
329
+ }
330
+
331
+ public func focused() -> Bool {
332
+ textView.isFirstResponder
333
+ }
334
+
335
+ // MARK: - UITextViewDelegate
336
+
337
+ public func textViewDidChange(_ textView: UITextView) {
338
+ updatePlaceholderVisibility()
339
+ onTextChange?()
340
+ }
341
+
342
+ public func textViewDidChangeSelection(_ textView: UITextView) {
343
+ let range = self.textView.selectedRange
344
+ onSelectionChange?(
345
+ NSNumber(value: range.location),
346
+ NSNumber(value: range.location + range.length),
347
+ activeFormats() as NSArray)
348
+ }
349
+
350
+ public func textViewDidBeginEditing(_ textView: UITextView) {
351
+ onFocus?()
352
+ }
353
+
354
+ public func textViewDidEndEditing(_ textView: UITextView) {
355
+ onBlur?()
356
+ }
357
+ }
@@ -178,6 +178,45 @@ export function RichTextEditor(props: RichTextEditorProps) @{
178
178
  },
179
179
  undo: () => e.undo(),
180
180
  redo: () => e.redo(),
181
+ split: () => {
182
+ const selStart = e.getSelectionStart()
183
+ const selEnd = e.getSelectionEnd()
184
+ if (selStart < 0 || selEnd < 0) return false
185
+ const start = Math.min(selStart, selEnd)
186
+ const end = Math.max(selStart, selEnd)
187
+ const text = e.getText()
188
+ if (end > String(text).length) return false
189
+ if (end > start) text.delete(start, end)
190
+ text.insert(start, '\n')
191
+ e.setSelection(start + 1)
192
+ emitChange()
193
+ return true
194
+ },
195
+ join: () => {
196
+ const start = e.getSelectionStart()
197
+ if (start <= 0 || e.getSelectionEnd() !== start) return false
198
+ const text = e.getText()
199
+ const s = String(text)
200
+ if (start > s.length || s.charAt(start - 1) !== '\n') return false
201
+ text.delete(start - 1, start)
202
+ e.setSelection(start - 1)
203
+ emitChange()
204
+ return true
205
+ },
206
+ indent: () => {
207
+ if (!e.isIndentAvailable()) return false
208
+ e.indent()
209
+ emitChange()
210
+ return true
211
+ },
212
+ outdent: () => {
213
+ if (!e.isOutdentAvailable()) return false
214
+ e.outdent()
215
+ emitChange()
216
+ return true
217
+ },
218
+ canIndent: () => Boolean(e.isIndentAvailable()),
219
+ canOutdent: () => Boolean(e.isOutdentAvailable()),
181
220
  focus: () => {
182
221
  e.requestFocus()
183
222
  showKeyboard(e)
@@ -1,15 +1,230 @@
1
1
  /** @jsxImportSource @nativescript-community/octane */
2
- import type { RichTextEditorProps } from './types'
2
+ import { useImperativeHandle, useCallback, useEffect, useRef } from 'octane'
3
+ import { View } from '@nativescript/core'
4
+ import { registerElement } from '@nativescript-community/octane'
5
+ import type { Attributes } from '@nativescript-community/octane/intrinsics'
6
+ import type {
7
+ RichTextEditorHandle,
8
+ RichTextEditorProps,
9
+ RichTextFormat,
10
+ } from './types'
3
11
 
4
- export const supported = false
12
+ /** `XplatAztecEditorView` arrives through the leaf's
13
+ * `platforms/ios/src` Swift facade over `Aztec.TextView` — there are no
14
+ * generated typings for it, so every touch goes through `as any` at one
15
+ * boundary instead of `declare const` per call site. */
16
+ declare const XplatAztecEditorView: any
5
17
 
6
- /** iOS bring-up is deferred — the Swift facade over Aztec-iOS lands in a
7
- * follow-up. Renders the placeholder so a shared screen still mounts. */
18
+ /** AztecEditor-iOS has no task-list or text-alignment API — the facade
19
+ * reports these as unsupported rather than faking a result. `link` is
20
+ * valid but goes through `linkTo`, matching the Android leaf. */
21
+ const UNSUPPORTED: ReadonlySet<RichTextFormat> = new Set([
22
+ 'taskList',
23
+ 'alignLeft',
24
+ 'alignCenter',
25
+ 'alignRight',
26
+ ])
27
+
28
+ class AztecEditor extends View {
29
+ createNativeView() {
30
+ return new XplatAztecEditorView()
31
+ }
32
+ }
33
+
34
+ declare module '@nativescript-community/octane/intrinsics' {
35
+ interface NativeScriptElements {
36
+ xplatrichtext: Attributes<typeof AztecEditor>
37
+ }
38
+ }
39
+
40
+ registerElement('xplatrichtext', AztecEditor)
41
+
42
+ export const supported = true
43
+
44
+ /** WordPress AztecEditor-iOS surface — a UITextView-backed native editor,
45
+ * not a WebView. Content in/out is HTML (`setHTML`/`getHTML`); JSON
46
+ * interchange lives one layer up in @octane-xplat/tiptap. */
8
47
  export function RichTextEditor(props: RichTextEditorProps) @{
9
- <label
48
+ const host = useRef<any>(null)
49
+ const lastEmitted = useRef<string | undefined>(undefined)
50
+ const applyingValue = useRef(false)
51
+ const emitTimer = useRef<any>(null)
52
+ const warned = useRef<Set<string>>(new Set())
53
+
54
+ // Stable identity — an inline closure re-fires ref(null)/ref(v) per
55
+ // render, which would bounce host.current around the effects below.
56
+ useImperativeHandle(props.ref, () => handle(editor()), []);
57
+
58
+ const bindHost = useCallback((v: any) => {
59
+ host.current = v
60
+ }, [])
61
+
62
+ const editor = () => host.current?.ios
63
+
64
+ const emitChange = () => {
65
+ const e = editor()
66
+ if (!e || applyingValue.current) return
67
+ clearTimeout(emitTimer.current)
68
+ emitTimer.current = setTimeout(() => {
69
+ const current = editor()
70
+ if (!current || applyingValue.current) return
71
+ const html = current.getHTML()
72
+ lastEmitted.current = html
73
+ props.onChange?.(html)
74
+ }, 150)
75
+ }
76
+
77
+ const reportUnsupported = (format: RichTextFormat) => {
78
+ if (warned.current.has(format)) return
79
+ warned.current.add(format)
80
+ console.warn(
81
+ `[richtext] apply('${format}') is not supported by the iOS Aztec surface`,
82
+ )
83
+ }
84
+
85
+ const handle = (e: any): RichTextEditorHandle => ({
86
+ getHTML: () => e.getHTML(),
87
+ setHTML: (html: string) => {
88
+ applyingValue.current = true
89
+ try {
90
+ e.setHTML(html)
91
+ lastEmitted.current = html
92
+ } finally {
93
+ applyingValue.current = false
94
+ }
95
+ },
96
+ apply: (format: RichTextFormat) => {
97
+ if (format === 'link') return
98
+ if (UNSUPPORTED.has(format) || !e.applyFormat(format)) {
99
+ reportUnsupported(format)
100
+ return
101
+ }
102
+
103
+ emitChange()
104
+ },
105
+ linkTo: (url: string, anchor?: string) => {
106
+ e.linkTo(url, anchor ?? null)
107
+ emitChange()
108
+ },
109
+ removeLink: () => {
110
+ e.removeLink()
111
+ emitChange()
112
+ },
113
+ isActive: (format: RichTextFormat) =>
114
+ !UNSUPPORTED.has(format) && Boolean(e.isFormatActive(format)),
115
+ undo: () => e.undo(),
116
+ redo: () => e.redo(),
117
+ split: () => {
118
+ const ok = Boolean(e.split())
119
+ if (ok) emitChange()
120
+ return ok
121
+ },
122
+ join: () => {
123
+ const ok = Boolean(e.join())
124
+ if (ok) emitChange()
125
+ return ok
126
+ },
127
+ indent: () => {
128
+ const ok = Boolean(e.indent())
129
+ if (ok) emitChange()
130
+ return ok
131
+ },
132
+ outdent: () => {
133
+ const ok = Boolean(e.outdent())
134
+ if (ok) emitChange()
135
+ return ok
136
+ },
137
+ canIndent: () => Boolean(e.canIndent()),
138
+ canOutdent: () => Boolean(e.canOutdent()),
139
+ focus: () => e.focus(),
140
+ blur: () => e.blur(),
141
+ isFocused: () => e.focused(),
142
+ native: e,
143
+ })
144
+
145
+ useEffect(() => {
146
+ const e = editor()
147
+ if (!e) return
148
+
149
+ e.onTextChange = () => emitChange()
150
+ e.onSelectionChange = (start: number, end: number, active: any) => {
151
+ const formats: RichTextFormat[] = []
152
+ const count = active?.count ?? 0
153
+ for (let i = 0; i < count; i++) {
154
+ formats.push(active.objectAtIndex(i))
155
+ }
156
+
157
+ props.onSelectionChange?.({ start, end, active: formats })
158
+ }
159
+
160
+ e.onFocus = () => props.onFocus?.()
161
+ e.onBlur = () => props.onBlur?.()
162
+
163
+ if (props.placeholder !== undefined) e.placeholder = props.placeholder
164
+ e.editable = props.editable !== false
165
+
166
+ if (props.value !== undefined) {
167
+ applyingValue.current = true
168
+ try {
169
+ e.setHTML(props.value)
170
+ } finally {
171
+ applyingValue.current = false
172
+ }
173
+ }
174
+
175
+ if (props.autofocus) e.focus()
176
+
177
+ if (props.ios) Object.assign(e, props.ios)
178
+ props.onReady?.()
179
+
180
+ return () => {
181
+ e.onTextChange = null
182
+ e.onSelectionChange = null
183
+ e.onFocus = null
184
+ e.onBlur = null
185
+ }
186
+ }, [])
187
+
188
+ // `value` lands like a controlled prop, minus the loop: text the editor
189
+ // emitted through onChange is never pushed back; any other change is an
190
+ // external reset (Aztec-iOS setHTML is non-undoable, matching Android).
191
+ useEffect(() => {
192
+ const e = editor()
193
+ if (!e || props.value === undefined) return
194
+ if (props.value === lastEmitted.current) return
195
+ applyingValue.current = true
196
+ try {
197
+ e.setHTML(props.value)
198
+ } finally {
199
+ applyingValue.current = false
200
+ }
201
+ }, [props.value])
202
+
203
+ useEffect(() => {
204
+ const e = editor()
205
+ if (e) e.editable = props.editable !== false
206
+ }, [props.editable])
207
+
208
+ useEffect(() => {
209
+ const e = editor()
210
+ if (e && props.placeholder !== undefined) e.placeholder = props.placeholder
211
+ }, [props.placeholder])
212
+
213
+ <xplatrichtext
214
+ ref={bindHost}
10
215
  id={props.id}
11
216
  className={props.className}
12
217
  style={props.style}
13
- text="Rich text editing is not supported on iOS yet."
218
+ row={props.row}
219
+ col={props.col}
220
+ rowSpan={props.rowSpan}
221
+ colSpan={props.colSpan}
222
+ dock={props.dock}
223
+ left={props.left}
224
+ top={props.top}
225
+ flexGrow={props.flexGrow}
226
+ flexShrink={props.flexShrink}
227
+ alignSelf={props.alignSelf}
228
+ order={props.order}
14
229
  />;
15
230
  }
@@ -49,6 +49,15 @@ export function createEditorSession(
49
49
  isActive: (format) => active.includes(format),
50
50
  undo: () => command('undo'),
51
51
  redo: () => command('redo'),
52
+ // Structural commands are Aztec-only for now: the AppKit transport
53
+ // is one-way async dispatch, so a synchronous "applied" result cannot
54
+ // be reported and a fire-and-forget forward would lie to the caller.
55
+ split: () => false,
56
+ join: () => false,
57
+ indent: () => false,
58
+ outdent: () => false,
59
+ canIndent: () => false,
60
+ canOutdent: () => false,
52
61
  focus: () => command('focus'),
53
62
  blur: () => command('blur'),
54
63
  isFocused: () => focused,
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  // Keep this relative import extensionless so the NativeScript resolver picks
2
- // the Android (Aztec) or iOS (stub until the Swift facade lands) component
3
- // for the current build target.
2
+ // the Android (Aztec) or iOS (AztecEditor-iOS facade) component for the
3
+ // current build target.
4
4
  export { RichTextEditor, supported } from './RichTextEditor'
5
5
  export type * from './types'