tabs to spaces whitespace

This commit is contained in:
Philipp Kühn
2018-11-08 22:03:10 +01:00
parent b8b82220ba
commit f04a6be6c1
114 changed files with 4214 additions and 4214 deletions

View File

@@ -3,37 +3,37 @@ import { wrappingInputRule, toggleWrap } from 'tiptap-commands'
export default class Blockquote extends Node {
get name() {
return 'blockquote'
}
get name() {
return 'blockquote'
}
get schema() {
return {
content: 'block*',
group: 'block',
defining: true,
draggable: false,
parseDOM: [
{ tag: 'blockquote' },
],
toDOM: () => ['blockquote', 0],
}
}
get schema() {
return {
content: 'block*',
group: 'block',
defining: true,
draggable: false,
parseDOM: [
{ tag: 'blockquote' },
],
toDOM: () => ['blockquote', 0],
}
}
commands({ type, schema }) {
return () => toggleWrap(type, schema.nodes.paragraph)
}
commands({ type, schema }) {
return () => toggleWrap(type, schema.nodes.paragraph)
}
keys({ type }) {
return {
'Ctrl->': toggleWrap(type),
}
}
keys({ type }) {
return {
'Ctrl->': toggleWrap(type),
}
}
inputRules({ type }) {
return [
wrappingInputRule(/^\s*>\s$/, type),
]
}
inputRules({ type }) {
return [
wrappingInputRule(/^\s*>\s$/, type),
]
}
}

View File

@@ -3,35 +3,35 @@ import { wrappingInputRule, toggleList } from 'tiptap-commands'
export default class Bullet extends Node {
get name() {
return 'bullet_list'
}
get name() {
return 'bullet_list'
}
get schema() {
return {
content: 'list_item+',
group: 'block',
parseDOM: [
{ tag: 'ul' },
],
toDOM: () => ['ul', 0],
}
}
get schema() {
return {
content: 'list_item+',
group: 'block',
parseDOM: [
{ tag: 'ul' },
],
toDOM: () => ['ul', 0],
}
}
commands({ type, schema }) {
return () => toggleList(type, schema.nodes.list_item)
}
commands({ type, schema }) {
return () => toggleList(type, schema.nodes.list_item)
}
keys({ type, schema }) {
return {
'Shift-Ctrl-8': toggleList(type, schema.nodes.list_item),
}
}
keys({ type, schema }) {
return {
'Shift-Ctrl-8': toggleList(type, schema.nodes.list_item),
}
}
inputRules({ type }) {
return [
wrappingInputRule(/^\s*([-+*])\s$/, type),
]
}
inputRules({ type }) {
return [
wrappingInputRule(/^\s*([-+*])\s$/, type),
]
}
}

View File

@@ -3,39 +3,39 @@ import { toggleBlockType, setBlockType, textblockTypeInputRule } from 'tiptap-co
export default class CodeBlock extends Node {
get name() {
return 'code_block'
}
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]],
}
}
get schema() {
return {
content: 'text*',
marks: '',
group: 'block',
code: true,
defining: true,
draggable: false,
parseDOM: [
{ tag: 'pre', preserveWhitespace: 'full' },
],
toDOM: () => ['pre', ['code', 0]],
}
}
commands({ type, schema }) {
return () => toggleBlockType(type, schema.nodes.paragraph)
}
commands({ type, schema }) {
return () => toggleBlockType(type, schema.nodes.paragraph)
}
keys({ type }) {
return {
'Shift-Ctrl-\\': setBlockType(type),
}
}
keys({ type }) {
return {
'Shift-Ctrl-\\': setBlockType(type),
}
}
inputRules({ type }) {
return [
textblockTypeInputRule(/^```$/, type),
]
}
inputRules({ type }) {
return [
textblockTypeInputRule(/^```$/, type),
]
}
}

View File

