diff --git a/src/components/deck-card-editor.vue b/src/components/deck-card-editor.vue index a39c67c..e7ad534 100644 --- a/src/components/deck-card-editor.vue +++ b/src/components/deck-card-editor.vue @@ -7,7 +7,7 @@ import { Component, Prop, Vue } from 'vue-property-decorator' import Editor from '@editorjs/editorjs' import List from '@editorjs/list' -import { Heading, Delimiter, Charges } from '@/editor' +import { Heading, Delimiter, Charges, DnDStats } from '@/editor' @Component export default class DeckCardEditor extends Vue { @@ -30,7 +30,8 @@ export default class DeckCardEditor extends Vue { list: { class: List, inlineToolbar: true }, heading: { class: Heading, inlineToolbar: true }, delimiter: { class: Delimiter, inlineToolbar: false }, - charges: { class: Charges, inlineToolbar: false } + charges: { class: Charges, inlineToolbar: false }, + dndstats: { class: DnDStats, inlineToolbar: false } }, data: this.content, placeholder: 'Click here to write your card.', @@ -126,5 +127,37 @@ export default class DeckCardEditor extends Vue { .card-content .card-charges-wrapper > .card-charge-size-3 { width: 1.4em; height: 1.4em; } .card-content .card-charges-wrapper > .card-charge-size-4 { width: 1.6em; height: 1.6em; } .card-content .card-charges-wrapper > .card-charge-size-5 { width: 1.8em; height: 1.8em; } + +.card-content .card-dnd-stats { + display: flex; + flex-flow: row nowrap; + justify-content: space-around; + align-items: center; + color: var(--highlight-color); +} +.card-content .dnd-stat-block { + flex: 1 1 auto; + display: flex; + flex-flow: row wrap; + font-size: .8em; +} +.card-content .dnd-stat-block > .dnd-stat-title { + width: 100%; + font-weight: bold; + text-align: center; +} +.card-content .dnd-stat-block > input { + width: 50%; + background: white; + color: var(--highlight-color); + border: none; + padding: 0; + margin: 0; + font-size: 1em; + text-align: center; +} +.card-content .dnd-stat-block { +} + [contenteditable="true"] { outline: none; } diff --git a/src/editor/dnd-stats.ts b/src/editor/dnd-stats.ts new file mode 100644 index 0000000..485b44d --- /dev/null +++ b/src/editor/dnd-stats.ts @@ -0,0 +1,106 @@ +import { ContentlessBlock, BlockToolArgs } from './contentless-block' +import icon from '../assets/editor/charges-circle.svg.txt' + +const title = 'DnDStats' + +interface DnDStatsData { + text: string; +} + +class DnDStats extends ContentlessBlock { + static _toolboxConfig = { icon, title } + private _stats = [10, 10, 10, 10, 10, 10] + + constructor (args: BlockToolArgs) { + super(args) + this.data = args.data as DnDStatsData + this._element = this._render() + } + + public get data () { + return { + text: this._stats.join(',') + } + } + + public set data (data: DnDStatsData) { + if (data.text === undefined) data.text = '' + + const newStats = data.text.split(',') + .map(x => parseInt(x, 10)) + .filter(x => !Number.isNaN(x)) + + while (newStats.length < 6) newStats.push(10) // fill missing stats + + this._stats = newStats + } + + // creates a random four character long id + private randomId (): string { + const min = 46656 // '1000' + const max = 1679615 /* 'zzzz' */ - 46656 /* '1000' */ + return (min + Math.floor(max * Math.random())).toString(36) + } + + private renderStatMod (value: number): string { + const mod = Math.floor((value - 10) / 2.0) + const sign = mod < 0 ? '' : '+' + return ` (${sign}${mod})` + } + + private createStatBlock (title: string, value: number, changeHandler: (newValue: number) => void): HTMLElement { + const id = `dnd-stat-${title}-${this.randomId()}` + + const labelWrapper = document.createElement('label') + const titleEl = document.createElement('span') + const statInputEl = document.createElement('input') + const statModEl = document.createElement('span') + + // should allow focussing block with tab + labelWrapper.setAttribute('z-index', '1') + labelWrapper.classList.add('dnd-stat-block') + labelWrapper.setAttribute('for', id) + + titleEl.classList.add('dnd-stat-title') + titleEl.innerText = title + + statInputEl.id = id + statInputEl.value = `${value}` + statInputEl.addEventListener('input', () => { + const value = parseInt(statInputEl.value, 10) + statModEl.innerText = this.renderStatMod(value) + changeHandler(value) + }) + + statModEl.innerText = this.renderStatMod(value) + + labelWrapper.appendChild(titleEl) + labelWrapper.appendChild(statInputEl) + labelWrapper.appendChild(statModEl) + + return labelWrapper + } + + protected _render (): HTMLElement { + const el = document.createElement('div') + el.classList.add('card-dnd-stats') + const stats = this._stats || [10, 10, 10, 10, 10, 10] + const titles = ['STR', 'DEX', 'CON', 'INT', 'WIS', 'CHA'] + + stats.forEach((stat, i) => { + const title = titles[i] + const block = this.createStatBlock(title, stat, newValue => { + this._stats[i] = newValue + }) + el.appendChild(block) + }) + + return el + } + + public save (): DnDStatsData { + return this.data + } +} + +export default DnDStats diff --git a/src/editor/index.ts b/src/editor/index.ts index 85039f8..84e4b2d 100644 --- a/src/editor/index.ts +++ b/src/editor/index.ts @@ -2,3 +2,4 @@ export { default as Delimiter } from './delimiter' export { default as Heading } from './heading' export { default as List } from './list' export { default as Charges } from './charges' +export { default as DnDStats } from './dnd-stats'