mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-12-01 09:29:02 +08:00
Merge branch 'main' of github.com:ueberdosis/tiptap
This commit is contained in:
commit
51aa314034
@ -65,7 +65,6 @@ const getInitialUser = () => {
|
||||
|
||||
export default () => {
|
||||
const [status, setStatus] = useState('connecting')
|
||||
const [users, setUsers] = useState([])
|
||||
const [currentUser, setCurrentUser] = useState(getInitialUser)
|
||||
|
||||
const editor = useEditor({
|
||||
@ -84,9 +83,6 @@ export default () => {
|
||||
}),
|
||||
CollaborationCursor.configure({
|
||||
provider: websocketProvider,
|
||||
onUpdate: updatedUsers => {
|
||||
setUsers(updatedUsers)
|
||||
},
|
||||
}),
|
||||
],
|
||||
})
|
||||
@ -96,7 +92,7 @@ export default () => {
|
||||
const indexeddbProvider = new IndexeddbPersistence(room, ydoc)
|
||||
|
||||
indexeddbProvider.on('synced', () => {
|
||||
console.log('Loaded content from database …')
|
||||
console.log('Loaded content from database …')
|
||||
})
|
||||
|
||||
// Update status changes
|
||||
@ -109,7 +105,7 @@ export default () => {
|
||||
useEffect(() => {
|
||||
if (editor && currentUser) {
|
||||
localStorage.setItem('currentUser', JSON.stringify(currentUser))
|
||||
editor.chain().focus().user(currentUser).run()
|
||||
editor.chain().focus().updateUser(currentUser).run()
|
||||
}
|
||||
}, [editor, currentUser])
|
||||
|
||||
@ -128,7 +124,7 @@ export default () => {
|
||||
<div className="editor__footer">
|
||||
<div className={`editor__status editor__status--${status}`}>
|
||||
{status === 'connected'
|
||||
? `${users.length} user${users.length === 1 ? '' : 's'} online in ${room}`
|
||||
? `${editor.storage.collaborationCursor.users.length} user${editor.storage.collaborationCursor.users.length === 1 ? '' : 's'} online in ${room}`
|
||||
: 'offline'}
|
||||
</div>
|
||||
<div className="editor__name">
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="editor__footer">
|
||||
<div :class="`editor__status editor__status--${status}`">
|
||||
<template v-if="status === 'connected'">
|
||||
{{ users.length }} user{{ users.length === 1 ? '' : 's' }} online in {{ room }}
|
||||
{{ editor.storage.collaborationCursor.users.length }} user{{ editor.storage.collaborationCursor.users.length === 1 ? '' : 's' }} online in {{ room }}
|
||||
</template>
|
||||
<template v-else>
|
||||
offline
|
||||
@ -61,7 +61,6 @@ export default {
|
||||
provider: null,
|
||||
indexdb: null,
|
||||
editor: null,
|
||||
users: [],
|
||||
status: 'connecting',
|
||||
room: getRandomRoom(),
|
||||
}
|
||||
@ -74,8 +73,6 @@ export default {
|
||||
this.status = event.status
|
||||
})
|
||||
|
||||
window.ydoc = ydoc
|
||||
|
||||
this.indexdb = new IndexeddbPersistence(this.room, ydoc)
|
||||
|
||||
this.editor = new Editor({
|
||||
@ -92,9 +89,6 @@ export default {
|
||||
CollaborationCursor.configure({
|
||||
provider: this.provider,
|
||||
user: this.currentUser,
|
||||
onUpdate: users => {
|
||||
this.users = users
|
||||
},
|
||||
}),
|
||||
CharacterCount.configure({
|
||||
limit: 10000,
|
||||
@ -120,7 +114,7 @@ export default {
|
||||
|
||||
updateCurrentUser(attributes) {
|
||||
this.currentUser = { ...this.currentUser, ...attributes }
|
||||
this.editor.chain().focus().user(this.currentUser).run()
|
||||
this.editor.chain().focus().updateUser(this.currentUser).run()
|
||||
|
||||
localStorage.setItem('currentUser', JSON.stringify(this.currentUser))
|
||||
},
|
||||
|
@ -43,11 +43,15 @@ A render function for the cursor, look at [the extension source code](https://gi
|
||||
|
||||
## Commands
|
||||
|
||||
### user()
|
||||
An object with the attributes of the current user. It expects a `name` and a `color`, but you can add additional fields, too.
|
||||
### updateUser()
|
||||
Pass an object with updated attributes of the current user. It expects a `name` and a `color`, but you can add additional fields, too.
|
||||
|
||||
```js
|
||||
editor.commands.user({ name: 'Philipp Kühn', color: '#000000' })
|
||||
editor.commands.updateUser({
|
||||
name: 'Philipp Kühn',
|
||||
color: '#000000',
|
||||
avatar: 'https://unavatar.io/github/philippkuehn',
|
||||
})
|
||||
```
|
||||
|
||||
## Source code
|
||||
|
@ -7,7 +7,7 @@ tableOfContents: true
|
||||
## Introduction
|
||||
The whole Tiptap is code base is written in TypeScript. If you haven’t heard of it or never used it, no worries. You don’t have to.
|
||||
|
||||
TypeScript extends JavaScript by adding types (hence the name). It adds new syntax, which doesn’t exist in Vanilla JavaScript. It’s actually removed before running in the browser, but this step – the compilation – is important to find bugs early. It checks if you passe the right types of data to functions. For a big and complex project, that’s very valuable. It means we’ll get notified of lot of bugs, before shipping code to you.
|
||||
TypeScript extends JavaScript by adding types (hence the name). It adds new syntax, which doesn’t exist in Vanilla JavaScript. It’s actually removed before running in the browser, but this step – the compilation – is important to find bugs early. It checks if you pass the right types of data to functions. For a big and complex project, that’s very valuable. It means we’ll get notified of lot of bugs, before shipping code to you.
|
||||
|
||||
**Anyway, if you don’t use TypeScript in your project, that’s fine.** You will still be able to use Tiptap and nevertheless get a nice autocomplete for the Tiptap API (if your editor supports it, but most do).
|
||||
|
||||
|
10
docs/jobs.md
10
docs/jobs.md
@ -1,13 +1,13 @@
|
||||
# Jobs
|
||||
Some great companies are looking for developers right now. If you’re looking for a job to work with Tiptap and/or Hocuspocus, consider applying:
|
||||
You enjoy to work with Tiptap? You are not alone. Some amazing companies are looking for developers with some Tiptap and/or [Hocuspocus](https://hocuspocus.dev) experience.
|
||||
|
||||
**[Software Engineer](https://gamma.app/docs/Software-Engineer-6s0e0grm9zk9w5s) @ Gamma**<br>
|
||||
- **[Software Engineer](https://gamma.app/docs/Software-Engineer-6s0e0grm9zk9w5s) @ Gamma**<br>
|
||||
React · Tiptap · San Francisco, CA
|
||||
|
||||
**[Frontend Engineer](https://birdeatsbug.recruitee.com/o/frontend-engineer-vuejs-graphql-browser-extension) @ Bird Eats Bug**<br>
|
||||
- **[Javascript Engineer, Software Engineer, …](https://birdeatsbug.recruitee.com/) @ Bird Eats Bug**<br>
|
||||
Vue.js · Tiptap · Remote · Berlin, Germany
|
||||
|
||||
**[Frontend Developer](https://bitcrowd.net/jobs) @ bitcrowd**<br>
|
||||
- **[Frontend Developer](https://bitcrowd.net/jobs) @ bitcrowd**<br>
|
||||
Tiptap · Remote · Berlin, Germany
|
||||
|
||||
Is your company hiring, too? We have more than 200,000 page views/month from people who love to work with Tiptap. Maybe we can find someone for you. Reach out to [humans@tiptap.dev](mailto:humans@tiptap.dev) with a link to your job description!
|
||||
Is your company hiring, too? We have [200,000 page views/month](https://plausible.io/tiptap.dev?period=30d) from people who love to work with Tiptap. Maybe we can help you find the right person. Reach out to [humans@tiptap.dev](mailto:humans@tiptap.dev) with a link to your job description!
|
||||
|
@ -1,10 +1,17 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { yCursorPlugin } from 'y-prosemirror'
|
||||
|
||||
type CollaborationCursorStorage = {
|
||||
users: { clientId: number, [key: string]: any }[],
|
||||
}
|
||||
|
||||
export interface CollaborationCursorOptions {
|
||||
provider: any,
|
||||
user: Record<string, any>,
|
||||
render (user: Record<string, any>): HTMLElement,
|
||||
/**
|
||||
* @deprecated The "onUpdate" option is deprecated. Please use `editor.storage.collaborationCursor.users` instead. Read more: https://tiptap.dev/api/extensions/collaboration-cursor
|
||||
*/
|
||||
onUpdate: (users: { clientId: number, [key: string]: any }[]) => null,
|
||||
}
|
||||
|
||||
@ -14,6 +21,12 @@ declare module '@tiptap/core' {
|
||||
/**
|
||||
* Update details of the current user
|
||||
*/
|
||||
updateUser: (attributes: Record<string, any>) => ReturnType,
|
||||
/**
|
||||
* Update details of the current user
|
||||
*
|
||||
* @deprecated The "user" command is deprecated. Please use "updateUser" instead. Read more: https://tiptap.dev/api/extensions/collaboration-cursor
|
||||
*/
|
||||
user: (attributes: Record<string, any>) => ReturnType,
|
||||
}
|
||||
}
|
||||
@ -28,7 +41,9 @@ const awarenessStatesToArray = (states: Map<number, Record<string, any>>) => {
|
||||
})
|
||||
}
|
||||
|
||||
export const CollaborationCursor = Extension.create<CollaborationCursorOptions>({
|
||||
const defaultOnUpdate = () => null
|
||||
|
||||
export const CollaborationCursor = Extension.create<CollaborationCursorOptions, CollaborationCursorStorage>({
|
||||
name: 'collaborationCursor',
|
||||
|
||||
addOptions() {
|
||||
@ -51,19 +66,36 @@ export const CollaborationCursor = Extension.create<CollaborationCursorOptions>(
|
||||
|
||||
return cursor
|
||||
},
|
||||
onUpdate: () => null,
|
||||
onUpdate: defaultOnUpdate,
|
||||
}
|
||||
},
|
||||
|
||||
onCreate() {
|
||||
if (this.options.onUpdate !== defaultOnUpdate) {
|
||||
console.warn('[tiptap warn]: DEPRECATED: The "onUpdate" option is deprecated. Please use `editor.storage.collaborationCursor.users` instead. Read more: https://tiptap.dev/api/extensions/collaboration-cursor')
|
||||
}
|
||||
},
|
||||
|
||||
addStorage() {
|
||||
return {
|
||||
users: [],
|
||||
}
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
user: attributes => () => {
|
||||
updateUser: attributes => () => {
|
||||
this.options.user = attributes
|
||||
|
||||
this.options.provider.awareness.setLocalStateField('user', this.options.user)
|
||||
|
||||
return true
|
||||
},
|
||||
user: attributes => ({ editor }) => {
|
||||
console.warn('[tiptap warn]: DEPRECATED: The "user" command is deprecated. Please use "updateUser" instead. Read more: https://tiptap.dev/api/extensions/collaboration-cursor')
|
||||
|
||||
return editor.commands.updateUser(attributes)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@ -72,22 +104,12 @@ export const CollaborationCursor = Extension.create<CollaborationCursorOptions>(
|
||||
yCursorPlugin((() => {
|
||||
this.options.provider.awareness.setLocalStateField('user', this.options.user)
|
||||
|
||||
this.options.provider.awareness.on('change', () => {
|
||||
this.options.onUpdate(awarenessStatesToArray(this.options.provider.awareness.states))
|
||||
})
|
||||
this.storage.users = awarenessStatesToArray(this.options.provider.awareness.states)
|
||||
|
||||
this.options.provider.awareness.on('update', () => {
|
||||
this.options.onUpdate(awarenessStatesToArray(this.options.provider.awareness.states))
|
||||
this.storage.users = awarenessStatesToArray(this.options.provider.awareness.states)
|
||||
})
|
||||
|
||||
this.options.provider.on('status', (event: { status: string }) => {
|
||||
if (event.status === 'connected') {
|
||||
this.options.provider.awareness.setLocalStateField('user', this.options.user)
|
||||
}
|
||||
})
|
||||
|
||||
this.options.onUpdate(awarenessStatesToArray(this.options.provider.awareness.states))
|
||||
|
||||
return this.options.provider.awareness
|
||||
})(),
|
||||
// @ts-ignore
|
||||
|
Loading…
Reference in New Issue
Block a user