@@ -5,139 +5,139 @@ import { findBlockNodes } from 'prosemirror-utils'
import low from 'lowlight/lib/core'
function getDecorations(doc) {
const decorations = []
const decorations = []
const blocks = findBlockNodes(doc)
.filter(item => item.node.type.name === 'code_block')
const blocks = findBlockNodes(doc)
.filter(item => item.node.type.name === 'code_block')
const flatten = list => list.reduce(
(a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [],
)
const flatten = list => list.reduce(
(a, b) => a.concat(Array.isArray(b) ? flatten(b) : b), [],
)
function parseNodes(nodes, className = []) {
return nodes.map(node => {
function parseNodes(nodes, className = []) {
return nodes.map(node => {
const classes = [
...className,
...node.properties ? node.properties.className : [],
]
const classes = [
...className,
...node.properties ? node.properties.className : [],
]
if (node.children) {
return parseNodes(node.children, classes)
}
if (node.children) {
return parseNodes(node.children, classes)
}
return {
text: node.value,
classes,
}
})
}
return {
text: node.value,
classes,
}
})
}
blocks.forEach(block => {
let startPos = block.pos + 1
const nodes = low.highlightAuto(block.node.textContent).value
blocks.forEach(block => {
let startPos = block.pos + 1
const nodes = low.highlightAuto(block.node.textContent).value
flatten(parseNodes(nodes))
.map(node => {
const from = startPos
const to = from + node.text.length
flatten(parseNodes(nodes))
.map(node => {
const from = startPos
const to = from + node.text.length
startPos = to
startPos = to
return {
...node,
from,
to,
}
})
.forEach(node => {
const decoration = Decoration.inline(node.from, node.to, {
class: node.classes.join(' '),
})
decorations.push(decoration)
})
})
return {
...node,
from,
to,
}
})
.forEach(node => {
const decoration = Decoration.inline(node.from, node.to, {
class: node.classes.join(' '),
})
decorations.push(decoration)
})
})
return DecorationSet.create(doc, decorations)
return DecorationSet.create(doc, decorations)
}
export default class CodeBlockHighlight extends Node {
constructor(options = {}) {
super(options)
try {
Object.entries(this.options.languages).forEach(([name, mapping]) => {
low.registerLanguage(name, mapping)
})
} catch (err) {
throw new Error('Invalid syntax highlight definitions: define at least one highlight.js language mapping')
}
}
constructor(options = {}) {
super(options)
try {
Object.entries(this.options.languages).forEach(([name, mapping]) => {
low.registerLanguage(name, mapping)
})
} catch (err) {
throw new Error('Invalid syntax highlight definitions: define at least one highlight.js language mapping')
}
}
get defaultOptions() {
return {
languages: {},
}
}
get defaultOptions() {
return {
languages: {},
}
}
get name() {
return 'code_block'
}
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]],
}
}
get schema() {
return {
content: 'text*',
marks: '',
group: 'block',
code: true,
defining: true,
draggable: false,
parseDOM: [
{ tag: 'pre', preserveWhitespace: 'full' },
],
toDOM: () => ['pre', ['code', 0]],
}
}
commands({ type, schema }) {
return () => toggleBlockType(type, schema.nodes.paragraph)
}
commands({ type, schema }) {
return () => toggleBlockType(type, schema.nodes.paragraph)
}
keys({ type }) {
return {
'Shift-Ctrl-\\': setBlockType(type),
}
}
keys({ type }) {
return {
'Shift-Ctrl-\\': setBlockType(type),
}
}
inputRules({ type }) {
return [
textblockTypeInputRule(/^```$/, type),
]
}
inputRules({ type }) {
return [
textblockTypeInputRule(/^```$/, type),
]
}
get plugins() {
return [
new Plugin({
state: {
init(_, { doc }) {
return getDecorations(doc)
},
apply(tr, set) {
// TODO: find way to cache decorations
// see: https://discuss.prosemirror.net/t/how-to-update-multiple-inline-decorations-on-node-change/1493
if (tr.docChanged) {
return getDecorations(tr.doc)
}
return set.map(tr.mapping, tr.doc)
},
},
props: {
decorations(state) {
return this.getState(state)
},
},
}),
]
}
get plugins() {
return [
new Plugin({
state: {
init(_, { doc }) {
return getDecorations(doc)
},
apply(tr, set) {
// TODO: find way to cache decorations
// see: https://discuss.prosemirror.net/t/how-to-update-multiple-inline-decorations-on-node-change/1493
if (tr.docChanged) {
return getDecorations(tr.doc)
}
return set.map(tr.mapping, tr.doc)
},
},
props: {
decorations(state) {
return this.getState(state)
},
},
}),
]
}
}

