mirror of https://github.com/requarks/wiki
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
911 lines
23 KiB
911 lines
23 KiB
<template lang="pug">
|
|
.wysiwyg-container
|
|
.wysiwyg-toolbar(v-if='editor')
|
|
template(v-for='menuItem of menuBar')
|
|
q-separator.q-mx-xs(
|
|
v-if='menuItem.type === `divider`'
|
|
vertical
|
|
)
|
|
q-btn(
|
|
v-else-if='menuItem.type === `dropdown`'
|
|
:key='`ddn-` + menuItem.key'
|
|
flat
|
|
:icon='menuItem.icon'
|
|
padding='xs'
|
|
:class='{ "is-active": menuItem.isActive && menuItem.isActive() }'
|
|
:color='menuItem.isActive && menuItem.isActive() ? `primary` : `grey-10`'
|
|
:aria-label='menuItem.title'
|
|
split
|
|
:disabled='menuItem.disabled && menuItem.disabled()'
|
|
)
|
|
q-menu
|
|
q-list(
|
|
dense
|
|
padding
|
|
)
|
|
template(v-for='child of menuItem.children')
|
|
q-separator.q-my-sm(v-if='child.type === `divider`')
|
|
q-item(
|
|
v-else
|
|
:key='child.key'
|
|
clickable
|
|
@click='child.action'
|
|
:active='child.isActive && child.isActive()'
|
|
active-class='text-primary'
|
|
:disabled='child.disabled && child.disabled()'
|
|
)
|
|
q-item-section(side)
|
|
q-icon(
|
|
:name='child.icon'
|
|
:color='child.color'
|
|
)
|
|
q-item-section
|
|
q-item-label {{child.title}}
|
|
q-btn-group(
|
|
v-else-if='menuItem.type === `btngroup`'
|
|
:key='`btngrp-` + menuItem.key'
|
|
flat
|
|
)
|
|
q-btn(
|
|
v-for='child of menuItem.children'
|
|
:key='child.key'
|
|
flat
|
|
:icon='child.icon'
|
|
padding='xs'
|
|
:class='{ "is-active": child.isActive && child.isActive() }'
|
|
:color='child.isActive && child.isActive() ? `primary` : `grey-10`'
|
|
@click='child.action'
|
|
:aria-label='child.title'
|
|
:disabled='menuItem.disabled && menuItem.disabled()'
|
|
)
|
|
q-btn(
|
|
v-else
|
|
:key='`btn-` + menuItem.key'
|
|
flat
|
|
:icon='menuItem.icon'
|
|
padding='xs'
|
|
:class='{ "is-active": menuItem.isActive && menuItem.isActive() }'
|
|
:color='menuItem.isActive && menuItem.isActive() ? `primary` : `grey-10`'
|
|
@click='menuItem.action'
|
|
:aria-label='menuItem.title'
|
|
:disabled='menuItem.disabled && menuItem.disabled()'
|
|
)
|
|
//- q-space
|
|
//- q-btn(
|
|
//- size='sm'
|
|
//- unelevated
|
|
//- color='red'
|
|
//- label='Test'
|
|
//- @click='snapshot'
|
|
//- )
|
|
//- q-scroll-area(
|
|
//- :thumb-style='thumbStyle'
|
|
//- :bar-style='barStyle'
|
|
//- style='height: 100%;'
|
|
//- )
|
|
editor-content(:editor='editor')
|
|
</template>
|
|
|
|
<script setup>
|
|
import { useEditor, EditorContent } from '@tiptap/vue-3'
|
|
import StarterKit from '@tiptap/starter-kit'
|
|
// import Collaboration from '@tiptap/extension-collaboration'
|
|
import CodeBlockLowlight from '@tiptap/extension-code-block-lowlight'
|
|
import { Color } from '@tiptap/extension-color'
|
|
import FontFamily from '@tiptap/extension-font-family'
|
|
import Highlight from '@tiptap/extension-highlight'
|
|
import Image from '@tiptap/extension-image'
|
|
import Mention from '@tiptap/extension-mention'
|
|
import Placeholder from '@tiptap/extension-placeholder'
|
|
import Table from '@tiptap/extension-table'
|
|
import TableRow from '@tiptap/extension-table-row'
|
|
import TableCell from '@tiptap/extension-table-cell'
|
|
import TableHeader from '@tiptap/extension-table-header'
|
|
import TaskList from '@tiptap/extension-task-list'
|
|
import TaskItem from '@tiptap/extension-task-item'
|
|
import TextAlign from '@tiptap/extension-text-align'
|
|
import TextStyle from '@tiptap/extension-text-style'
|
|
import Typography from '@tiptap/extension-typography'
|
|
import { common, createLowlight } from 'lowlight'
|
|
import { onBeforeUnmount, onMounted, reactive, shallowRef } from 'vue'
|
|
// import * as Y from 'yjs'
|
|
// import { IndexeddbPersistence } from 'y-indexeddb'
|
|
// import { WebsocketProvider } from 'y-websocket'
|
|
|
|
import { useMeta, useQuasar, setCssVar } from 'quasar'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { DateTime } from 'luxon'
|
|
|
|
import { useEditorStore } from 'src/stores/editor'
|
|
import { usePageStore } from 'src/stores/page'
|
|
import { useSiteStore } from 'src/stores/site'
|
|
|
|
const lowlight = createLowlight(common)
|
|
|
|
// QUASAR
|
|
|
|
const $q = useQuasar()
|
|
|
|
// STORES
|
|
|
|
const editorStore = useEditorStore()
|
|
const pageStore = usePageStore()
|
|
const siteStore = useSiteStore()
|
|
|
|
// I18N
|
|
|
|
const { t } = useI18n()
|
|
|
|
// STATE
|
|
|
|
const state = reactive({
|
|
// editor: null,
|
|
ydoc: null
|
|
})
|
|
|
|
let editor = null
|
|
|
|
const thumbStyle = {
|
|
right: '2px',
|
|
borderRadius: '5px',
|
|
backgroundColor: '#000',
|
|
width: '5px',
|
|
opacity: 0.15
|
|
}
|
|
const barStyle = {
|
|
backgroundColor: '#FAFAFA',
|
|
width: '9px',
|
|
opacity: 1
|
|
}
|
|
const menuBar = [
|
|
{
|
|
key: 'bold',
|
|
icon: 'mdi-format-bold',
|
|
title: 'Bold',
|
|
action: () => editor.value.chain().focus().toggleBold().run(),
|
|
isActive: () => editor.value.isActive('bold')
|
|
},
|
|
{
|
|
key: 'italic',
|
|
icon: 'mdi-format-italic',
|
|
title: 'Italic',
|
|
action: () => editor.value.chain().focus().toggleItalic().run(),
|
|
isActive: () => editor.value.isActive('italic')
|
|
},
|
|
{
|
|
key: 'strikethrough',
|
|
icon: 'mdi-format-strikethrough',
|
|
title: 'Strike',
|
|
action: () => editor.value.chain().focus().toggleStrike().run(),
|
|
isActive: () => editor.value.isActive('strike')
|
|
},
|
|
{
|
|
key: 'code',
|
|
icon: 'mdi-code-tags',
|
|
title: 'Code',
|
|
action: () => editor.value.chain().focus().toggleCode().run(),
|
|
isActive: () => editor.value.isActive('code')
|
|
},
|
|
{
|
|
key: 'fontfamily',
|
|
icon: 'mdi-format-font',
|
|
title: 'Font Family',
|
|
type: 'dropdown',
|
|
isActive: () => editor.value.isActive('fontFamily'),
|
|
children: [
|
|
{
|
|
key: 'fontunset',
|
|
icon: 'mdi-format-font',
|
|
title: 'Sans-Serif',
|
|
action: () => editor.value.chain().focus().unsetFontFamily().run()
|
|
},
|
|
{
|
|
key: 'monospace',
|
|
icon: 'mdi-format-font',
|
|
title: 'Monospace',
|
|
action: () => editor.value.chain().focus().setFontFamily('monospace').run()
|
|
}
|
|
]
|
|
},
|
|
{
|
|
key: 'color',
|
|
icon: 'mdi-palette',
|
|
title: 'Text Color',
|
|
type: 'dropdown',
|
|
isActive: () => editor.value.isActive('color'),
|
|
children: [
|
|
{
|
|
key: 'color-blue',
|
|
icon: 'mdi-palette',
|
|
title: 'Blue',
|
|
color: 'blue',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'color-brown',
|
|
icon: 'mdi-palette',
|
|
title: 'Brown',
|
|
color: 'brown',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'color-green',
|
|
icon: 'mdi-palette',
|
|
title: 'Green',
|
|
color: 'green',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'color-orange',
|
|
icon: 'mdi-palette',
|
|
title: 'Orange',
|
|
color: 'orange',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'color-pink',
|
|
icon: 'mdi-palette',
|
|
title: 'Pink',
|
|
color: 'pink',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'color-purple',
|
|
icon: 'mdi-palette',
|
|
title: 'Purple',
|
|
color: 'purple',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'color-red',
|
|
icon: 'mdi-palette',
|
|
title: 'Red',
|
|
color: 'red',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'color-teal',
|
|
icon: 'mdi-palette',
|
|
title: 'Teal',
|
|
color: 'teal',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'color-yellow',
|
|
icon: 'mdi-palette',
|
|
title: 'Yellow',
|
|
color: 'yellow',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'color-remove',
|
|
icon: 'mdi-palette',
|
|
title: 'Default',
|
|
color: 'grey',
|
|
action: () => editor.value.chain().focus().unsetHighlight().run()
|
|
}
|
|
]
|
|
},
|
|
{
|
|
key: 'highlight',
|
|
icon: 'mdi-marker',
|
|
title: 'Highlight',
|
|
type: 'dropdown',
|
|
isActive: () => editor.value.isActive('highlight'),
|
|
children: [
|
|
{
|
|
key: 'highlight-yellow',
|
|
icon: 'mdi-marker',
|
|
title: 'Yellow',
|
|
color: 'yellow',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'highlight-blue',
|
|
icon: 'mdi-marker',
|
|
title: 'Blue',
|
|
color: 'blue',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'highlight-pink',
|
|
icon: 'mdi-marker',
|
|
title: 'Pink',
|
|
color: 'pink',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'highlight-green',
|
|
icon: 'mdi-marker',
|
|
title: 'Green',
|
|
color: 'green',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
key: 'highlight-orange',
|
|
icon: 'mdi-marker',
|
|
title: 'Orange',
|
|
color: 'orange',
|
|
action: () => editor.value.chain().focus().toggleHighlight().run()
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'highlight-remove',
|
|
icon: 'mdi-marker-cancel',
|
|
title: 'Remove',
|
|
color: 'grey',
|
|
action: () => editor.value.chain().focus().unsetHighlight().run()
|
|
}
|
|
]
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'header',
|
|
icon: 'mdi-format-header-pound',
|
|
title: 'Header',
|
|
type: 'dropdown',
|
|
isActive: () => editor.value.isActive('heading'),
|
|
children: [
|
|
{
|
|
key: 'h1',
|
|
icon: 'mdi-format-header-1',
|
|
title: 'Header 1',
|
|
action: () => editor.value.chain().focus().toggleHeading({ level: 1 }).run(),
|
|
isActive: () => editor.value.isActive('heading', { level: 1 })
|
|
},
|
|
{
|
|
key: 'h2',
|
|
icon: 'mdi-format-header-2',
|
|
title: 'Header 2',
|
|
action: () => editor.value.chain().focus().toggleHeading({ level: 2 }).run(),
|
|
isActive: () => editor.value.isActive('heading', { level: 2 })
|
|
},
|
|
{
|
|
key: 'h3',
|
|
icon: 'mdi-format-header-3',
|
|
title: 'Header 3',
|
|
action: () => editor.value.chain().focus().toggleHeading({ level: 3 }).run(),
|
|
isActive: () => editor.value.isActive('heading', { level: 3 })
|
|
},
|
|
{
|
|
key: 'h4',
|
|
icon: 'mdi-format-header-4',
|
|
title: 'Header 4',
|
|
action: () => editor.value.chain().focus().toggleHeading({ level: 4 }).run(),
|
|
isActive: () => editor.value.isActive('heading', { level: 4 })
|
|
},
|
|
{
|
|
key: 'h5',
|
|
icon: 'mdi-format-header-5',
|
|
title: 'Header 5',
|
|
action: () => editor.value.chain().focus().toggleHeading({ level: 5 }).run(),
|
|
isActive: () => editor.value.isActive('heading', { level: 5 })
|
|
},
|
|
{
|
|
key: 'h6',
|
|
icon: 'mdi-format-header-6',
|
|
title: 'Header 6',
|
|
action: () => editor.value.chain().focus().toggleHeading({ level: 6 }).run(),
|
|
isActive: () => editor.value.isActive('heading', { level: 6 })
|
|
}
|
|
]
|
|
},
|
|
{
|
|
key: 'paragraph',
|
|
icon: 'mdi-format-paragraph',
|
|
title: 'Paragraph',
|
|
action: () => editor.value.chain().focus().setParagraph().run(),
|
|
isActive: () => editor.value.isActive('paragraph')
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'align',
|
|
type: 'btngroup',
|
|
children: [
|
|
{
|
|
key: 'align-left',
|
|
icon: 'mdi-format-align-left',
|
|
title: 'Left Align',
|
|
action: () => editor.value.chain().focus().setTextAlign('left').run(),
|
|
isActive: () => editor.value.isActive({ textAlign: 'left' })
|
|
},
|
|
{
|
|
key: 'align-center',
|
|
icon: 'mdi-format-align-center',
|
|
title: 'Center Align',
|
|
action: () => editor.value.chain().focus().setTextAlign('center').run(),
|
|
isActive: () => editor.value.isActive({ textAlign: 'center' })
|
|
},
|
|
{
|
|
key: 'align-right',
|
|
icon: 'mdi-format-align-right',
|
|
title: 'Right Align',
|
|
action: () => editor.value.chain().focus().setTextAlign('right').run(),
|
|
isActive: () => editor.value.isActive({ textAlign: 'right' })
|
|
},
|
|
{
|
|
key: 'align-justify',
|
|
icon: 'mdi-format-align-justify',
|
|
title: 'Justify Align',
|
|
action: () => editor.value.chain().focus().setTextAlign('justify').run(),
|
|
isActive: () => editor.value.isActive({ textAlign: 'justify' })
|
|
}
|
|
]
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'bulletlist',
|
|
icon: 'mdi-format-list-bulleted',
|
|
title: 'Bullet List',
|
|
action: () => editor.value.chain().focus().toggleBulletList().run(),
|
|
isActive: () => editor.value.isActive('bulletList')
|
|
},
|
|
{
|
|
key: 'orderedlist',
|
|
icon: 'mdi-format-list-numbered',
|
|
title: 'Ordered List',
|
|
action: () => editor.value.chain().focus().toggleOrderedList().run(),
|
|
isActive: () => editor.value.isActive('orderedList')
|
|
},
|
|
{
|
|
key: 'tasklist',
|
|
icon: 'mdi-format-list-checks',
|
|
title: 'Task List',
|
|
action: () => editor.value.chain().focus().toggleTaskList().run(),
|
|
isActive: () => editor.value.isActive('taskList')
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'codeblock',
|
|
icon: 'mdi-code-json',
|
|
title: 'Code Block',
|
|
action: () => editor.value.chain().focus().toggleCodeBlock().run(),
|
|
isActive: () => editor.value.isActive('codeBlock')
|
|
},
|
|
{
|
|
key: 'blockquote',
|
|
icon: 'mdi-format-quote-open',
|
|
title: 'Blockquote',
|
|
action: () => editor.value.chain().focus().toggleBlockquote().run(),
|
|
isActive: () => editor.value.isActive('blockquote')
|
|
},
|
|
{
|
|
key: 'rule',
|
|
icon: 'mdi-minus',
|
|
title: 'Horizontal Rule',
|
|
action: () => editor.value.chain().focus().setHorizontalRule().run()
|
|
},
|
|
{
|
|
key: 'link',
|
|
icon: 'mdi-link-variant',
|
|
title: 'Link',
|
|
action: () => {
|
|
// TODO: insert link
|
|
}
|
|
},
|
|
{
|
|
key: 'image',
|
|
icon: 'mdi-image-plus',
|
|
title: 'Image',
|
|
action: () => {
|
|
siteStore.openFileManager({ insertMode: true })
|
|
}
|
|
},
|
|
{
|
|
key: 'table',
|
|
icon: 'mdi-table',
|
|
title: 'Table',
|
|
type: 'dropdown',
|
|
isActive: () => editor.value.isActive('table'),
|
|
children: [
|
|
{
|
|
key: 'table-insert',
|
|
icon: 'mdi-table-large-plus',
|
|
title: 'Insert Table',
|
|
action: () => editor.value.chain().focus().insertTable({ rows: 3, cols: 3, withHeaderRow: true }).run()
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'table-addcolumnbefore',
|
|
icon: 'mdi-table-column-plus-before',
|
|
title: 'Add Column Before',
|
|
action: () => editor.value.chain().focus().addColumnBefore().run(),
|
|
disabled: () => !editor.value.can().addColumnBefore()
|
|
},
|
|
{
|
|
key: 'table-addcolumnafter',
|
|
icon: 'mdi-table-column-plus-after',
|
|
title: 'Add Column After',
|
|
action: () => editor.value.chain().focus().addColumnAfter().run(),
|
|
disabled: () => !editor.value.can().addColumnAfter()
|
|
},
|
|
{
|
|
key: 'table-deletecolumn',
|
|
icon: 'mdi-table-column-remove',
|
|
title: 'Remove Column',
|
|
action: () => editor.value.chain().focus().deleteColumn().run(),
|
|
disabled: () => !editor.value.can().deleteColumn()
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'table-addrowbefore',
|
|
icon: 'mdi-table-row-plus-before',
|
|
title: 'Add Row Before',
|
|
action: () => editor.value.chain().focus().addRowBefore().run(),
|
|
disabled: () => !editor.value.can().addRowBefore()
|
|
},
|
|
{
|
|
key: 'table-addrowafter',
|
|
icon: 'mdi-table-row-plus-after',
|
|
title: 'Add Row After',
|
|
action: () => editor.value.chain().focus().addRowAfter().run(),
|
|
disabled: () => !editor.value.can().addRowAfter()
|
|
},
|
|
{
|
|
key: 'table-deleterow',
|
|
icon: 'mdi-table-row-remove',
|
|
title: 'Remove Row',
|
|
action: () => editor.value.chain().focus().deleteRow().run(),
|
|
disabled: () => !editor.value.can().deleteRow()
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'table-merge',
|
|
icon: 'mdi-table-merge-cells',
|
|
title: 'Merge Cells',
|
|
action: () => editor.value.chain().focus().mergeCells().run(),
|
|
disabled: () => !editor.value.can().mergeCells()
|
|
},
|
|
{
|
|
key: 'table-split',
|
|
icon: 'mdi-table-split-cell',
|
|
title: 'Split Cell',
|
|
action: () => editor.value.chain().focus().splitCell().run(),
|
|
disabled: () => !editor.value.can().splitCell()
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'table-toggleHeaderColumn',
|
|
icon: 'mdi-table-column',
|
|
title: 'Toggle Header Column',
|
|
action: () => editor.value.chain().focus().toggleHeaderColumn().run(),
|
|
disabled: () => !editor.value.can().toggleHeaderColumn()
|
|
},
|
|
{
|
|
key: 'table-toggleHeaderRow',
|
|
icon: 'mdi-table-row',
|
|
title: 'Toggle Header Row',
|
|
action: () => editor.value.chain().focus().toggleHeaderRow().run(),
|
|
disabled: () => !editor.value.can().toggleHeaderRow()
|
|
},
|
|
{
|
|
key: 'table-toggleHeaderCell',
|
|
icon: 'mdi-crop-square',
|
|
title: 'Toggle Header Cell',
|
|
action: () => editor.value.chain().focus().toggleHeaderCell().run(),
|
|
disabled: () => !editor.value.can().toggleHeaderCell()
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'table-fix',
|
|
icon: 'mdi-table-heart',
|
|
title: 'Fix Table',
|
|
action: () => editor.value.chain().focus().fixTables().run(),
|
|
disabled: () => !editor.value.can().fixTables()
|
|
},
|
|
{
|
|
key: 'table-remove',
|
|
icon: 'mdi-table-large-remove',
|
|
title: 'Delete Table',
|
|
action: () => editor.value.chain().focus().deleteTable().run(),
|
|
disabled: () => !editor.value.can().deleteTable()
|
|
}
|
|
]
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'pagebreak',
|
|
icon: 'mdi-format-page-break',
|
|
title: 'Hard Break',
|
|
action: () => editor.value.chain().focus().setHardBreak().run()
|
|
},
|
|
{
|
|
key: 'clearformat',
|
|
icon: 'mdi-format-clear',
|
|
title: 'Clear Format',
|
|
action: () => editor.value.chain()
|
|
.focus()
|
|
.clearNodes()
|
|
.unsetAllMarks()
|
|
.run()
|
|
},
|
|
{
|
|
type: 'divider'
|
|
},
|
|
{
|
|
key: 'undo',
|
|
icon: 'mdi-undo-variant',
|
|
title: 'Undo',
|
|
action: () => editor.value.chain().focus().undo().run(),
|
|
disabled: () => !editor.value.can().undo()
|
|
},
|
|
{
|
|
key: 'redo',
|
|
icon: 'mdi-redo-variant',
|
|
title: 'Redo',
|
|
action: () => editor.value.chain().focus().redo().run(),
|
|
disabled: () => !editor.value.can().redo()
|
|
}
|
|
]
|
|
|
|
// METHODS
|
|
|
|
function init () {
|
|
// -> Setup Editor View
|
|
editorStore.$patch({
|
|
hideSideNav: false
|
|
})
|
|
|
|
// -> Init Live Collab
|
|
// this.ydoc = new Y.Doc()
|
|
|
|
/* eslint-disable no-unused-vars */
|
|
// const dbProvider = new IndexeddbPersistence('example-document', this.ydoc)
|
|
// const wsProvider = new WebsocketProvider('ws://127.0.0.1:1234', 'example-document', this.ydoc)
|
|
/* eslint-enable no-unused-vars */
|
|
|
|
// -> Initialize TipTap
|
|
editor = useEditor({
|
|
content: pageStore.content && pageStore.content.startsWith('{') ? JSON.parse(pageStore.content) : `<p>${pageStore.content}</p>`,
|
|
extensions: [
|
|
StarterKit.configure({
|
|
codeBlock: false,
|
|
history: {
|
|
depth: 500
|
|
}
|
|
}),
|
|
CodeBlockLowlight.configure({
|
|
lowlight
|
|
}),
|
|
Color,
|
|
// Collaboration.configure({
|
|
// document: this.ydoc
|
|
// }),
|
|
FontFamily,
|
|
Highlight.configure({
|
|
multicolor: true
|
|
}),
|
|
Image,
|
|
Mention.configure({
|
|
// TODO: suggestions
|
|
}),
|
|
Placeholder.configure({
|
|
placeholder: 'Enter some content here...'
|
|
}),
|
|
Table.configure({
|
|
resizable: true
|
|
}),
|
|
TableRow,
|
|
TableHeader,
|
|
TableCell,
|
|
TaskList,
|
|
TaskItem,
|
|
TextAlign,
|
|
TextStyle,
|
|
Typography
|
|
],
|
|
onUpdate: ({ editor }) => {
|
|
editorStore.$patch({
|
|
lastChangeTimestamp: DateTime.utc()
|
|
})
|
|
pageStore.$patch({
|
|
content: JSON.stringify(editor.getJSON()),
|
|
render: editor.getHTML()
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
function insertTable () {
|
|
// this.ql.getModule('table').insertTable(3, 3)
|
|
}
|
|
function snapshot () {
|
|
// console.info(Y.encodeStateVector(this.ydoc))
|
|
}
|
|
|
|
// MOUNTED
|
|
|
|
onMounted(() => {
|
|
// init()
|
|
})
|
|
|
|
onBeforeUnmount(() => {
|
|
editor.value.destroy()
|
|
})
|
|
|
|
init()
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.wysiwyg-container {
|
|
height: calc(100% - 41px);
|
|
|
|
.wysiwyg-toolbar {
|
|
border: none;
|
|
border-bottom: 1px solid $grey-4;
|
|
display: flex;
|
|
align-items: center;
|
|
padding: 4px;
|
|
background: linear-gradient(to top, $grey-1 0%, #FFF 100%);
|
|
}
|
|
|
|
.ProseMirror {
|
|
padding: 16px;
|
|
min-height: 75vh;
|
|
|
|
&-focused {
|
|
border: none;
|
|
outline: none;
|
|
}
|
|
|
|
> * + * {
|
|
margin-top: 0.75em;
|
|
}
|
|
|
|
ul,
|
|
ol {
|
|
padding: 0 1rem;
|
|
}
|
|
|
|
h1,
|
|
h2,
|
|
h3,
|
|
h4,
|
|
h5,
|
|
h6 {
|
|
line-height: 1.1;
|
|
}
|
|
|
|
code {
|
|
background-color: rgba(#616161, 0.1);
|
|
color: #616161;
|
|
}
|
|
|
|
pre {
|
|
background: #0D0D0D;
|
|
color: #FFF;
|
|
font-family: 'JetBrainsMono', monospace;
|
|
padding: 0.75rem 1rem;
|
|
border-radius: 0.5rem;
|
|
|
|
code {
|
|
color: inherit;
|
|
padding: 0;
|
|
background: none;
|
|
font-size: 0.8rem;
|
|
}
|
|
}
|
|
|
|
img {
|
|
max-width: 100%;
|
|
height: auto;
|
|
}
|
|
|
|
blockquote {
|
|
padding-left: 1rem;
|
|
border-left: 2px solid rgba(#0D0D0D, 0.1);
|
|
}
|
|
|
|
hr {
|
|
border: none;
|
|
border-top: 2px solid rgba(#0D0D0D, 0.1);
|
|
margin: 2rem 0;
|
|
}
|
|
|
|
table {
|
|
border-collapse: collapse;
|
|
table-layout: fixed;
|
|
width: 100%;
|
|
margin: 0;
|
|
overflow: hidden;
|
|
|
|
td,
|
|
th {
|
|
min-width: 1em;
|
|
border: 2px solid #ced4da;
|
|
padding: 3px 5px;
|
|
vertical-align: top;
|
|
box-sizing: border-box;
|
|
position: relative;
|
|
|
|
> * {
|
|
margin-bottom: 0;
|
|
}
|
|
}
|
|
|
|
th {
|
|
font-weight: bold;
|
|
text-align: left;
|
|
background-color: #f1f3f5;
|
|
}
|
|
|
|
.selectedCell:after {
|
|
z-index: 2;
|
|
position: absolute;
|
|
content: "";
|
|
left: 0; right: 0; top: 0; bottom: 0;
|
|
background: rgba(200, 200, 255, 0.4);
|
|
pointer-events: none;
|
|
}
|
|
|
|
.column-resize-handle {
|
|
position: absolute;
|
|
right: -2px;
|
|
top: 0;
|
|
bottom: -2px;
|
|
width: 4px;
|
|
background-color: #adf;
|
|
pointer-events: none;
|
|
}
|
|
}
|
|
|
|
.tableWrapper {
|
|
overflow-x: auto;
|
|
}
|
|
|
|
.resize-cursor {
|
|
cursor: ew-resize;
|
|
cursor: col-resize;
|
|
}
|
|
|
|
ul[data-type="taskList"] {
|
|
list-style: none;
|
|
padding: 0;
|
|
|
|
li {
|
|
display: flex;
|
|
align-items: center;
|
|
|
|
> label {
|
|
flex: 0 0 auto;
|
|
margin-right: 0.5rem;
|
|
}
|
|
}
|
|
}
|
|
|
|
p.is-editor-empty:first-child::before {
|
|
content: attr(data-placeholder);
|
|
float: left;
|
|
color: #ced4da;
|
|
pointer-events: none;
|
|
height: 0;
|
|
}
|
|
}
|
|
}
|
|
</style>
|