webapp/src/components/EditorNew/Editor.tsx

91 lines
2.8 KiB
TypeScript
Raw Normal View History

2022-11-10 12:50:47 +00:00
import { onMount } from 'solid-js'
2022-11-09 17:57:35 +00:00
import { EditorState, Transaction } from 'prosemirror-state'
import { EditorView, MarkViewConstructor, NodeViewConstructor } from 'prosemirror-view'
import './prosemirror/styles/ProseMirror.scss'
import type { Nodes, Marks } from './prosemirror/schema'
import { createImageView } from './prosemirror/views/image'
import { schema } from './prosemirror/schema'
import { createPlugins } from './prosemirror/plugins'
2023-02-11 09:32:52 +00:00
import { DOMParser as ProseDOMParser, DOMSerializer } from 'prosemirror-model'
2022-11-09 17:57:35 +00:00
import { clsx } from 'clsx'
import { createArticle } from '../../stores/zine/articles'
2022-11-10 12:50:47 +00:00
import type { ShoutInput } from '../../graphql/types.gen'
2022-11-09 17:57:35 +00:00
import { Sidebar } from './Sidebar'
2023-02-11 09:32:52 +00:00
import styles from './Sidebar.module.scss'
2023-02-17 09:21:02 +00:00
import { Button } from '../_shared/Button'
import { useLocalize } from '../../context/localize'
2023-02-11 09:32:52 +00:00
type Props = {
initialContent?: string
}
2022-11-09 17:57:35 +00:00
const htmlContainer = typeof document === 'undefined' ? null : document.createElement('div')
const getHtml = (state: EditorState) => {
const fragment = DOMSerializer.fromSchema(schema).serializeFragment(state.doc.content)
htmlContainer.replaceChildren(fragment)
return htmlContainer.innerHTML
}
2023-02-11 09:32:52 +00:00
export const Editor = (props: Props) => {
2023-02-17 09:21:02 +00:00
const { t } = useLocalize()
2022-11-09 17:57:35 +00:00
const editorElRef: {
current: HTMLDivElement
} = {
current: null
}
const editorViewRef: { current: EditorView } = { current: null }
const dispatchTransaction = (tr: Transaction) => {
const newState = editorViewRef.current.state.apply(tr)
editorViewRef.current.updateState(newState)
}
onMount(async () => {
const plugins = createPlugins({ schema })
const nodeViews: Partial<Record<Nodes, NodeViewConstructor>> = {
image: createImageView
}
const markViews: Partial<Record<Marks, MarkViewConstructor>> = {}
2023-02-11 09:32:52 +00:00
const domNew = new DOMParser().parseFromString(`<div>${props.initialContent}</div>`, 'text/xml')
const doc = ProseDOMParser.fromSchema(schema).parse(domNew)
2022-11-09 17:57:35 +00:00
editorViewRef.current = new EditorView(editorElRef.current, {
state: EditorState.create({
2023-02-11 09:32:52 +00:00
doc,
2022-11-09 17:57:35 +00:00
schema,
plugins
}),
nodeViews,
markViews,
dispatchTransaction
})
2023-02-11 10:31:28 +00:00
editorViewRef.current.dom.classList.add('createArticle')
2022-11-09 17:57:35 +00:00
editorViewRef.current.focus()
})
const handleSaveButtonClick = () => {
const article: ShoutInput = {
body: getHtml(editorViewRef.current.state),
community: 1, // 'discours' ?
2022-11-09 17:57:35 +00:00
slug: 'new-' + Math.floor(Math.random() * 1000000)
}
createArticle({ article })
}
return (
2023-02-11 09:32:52 +00:00
<div class={clsx('container')} style={{ width: '100%', 'max-width': '670px' }}>
<div class={styles.editor} ref={(el) => (editorElRef.current = el)} />
2023-02-11 10:36:32 +00:00
<Button value={t('Publish')} onClick={handleSaveButtonClick} />
2022-11-09 17:57:35 +00:00
<Sidebar editorViewRef={editorViewRef} />
</div>
)
}
2023-02-11 09:32:52 +00:00
export default Editor