View File

@@ -3,31 +3,31 @@ import { chainCommands, exitCode } from 'tiptap-commands'
export default class HardBreak extends Node {
get name() {
return 'hard_break'
}
get name() {
return 'hard_break'
}
get schema() {
return {
inline: true,
group: 'inline',
selectable: false,
parseDOM: [
{ tag: 'br' },
],
toDOM: () => ['br'],
}
}
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,
}
}
keys({ type }) {
const command = chainCommands(exitCode, (state, dispatch) => {
dispatch(state.tr.replaceSelectionWith(type.create()).scrollIntoView())
return true
})
return {
'Mod-Enter': command,
'Shift-Enter': command,
}
}
}

View File

@@ -3,55 +3,55 @@ import { setBlockType, textblockTypeInputRule, toggleBlockType } from 'tiptap-co
export default class Heading extends Node {
get name() {
return 'heading'
}
get name() {
return 'heading'
}
get defaultOptions() {
return {
levels: [1, 2, 3, 4, 5, 6],
}
}
get defaultOptions() {
return {
levels: [1, 2, 3, 4, 5, 6],
}
}
get schema() {
return {
attrs: {
level: {
default: 1,
},
},
content: 'inline*',
group: 'block',
defining: true,
draggable: false,
parseDOM: this.options.levels
.map(level => ({
tag: `h${level}`,
attrs: { level },
})),
toDOM: node => [`h${node.attrs.level}`, 0],
}
}
get schema() {
return {
attrs: {
level: {
default: 1,
},
},
content: 'inline*',
group: 'block',
defining: true,
draggable: false,
parseDOM: this.options.levels
.map(level => ({
tag: `h${level}`,
attrs: { level },
})),
toDOM: node => [`h${node.attrs.level}`, 0],
}
}
commands({ type, schema }) {
return attrs => toggleBlockType(type, schema.nodes.paragraph, attrs)
}
commands({ type, schema }) {
return attrs => toggleBlockType(type, schema.nodes.paragraph, attrs)
}
keys({ type }) {
return this.options.levels.reduce((items, level) => ({
...items,
...{
[`Shift-Ctrl-${level}`]: setBlockType(type, { level }),
},
}), {})
}
keys({ type }) {
return this.options.levels.reduce((items, level) => ({
...items,
...{
[`Shift-Ctrl-${level}`]: setBlockType(type, { level }),
},
}), {})
}
inputRules({ type }) {
return this.options.levels.map(level => textblockTypeInputRule(
new RegExp(`^(#{1,${level}})\\s$`),
type,
match => ({ level }),
))
}
inputRules({ type }) {
return this.options.levels.map(level => textblockTypeInputRule(
new RegExp(`^(#{1,${level}})\\s$`),
type,
match => ({ level }),
))
}
}

View File

