trying to extend that editor

master
koehr 5 years ago committed by Norman
parent 936ada94bd
commit a33363eef0

@ -1,15 +1,17 @@
<template> <template>
<editor-menu-bar :editor="editor" v-slot="{ commands, isActive, focused }"> <editor-menu-bar :editor="editor" v-slot="{ commands, isActive, focused }">
<div class="menu-bar" :class="{ active: focused }"> <div class="menu-bar" :class="{ active: focused }">
<button class="editor-button-bold" :class="{ active: isActive.bold() }" @click="commands.bold" /> <button class="editor-button-bold" :class="{ active: active.bold }" @click="menuAction('bold', commands.bold, isActive)" />
<button class="editor-button-italic" :class="{ active: isActive.italic() }" @click="commands.italic" /> <button class="editor-button-italic" :class="{ active: active.italic }" @click="menuAction('italic', commands.italic, isActive)" />
<button class="editor-button-paragraph" :class="{ active: isActive.paragraph() }" @click="commands.paragraph" /> <button class="editor-button-paragraph" :class="{ active: active.paragraph }" @click="menuAction('paragraph', commands.paragraph, isActive)" />
<button class="editor-button-heading2" :class="{ active: isActive.heading({ level: 2 }) }" @click="commands.heading({ level: 2})" /> <button class="editor-button-heading2" :class="{ active: active.heading2 }" @click="menuAction('heading2', commands.heading({ level: 2}), isActive)" />
<button class="editor-button-heading3" :class="{ active: isActive.heading({ level: 3 }) }" @click="commands.heading({ level: 3})" /> <button class="editor-button-heading3" :class="{ active: active.heading3 }" @click="menuAction('heading3', commands.heading({ level: 3}), isActive)" />
<button class="editor-button-bullet-list" :class="{ active: isActive.bullet_list() }" @click="commands.bullet_list" /> <button class="editor-button-bullet-list" :class="{ active: active.bullet_list }" @click="menuAction('bullet_list', commands.bullet_list, isActive)" />
<button class="editor-button-horizontal-rule" :class="{ active: isActive.horizontal_rule() }" @click="commands.horizontal_rule" /> <button class="editor-button-horizontal-rule" :class="{ active: active.horizontal_rule }" @click="menuAction('horizontal_rule', commands.horizontal_rule, isActive)" />
<button class="editor-button-stat-block" :class="{ active: active.stat_block }" @click="menuAction('stat_block', commands.stat_block, isActive)" />
</div> </div>
</editor-menu-bar> </editor-menu-bar>
</template> </template>
@ -23,6 +25,26 @@ import { Editor, EditorMenuBar } from 'tiptap'
}) })
export default class DeckCardEditorMenu extends Vue { export default class DeckCardEditorMenu extends Vue {
@Prop() public readonly editor!: Editor @Prop() public readonly editor!: Editor
private active: {[key: string]: boolean} = {
bold: false,
italic: false,
paragraph: false,
heading2: false,
heading3: false,
bulletList: false,
horizontalLule: false
}
private menuAction (name: string, command: () => void, isActive: {[key: string]: () => boolean}) {
command()
Object.keys(this.active).forEach(action => {
this.active[action] = isActive[action]()
})
}
} }
</script> </script>
@ -63,6 +85,9 @@ export default class DeckCardEditorMenu extends Vue {
font-size: 1.2rem; font-size: 1.2rem;
color: black; color: black;
} }
.menu-bar > button.active {
background-color: #FF0;
}
.editor-button-bold { background-image: url(../assets/zondicons/format-bold.svg); } .editor-button-bold { background-image: url(../assets/zondicons/format-bold.svg); }
.editor-button-italic { background-image: url(../assets/zondicons/format-italic.svg); } .editor-button-italic { background-image: url(../assets/zondicons/format-italic.svg); }
.editor-button-bullet-list { background-image: url(../assets/zondicons/list-bullet.svg); } .editor-button-bullet-list { background-image: url(../assets/zondicons/list-bullet.svg); }
@ -71,4 +96,6 @@ export default class DeckCardEditorMenu extends Vue {
.editor-button-heading3:after { content: 'H3'; } .editor-button-heading3:after { content: 'H3'; }
.editor-button-paragraph:after { content: 'P'; } .editor-button-paragraph:after { content: 'P'; }
.editor-button-horizontal-rule:after { content: '—'; } .editor-button-horizontal-rule:after { content: '—'; }
.editor-button-stat-block:after { content: 'ST'; }
</style> </style>

@ -34,7 +34,20 @@
import { Component, Prop, Vue } from 'vue-property-decorator' import { Component, Prop, Vue } from 'vue-property-decorator'
import { cardWHtoStyle, iconPath } from '@/lib' import { cardWHtoStyle, iconPath } from '@/lib'
import { Editor, EditorContent } from 'tiptap' import { Editor, EditorContent } from 'tiptap'
import { Heading, Bold, Italic, HorizontalRule, BulletList, ListItem, History } from 'tiptap-extensions' import {
Heading,
Bold,
Italic,
HorizontalRule,
BulletList,
ListItem,
History,
Table,
TableCell,
TableRow,
TableHeader
} from 'tiptap-extensions'
import StatBlock from '@/editor/stat-block.js'
import DeckCardEditorMenu from '@/components/deck-card-editor-menu.vue' import DeckCardEditorMenu from '@/components/deck-card-editor-menu.vue'
interface EditorContext { interface EditorContext {
@ -51,7 +64,8 @@ const extensions = [
new HorizontalRule(), new HorizontalRule(),
new BulletList(), new BulletList(),
new ListItem(), new ListItem(),
new History() new History(),
new StatBlock()
] ]
@Component({ @Component({

@ -0,0 +1,53 @@
import { Node } from 'tiptap'
import { tableNodes, tableEditing, goToNextCell, deleteTable } from 'prosemirror-tables'
import { createTable } from 'prosemirror-utils'
import { TextSelection } from 'prosemirror-state'
export default class StatBlock extends Node {
get name () {
return 'stat_block'
}
get defaultOptions () {
return {
resizable: false
}
}
get schema () {
return {
group: 'block',
content: 'stat_column+',
toDOM: () => ['ol', { 'data-type': this.name }, 0],
parseDOM: [{
priority: 51,
tag: `[data-type="${this.name}"]`
}]
}
}
commands ({ schema }) {
return () => (state, dispatch) => {
const offset = state.tr.selection.anchor + 1
const nodes = createTable(schema, 2, 6, true)
const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView()
const resolvedPos = tr.doc.resolve(offset)
tr.setSelection(TextSelection.near(resolvedPos))
dispatch(tr)
}
}
keys () {
return {
Tab: goToNextCell(1),
'Shift-Tab': goToNextCell(-1)
}
}
get plugins () {
return [tableEditing()]
}
}

@ -0,0 +1,49 @@
import { Node } from 'tiptap'
import { tableEditing, goToNextCell, deleteTable } from 'prosemirror-tables'
import { createTable } from 'prosemirror-utils'
import { TextSelection } from 'prosemirror-state'
import { TableNodes } from 'tiptap-extensions'
export default class StatBlock extends Node {
public get name () {
return 'stat_block'
}
public get defaultOptions () {
return {
resizable: false
}
}
public get schema () {
return TableNodes.table
}
public commands ({ schema }) {
return {
createStatBlock: () => (state, dispatch) => {
const offset = state.tr.selection.anchor + 1
const nodes = createTable(schema, 2, 6, true)
const tr = state.tr.replaceSelectionWith(nodes).scrollIntoView()
const resolvedPos = tr.doc.resolve(offset)
tr.setSelection(TextSelection.near(resolvedPos))
dispatch(tr)
},
deleteTable: () => deleteTable
}
}
public keys () {
return {
Tab: goToNextCell(1),
'Shift-Tab': goToNextCell(-1)
}
}
public get plugins () {
return [tableEditing()]
}
}
Loading…
Cancel
Save