add dndstats component
This commit is contained in:
parent
600af1679a
commit
136da9d694
3 changed files with 142 additions and 2 deletions
|
@ -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; }
|
||||
</style>
|
||||
|
|
106
src/editor/dnd-stats.ts
Normal file
106
src/editor/dnd-stats.ts
Normal file
|
@ -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
|
|
@ -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'
|
||||
|
|
Loading…
Reference in a new issue