feat: Tiptap collab demo styling

This commit is contained in:
svenadlung 2023-03-31 17:03:38 +02:00
parent 958925f256
commit 87840b0f08
6 changed files with 45 additions and 231 deletions

View File

@ -1,136 +0,0 @@
import './MenuBar.scss'
import React, { Fragment } from 'react'
import MenuItem from './MenuItem'
export default ({ editor }) => {
const items = [
{
icon: 'bold',
title: 'Bold',
action: () => editor.chain().focus().toggleBold().run(),
isActive: () => editor.isActive('bold'),
},
{
icon: 'italic',
title: 'Italic',
action: () => editor.chain().focus().toggleItalic().run(),
isActive: () => editor.isActive('italic'),
},
{
icon: 'strikethrough',
title: 'Strike',
action: () => editor.chain().focus().toggleStrike().run(),
isActive: () => editor.isActive('strike'),
},
{
icon: 'code-view',
title: 'Code',
action: () => editor.chain().focus().toggleCode().run(),
isActive: () => editor.isActive('code'),
},
{
icon: 'mark-pen-line',
title: 'Highlight',
action: () => editor.chain().focus().toggleHighlight().run(),
isActive: () => editor.isActive('highlight'),
},
{
type: 'divider',
},
{
icon: 'h-1',
title: 'Heading 1',
action: () => editor.chain().focus().toggleHeading({ level: 1 }).run(),
isActive: () => editor.isActive('heading', { level: 1 }),
},
{
icon: 'h-2',
title: 'Heading 2',
action: () => editor.chain().focus().toggleHeading({ level: 2 }).run(),
isActive: () => editor.isActive('heading', { level: 2 }),
},
{
icon: 'paragraph',
title: 'Paragraph',
action: () => editor.chain().focus().setParagraph().run(),
isActive: () => editor.isActive('paragraph'),
},
{
icon: 'list-unordered',
title: 'Bullet List',
action: () => editor.chain().focus().toggleBulletList().run(),
isActive: () => editor.isActive('bulletList'),
},
{
icon: 'list-ordered',
title: 'Ordered List',
action: () => editor.chain().focus().toggleOrderedList().run(),
isActive: () => editor.isActive('orderedList'),
},
{
icon: 'list-check-2',
title: 'Task List',
action: () => editor.chain().focus().toggleTaskList().run(),
isActive: () => editor.isActive('taskList'),
},
{
icon: 'code-box-line',
title: 'Code Block',
action: () => editor.chain().focus().toggleCodeBlock().run(),
isActive: () => editor.isActive('codeBlock'),
},
{
type: 'divider',
},
{
icon: 'double-quotes-l',
title: 'Blockquote',
action: () => editor.chain().focus().toggleBlockquote().run(),
isActive: () => editor.isActive('blockquote'),
},
{
icon: 'separator',
title: 'Horizontal Rule',
action: () => editor.chain().focus().setHorizontalRule().run(),
},
{
type: 'divider',
},
{
icon: 'text-wrap',
title: 'Hard Break',
action: () => editor.chain().focus().setHardBreak().run(),
},
{
icon: 'format-clear',
title: 'Clear Format',
action: () => editor.chain().focus().clearNodes().unsetAllMarks()
.run(),
},
{
type: 'divider',
},
{
icon: 'arrow-go-back-line',
title: 'Undo',
action: () => editor.chain().focus().undo().run(),
},
{
icon: 'arrow-go-forward-line',
title: 'Redo',
action: () => editor.chain().focus().redo().run(),
},
]
return (
<div className="editor__header">
{items.map((item, index) => (
<Fragment key={index}>
{item.type === 'divider' ? <div className="divider" /> : <MenuItem {...item} />}
</Fragment>
))}
</div>
)
}

View File

