diff --git a/src/components/deck-card-editor-menu.vue b/src/components/deck-card-editor-menu.vue index 1732fc4..a0296db 100644 --- a/src/components/deck-card-editor-menu.vue +++ b/src/components/deck-card-editor-menu.vue @@ -1,15 +1,17 @@ @@ -23,6 +25,26 @@ import { Editor, EditorMenuBar } from 'tiptap' }) export default class DeckCardEditorMenu extends Vue { @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]() + }) + } } @@ -63,6 +85,9 @@ export default class DeckCardEditorMenu extends Vue { font-size: 1.2rem; color: black; } +.menu-bar > button.active { + background-color: #FF0; +} .editor-button-bold { background-image: url(../assets/zondicons/format-bold.svg); } .editor-button-italic { background-image: url(../assets/zondicons/format-italic.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-paragraph:after { content: 'P'; } .editor-button-horizontal-rule:after { content: '—'; } + +.editor-button-stat-block:after { content: 'ST'; } diff --git a/src/components/deck-card.vue b/src/components/deck-card.vue index a3354e8..405f5bc 100644 --- a/src/components/deck-card.vue +++ b/src/components/deck-card.vue @@ -34,7 +34,20 @@ import { Component, Prop, Vue } from 'vue-property-decorator' import { cardWHtoStyle, iconPath } from '@/lib' 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' interface EditorContext { @@ -51,7 +64,8 @@ const extensions = [ new HorizontalRule(), new BulletList(), new ListItem(), - new History() + new History(), + new StatBlock() ] @Component({ diff --git a/src/editor/stat-block.js b/src/editor/stat-block.js new file mode 100644 index 0000000..db9801d --- /dev/null +++ b/src/editor/stat-block.js @@ -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()] + } +} diff --git a/src/editor/stat-block.ts b/src/editor/stat-block.ts new file mode 100644 index 0000000..d1a387d --- /dev/null +++ b/src/editor/stat-block.ts @@ -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()] + } +}