WebView "mode" #1968
Replies: 3 comments 6 replies
-
UpdateI got a working demo of Plate loading on a WebView without a toolbar and report its state to SwiftUI. Demo Code Here’s the code for the React portion that lives inside the WebView: import {
PlateProvider,
usePlateEditorState,
usePlateEditorRef,
isMarkActive,
PlateEditor,
someNode,
toggleMark,
toggleNodeType,
} from '@udecode/plate-core'
import { getListItemEntry, toggleList } from '@udecode/plate-list'
import { Box } from 'components/Box'
import { TextEditor } from 'components/TextEditor'
import { ElementType } from 'modules/textEditor'
import Head from 'next/head'
import { useEffect } from 'react'
const isListBlock = (editor: PlateEditor, type: 'ul' | 'ol'): boolean => {
const res = !!editor?.selection && getListItemEntry(editor)
return Boolean(res && res.list[0].type === type)
}
const isBlock = (editor: PlateEditor, type: ElementType): boolean => {
return Boolean(
!!editor?.selection && someNode(editor, { match: { type: type } })
)
}
const ParentViewEditorHandler = () => {
const state = usePlateEditorState()
const editorRef = usePlateEditorRef()
useEffect(() => {
const handler = (e: FormatEvent) => {
if (editorRef) {
switch (e.detail.type) {
case 'bold':
case 'italic':
case 'strikethrough': {
toggleMark(editorRef, { key: e.detail.type })
return
}
case 'h1':
case 'h2':
case 'h3':
case 'action_item': {
toggleNodeType(editorRef, {
activeType: e.detail.type,
})
return
}
case 'ol':
case 'ul': {
toggleList(editorRef, { type: e.detail.type })
return
}
}
}
}
document.addEventListener('format', handler)
return () => {
document.removeEventListener('format', handler)
}
}, [editorRef])
if (typeof window === 'undefined') {
return null
}
const _win = (window as unknown) as {
webkit?: {
messageHandlers?: {
textEditor?: {
postMessage(payload: { textJson: string; toolbarState: string }): void
}
}
}
}
const toolbarState = JSON.stringify({
bold: isMarkActive(state, 'bold'),
italic: isMarkActive(state, 'italic'),
strikethrough: isMarkActive(state, 'strikethrough'),
ul: isListBlock(state, 'ul'),
ol: isListBlock(state, 'ol'),
h1: isBlock(state, 'h1'),
h2: isBlock(state, 'h2'),
h3: isBlock(state, 'h3'),
action_item: isBlock(state, 'action_item'),
})
if (_win.webkit?.messageHandlers?.textEditor) {
_win.webkit.messageHandlers.textEditor.postMessage({
textJson: JSON.stringify(state?.children || []),
toolbarState,
})
}
return null
}
export default function WebViewTextEditor() {
return (
<Box
css={{
background: '$white',
minHeight: '100%',
p: 10,
}}
>
<Head>
<meta name="viewport" content="width=device-width, user-scalable=no" />
</Head>
<PlateProvider>
<ParentViewEditorHandler />
<TextEditor
initialValue={[
{ type: 'h1', children: [{ text: 'Hello' }] },
{
type: 'p',
children: [
{ text: 'This is ' },
{ text: 'how', bold: true },
{ text: ' we do it, obviously' },
],
},
]}
/>
</PlateProvider>
</Box>
)
}
Being a renderless component means it won’t have any rendering performance issues and its behavior is encapsulated. I’ll post the code for SwiftUI once it’s a bit more polished. |
Beta Was this translation helpful? Give feedback.
-
Super initiative, please keep us updated 👍 |
Beta Was this translation helpful? Give feedback.
-
Hey all! I just made a few more improvements in my implementation and I'm really happy with the result. It can now compete with native note taking apps! For posterity, if anyone ever attempts to implement Plate inside a WebView, hit me up. RPReplay_Final1691035939.MP4RPReplay_Final1691035909.MP4 |
Beta Was this translation helpful? Give feedback.
-
Hey all, I'm building the mobile version of my project which uses Plate. My plan is to keep most of it actually native, but integrate Plate by embedding it in a local WebView (local file with react bundled etc) and have it communicate with the parent screen/view.
My plan is to have something like this:
As you can see, the idea is to have native controls which would be easier to position relative to the keyboard etc while retaining full capability of Plate.
The ask: posting here to see if anyone's tried something similar and if they have any tips or suggestions. I'm also happy to share my progress publicly here in case someone needs to do the same.
In case you were wondering, the reason for me using Plate even for native is due to the lack of either Swift rich text libraries that have as much functionality as Slate/Plate, and same goes for React Native. It's also easier to deal with the same data structure for a cross-platform app.
Beta Was this translation helpful? Give feedback.
All reactions