@@ -2,92 +2,92 @@ import { Node, Plugin } from 'tiptap'
export default class Image extends Node {
get name() {
return 'image'
}
get name() {
return 'image'
}
get schema() {
return {
inline: true,
attrs: {
src: {},
alt: {
default: null,
},
title: {
default: null,
},
},
group: 'inline',
draggable: true,
parseDOM: [
{
tag: 'img[src]',
getAttrs: dom => ({
src: dom.getAttribute('src'),
title: dom.getAttribute('title'),
alt: dom.getAttribute('alt'),
}),
},
],
toDOM: node => ['img', node.attrs],
}
}
get schema() {
return {
inline: true,
attrs: {
src: {},
alt: {
default: null,
},
title: {
default: null,
},
},
group: 'inline',
draggable: true,
parseDOM: [
{
tag: 'img[src]',
getAttrs: dom => ({
src: dom.getAttribute('src'),
title: dom.getAttribute('title'),
alt: dom.getAttribute('alt'),
}),
},
],
toDOM: node => ['img', node.attrs],
}
}
commands({ type }) {
return attrs => (state, dispatch) => {
const { selection } = state
const position = selection.$cursor ? selection.$cursor.pos : selection.$to.pos
const node = type.create(attrs)
const transaction = state.tr.insert(position, node)
dispatch(transaction)
}
}
commands({ type }) {
return attrs => (state, dispatch) => {
const { selection } = state
const position = selection.$cursor ? selection.$cursor.pos : selection.$to.pos
const node = type.create(attrs)
const transaction = state.tr.insert(position, node)
dispatch(transaction)
}
}
get plugins() {
return [
new Plugin({
props: {
handleDOMEvents: {
drop(view, event) {
const hasFiles = event.dataTransfer
&& event.dataTransfer.files
&& event.dataTransfer.files.length
get plugins() {
return [
new Plugin({
props: {
handleDOMEvents: {
drop(view, event) {
const hasFiles = event.dataTransfer
&& event.dataTransfer.files
&& event.dataTransfer.files.length
if (!hasFiles) {
return
}
if (!hasFiles) {
return
}
const images = Array
.from(event.dataTransfer.files)
.filter(file => (/image/i).test(file.type))
const images = Array
.from(event.dataTransfer.files)
.filter(file => (/image/i).test(file.type))
if (images.length === 0) {
return
}
if (images.length === 0) {
return
}
event.preventDefault()
event.preventDefault()
const { schema } = view.state
const coordinates = view.posAtCoords({ left: event.clientX, top: event.clientY })
const { schema } = view.state
const coordinates = view.posAtCoords({ left: event.clientX, top: event.clientY })
images.forEach(image => {
const reader = new FileReader()
images.forEach(image => {
const reader = new FileReader()
reader.onload = readerEvent => {
const node = schema.nodes.image.create({
src: readerEvent.target.result,
})
const transaction = view.state.tr.insert(coordinates.pos, node)
view.dispatch(transaction)
}
reader.readAsDataURL(image)
})
},
},
},
}),
]
}
reader.onload = readerEvent => {
const node = schema.nodes.image.create({
src: readerEvent.target.result,
})
const transaction = view.state.tr.insert(coordinates.pos, node)
view.dispatch(transaction)
}
reader.readAsDataURL(image)
})
},
},
},
}),
]
}
}

View File

@@ -3,28 +3,28 @@ import { splitListItem, liftListItem, sinkListItem } from 'tiptap-commands'
export default class ListItem extends Node {
get name() {
return 'list_item'
}
get name() {
return 'list_item'
}
get schema() {
return {
content: 'paragraph block*',
defining: true,
draggable: false,
parseDOM: [
{ tag: 'li' },
],
toDOM: () => ['li', 0],
}
}
get schema() {
return {
content: 'paragraph block*',
defining: true,
draggable: false,
parseDOM: [
{ tag: 'li' },
],
toDOM: () => ['li', 0],
}
}
keys({ type }) {
return {
Enter: splitListItem(type),
Tab: sinkListItem(type),
'Shift-Tab': liftListItem(type),
}
}
keys({ type }) {
return {
Enter: splitListItem(type),
Tab: sinkListItem(type),
'Shift-Tab': liftListItem(type),
}
}
}

View File

