mirror of
https://github.com/ueberdosis/tiptap.git
synced 2024-11-27 23:15:15 +08:00
add new syntax to all extensions
This commit is contained in:
parent
e442b5a8fe
commit
79172753ef
@ -3,11 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// import { Editor, EditorContent, defaultExtensions } from '@tiptap/vue-starter-kit'
|
||||
import { Editor, EditorContent } from '@tiptap/vue-starter-kit'
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import { Editor, EditorContent, defaultExtensions } from '@tiptap/vue-starter-kit'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
@ -23,12 +19,7 @@ export default {
|
||||
mounted() {
|
||||
this.editor = new Editor({
|
||||
content: '<p>I’m running tiptap with Vue.js. 🎉</p>',
|
||||
// extensions: defaultExtensions(),
|
||||
extensions: [
|
||||
new Document(),
|
||||
new Paragraph(),
|
||||
new Text(),
|
||||
],
|
||||
extensions: defaultExtensions(),
|
||||
})
|
||||
},
|
||||
|
||||
|
@ -1,141 +1,73 @@
|
||||
// import cloneDeep from 'clone-deep'
|
||||
// import { Plugin } from 'prosemirror-state'
|
||||
// import { Editor, CommandsSpec } from './Editor'
|
||||
|
||||
// type AnyObject = {
|
||||
// [key: string]: any
|
||||
// }
|
||||
|
||||
// type NoInfer<T> = [T][T extends any ? 0 : never]
|
||||
|
||||
// type MergeStrategy = 'extend' | 'overwrite'
|
||||
|
||||
// type Configs = {
|
||||
// [key: string]: {
|
||||
// stategy: MergeStrategy
|
||||
// value: any
|
||||
// }[]
|
||||
// }
|
||||
|
||||
// export interface ExtensionProps<Options> {
|
||||
// name: string
|
||||
// editor: Editor
|
||||
// options: Options
|
||||
// }
|
||||
|
||||
// export interface ExtensionMethods<Props, Options> {
|
||||
// name: string
|
||||
// options: Options
|
||||
// commands: (params: Props) => CommandsSpec
|
||||
// inputRules: (params: Props) => any[]
|
||||
// pasteRules: (params: Props) => any[]
|
||||
// keys: (params: Props) => {
|
||||
// [key: string]: Function
|
||||
// }
|
||||
// plugins: (params: Props) => Plugin[]
|
||||
// }
|
||||
|
||||
// export default class Extension<
|
||||
// Options = {},
|
||||
// Props = ExtensionProps<Options>,
|
||||
// Methods extends ExtensionMethods<Props, Options> = ExtensionMethods<Props, Options>,
|
||||
// > {
|
||||
// type = 'extension'
|
||||
|
||||
// config: AnyObject = {}
|
||||
|
||||
// configs: Configs = {}
|
||||
|
||||
// options: Partial<Options> = {}
|
||||
|
||||
// protected storeConfig(key: string, value: any, stategy: MergeStrategy) {
|
||||
// const item = {
|
||||
// stategy,
|
||||
// value,
|
||||
// }
|
||||
|
||||
// if (this.configs[key]) {
|
||||
// this.configs[key].push(item)
|
||||
// } else {
|
||||
// this.configs[key] = [item]
|
||||
// }
|
||||
// }
|
||||
|
||||
// public configure(options: Partial<Options>) {
|
||||
// this.options = { ...this.options, ...options }
|
||||
// return this
|
||||
// }
|
||||
|
||||
// public name(value: Methods['name']) {
|
||||
// this.storeConfig('name', value, 'overwrite')
|
||||
// return this
|
||||
// }
|
||||
|
||||
// public defaults(value: Options) {
|
||||
// this.storeConfig('defaults', value, 'overwrite')
|
||||
// return this
|
||||
// }
|
||||
|
||||
// public commands(value: Methods['commands']) {
|
||||
// this.storeConfig('commands', value, 'overwrite')
|
||||
// return this
|
||||
// }
|
||||
|
||||
// public keys(value: Methods['keys']) {
|
||||
// this.storeConfig('keys', value, 'overwrite')
|
||||
// return this
|
||||
// }
|
||||
|
||||
// public inputRules(value: Methods['inputRules']) {
|
||||
// this.storeConfig('inputRules', value, 'overwrite')
|
||||
// return this
|
||||
// }
|
||||
|
||||
// public pasteRules(value: Methods['pasteRules']) {
|
||||
// this.storeConfig('pasteRules', value, 'overwrite')
|
||||
// return this
|
||||
// }
|
||||
|
||||
// public plugins(value: Methods['plugins']) {
|
||||
// this.storeConfig('plugins', value, 'overwrite')
|
||||
// return this
|
||||
// }
|
||||
|
||||
// public extend<T extends Extract<keyof Methods, string>>(key: T, value: Methods[T]) {
|
||||
// this.storeConfig(key, value, 'extend')
|
||||
// return this
|
||||
// }
|
||||
|
||||
// public create() {
|
||||
// return <NewOptions = Options>(options?: Partial<NoInfer<NewOptions>>) => {
|
||||
// return cloneDeep(this, true).configure(options as NewOptions)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
import { Plugin } from 'prosemirror-state'
|
||||
import { Editor } from './Editor'
|
||||
import { GlobalAttributes } from './types'
|
||||
|
||||
export interface ExtensionSpec<Options = {}, Commands = {}> {
|
||||
/**
|
||||
* The name of your extension
|
||||
*/
|
||||
name: string,
|
||||
|
||||
/**
|
||||
* Default options
|
||||
*/
|
||||
defaultOptions?: Options,
|
||||
|
||||
/**
|
||||
* Global attributes
|
||||
*/
|
||||
addGlobalAttributes?: (
|
||||
this: {
|
||||
options: Options,
|
||||
},
|
||||
) => GlobalAttributes,
|
||||
|
||||
/**
|
||||
* Commands
|
||||
*/
|
||||
addCommands?: (this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
}) => Commands,
|
||||
|
||||
/**
|
||||
* Keyboard shortcuts
|
||||
*/
|
||||
addKeyboardShortcuts?: (this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
}) => {
|
||||
[key: string]: any
|
||||
},
|
||||
|
||||
/**
|
||||
* Input rules
|
||||
*/
|
||||
addInputRules?: (this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
}) => any[],
|
||||
|
||||
/**
|
||||
* Paste rules
|
||||
*/
|
||||
addPasteRules?: (this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
}) => any[],
|
||||
|
||||
/**
|
||||
* ProseMirror plugins
|
||||
*/
|
||||
addProseMirrorPlugins?: (this: {
|
||||
options: Options,
|
||||
editor: Editor,
|
||||
}) => Plugin[],
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension interface for internal usage
|
||||
*/
|
||||
export type Extension = Required<Omit<ExtensionSpec, 'defaultOptions'> & {
|
||||
type: string,
|
||||
options: {
|
||||
@ -143,6 +75,9 @@ export type Extension = Required<Omit<ExtensionSpec, 'defaultOptions'> & {
|
||||
},
|
||||
}>
|
||||
|
||||
/**
|
||||
* Default extension
|
||||
*/
|
||||
export const defaultExtension: Extension = {
|
||||
type: 'extension',
|
||||
name: 'extension',
|
||||
@ -150,6 +85,9 @@ export const defaultExtension: Extension = {
|
||||
addGlobalAttributes: () => [],
|
||||
addCommands: () => ({}),
|
||||
addKeyboardShortcuts: () => ({}),
|
||||
addInputRules: () => [],
|
||||
addPasteRules: () => [],
|
||||
addProseMirrorPlugins: () => [],
|
||||
}
|
||||
|
||||
export function createExtension<Options extends {}, Commands extends {}>(config: ExtensionSpec<Options, Commands>) {
|
||||
|
@ -1,37 +1,52 @@
|
||||
import { Command, Node } from '@tiptap/core'
|
||||
import { Command, createNode } from '@tiptap/core'
|
||||
import { wrappingInputRule } from 'prosemirror-inputrules'
|
||||
|
||||
export type BlockquoteCommand = () => Command
|
||||
// export type BlockquoteCommand = () => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
blockquote: BlockquoteCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// blockquote: BlockquoteCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export const inputRegex = /^\s*>\s$/gm
|
||||
|
||||
export default new Node()
|
||||
.name('blockquote')
|
||||
.schema(() => ({
|
||||
content: 'block*',
|
||||
group: 'block',
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: [
|
||||
export default createNode({
|
||||
name: 'blockquote',
|
||||
|
||||
content: 'block*',
|
||||
|
||||
group: 'block',
|
||||
|
||||
defining: true,
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{ tag: 'blockquote' },
|
||||
],
|
||||
toDOM: () => ['blockquote', 0],
|
||||
}))
|
||||
.commands(({ name }) => ({
|
||||
[name]: () => ({ commands }) => {
|
||||
return commands.toggleWrap(name)
|
||||
},
|
||||
}))
|
||||
.keys(({ editor }) => ({
|
||||
'Shift-Mod-9': () => editor.blockquote(),
|
||||
}))
|
||||
.inputRules(({ type }) => [
|
||||
wrappingInputRule(inputRegex, type),
|
||||
])
|
||||
.create()
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ attributes }) {
|
||||
return ['blockquote', attributes, 0]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
blockquote: () => ({ commands }) => {
|
||||
return commands.toggleWrap('blockquote')
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Shift-Mod-9': () => this.editor.blockquote(),
|
||||
}
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
wrappingInputRule(inputRegex, this.type),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -1,24 +1,25 @@
|
||||
import {
|
||||
Command, Mark, markInputRule, markPasteRule,
|
||||
Command, createMark, markInputRule, markPasteRule,
|
||||
} from '@tiptap/core'
|
||||
|
||||
export type BoldCommand = () => Command
|
||||
// export type BoldCommand = () => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
bold: BoldCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// bold: BoldCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export const starInputRegex = /(?:^|\s)((?:\*\*)((?:[^*]+))(?:\*\*))$/gm
|
||||
export const starPasteRegex = /(?:^|\s)((?:\*\*)((?:[^*]+))(?:\*\*))/gm
|
||||
export const underscoreInputRegex = /(?:^|\s)((?:__)((?:[^__]+))(?:__))$/gm
|
||||
export const underscorePasteRegex = /(?:^|\s)((?:__)((?:[^__]+))(?:__))/gm
|
||||
|
||||
export default new Mark()
|
||||
.name('bold')
|
||||
.schema(() => ({
|
||||
parseDOM: [
|
||||
export default createMark({
|
||||
name: 'bold',
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'strong',
|
||||
},
|
||||
@ -30,23 +31,38 @@ export default new Mark()
|
||||
style: 'font-weight',
|
||||
getAttrs: value => /^(bold(er)?|[5-9]\d{2,})$/.test(value as string) && null,
|
||||
},
|
||||
],
|
||||
toDOM: () => ['strong', 0],
|
||||
}))
|
||||
.commands(({ name }) => ({
|
||||
bold: () => ({ commands }) => {
|
||||
return commands.toggleMark(name)
|
||||
},
|
||||
}))
|
||||
.keys(({ editor }) => ({
|
||||
'Mod-b': () => editor.bold(),
|
||||
}))
|
||||
.inputRules(({ type }) => [
|
||||
markInputRule(starInputRegex, type),
|
||||
markInputRule(underscoreInputRegex, type),
|
||||
])
|
||||
.pasteRules(({ type }) => [
|
||||
markPasteRule(starPasteRegex, type),
|
||||
markPasteRule(underscorePasteRegex, type),
|
||||
])
|
||||
.create()
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ attributes }) {
|
||||
return ['strong', attributes, 0]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
bold: () => ({ commands }) => {
|
||||
return commands.toggleMark('bold')
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-b': () => this.editor.bold(),
|
||||
}
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
markInputRule(starInputRegex, this.type),
|
||||
markInputRule(underscoreInputRegex, this.type),
|
||||
]
|
||||
},
|
||||
|
||||
addPasteRules() {
|
||||
return [
|
||||
markPasteRule(starPasteRegex, this.type),
|
||||
markPasteRule(underscorePasteRegex, this.type),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -1,33 +1,48 @@
|
||||
import { Command, Node } from '@tiptap/core'
|
||||
import { Command, createNode } from '@tiptap/core'
|
||||
import { wrappingInputRule } from 'prosemirror-inputrules'
|
||||
|
||||
export type BulletListCommand = () => Command
|
||||
// export type BulletListCommand = () => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
bulletList: BulletListCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// bulletList: BulletListCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export default new Node()
|
||||
.name('bullet_list')
|
||||
.schema(() => ({
|
||||
content: 'list_item+',
|
||||
group: 'block',
|
||||
parseDOM: [
|
||||
export default createNode({
|
||||
name: 'bullet_list',
|
||||
|
||||
content: 'list_item+',
|
||||
|
||||
group: 'block',
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{ tag: 'ul' },
|
||||
],
|
||||
toDOM: () => ['ul', 0],
|
||||
}))
|
||||
.commands(({ name }) => ({
|
||||
bulletList: () => ({ commands }) => {
|
||||
return commands.toggleList(name, 'list_item')
|
||||
},
|
||||
}))
|
||||
.keys(({ editor }) => ({
|
||||
'Shift-Control-8': () => editor.bulletList(),
|
||||
}))
|
||||
.inputRules(({ type }) => [
|
||||
wrappingInputRule(/^\s*([-+*])\s$/, type),
|
||||
])
|
||||
.create()
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ attributes }) {
|
||||
return ['ul', attributes, 0]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
bulletList: () => ({ commands }) => {
|
||||
return commands.toggleList('bullet_list', 'list_item')
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Shift-Control-8': () => this.editor.bulletList(),
|
||||
}
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
wrappingInputRule(/^\s*([-+*])\s$/, this.type),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -1,69 +1,91 @@
|
||||
import { Command, Node } from '@tiptap/core'
|
||||
import { Command, createNode } from '@tiptap/core'
|
||||
import { textblockTypeInputRule } from 'prosemirror-inputrules'
|
||||
|
||||
export interface CodeBlockOptions {
|
||||
languageClassPrefix: string,
|
||||
}
|
||||
|
||||
export type CodeBlockCommand = () => Command
|
||||
// export type CodeBlockCommand = () => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
codeBlock: CodeBlockCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// codeBlock: CodeBlockCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export const backtickInputRegex = /^```(?<language>[a-z]*)? $/
|
||||
export const tildeInputRegex = /^~~~(?<language>[a-z]*)? $/
|
||||
|
||||
export default new Node<CodeBlockOptions>()
|
||||
.name('code_block')
|
||||
.defaults({
|
||||
export default createNode({
|
||||
name: 'code_block',
|
||||
|
||||
defaultOptions: <CodeBlockOptions>{
|
||||
languageClassPrefix: 'language-',
|
||||
})
|
||||
.schema(({ options }) => ({
|
||||
attrs: {
|
||||
},
|
||||
|
||||
content: 'text*',
|
||||
|
||||
marks: '',
|
||||
|
||||
group: 'block',
|
||||
|
||||
code: true,
|
||||
|
||||
defining: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
language: {
|
||||
default: null,
|
||||
rendered: false,
|
||||
},
|
||||
},
|
||||
content: 'text*',
|
||||
marks: '',
|
||||
group: 'block',
|
||||
code: true,
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: [
|
||||
}
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'pre',
|
||||
preserveWhitespace: 'full',
|
||||
getAttrs(node) {
|
||||
getAttrs: node => {
|
||||
const classAttribute = (node as Element).firstElementChild?.getAttribute('class')
|
||||
|
||||
if (!classAttribute) {
|
||||
return null
|
||||
}
|
||||
|
||||
const regexLanguageClassPrefix = new RegExp(`^(${options.languageClassPrefix})`)
|
||||
const regexLanguageClassPrefix = new RegExp(`^(${this.options.languageClassPrefix})`)
|
||||
|
||||
return { language: classAttribute.replace(regexLanguageClassPrefix, '') }
|
||||
},
|
||||
},
|
||||
],
|
||||
toDOM: node => ['pre', ['code', {
|
||||
class: node.attrs.language && options.languageClassPrefix + node.attrs.language,
|
||||
}, 0]],
|
||||
}))
|
||||
.commands(({ name }) => ({
|
||||
codeBlock: attrs => ({ commands }) => {
|
||||
return commands.toggleBlockType(name, 'paragraph', attrs)
|
||||
},
|
||||
}))
|
||||
.keys(({ editor }) => ({
|
||||
'Mod-Shift-c': () => editor.codeBlock(),
|
||||
}))
|
||||
.inputRules(({ type }) => [
|
||||
textblockTypeInputRule(backtickInputRegex, type, ({ groups }: any) => groups),
|
||||
textblockTypeInputRule(tildeInputRegex, type, ({ groups }: any) => groups),
|
||||
])
|
||||
.create()
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ node, attributes }) {
|
||||
return ['pre', attributes, ['code', {
|
||||
class: node.attrs.language && this.options.languageClassPrefix + node.attrs.language,
|
||||
}, 0]]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
codeBlock: attrs => ({ commands }) => {
|
||||
return commands.toggleBlockType('code_block', 'paragraph', attrs)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-Shift-c': () => this.editor.codeBlock(),
|
||||
}
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
textblockTypeInputRule(backtickInputRegex, this.type, ({ groups }: any) => groups),
|
||||
textblockTypeInputRule(tildeInputRegex, this.type, ({ groups }: any) => groups),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -1,39 +1,56 @@
|
||||
import {
|
||||
Command, Mark, markInputRule, markPasteRule,
|
||||
Command, createMark, markInputRule, markPasteRule,
|
||||
} from '@tiptap/core'
|
||||
|
||||
export type CodeCommand = () => Command
|
||||
// export type CodeCommand = () => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
code: CodeCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// code: CodeCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export const inputRegex = /(?:^|\s)((?:`)((?:[^`]+))(?:`))$/gm
|
||||
export const pasteRegex = /(?:^|\s)((?:`)((?:[^`]+))(?:`))/gm
|
||||
|
||||
export default new Mark()
|
||||
.name('code')
|
||||
.schema(() => ({
|
||||
excludes: '_',
|
||||
parseDOM: [
|
||||
export default createMark({
|
||||
name: 'code',
|
||||
|
||||
excludes: '_',
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{ tag: 'code' },
|
||||
],
|
||||
toDOM: () => ['code', 0],
|
||||
}))
|
||||
.commands(({ name }) => ({
|
||||
code: () => ({ commands }) => {
|
||||
return commands.toggleMark(name)
|
||||
},
|
||||
}))
|
||||
.keys(({ editor }) => ({
|
||||
'Mod-`': () => editor.code(),
|
||||
}))
|
||||
.inputRules(({ type }) => [
|
||||
markInputRule(inputRegex, type),
|
||||
])
|
||||
.pasteRules(({ type }) => [
|
||||
markPasteRule(inputRegex, type),
|
||||
])
|
||||
.create()
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ attributes }) {
|
||||
return ['strong', attributes, 0]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
code: () => ({ commands }) => {
|
||||
return commands.toggleMark('code')
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-`': () => this.editor.code(),
|
||||
}
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
markInputRule(inputRegex, this.type),
|
||||
]
|
||||
},
|
||||
|
||||
addPasteRules() {
|
||||
return [
|
||||
markPasteRule(inputRegex, this.type),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Extension, Command } from '@tiptap/core'
|
||||
import { createExtension, Command } from '@tiptap/core'
|
||||
import { yCursorPlugin } from 'y-prosemirror'
|
||||
|
||||
export interface CollaborationCursorOptions {
|
||||
@ -8,27 +8,21 @@ export interface CollaborationCursorOptions {
|
||||
render (user: { name: string, color: string }): HTMLElement,
|
||||
}
|
||||
|
||||
export type UserCommand = (attributes: {
|
||||
name: string,
|
||||
color: string,
|
||||
}) => Command
|
||||
// export type UserCommand = (attributes: {
|
||||
// name: string,
|
||||
// color: string,
|
||||
// }) => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
user: UserCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// user: UserCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export default new Extension<CollaborationCursorOptions>()
|
||||
.name('collaboration_cursor')
|
||||
.commands(({ options }) => ({
|
||||
user: attributes => () => {
|
||||
options.provider.awareness.setLocalStateField('user', attributes)
|
||||
export default createExtension({
|
||||
name: 'collaboration_cursor',
|
||||
|
||||
return true
|
||||
},
|
||||
}))
|
||||
.defaults({
|
||||
defaultOptions: <CollaborationCursorOptions>{
|
||||
provider: null,
|
||||
name: 'Someone',
|
||||
color: '#cccccc',
|
||||
@ -45,19 +39,32 @@ export default new Extension<CollaborationCursorOptions>()
|
||||
|
||||
return cursor
|
||||
},
|
||||
})
|
||||
.plugins(({ options }) => [
|
||||
yCursorPlugin((() => {
|
||||
options.provider.awareness.setLocalStateField('user', {
|
||||
name: options.name,
|
||||
color: options.color,
|
||||
})
|
||||
},
|
||||
|
||||
return options.provider.awareness
|
||||
})(),
|
||||
// @ts-ignore
|
||||
{
|
||||
cursorBuilder: options.render,
|
||||
}),
|
||||
])
|
||||
.create()
|
||||
addCommands() {
|
||||
return {
|
||||
user: attributes => () => {
|
||||
this.options.provider.awareness.setLocalStateField('user', attributes)
|
||||
|
||||
return true
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
yCursorPlugin((() => {
|
||||
this.options.provider.awareness.setLocalStateField('user', {
|
||||
name: this.options.name,
|
||||
color: this.options.color,
|
||||
})
|
||||
|
||||
return this.options.provider.awareness
|
||||
})(),
|
||||
// @ts-ignore
|
||||
{
|
||||
cursorBuilder: this.options.render,
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { createExtension } from '@tiptap/core'
|
||||
import {
|
||||
redo, undo, ySyncPlugin, yUndoPlugin,
|
||||
} from 'y-prosemirror'
|
||||
@ -8,21 +8,26 @@ export interface CollaborationOptions {
|
||||
type: any,
|
||||
}
|
||||
|
||||
export default new Extension<CollaborationOptions>()
|
||||
.name('collaboration')
|
||||
.defaults({
|
||||
export default createExtension({
|
||||
name: 'collaboration',
|
||||
|
||||
defaultOptions: <CollaborationOptions>{
|
||||
provider: null,
|
||||
type: null,
|
||||
})
|
||||
.plugins(({ options }) => [
|
||||
ySyncPlugin(options.type),
|
||||
yUndoPlugin(),
|
||||
])
|
||||
.keys(() => {
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
ySyncPlugin(this.options.type),
|
||||
yUndoPlugin(),
|
||||
]
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-z': undo,
|
||||
'Mod-y': redo,
|
||||
'Mod-Shift-z': redo,
|
||||
}
|
||||
})
|
||||
.create()
|
||||
},
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Extension } from '@tiptap/core'
|
||||
import { createExtension } from '@tiptap/core'
|
||||
import { Plugin } from 'prosemirror-state'
|
||||
import { DecorationSet, Decoration } from 'prosemirror-view'
|
||||
|
||||
@ -7,40 +7,44 @@ export interface FocusOptions {
|
||||
nested: boolean,
|
||||
}
|
||||
|
||||
export default new Extension<FocusOptions>()
|
||||
.name('focus')
|
||||
.defaults({
|
||||
export default createExtension({
|
||||
name: 'focus',
|
||||
|
||||
defaultOptions: <FocusOptions>{
|
||||
className: 'has-focus',
|
||||
nested: false,
|
||||
})
|
||||
.plugins(({ editor, options }) => [
|
||||
new Plugin({
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const { isEditable, isFocused } = editor
|
||||
const { anchor } = selection
|
||||
const decorations: Decoration[] = []
|
||||
},
|
||||
|
||||
if (!isEditable || !isFocused) {
|
||||
return DecorationSet.create(doc, [])
|
||||
}
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
new Plugin({
|
||||
props: {
|
||||
decorations: ({ doc, selection }) => {
|
||||
const { isEditable, isFocused } = this.editor
|
||||
const { anchor } = selection
|
||||
const decorations: Decoration[] = []
|
||||
|
||||
doc.descendants((node, pos) => {
|
||||
const hasAnchor = anchor >= pos && anchor <= (pos + node.nodeSize)
|
||||
|
||||
if (hasAnchor && !node.isText) {
|
||||
const decoration = Decoration.node(pos, pos + node.nodeSize, {
|
||||
class: options.className,
|
||||
})
|
||||
decorations.push(decoration)
|
||||
if (!isEditable || !isFocused) {
|
||||
return DecorationSet.create(doc, [])
|
||||
}
|
||||
|
||||
return options.nested
|
||||
})
|
||||
doc.descendants((node, pos) => {
|
||||
const hasAnchor = anchor >= pos && anchor <= (pos + node.nodeSize)
|
||||
|
||||
return DecorationSet.create(doc, decorations)
|
||||
if (hasAnchor && !node.isText) {
|
||||
const decoration = Decoration.node(pos, pos + node.nodeSize, {
|
||||
class: this.options.className,
|
||||
})
|
||||
decorations.push(decoration)
|
||||
}
|
||||
|
||||
return this.options.nested
|
||||
})
|
||||
|
||||
return DecorationSet.create(doc, decorations)
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
])
|
||||
.create()
|
||||
}),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -1,37 +1,50 @@
|
||||
import { Command, Node } from '@tiptap/core'
|
||||
import { Command, createNode } from '@tiptap/core'
|
||||
import { chainCommands, exitCode } from 'prosemirror-commands'
|
||||
|
||||
export type HardBreakCommand = () => Command
|
||||
// export type HardBreakCommand = () => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
hardBreak: HardBreakCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// hardBreak: HardBreakCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export default new Node()
|
||||
.name('hardBreak')
|
||||
.schema(() => ({
|
||||
inline: true,
|
||||
group: 'inline',
|
||||
selectable: false,
|
||||
parseDOM: [
|
||||
export default createNode({
|
||||
name: 'hardBreak',
|
||||
|
||||
inline: true,
|
||||
|
||||
group: 'inline',
|
||||
|
||||
selectable: false,
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{ tag: 'br' },
|
||||
],
|
||||
toDOM: () => ['br'],
|
||||
}))
|
||||
.commands(({ type }) => ({
|
||||
hardBreak: () => ({
|
||||
tr, state, dispatch, view,
|
||||
}) => {
|
||||
return chainCommands(exitCode, () => {
|
||||
dispatch(tr.replaceSelectionWith(type.create()).scrollIntoView())
|
||||
return true
|
||||
})(state, dispatch, view)
|
||||
},
|
||||
}))
|
||||
.keys(({ editor }) => ({
|
||||
'Mod-Enter': () => editor.hardBreak(),
|
||||
'Shift-Enter': () => editor.hardBreak(),
|
||||
}))
|
||||
.create()
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ attributes }) {
|
||||
return ['br', attributes]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
hardBreak: () => ({
|
||||
tr, state, dispatch, view,
|
||||
}) => {
|
||||
return chainCommands(exitCode, () => {
|
||||
dispatch(tr.replaceSelectionWith(this.type.create()).scrollIntoView())
|
||||
return true
|
||||
})(state, dispatch, view)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-Enter': () => this.editor.hardBreak(),
|
||||
'Shift-Enter': () => this.editor.hardBreak(),
|
||||
}
|
||||
},
|
||||
})
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { Command, Node } from '@tiptap/core'
|
||||
import { Command, createNode } from '@tiptap/core'
|
||||
import { textblockTypeInputRule } from 'prosemirror-inputrules'
|
||||
|
||||
type Level = 1 | 2 | 3 | 4 | 5 | 6
|
||||
@ -7,52 +7,68 @@ export interface HeadingOptions {
|
||||
levels: Level[],
|
||||
}
|
||||
|
||||
export type HeadingCommand = (options: { level: Level }) => Command
|
||||
// export type HeadingCommand = (options: { level: Level }) => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
heading: HeadingCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// heading: HeadingCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export default new Node<HeadingOptions>()
|
||||
.name('heading')
|
||||
.defaults({
|
||||
export default createNode({
|
||||
name: 'heading',
|
||||
|
||||
defaultOptions: <HeadingOptions>{
|
||||
levels: [1, 2, 3, 4, 5, 6],
|
||||
})
|
||||
.schema(({ options }) => ({
|
||||
attrs: {
|
||||
},
|
||||
|
||||
content: 'inline*',
|
||||
|
||||
group: 'block',
|
||||
|
||||
defining: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
level: {
|
||||
default: 1,
|
||||
rendered: false,
|
||||
},
|
||||
},
|
||||
content: 'inline*',
|
||||
group: 'block',
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: options.levels
|
||||
}
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return this.options.levels
|
||||
.map((level: Level) => ({
|
||||
tag: `h${level}`,
|
||||
attrs: { level },
|
||||
})),
|
||||
toDOM: node => [`h${node.attrs.level}`, 0],
|
||||
}))
|
||||
.commands(({ name }) => ({
|
||||
heading: attrs => ({ commands }) => {
|
||||
return commands.toggleBlockType(name, 'paragraph', attrs)
|
||||
},
|
||||
}))
|
||||
.keys(({ name, options, editor }) => {
|
||||
return options.levels.reduce((items, level) => ({
|
||||
}))
|
||||
},
|
||||
|
||||
renderHTML({ node, attributes }) {
|
||||
return [`h${node.attrs.level}`, attributes, 0]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
heading: attrs => ({ commands }) => {
|
||||
return commands.toggleBlockType('heading', 'paragraph', attrs)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return this.options.levels.reduce((items, level) => ({
|
||||
...items,
|
||||
...{
|
||||
[`Mod-Alt-${level}`]: () => editor.setBlockType(name, { level }),
|
||||
[`Mod-Alt-${level}`]: () => this.editor.setBlockType('heading', { level }),
|
||||
},
|
||||
}), {})
|
||||
})
|
||||
.inputRules(({ options, type }) => {
|
||||
return options.levels.map((level: Level) => {
|
||||
return textblockTypeInputRule(new RegExp(`^(#{1,${level}})\\s$`), type, { level })
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return this.options.levels.map(level => {
|
||||
return textblockTypeInputRule(new RegExp(`^(#{1,${level}})\\s$`), this.type, { level })
|
||||
})
|
||||
})
|
||||
.create()
|
||||
},
|
||||
})
|
||||
|
@ -1,42 +1,52 @@
|
||||
import { Command, Extension } from '@tiptap/core'
|
||||
import { Command, createExtension } from '@tiptap/core'
|
||||
import {
|
||||
history,
|
||||
undo,
|
||||
redo,
|
||||
} from 'prosemirror-history'
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
undo: () => Command,
|
||||
redo: () => Command,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// undo: () => Command,
|
||||
// redo: () => Command,
|
||||
// }
|
||||
// }
|
||||
|
||||
export interface HistoryOptions {
|
||||
depth: number,
|
||||
newGroupDelay: number,
|
||||
}
|
||||
|
||||
export default new Extension<HistoryOptions>()
|
||||
.name('history')
|
||||
.defaults({
|
||||
export default createExtension({
|
||||
name: 'history',
|
||||
|
||||
defaultOptions: <HistoryOptions>{
|
||||
depth: 100,
|
||||
newGroupDelay: 500,
|
||||
})
|
||||
.commands(() => ({
|
||||
undo: () => ({ state, dispatch }) => {
|
||||
return undo(state, dispatch)
|
||||
},
|
||||
redo: () => ({ state, dispatch }) => {
|
||||
return redo(state, dispatch)
|
||||
},
|
||||
}))
|
||||
.keys(({ editor }) => ({
|
||||
'Mod-z': () => editor.undo(),
|
||||
'Mod-y': () => editor.redo(),
|
||||
'Shift-Mod-z': () => editor.redo(),
|
||||
}))
|
||||
.plugins(({ options }) => [
|
||||
history(options),
|
||||
])
|
||||
.create()
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
undo: () => ({ state, dispatch }) => {
|
||||
return undo(state, dispatch)
|
||||
},
|
||||
redo: () => ({ state, dispatch }) => {
|
||||
return redo(state, dispatch)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
return [
|
||||
history(this.options),
|
||||
]
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-z': () => this.editor.undo(),
|
||||
'Mod-y': () => this.editor.redo(),
|
||||
'Shift-Mod-z': () => this.editor.redo(),
|
||||
}
|
||||
},
|
||||
})
|
||||
|
@ -1,28 +1,41 @@
|
||||
import { Command, Node, nodeInputRule } from '@tiptap/core'
|
||||
import { Command, createNode, nodeInputRule } from '@tiptap/core'
|
||||
|
||||
export type HorizontalRuleCommand = () => Command
|
||||
// export type HorizontalRuleCommand = () => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
horizontalRule: HorizontalRuleCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// horizontalRule: HorizontalRuleCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export default new Node()
|
||||
.name('horizontalRule')
|
||||
.schema(() => ({
|
||||
group: 'block',
|
||||
parseDOM: [{ tag: 'hr' }],
|
||||
toDOM: () => ['hr'],
|
||||
}))
|
||||
.commands(({ type }) => ({
|
||||
horizontalRule: () => ({ tr }) => {
|
||||
tr.replaceSelectionWith(type.create())
|
||||
export default createNode({
|
||||
name: 'horizontalRule',
|
||||
|
||||
return true
|
||||
},
|
||||
}))
|
||||
.inputRules(({ type }) => [
|
||||
nodeInputRule(/^(?:---|___\s|\*\*\*\s)$/, type),
|
||||
])
|
||||
.create()
|
||||
group: 'block',
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{ tag: 'hr' },
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ attributes }) {
|
||||
return ['hr', attributes]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
horizontalRule: () => ({ tr }) => {
|
||||
tr.replaceSelectionWith(this.type.create())
|
||||
|
||||
return true
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
nodeInputRule(/^(?:---|___\s|\*\*\*\s)$/, this.type),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -1,24 +1,25 @@
|
||||
import {
|
||||
Command, Mark, markInputRule, markPasteRule,
|
||||
Command, createMark, markInputRule, markPasteRule,
|
||||
} from '@tiptap/core'
|
||||
|
||||
export type ItalicCommand = () => Command
|
||||
// export type ItalicCommand = () => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
italic: ItalicCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// italic: ItalicCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export const starInputRegex = /(?:^|\s)((?:\*)((?:[^*]+))(?:\*))$/gm
|
||||
export const starPasteRegex = /(?:^|\s)((?:\*)((?:[^*]+))(?:\*))/gm
|
||||
export const underscoreInputRegex = /(?:^|\s)((?:_)((?:[^_]+))(?:_))$/gm
|
||||
export const underscorePasteRegex = /(?:^|\s)((?:_)((?:[^_]+))(?:_))/gm
|
||||
|
||||
export default new Mark()
|
||||
.name('italic')
|
||||
.schema(() => ({
|
||||
parseDOM: [
|
||||
export default createMark({
|
||||
name: 'italic',
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'em',
|
||||
},
|
||||
@ -29,23 +30,38 @@ export default new Mark()
|
||||
{
|
||||
style: 'font-style=italic',
|
||||
},
|
||||
],
|
||||
toDOM: () => ['em', 0],
|
||||
}))
|
||||
.commands(({ name }) => ({
|
||||
italic: () => ({ commands }) => {
|
||||
return commands.toggleMark(name)
|
||||
},
|
||||
}))
|
||||
.keys(({ editor }) => ({
|
||||
'Mod-i': () => editor.italic(),
|
||||
}))
|
||||
.inputRules(({ type }) => [
|
||||
markInputRule(starInputRegex, type),
|
||||
markInputRule(underscoreInputRegex, type),
|
||||
])
|
||||
.pasteRules(({ type }) => [
|
||||
markPasteRule(starPasteRegex, type),
|
||||
markPasteRule(underscorePasteRegex, type),
|
||||
])
|
||||
.create()
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ attributes }) {
|
||||
return ['em', attributes, 0]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
italic: () => ({ commands }) => {
|
||||
return commands.toggleMark('italic')
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-i': () => this.editor.italic(),
|
||||
}
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
markInputRule(starInputRegex, this.type),
|
||||
markInputRule(underscoreInputRegex, this.type),
|
||||
]
|
||||
},
|
||||
|
||||
addPasteRules() {
|
||||
return [
|
||||
markPasteRule(starPasteRegex, this.type),
|
||||
markPasteRule(underscorePasteRegex, this.type),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {
|
||||
Command, Mark, markPasteRule,
|
||||
Command, createMark, markPasteRule,
|
||||
} from '@tiptap/core'
|
||||
import { Plugin, PluginKey } from 'prosemirror-state'
|
||||
|
||||
@ -9,34 +9,42 @@ export interface LinkOptions {
|
||||
rel: string,
|
||||
}
|
||||
|
||||
export type LinkCommand = (options: {href?: string, target?: string}) => Command
|
||||
// export type LinkCommand = (options: {href?: string, target?: string}) => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
link: LinkCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// link: LinkCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export const pasteRegex = /https?:\/\/(?:www\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\.[a-zA-Z]{2,}\b(?:[-a-zA-Z0-9@:%_+.~#?&//=]*)/gi
|
||||
|
||||
export default new Mark<LinkOptions>()
|
||||
.name('link')
|
||||
.defaults({
|
||||
export default createMark({
|
||||
name: 'link',
|
||||
|
||||
inclusive: false,
|
||||
|
||||
defaultOptions: <LinkOptions>{
|
||||
openOnClick: true,
|
||||
target: '_blank',
|
||||
rel: 'noopener noreferrer nofollow',
|
||||
})
|
||||
.schema(({ options }) => ({
|
||||
attrs: {
|
||||
},
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
href: {
|
||||
default: null,
|
||||
rendered: false,
|
||||
},
|
||||
target: {
|
||||
default: null,
|
||||
rendered: false,
|
||||
},
|
||||
},
|
||||
inclusive: false,
|
||||
parseDOM: [
|
||||
}
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'a[href]',
|
||||
getAttrs: node => ({
|
||||
@ -44,27 +52,44 @@ export default new Mark<LinkOptions>()
|
||||
target: (node as HTMLElement).getAttribute('target'),
|
||||
}),
|
||||
},
|
||||
],
|
||||
toDOM: node => ['a', {
|
||||
...node.attrs,
|
||||
rel: options.rel,
|
||||
target: node.attrs.target ? node.attrs.target : options.target,
|
||||
}, 0],
|
||||
}))
|
||||
.commands(({ name }) => ({
|
||||
link: attributes => ({ commands }) => {
|
||||
if (!attributes.href) {
|
||||
return commands.removeMark(name)
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
return commands.updateMark(name, attributes)
|
||||
},
|
||||
}))
|
||||
.pasteRules(({ type }) => [
|
||||
markPasteRule(pasteRegex, type, (url: string) => ({ href: url })),
|
||||
])
|
||||
.plugins(({ editor, options, name }) => {
|
||||
if (!options.openOnClick) {
|
||||
renderHTML({ mark, attributes }) {
|
||||
return ['a', {
|
||||
...attributes,
|
||||
...mark.attrs,
|
||||
rel: this.options.rel,
|
||||
target: mark.attrs.target ? mark.attrs.target : this.options.target,
|
||||
}, 0]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
link: attributes => ({ commands }) => {
|
||||
if (!attributes.href) {
|
||||
return commands.removeMark('link')
|
||||
}
|
||||
|
||||
return commands.updateMark('link', attributes)
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-i': () => this.editor.italic(),
|
||||
}
|
||||
},
|
||||
|
||||
addPasteRules() {
|
||||
return [
|
||||
markPasteRule(pasteRegex, this.type, (url: string) => ({ href: url })),
|
||||
]
|
||||
},
|
||||
|
||||
addProseMirrorPlugins() {
|
||||
if (!this.options.openOnClick) {
|
||||
return []
|
||||
}
|
||||
|
||||
@ -73,7 +98,7 @@ export default new Mark<LinkOptions>()
|
||||
key: new PluginKey('handleClick'),
|
||||
props: {
|
||||
handleClick: (view, pos, event) => {
|
||||
const attrs = editor.getMarkAttrs(name)
|
||||
const attrs = this.editor.getMarkAttrs('link')
|
||||
|
||||
if (attrs.href && event.target instanceof HTMLAnchorElement) {
|
||||
window.open(attrs.href, attrs.target)
|
||||
@ -86,5 +111,5 @@ export default new Mark<LinkOptions>()
|
||||
},
|
||||
}),
|
||||
]
|
||||
})
|
||||
.create()
|
||||
},
|
||||
})
|
||||
|
@ -1,17 +1,27 @@
|
||||
import { Node } from '@tiptap/core'
|
||||
import { createNode } from '@tiptap/core'
|
||||
|
||||
export default new Node()
|
||||
.name('list_item')
|
||||
.schema(() => ({
|
||||
content: 'paragraph block*',
|
||||
defining: true,
|
||||
draggable: false,
|
||||
parseDOM: [{ tag: 'li' }],
|
||||
toDOM: () => ['li', 0],
|
||||
}))
|
||||
.keys(({ editor, name }) => ({
|
||||
Enter: () => editor.splitListItem(name),
|
||||
Tab: () => editor.sinkListItem(name),
|
||||
'Shift-Tab': () => editor.liftListItem(name),
|
||||
}))
|
||||
.create()
|
||||
export default createNode({
|
||||
name: 'list_item',
|
||||
|
||||
content: 'paragraph block*',
|
||||
|
||||
defining: true,
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{ tag: 'li' },
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ attributes }) {
|
||||
return ['li', attributes, 0]
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
Enter: () => this.editor.splitListItem('list_item'),
|
||||
Tab: () => this.editor.sinkListItem('list_item'),
|
||||
'Shift-Tab': () => this.editor.liftListItem('list_item'),
|
||||
}
|
||||
},
|
||||
})
|
||||
|
@ -1,51 +1,71 @@
|
||||
import { Command, Node } from '@tiptap/core'
|
||||
import { Command, createNode } from '@tiptap/core'
|
||||
import { wrappingInputRule } from 'prosemirror-inputrules'
|
||||
|
||||
export type OrderedListCommand = () => Command
|
||||
// export type OrderedListCommand = () => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
orderedList: OrderedListCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// orderedList: OrderedListCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export default new Node()
|
||||
.name('ordered_list')
|
||||
.schema(() => ({
|
||||
attrs: {
|
||||
export default createNode({
|
||||
name: 'ordered_list',
|
||||
|
||||
content: 'list_item+',
|
||||
|
||||
group: 'block',
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
order: {
|
||||
default: 1,
|
||||
rendered: false,
|
||||
},
|
||||
},
|
||||
content: 'list_item+',
|
||||
group: 'block',
|
||||
parseDOM: [{
|
||||
tag: 'ol',
|
||||
getAttrs: node => ({
|
||||
order: (node as HTMLElement).hasAttribute('start')
|
||||
? parseInt((node as HTMLElement).getAttribute('start') || '', 10)
|
||||
: 1,
|
||||
}),
|
||||
}],
|
||||
toDOM: node => (node.attrs.order === 1
|
||||
? ['ol', 0]
|
||||
: ['ol', { start: node.attrs.order }, 0]
|
||||
),
|
||||
}))
|
||||
.commands(({ name }) => ({
|
||||
orderedList: () => ({ commands }) => {
|
||||
return commands.toggleList(name, 'list_item')
|
||||
},
|
||||
}))
|
||||
.keys(({ editor }) => ({
|
||||
'Shift-Control-9': () => editor.orderedList(),
|
||||
}))
|
||||
.inputRules(({ type }) => [
|
||||
wrappingInputRule(
|
||||
/^(\d+)\.\s$/,
|
||||
type,
|
||||
match => ({ order: +match[1] }),
|
||||
(match, node) => node.childCount + node.attrs.order === +match[1],
|
||||
),
|
||||
])
|
||||
.create()
|
||||
}
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'ol',
|
||||
getAttrs: node => ({
|
||||
order: (node as HTMLElement).hasAttribute('start')
|
||||
? parseInt((node as HTMLElement).getAttribute('start') || '', 10)
|
||||
: 1,
|
||||
}),
|
||||
},
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ node, attributes }) {
|
||||
return node.attrs.order === 1
|
||||
? ['ol', attributes, 0]
|
||||
: ['ol', { ...attributes, start: node.attrs.order }, 0]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
orderedList: () => ({ commands }) => {
|
||||
return commands.toggleList('ordered_list', 'list_item')
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Shift-Control-9': () => this.editor.orderedList(),
|
||||
}
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
wrappingInputRule(
|
||||
/^(\d+)\.\s$/,
|
||||
this.type,
|
||||
match => ({ order: +match[1] }),
|
||||
(match, node) => node.childCount + node.attrs.order === +match[1],
|
||||
),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -16,35 +16,35 @@ export default createNode({
|
||||
|
||||
content: 'inline*',
|
||||
|
||||
addGlobalAttributes() {
|
||||
return [
|
||||
{
|
||||
types: ['paragraph'],
|
||||
attributes: {
|
||||
align: {
|
||||
default: 'right',
|
||||
renderHTML: attributes => ({
|
||||
class: 'global',
|
||||
style: `text-align: ${attributes.align}`,
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
},
|
||||
// addGlobalAttributes() {
|
||||
// return [
|
||||
// {
|
||||
// types: ['paragraph'],
|
||||
// attributes: {
|
||||
// align: {
|
||||
// default: 'right',
|
||||
// renderHTML: attributes => ({
|
||||
// class: 'global',
|
||||
// style: `text-align: ${attributes.align}`,
|
||||
// }),
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// ]
|
||||
// },
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
id: {
|
||||
default: '123',
|
||||
rendered: true,
|
||||
renderHTML: attributes => ({
|
||||
class: `foo-${attributes.id}`,
|
||||
id: 'foo',
|
||||
}),
|
||||
},
|
||||
}
|
||||
},
|
||||
// addAttributes() {
|
||||
// return {
|
||||
// id: {
|
||||
// default: '123',
|
||||
// rendered: true,
|
||||
// renderHTML: attributes => ({
|
||||
// class: `foo-${attributes.id}`,
|
||||
// id: 'foo',
|
||||
// }),
|
||||
// },
|
||||
// }
|
||||
// },
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
|
@ -1,22 +1,23 @@
|
||||
import {
|
||||
Command, Mark, markInputRule, markPasteRule,
|
||||
Command, createMark, markInputRule, markPasteRule,
|
||||
} from '@tiptap/core'
|
||||
|
||||
type StrikeCommand = () => Command
|
||||
// type StrikeCommand = () => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
strike: StrikeCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// strike: StrikeCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export const inputRegex = /(?:^|\s)((?:~~)((?:[^~]+))(?:~~))$/gm
|
||||
export const pasteRegex = /(?:^|\s)((?:~~)((?:[^~]+))(?:~~))/gm
|
||||
|
||||
export default new Mark()
|
||||
.name('strike')
|
||||
.schema(() => ({
|
||||
parseDOM: [
|
||||
export default createMark({
|
||||
name: 'strike',
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 's',
|
||||
},
|
||||
@ -30,21 +31,36 @@ export default new Mark()
|
||||
style: 'text-decoration',
|
||||
getAttrs: node => (node === 'line-through' ? {} : false),
|
||||
},
|
||||
],
|
||||
toDOM: () => ['s', 0],
|
||||
}))
|
||||
.commands(({ name }) => ({
|
||||
strike: () => ({ commands }) => {
|
||||
return commands.toggleMark(name)
|
||||
},
|
||||
}))
|
||||
.keys(({ editor }) => ({
|
||||
'Mod-d': () => editor.strike(),
|
||||
}))
|
||||
.inputRules(({ type }) => [
|
||||
markInputRule(inputRegex, type),
|
||||
])
|
||||
.pasteRules(({ type }) => [
|
||||
markPasteRule(inputRegex, type),
|
||||
])
|
||||
.create()
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ attributes }) {
|
||||
return ['s', attributes, 0]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
strike: () => ({ commands }) => {
|
||||
return commands.toggleMark('strike')
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-d': () => this.editor.strike(),
|
||||
}
|
||||
},
|
||||
|
||||
addInputRules() {
|
||||
return [
|
||||
markInputRule(inputRegex, this.type),
|
||||
]
|
||||
},
|
||||
|
||||
addPasteRules() {
|
||||
return [
|
||||
markPasteRule(inputRegex, this.type),
|
||||
]
|
||||
},
|
||||
})
|
||||
|
@ -1,17 +1,18 @@
|
||||
import { Command, Mark } from '@tiptap/core'
|
||||
import { Command, createMark } from '@tiptap/core'
|
||||
|
||||
export type UnderlineCommand = () => Command
|
||||
// export type UnderlineCommand = () => Command
|
||||
|
||||
declare module '@tiptap/core/src/Editor' {
|
||||
interface Commands {
|
||||
underline: UnderlineCommand,
|
||||
}
|
||||
}
|
||||
// declare module '@tiptap/core/src/Editor' {
|
||||
// interface Commands {
|
||||
// underline: UnderlineCommand,
|
||||
// }
|
||||
// }
|
||||
|
||||
export default new Mark()
|
||||
.name('underline')
|
||||
.schema(() => ({
|
||||
parseDOM: [
|
||||
export default createMark({
|
||||
name: 'underline',
|
||||
|
||||
parseHTML() {
|
||||
return [
|
||||
{
|
||||
tag: 'u',
|
||||
},
|
||||
@ -19,15 +20,24 @@ export default new Mark()
|
||||
style: 'text-decoration',
|
||||
getAttrs: node => (node === 'underline' ? {} : false),
|
||||
},
|
||||
],
|
||||
toDOM: () => ['u', 0],
|
||||
}))
|
||||
.commands(({ name }) => ({
|
||||
underline: () => ({ commands }) => {
|
||||
return commands.toggleMark(name)
|
||||
},
|
||||
}))
|
||||
.keys(({ editor }) => ({
|
||||
'Mod-u': () => editor.underline(),
|
||||
}))
|
||||
.create()
|
||||
]
|
||||
},
|
||||
|
||||
renderHTML({ attributes }) {
|
||||
return ['u', attributes, 0]
|
||||
},
|
||||
|
||||
addCommands() {
|
||||
return {
|
||||
underline: () => ({ commands }) => {
|
||||
return commands.toggleMark('underline')
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
addKeyboardShortcuts() {
|
||||
return {
|
||||
'Mod-u': () => this.editor.underline(),
|
||||
}
|
||||
},
|
||||
})
|
||||
|
@ -1,16 +1,7 @@
|
||||
// import originalDefaultExtensions from '@tiptap/starter-kit'
|
||||
|
||||
import Document from '@tiptap/extension-document'
|
||||
import Text from '@tiptap/extension-text'
|
||||
import Paragraph from '@tiptap/extension-paragraph'
|
||||
import originalDefaultExtensions from '@tiptap/starter-kit'
|
||||
|
||||
export * from '@tiptap/vue'
|
||||
|
||||
export function defaultExtensions() {
|
||||
return [
|
||||
Document(),
|
||||
Text(),
|
||||
Paragraph(),
|
||||
]
|
||||
// return originalDefaultExtensions()
|
||||
return originalDefaultExtensions()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user