Files
tiptap/demos/src/Examples/CollaborativeEditing/React/index.jsx
Dominik 8c6751f0c6 add precommit hook for linting and automatic eslint fixes + update eslint packages (#2862)
* chore: add precommit hook for eslint fixes, fix linting issues
* chore: add eslint import sort plugin
2022-06-08 14:10:25 +02:00

139 lines
3.6 KiB
JavaScript

import './styles.scss'
import { HocuspocusProvider } from '@hocuspocus/provider'
import CharacterCount from '@tiptap/extension-character-count'
import Collaboration from '@tiptap/extension-collaboration'
import CollaborationCursor from '@tiptap/extension-collaboration-cursor'
import Highlight from '@tiptap/extension-highlight'
import TaskItem from '@tiptap/extension-task-item'
import TaskList from '@tiptap/extension-task-list'
import { EditorContent, useEditor } from '@tiptap/react'
import StarterKit from '@tiptap/starter-kit'
import React, {
useCallback, useEffect,
useState,
} from 'react'
import * as Y from 'yjs'
import MenuBar from './MenuBar'
const colors = ['#958DF1', '#F98181', '#FBBC88', '#FAF594', '#70CFF8', '#94FADB', '#B9F18D']
const rooms = ['rooms.30', 'rooms.31', 'rooms.32']
const names = [
'Lea Thompson',
'Cyndi Lauper',
'Tom Cruise',
'Madonna',
'Jerry Hall',
'Joan Collins',
'Winona Ryder',
'Christina Applegate',
'Alyssa Milano',
'Molly Ringwald',
'Ally Sheedy',
'Debbie Harry',
'Olivia Newton-John',
'Elton John',
'Michael J. Fox',
'Axl Rose',
'Emilio Estevez',
'Ralph Macchio',
'Rob Lowe',
'Jennifer Grey',
'Mickey Rourke',
'John Cusack',
'Matthew Broderick',
'Justine Bateman',
'Lisa Bonet',
]
const getRandomElement = list => list[Math.floor(Math.random() * list.length)]
const getRandomRoom = () => getRandomElement(rooms)
const getRandomColor = () => getRandomElement(colors)
const getRandomName = () => getRandomElement(names)
const room = getRandomRoom()
const ydoc = new Y.Doc()
const websocketProvider = new HocuspocusProvider({
url: 'wss://connect.gethocuspocus.com',
parameters: {
key: 'write_bqgvQ3Zwl34V4Nxt43zR',
},
name: room,
document: ydoc,
})
const getInitialUser = () => {
return JSON.parse(localStorage.getItem('currentUser')) || {
name: getRandomName(),
color: getRandomColor(),
}
}
export default () => {
const [status, setStatus] = useState('connecting')
const [currentUser, setCurrentUser] = useState(getInitialUser)
const editor = useEditor({
extensions: [
StarterKit.configure({
history: false,
}),
Highlight,
TaskList,
TaskItem,
CharacterCount.configure({
limit: 10000,
}),
Collaboration.configure({
document: ydoc,
}),
CollaborationCursor.configure({
provider: websocketProvider,
}),
],
})
useEffect(() => {
// Update status changes
websocketProvider.on('status', event => {
setStatus(event.status)
})
}, [])
// Save current user to localStorage and emit to editor
useEffect(() => {
if (editor && currentUser) {
localStorage.setItem('currentUser', JSON.stringify(currentUser))
editor.chain().focus().updateUser(currentUser).run()
}
}, [editor, currentUser])
const setName = useCallback(() => {
const name = (window.prompt('Name') || '').trim().substring(0, 32)
if (name) {
return setCurrentUser({ ...currentUser, name })
}
}, [currentUser])
return (
<div className="editor">
{editor && <MenuBar editor={editor} />}
<EditorContent className="editor__content" editor={editor} />
<div className="editor__footer">
<div className={`editor__status editor__status--${status}`}>
{status === 'connected'
? `${editor.storage.collaborationCursor.users.length} user${editor.storage.collaborationCursor.users.length === 1 ? '' : 's'} online in ${room}`
: 'offline'}
</div>
<div className="editor__name">
<button onClick={setName}>{currentUser.name}</button>
</div>
</div>
</div>
)
}