@@ -4,68 +4,68 @@ import SuggestionsPlugin from '../plugins/Suggestions'
export default class Mention extends Node {
get name() {
return 'mention'
}
get name() {
return 'mention'
}
get defaultOptions() {
return {
matcher: {
char: '@',
allowSpaces: false,
startOfLine: false,
},
mentionClass: 'mention',
suggestionClass: 'mention-suggestion',
}
}
get defaultOptions() {
return {
matcher: {
char: '@',
allowSpaces: false,
startOfLine: false,
},
mentionClass: 'mention',
suggestionClass: 'mention-suggestion',
}
}
get schema() {
return {
attrs: {
id: {},
label: {},
},
group: 'inline',
inline: true,
selectable: false,
atom: true,
toDOM: node => [
'span',
{
class: this.options.mentionClass,
'data-mention-id': node.attrs.id,
},
`${this.options.matcher.char}${node.attrs.label}`,
],
parseDOM: [
{
tag: 'span[data-mention-id]',
getAttrs: dom => {
const id = dom.getAttribute('data-mention-id')
const label = dom.innerText.split(this.options.matcher.char).join('')
return { id, label }
},
},
],
}
}
get schema() {
return {
attrs: {
id: {},
label: {},
},
group: 'inline',
inline: true,
selectable: false,
atom: true,
toDOM: node => [
'span',
{
class: this.options.mentionClass,
'data-mention-id': node.attrs.id,
},
`${this.options.matcher.char}${node.attrs.label}`,
],
parseDOM: [
{
tag: 'span[data-mention-id]',
getAttrs: dom => {
const id = dom.getAttribute('data-mention-id')
const label = dom.innerText.split(this.options.matcher.char).join('')
return { id, label }
},
},
],
}
}
get plugins() {
return [
SuggestionsPlugin({
command: ({ range, attrs, schema }) => replaceText(range, schema.nodes.mention, attrs),
appendText: ' ',
matcher: this.options.matcher,
items: this.options.items,
onEnter: this.options.onEnter,
onChange: this.options.onChange,
onExit: this.options.onExit,
onKeyDown: this.options.onKeyDown,
onFilter: this.options.onFilter,
suggestionClass: this.options.suggestionClass,
}),
]
}
get plugins() {
return [
SuggestionsPlugin({
command: ({ range, attrs, schema }) => replaceText(range, schema.nodes.mention, attrs),
appendText: ' ',
matcher: this.options.matcher,
items: this.options.items,
onEnter: this.options.onEnter,
onChange: this.options.onChange,
onExit: this.options.onExit,
onKeyDown: this.options.onKeyDown,
onFilter: this.options.onFilter,
suggestionClass: this.options.suggestionClass,
}),
]
}
}

View File

@@ -3,50 +3,50 @@ import { wrappingInputRule, toggleList } from 'tiptap-commands'
export default class OrderedList extends Node {
get name() {
return 'ordered_list'
}
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]),
}
}
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]),
}
}
commands({ type, schema }) {
return () => toggleList(type, schema.nodes.list_item)
}
commands({ type, schema }) {
return () => toggleList(type, schema.nodes.list_item)
}
keys({ type, schema }) {
return {
'Shift-Ctrl-9': toggleList(type, schema.nodes.list_item),
}
}
keys({ type, schema }) {
return {
'Shift-Ctrl-9': toggleList(type, schema.nodes.list_item),
}
}
inputRules({ type }) {
return [
wrappingInputRule(
/^(\d+)\.\s$/,
type,
match => ({ order: +match[1] }),
(match, node) => node.childCount + node.attrs.order === +match[1],
),
]
}
inputRules({ type }) {
return [
wrappingInputRule(
/^(\d+)\.\s$/,
type,
match => ({ order: +match[1] }),
(match, node) => node.childCount + node.attrs.order === +match[1],
),
]
}
}

View File

@@ -3,64 +3,64 @@ import { splitToDefaultListItem, liftListItem } from 'tiptap-commands'
export default class TodoItem extends Node {
get name() {
return 'todo_item'
}
get name() {
return 'todo_item'
}
get view() {
return {
props: ['node', 'updateAttrs', 'editable'],
methods: {
onChange() {
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 view() {
return {
props: ['node', 'updateAttrs', 'editable'],
methods: {
onChange() {
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
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',
}),
}],
}
}
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: splitToDefaultListItem(type),
'Shift-Tab': liftListItem(type),
}
}
keys({ type }) {
return {
Enter: splitToDefaultListItem(type),
'Shift-Tab': liftListItem(type),
}
}
}

View File

@@ -3,30 +3,30 @@ import { wrapInList, wrappingInputRule } from 'tiptap-commands'
export default class TodoList extends Node {
get name() {
return 'todo_list'
}
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"]',
}],
}
}
get schema() {
return {
group: 'block',
content: 'todo_item+',
toDOM: () => ['ul', { 'data-type': 'todo_list' }, 0],
parseDOM: [{
priority: 51,
tag: '[data-type="todo_list"]',
}],
}
}
commands({ type }) {
return () => wrapInList(type)
}
commands({ type }) {
return () => wrapInList(type)
}
inputRules({ type }) {
return [
wrappingInputRule(/^\s*(\[ \])\s$/, type),
]
}
inputRules({ type }) {
return [
wrappingInputRule(/^\s*(\[ \])\s$/, type),
]
}
}