Skip to content

Instantly share code, notes, and snippets.

@jamesporter
Created April 6, 2023 21:26
Show Gist options
  • Save jamesporter/53ee28e135f2d540a032c439fa834737 to your computer and use it in GitHub Desktop.
Save jamesporter/53ee28e135f2d540a032c439fa834737 to your computer and use it in GitHub Desktop.
Lexical iOS SwiftUI integration PoC
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