move extensions to own package
This commit is contained in:
@@ -16,7 +16,6 @@ import {
|
||||
builtInKeymap,
|
||||
} from '../utils'
|
||||
import builtInNodes from '../nodes'
|
||||
import builtInMarks from '../marks'
|
||||
|
||||
export default {
|
||||
|
||||
@@ -40,7 +39,6 @@ export default {
|
||||
data() {
|
||||
const plugins = new PluginManager([
|
||||
...builtInNodes,
|
||||
...builtInMarks,
|
||||
...this.extensions,
|
||||
])
|
||||
const { nodes, marks, views } = plugins
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import Editor from './components/editor'
|
||||
import Node from './utils/node'
|
||||
import Mark from './utils/mark'
|
||||
|
||||
export { Editor }
|
||||
export { Editor, Node, Mark }
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Mark } from 'tiptap-models'
|
||||
import { toggleMark } from 'tiptap-commands'
|
||||
|
||||
export default class BoldMark extends Mark {
|
||||
|
||||
get name() {
|
||||
return 'bold'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'strong',
|
||||
},
|
||||
{
|
||||
tag: 'b',
|
||||
getAttrs: node => node.style.fontWeight != 'normal' && null,
|
||||
},
|
||||
{
|
||||
style: 'font-weight',
|
||||
getAttrs: value => /^(bold(er)?|[5-9]\d{2,})$/.test(value) && null,
|
||||
},
|
||||
],
|
||||
toDOM: () => ['strong', 0],
|
||||
}
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return {
|
||||
'Mod-b': toggleMark(type),
|
||||
}
|
||||
}
|
||||
|
||||
command({ type }) {
|
||||
return toggleMark(type)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Mark } from 'tiptap-models'
|
||||
import { toggleMark } from 'tiptap-commands'
|
||||
|
||||
export default class CodeMark extends Mark {
|
||||
|
||||
get name() {
|
||||
return 'code'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
parseDOM: [
|
||||
{ tag: 'code' },
|
||||
],
|
||||
toDOM: () => ['code', 0],
|
||||
}
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return {
|
||||
'Mod-`': toggleMark(type),
|
||||
}
|
||||
}
|
||||
|
||||
command({ type }) {
|
||||
return toggleMark(type)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { Mark } from 'tiptap-models'
|
||||
import { toggleMark } from 'tiptap-commands'
|
||||
|
||||
export default class ItalicMark extends Mark {
|
||||
|
||||
get name() {
|
||||
return 'italic'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
parseDOM: [
|
||||
{ tag: 'i' },
|
||||
{ tag: 'em' },
|
||||
{ style: 'font-style=italic' },
|
||||
],
|
||||
toDOM: () => ['em', 0],
|
||||
}
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return {
|
||||
'Mod-i': toggleMark(type),
|
||||
}
|
||||
}
|
||||
|
||||
command({ type }) {
|
||||
return toggleMark(type)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
import { Mark } from 'tiptap-models'
|
||||
import { updateMark, removeMark } from 'tiptap-commands'
|
||||
|
||||
export default class LinkMark extends Mark {
|
||||
|
||||
get name() {
|
||||
return 'link'
|
||||
}
|
||||
|
||||
get view() {
|
||||
return {
|
||||
props: ['node'],
|
||||
methods: {
|
||||
onClick() {
|
||||
console.log('click on link')
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<a :href="node.attrs.href" rel="noopener noreferrer nofollow" ref="content" @click="onClick"></a>
|
||||
`,
|
||||
}
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
attrs: {
|
||||
href: {
|
||||
default: null,
|
||||
},
|
||||
},
|
||||
inclusive: false,
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'a[href]',
|
||||
getAttrs: dom => ({
|
||||
href: dom.getAttribute('href'),
|
||||
}),
|
||||
},
|
||||
],
|
||||
toDOM: node => ['a', {
|
||||
...node.attrs,
|
||||
rel: 'noopener noreferrer nofollow',
|
||||
}, 0],
|
||||
}
|
||||
}
|
||||
|
||||
command({ type, attrs }) {
|
||||
if (attrs.href) {
|
||||
return updateMark(type, attrs)
|
||||
}
|
||||
|
||||
return removeMark(type)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import Code from './Code'
|
||||
import Italic from './Italic'
|
||||
import Link from './Link'
|
||||
import Bold from './Bold'
|
||||
|
||||
export default [
|
||||
new Code(),
|
||||
new Italic(),
|
||||
new Link(),
|
||||
new Bold(),
|
||||
]
|
||||
@@ -1,39 +0,0 @@
|
||||
import { Node } from 'tiptap-models'
|
||||
import { wrappingInputRule, setBlockType, wrapIn } from 'tiptap-commands'
|
||||
|
||||
export default class BlockquoteNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'blockquote'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'block+',
|
||||
group: 'block',
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: [
|
||||
{ tag: 'blockquote' },
|
||||
],
|
||||
toDOM: () => ['blockquote', 0],
|
||||
}
|
||||
}
|
||||
|
||||
command({ type }) {
|
||||
return setBlockType(type)
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return {
|
||||
'Ctrl->': wrapIn(type),
|
||||
}
|
||||
}
|
||||
|
||||
inputRules({ type }) {
|
||||
return [
|
||||
wrappingInputRule(/^\s*>\s$/, type),
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import { Node } from 'tiptap-models'
|
||||
import { wrappingInputRule, wrapInList, toggleList } from 'tiptap-commands'
|
||||
|
||||
export default class BulletNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'bullet_list'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'list_item+',
|
||||
group: 'block',
|
||||
parseDOM: [
|
||||
{ tag: 'ul' },
|
||||
],
|
||||
toDOM: () => ['ul', 0],
|
||||
}
|
||||
}
|
||||
|
||||
command({ type, schema }) {
|
||||
return toggleList(type, schema.nodes.list_item)
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return {
|
||||
'Shift-Ctrl-8': wrapInList(type),
|
||||
}
|
||||
}
|
||||
|
||||
inputRules({ type }) {
|
||||
return [
|
||||
wrappingInputRule(/^\s*([-+*])\s$/, type),
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
import { Node } from 'tiptap-models'
|
||||
import { toggleBlockType, setBlockType, textblockTypeInputRule } from 'tiptap-commands'
|
||||
|
||||
export default class CodeBlockNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'code_block'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'text*',
|
||||
marks: '',
|
||||
group: 'block',
|
||||
code: true,
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: [
|
||||
{ tag: 'pre', preserveWhitespace: 'full' },
|
||||
],
|
||||
toDOM: () => ['pre', ['code', 0]],
|
||||
}
|
||||
}
|
||||
|
||||
command({ type, schema }) {
|
||||
return toggleBlockType(type, schema.nodes.paragraph)
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return {
|
||||
'Shift-Ctrl-\\': setBlockType(type),
|
||||
}
|
||||
}
|
||||
|
||||
inputRules({ type }) {
|
||||
return [
|
||||
textblockTypeInputRule(/^```$/, type),
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
import { Node } from 'tiptap-models'
|
||||
import { chainCommands, exitCode } from 'tiptap-commands'
|
||||
|
||||
export default class HardBreakNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'hard_break'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
inline: true,
|
||||
group: 'inline',
|
||||
selectable: false,
|
||||
parseDOM: [
|
||||
{ tag: 'br' },
|
||||
],
|
||||
toDOM: () => ['br'],
|
||||
}
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
const command = chainCommands(exitCode, (state, dispatch) => {
|
||||
dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView())
|
||||
return true
|
||||
})
|
||||
return {
|
||||
'Mod-Enter': command,
|
||||
'Shift-Enter': command,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
import { Node } from 'tiptap-models'
|
||||
import { setBlockType, textblockTypeInputRule, toggleBlockType } from 'tiptap-commands'
|
||||
|
||||
export default class HeadingNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'heading'
|
||||
}
|
||||
|
||||
get defaultOptions() {
|
||||
return {
|
||||
maxLevel: 6,
|
||||
}
|
||||
}
|
||||
|
||||
get levels() {
|
||||
return Array.from(new Array(this.options.maxLevel), (value, index) => index + 1)
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
attrs: {
|
||||
level: {
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
content: 'inline*',
|
||||
group: 'block',
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: this.levels.map(level => ({ tag: `h${level}`, attrs: { level } })),
|
||||
toDOM: node => [`h${node.attrs.level}`, 0],
|
||||
}
|
||||
}
|
||||
|
||||
command({ type, schema, attrs }) {
|
||||
return toggleBlockType(type, schema.nodes.paragraph, attrs)
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return this.levels.reduce((items, level) => ({
|
||||
...items,
|
||||
...{
|
||||
[`Shift-Ctrl-${level}`]: setBlockType(type, { level }),
|
||||
},
|
||||
}), {})
|
||||
}
|
||||
|
||||
inputRules({ type }) {
|
||||
return [
|
||||
textblockTypeInputRule(
|
||||
new RegExp(`^(#{1,${this.options.maxLevel}})\\s$`),
|
||||
type,
|
||||
match => ({ level: match[1].length }),
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
import { Node } from 'tiptap-models'
|
||||
import { splitListItem, liftListItem, sinkListItem } from 'tiptap-commands'
|
||||
|
||||
export default class OrderedListNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'list_item'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
content: 'paragraph block*',
|
||||
group: 'block',
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: [
|
||||
{ tag: 'li' },
|
||||
],
|
||||
toDOM: () => ['li', 0],
|
||||
}
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return {
|
||||
Enter: splitListItem(type),
|
||||
Tab: sinkListItem(type),
|
||||
'Shift-Tab': liftListItem(type),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import { Node } from 'tiptap-models'
|
||||
import { wrappingInputRule, wrapInList, toggleList } from 'tiptap-commands'
|
||||
|
||||
export default class OrderedListNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'ordered_list'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
attrs: {
|
||||
order: {
|
||||
default: 1,
|
||||
},
|
||||
},
|
||||
content: 'list_item+',
|
||||
group: 'block',
|
||||
parseDOM: [
|
||||
{
|
||||
tag: 'ol',
|
||||
getAttrs: dom => ({
|
||||
order: dom.hasAttribute('start') ? +dom.getAttribute('start') : 1,
|
||||
}),
|
||||
},
|
||||
],
|
||||
toDOM: node => (node.attrs.order === 1 ? ['ol', 0] : ['ol', { start: node.attrs.order }, 0]),
|
||||
}
|
||||
}
|
||||
|
||||
command({ type, schema }) {
|
||||
return toggleList(type, schema.nodes.list_item)
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return {
|
||||
'Shift-Ctrl-9': wrapInList(type),
|
||||
}
|
||||
}
|
||||
|
||||
inputRules({ type }) {
|
||||
return [
|
||||
wrappingInputRule(
|
||||
/^(\d+)\.\s$/,
|
||||
type,
|
||||
match => ({ order: +match[1] }),
|
||||
(match, node) => node.childCount + node.attrs.order === +match[1],
|
||||
),
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,10 +23,4 @@ export default class ParagraphNode extends Node {
|
||||
return setBlockType(type)
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return {
|
||||
'Shift-Ctrl-0': setBlockType(type),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
import { Node } from 'tiptap-models'
|
||||
import { splitListItem, liftListItem } from 'tiptap-commands'
|
||||
|
||||
export default class TodoItemNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'todo_item'
|
||||
}
|
||||
|
||||
get view() {
|
||||
return {
|
||||
props: ['node', 'updateAttrs', 'editable'],
|
||||
methods: {
|
||||
onChange() {
|
||||
if (!this.editable) {
|
||||
return
|
||||
}
|
||||
this.updateAttrs({
|
||||
done: !this.node.attrs.done,
|
||||
})
|
||||
},
|
||||
},
|
||||
template: `
|
||||
<li data-type="todo_item" :data-done="node.attrs.done.toString()">
|
||||
<span class="todo-checkbox" contenteditable="false" @click="onChange"></span>
|
||||
<div class="todo-content" ref="content" :contenteditable="editable.toString()"></div>
|
||||
</li>
|
||||
`,
|
||||
}
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
attrs: {
|
||||
done: {
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
draggable: false,
|
||||
content: 'paragraph',
|
||||
toDOM(node) {
|
||||
const { done } = node.attrs
|
||||
|
||||
return ['li', {
|
||||
'data-type': 'todo_item',
|
||||
'data-done': done.toString(),
|
||||
},
|
||||
['span', { class: 'todo-checkbox', contenteditable: 'false' }],
|
||||
['div', { class: 'todo-content' }, 0],
|
||||
]
|
||||
},
|
||||
parseDOM: [{
|
||||
priority: 51,
|
||||
tag: '[data-type="todo_item"]',
|
||||
getAttrs: dom => ({
|
||||
done: dom.getAttribute('data-done') === 'true',
|
||||
}),
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
keys({ type }) {
|
||||
return {
|
||||
Enter: splitListItem(type),
|
||||
'Shift-Tab': liftListItem(type),
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
import { Node } from 'tiptap-models'
|
||||
import { wrapInList, wrappingInputRule } from 'tiptap-commands'
|
||||
|
||||
export default class BulletNode extends Node {
|
||||
|
||||
get name() {
|
||||
return 'todo_list'
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return {
|
||||
group: 'block',
|
||||
content: 'todo_item+',
|
||||
toDOM: () => ['ul', { 'data-type': 'todo_list' }, 0],
|
||||
parseDOM: [{
|
||||
priority: 51,
|
||||
tag: '[data-type="todo_list"]',
|
||||
}],
|
||||
}
|
||||
}
|
||||
|
||||
command({ type }) {
|
||||
return wrapInList(type)
|
||||
}
|
||||
|
||||
inputRules({ type }) {
|
||||
return [
|
||||
wrappingInputRule(/^\s*(\[ \])\s$/, type),
|
||||
]
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,29 +1,9 @@
|
||||
import Blockquote from './Blockquote'
|
||||
import BulletList from './BulletList'
|
||||
import CodeBlock from './CodeBlock'
|
||||
import Doc from './Doc'
|
||||
import HardBreak from './HardBreak'
|
||||
import Heading from './Heading'
|
||||
import ListItem from './ListItem'
|
||||
import OrderedList from './OrderedList'
|
||||
import Paragraph from './Paragraph'
|
||||
import Text from './Text'
|
||||
import TodoList from './TodoList'
|
||||
import TodoItem from './TodoItem'
|
||||
|
||||
export default [
|
||||
// essentials
|
||||
new Doc(),
|
||||
new Paragraph(),
|
||||
new Text(),
|
||||
|
||||
new Blockquote(),
|
||||
new CodeBlock(),
|
||||
new Heading({ maxLevel: 3 }),
|
||||
new HardBreak(),
|
||||
new OrderedList(),
|
||||
new BulletList(),
|
||||
new ListItem(),
|
||||
new TodoList(),
|
||||
new TodoItem(),
|
||||
new Paragraph(),
|
||||
]
|
||||
|
||||
46
packages/tiptap/src/utils/mark.js
Normal file
46
packages/tiptap/src/utils/mark.js
Normal file
@@ -0,0 +1,46 @@
|
||||
export default class Mark {
|
||||
|
||||
constructor(options = {}) {
|
||||
this.options = {
|
||||
...this.defaultOptions,
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
get name() {
|
||||
return null
|
||||
}
|
||||
|
||||
get defaultOptions() {
|
||||
return {}
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'mark'
|
||||
}
|
||||
|
||||
get view() {
|
||||
return null
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return null
|
||||
}
|
||||
|
||||
get plugins() {
|
||||
return []
|
||||
}
|
||||
|
||||
command() {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
keys() {
|
||||
return {}
|
||||
}
|
||||
|
||||
inputRules() {
|
||||
return []
|
||||
}
|
||||
|
||||
}
|
||||
46
packages/tiptap/src/utils/node.js
Normal file
46
packages/tiptap/src/utils/node.js
Normal file
@@ -0,0 +1,46 @@
|
||||
export default class Node {
|
||||
|
||||
constructor(options = {}) {
|
||||
this.options = {
|
||||
...this.defaultOptions,
|
||||
...options,
|
||||
}
|
||||
}
|
||||
|
||||
get name() {
|
||||
return null
|
||||
}
|
||||
|
||||
get defaultOptions() {
|
||||
return {}
|
||||
}
|
||||
|
||||
get type() {
|
||||
return 'node'
|
||||
}
|
||||
|
||||
get view() {
|
||||
return null
|
||||
}
|
||||
|
||||
get schema() {
|
||||
return null
|
||||
}
|
||||
|
||||
get plugins() {
|
||||
return []
|
||||
}
|
||||
|
||||
command() {
|
||||
return () => {}
|
||||
}
|
||||
|
||||
keys() {
|
||||
return {}
|
||||
}
|
||||
|
||||
inputRules() {
|
||||
return []
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user