@ -1,7 +0,0 @@
.divider {
background-color: rgba(#fff, 0.25);
height: 1.25rem;
margin-left: 0.5rem;
margin-right: 0.75rem;
width: 1px;
}

View File

@ -1,18 +0,0 @@
import './MenuItem.scss'
import React from 'react'
import remixiconUrl from 'remixicon/fonts/remixicon.symbol.svg'
export default ({
icon, title, action, isActive = null,
}) => (
<button
className={`menu-item${isActive && isActive() ? ' is-active' : ''}`}
onClick={action}
title={title}
>
<svg className="remix">
<use xlinkHref={`${remixiconUrl}#ri-${icon}`} />
</svg>
</button>
)

View File

@ -1,22 +0,0 @@
.menu-item {
background-color: transparent;
border: none;
border-radius: 0.4rem;
color: #fff;
cursor: pointer;
height: 1.75rem;
margin-right: 0.25rem;
padding: 0.25rem;
width: 1.75rem;
svg {
fill: currentColor;
height: 100%;
width: 100%;
}
&:hover,
&.is-active {
background-color: #303030;
}
}

View File

@ -4,9 +4,6 @@ import { TiptapCollabProvider } 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, {
@ -15,8 +12,6 @@ import React, {
} from 'react'
import * as Y from 'yjs'
import MenuBar from './MenuBar'
const room = 'room-1'
const colors = ['#958DF1', '#F98181', '#FBBC88', '#FAF594', '#70CFF8', '#94FADB', '#B9F18D']
const names = [
@ -75,9 +70,6 @@ export default () => {
StarterKit.configure({
history: false,
}),
Highlight,
TaskList,
TaskItem,
CharacterCount.configure({
limit: 10000,
}),
@ -115,9 +107,13 @@ export default () => {
return (
<div className="editor">
{editor && <MenuBar editor={editor} />}
<EditorContent className="editor__content" editor={editor} />
<div className="editor__footer">
<div className="editor__header">
<div className="dots">
<span className="dot"></span>
<span className="dot"></span>
<span className="dot"></span>
</div>
<div className="editor__users">
<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}`
@ -128,5 +124,7 @@ export default () => {
</div>
</div>
</div>
<EditorContent className="editor__content" editor={editor} />
</div>
)
}

View File

@ -85,7 +85,7 @@
.editor {
background-color: #fff;
border: 3px solid #0d0d0d;
border: 2px solid #0d0d0d;
border-radius: 0.75rem;
color: #0d0d0d;
display: flex;
@ -94,14 +94,19 @@
&__header {
align-items: center;
background: #0d0d0d;
border-bottom: 3px solid #0d0d0d;
border-bottom: 2px solid #0d0d0d;
border-top-left-radius: 0.25rem;
border-top-right-radius: 0.25rem;
display: flex;
flex: 0 0 auto;
flex-wrap: wrap;
padding: 0.25rem;
justify-content: space-between;
padding: 0.5rem 1rem;
}
&__users {
color: rgba(#000, 0.8);
display: flex;
font-size: 0.85rem;
gap: 1rem;
}
&__content {
@ -112,20 +117,6 @@
-webkit-overflow-scrolling: touch;
}
&__footer {
align-items: center;
border-top: 3px solid #0d0d0d;
color: #0d0d0d;
display: flex;
flex: 0 0 auto;
font-size: 12px;
flex-wrap: wrap;
font-weight: 600;
justify-content: space-between;
padding: 0.25rem 0.75rem;
white-space: nowrap;
}
/* Some information about the status */
&__status {
align-items: center;
@ -152,22 +143,18 @@
}
}
&__name {
button {
background: none;
&__name button {
appearance: none;
background: transparent;
border: none;
border-radius: 0.4rem;
color: #0d0d0d;
color: inherit;
cursor: pointer;
font: inherit;
font-size: 12px;
font-weight: 600;
padding: 0.25rem 0.5rem;
&:hover {
background-color: #0d0d0d;
color: #fff;
}
}
line-height: normal;
margin: 0;
padding: 0;
overflow: visible;
width: auto;
}
}
@ -186,7 +173,7 @@
.collaboration-cursor__label {
border-radius: 3px 3px 3px 0;
color: #0d0d0d;
font-size: 12px;
font-size: 0.75rem;
font-style: normal;
font-weight: 600;
left: -1px;
@ -197,3 +184,15 @@
user-select: none;
white-space: nowrap;
}
.dots {
display: flex;
gap: 6px;
}
.dot {
background: #000;
border-radius: 100%;
height: 0.625rem;
width: 0.625rem;
}