Created
April 6, 2023 21:26
-
-
Save jamesporter/53ee28e135f2d540a032c439fa834737 to your computer and use it in GitHub Desktop.
Lexical iOS SwiftUI integration PoC
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import SwiftUI | |
import Lexical | |
struct ContentView: View { | |
@StateObject var store = LexicalStore() | |
var body: some View { | |
VStack { | |
LexicalText(store: store) | |
.toolbar { | |
Button { | |
store.dispatchCommand(type: .formatText, payload: TextFormatType.bold) | |
} label: { | |
ToolbarImage(systemName: "bold", active: store.isBold) | |
}.keyboardShortcut("b") | |
Button { | |
store.dispatchCommand(type: .formatText, payload: TextFormatType.italic) | |
} label: { | |
ToolbarImage(systemName: "italic", active: store.isItalic) | |
}.keyboardShortcut("i") | |
Button { | |
do { | |
print(try store.editor?.getEditorState().toJSON() ?? "N/A") | |
} catch {} | |
} label: { | |
ToolbarImage(systemName: "square.and.arrow.down") | |
}.keyboardShortcut("s") | |
Button { | |
do { | |
try store.update { | |
guard let root = getRoot() else { | |
return | |
} | |
let paragraphNode = ParagraphNode() | |
let textNode = TextNode(text: "Hello World") | |
try paragraphNode.append([textNode]) | |
try root.append([paragraphNode]) | |
} | |
} catch { | |
} | |
} label: { | |
ToolbarImage(systemName: "bubble.right") | |
} | |
} | |
.navigationTitle("Hello Lexical") | |
} | |
} | |
} | |
struct ContentView_Previews: PreviewProvider { | |
static var previews: some View { | |
ContentView() | |
} | |
} | |
class LexicalStore: ObservableObject { | |
weak var view: LexicalView? = nil | |
let theme: Theme | |
@Published var isBold = false | |
@Published var isItalic = false | |
init() { | |
theme = Theme() | |
theme.paragraph = [ | |
.fontSize: 24.0, | |
.lineHeight: 24.0 | |
] | |
} | |
var editorState: EditorState? { | |
view?.editor.getEditorState() | |
} | |
var editor: Editor? { | |
view?.editor | |
} | |
func dispatchCommand(type: CommandType, payload: Any?) { | |
view?.editor.dispatchCommand(type: type, payload: payload) | |
} | |
func update(closure: @escaping () throws -> Void) throws { | |
try view?.editor.update(closure) | |
} | |
} | |
struct LexicalText: UIViewRepresentable { | |
typealias UIViewType = LexicalView | |
public var store: LexicalStore | |
func makeUIView(context: Context) -> Lexical.LexicalView { | |
let view = LexicalView( | |
editorConfig: EditorConfig( | |
theme: store.theme, | |
plugins: [] | |
), featureFlags: FeatureFlags(), placeholderText: LexicalPlaceholderText(text: "write here...", font: .systemFont(ofSize: 18), color: UIColor.placeholderText)) | |
store.view = view | |
_ = view.editor.registerUpdateListener { activeEditorState, previousEditorState, dirtyNodes in | |
updateStoreState() | |
} | |
return view | |
} | |
func updateUIView(_ uiView: Lexical.LexicalView, context: Context) { | |
uiView.placeholderText = LexicalPlaceholderText(text: "...", font: .systemFont(ofSize: 15), color: UIColor.placeholderText) | |
} | |
func updateStoreState() { | |
if let selection = getSelection() { | |
store.isBold = selection.hasFormat(type: .bold) | |
store.isItalic = selection.hasFormat(type: .italic) | |
} | |
} | |
} | |
struct ToolbarImage: View { | |
var systemName: String | |
var active = false | |
var body: some View { | |
Image(systemName: systemName) | |
.resizable().aspectRatio(contentMode: .fit).frame(width: 32, height: 32).padding() | |
.background(active ? Color.gray.opacity(0.5) : Color.clear) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment