adds editorjs and some simple extensions
@ -0,0 +1 @@
|
||||
<svg width="19" height="4" viewBox="0 0 19 4" xmlns="http://www.w3.org/2000/svg"><path d="M1.25 0H7a1.25 1.25 0 1 1 0 2.5H1.25a1.25 1.25 0 1 1 0-2.5zM11 0h5.75a1.25 1.25 0 0 1 0 2.5H11A1.25 1.25 0 0 1 11 0z"/></svg>
|
After Width: | Height: | Size: 216 B |
@ -0,0 +1 @@
|
||||
<svg width="10" height="14" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 14"><path d="M7.6 8.15H2.25v4.525a1.125 1.125 0 0 1-2.25 0V1.125a1.125 1.125 0 1 1 2.25 0V5.9H7.6V1.125a1.125 1.125 0 0 1 2.25 0v11.55a1.125 1.125 0 0 1-2.25 0V8.15z"/></svg>
|
After Width: | Height: | Size: 254 B |
@ -0,0 +1 @@
|
||||
<svg width="16" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.14 1.494V4.98h4.62V1.494c0-.498.098-.871.293-1.12A.927.927 0 0 1 7.82 0c.322 0 .583.123.782.37.2.246.3.62.3 1.124v9.588c0 .503-.101.88-.303 1.128a.957.957 0 0 1-.779.374.921.921 0 0 1-.77-.378c-.193-.251-.29-.626-.29-1.124V6.989H2.14v4.093c0 .503-.1.88-.302 1.128a.957.957 0 0 1-.778.374.921.921 0 0 1-.772-.378C.096 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.285.374A.922.922 0 0 1 1.06 0c.321 0 .582.123.782.37.199.246.299.62.299 1.124zm11.653 9.985V5.27c-1.279.887-2.14 1.33-2.583 1.33a.802.802 0 0 1-.563-.228.703.703 0 0 1-.245-.529c0-.232.08-.402.241-.511.161-.11.446-.25.854-.424.61-.259 1.096-.532 1.462-.818a5.84 5.84 0 0 0 .97-.962c.282-.355.466-.573.552-.655.085-.082.246-.123.483-.123.267 0 .481.093.642.28.161.186.242.443.242.77v7.813c0 .914-.345 1.371-1.035 1.371-.307 0-.554-.093-.74-.28-.187-.186-.28-.461-.28-.825z"/></svg>
|
After Width: | Height: | Size: 918 B |
@ -0,0 +1 @@
|
||||
<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm10.99 9.288h3.527c.351 0 .62.072.804.216.185.144.277.34.277.588 0 .22-.073.408-.22.56-.146.154-.368.23-.665.23h-4.972c-.338 0-.601-.093-.79-.28a.896.896 0 0 1-.284-.659c0-.162.06-.377.182-.645s.255-.478.399-.631a38.617 38.617 0 0 1 1.621-1.598c.482-.444.827-.735 1.034-.875.369-.261.676-.523.922-.787.245-.263.432-.534.56-.81.129-.278.193-.549.193-.815 0-.288-.069-.546-.206-.773a1.428 1.428 0 0 0-.56-.53 1.618 1.618 0 0 0-.774-.19c-.59 0-1.054.26-1.392.777-.045.068-.12.252-.226.554-.106.302-.225.534-.358.696-.133.162-.328.243-.585.243a.76.76 0 0 1-.56-.223c-.149-.148-.223-.351-.223-.608 0-.31.07-.635.21-.972.139-.338.347-.645.624-.92a3.093 3.093 0 0 1 1.054-.665c.426-.169.924-.253 1.496-.253.69 0 1.277.108 1.764.324.315.144.592.343.83.595.24.252.425.544.558.875.133.33.2.674.2 1.03 0 .558-.14 1.066-.416 1.523-.277.457-.56.815-.848 1.074-.288.26-.771.666-1.45 1.22-.677.554-1.142.984-1.394 1.29a3.836 3.836 0 0 0-.331.44z"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1 @@
|
||||
<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm11.61 4.919c.418 0 .778-.123 1.08-.368.301-.245.452-.597.452-1.055 0-.35-.12-.65-.36-.902-.241-.252-.566-.378-.974-.378-.277 0-.505.038-.684.116a1.1 1.1 0 0 0-.426.306 2.31 2.31 0 0 0-.296.49c-.093.2-.178.388-.255.565a.479.479 0 0 1-.245.225.965.965 0 0 1-.409.081.706.706 0 0 1-.5-.22c-.152-.148-.228-.345-.228-.59 0-.236.071-.484.214-.745a2.72 2.72 0 0 1 .627-.746 3.149 3.149 0 0 1 1.024-.568 4.122 4.122 0 0 1 1.368-.214c.44 0 .842.06 1.205.18.364.12.679.294.947.52.267.228.47.49.606.79.136.3.204.622.204.967 0 .454-.099.843-.296 1.168-.198.324-.48.64-.848.95.354.19.653.408.895.653.243.245.426.516.548.813.123.298.184.619.184.964 0 .413-.083.812-.248 1.198-.166.386-.41.73-.732 1.031a3.49 3.49 0 0 1-1.147.708c-.443.17-.932.256-1.467.256a3.512 3.512 0 0 1-1.464-.293 3.332 3.332 0 0 1-1.699-1.64c-.142-.314-.214-.573-.214-.777 0-.263.085-.475.255-.636a.89.89 0 0 1 .637-.242c.127 0 .25.037.367.112a.53.53 0 0 1 .232.27c.236.63.489 1.099.759 1.405.27.306.65.46 1.14.46a1.714 1.714 0 0 0 1.46-.824c.17-.273.256-.588.256-.947 0-.53-.145-.947-.436-1.249-.29-.302-.694-.453-1.212-.453-.09 0-.231.01-.422.028-.19.018-.313.027-.367.027-.25 0-.443-.062-.579-.187-.136-.125-.204-.299-.204-.521 0-.218.081-.394.245-.528.163-.134.406-.2.728-.2h.28z"/></svg>
|
After Width: | Height: | Size: 1.7 KiB |
@ -0,0 +1 @@
|
||||
<svg width="20" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm13.003 10.09v-1.252h-3.38c-.427 0-.746-.097-.96-.29-.213-.193-.32-.456-.32-.788 0-.085.016-.171.048-.259.031-.088.078-.18.141-.276.063-.097.128-.19.195-.28.068-.09.15-.2.25-.33l3.568-4.774a5.44 5.44 0 0 1 .576-.683.763.763 0 0 1 .542-.212c.682 0 1.023.39 1.023 1.171v5.212h.29c.346 0 .623.047.832.142.208.094.313.3.313.62 0 .26-.086.45-.256.568-.17.12-.427.179-.768.179h-.41v1.252c0 .346-.077.603-.23.771-.152.168-.356.253-.612.253a.78.78 0 0 1-.61-.26c-.154-.173-.232-.427-.232-.764zm-2.895-2.76h2.895V4.91L12.26 8.823z"/></svg>
|
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1 @@
|
||||
<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zm14.16 2.645h-3.234l-.388 2.205c.644-.344 1.239-.517 1.783-.517.436 0 .843.082 1.222.245.38.164.712.39.998.677.286.289.51.63.674 1.025.163.395.245.82.245 1.273 0 .658-.148 1.257-.443 1.797-.295.54-.72.97-1.276 1.287-.556.318-1.197.477-1.923.477-.813 0-1.472-.15-1.978-.45-.506-.3-.865-.643-1.076-1.031-.21-.388-.316-.727-.316-1.018 0-.177.073-.345.22-.504a.725.725 0 0 1 .556-.238c.381 0 .665.22.85.66.182.404.427.719.736.943.309.225.654.337 1.035.337.35 0 .656-.09.919-.272.263-.182.466-.431.61-.749.142-.318.214-.678.214-1.082 0-.436-.078-.808-.232-1.117a1.607 1.607 0 0 0-.62-.69 1.674 1.674 0 0 0-.864-.229c-.39 0-.67.048-.837.143-.168.095-.41.262-.725.5-.316.239-.576.358-.78.358a.843.843 0 0 1-.592-.242c-.173-.16-.259-.344-.259-.548 0-.022.025-.177.075-.463l.572-3.26c.063-.39.181-.675.354-.852.172-.177.454-.265.844-.265h3.595c.708 0 1.062.27 1.062.81a.711.711 0 0 1-.26.572c-.172.145-.426.218-.762.218z"/></svg>
|
After Width: | Height: | Size: 1.4 KiB |
@ -0,0 +1 @@
|
||||
<svg width="18" height="14" xmlns="http://www.w3.org/2000/svg"><path d="M2.152 1.494V4.98h4.646V1.494c0-.498.097-.871.293-1.12A.934.934 0 0 1 7.863 0c.324 0 .586.123.786.37.2.246.301.62.301 1.124v9.588c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378c-.194-.251-.29-.626-.29-1.124V6.989H2.152v4.093c0 .503-.101.88-.304 1.128a.964.964 0 0 1-.783.374.928.928 0 0 1-.775-.378C.097 11.955 0 11.58 0 11.082V1.494C0 .996.095.623.286.374A.929.929 0 0 1 1.066 0c.323 0 .585.123.786.37.2.246.3.62.3 1.124zM12.53 7.058a3.093 3.093 0 0 1 1.004-.814 2.734 2.734 0 0 1 1.214-.264c.43 0 .827.08 1.19.24.365.161.684.39.957.686.274.296.485.645.635 1.048a3.6 3.6 0 0 1 .223 1.262c0 .637-.145 1.216-.437 1.736-.292.52-.699.926-1.221 1.218-.522.292-1.114.438-1.774.438-.76 0-1.416-.186-1.967-.557-.552-.37-.974-.919-1.265-1.645-.292-.726-.438-1.613-.438-2.662 0-.855.088-1.62.265-2.293.176-.674.43-1.233.76-1.676.33-.443.73-.778 1.2-1.004.47-.226 1.006-.339 1.608-.339.579 0 1.089.113 1.53.34.44.225.773.506.997.84.224.335.335.656.335.964 0 .185-.07.354-.21.505a.698.698 0 0 1-.536.227.874.874 0 0 1-.529-.18 1.039 1.039 0 0 1-.36-.498 1.42 1.42 0 0 0-.495-.655 1.3 1.3 0 0 0-.786-.247c-.24 0-.479.069-.716.207a1.863 1.863 0 0 0-.6.56c-.33.479-.525 1.333-.584 2.563zm1.832 4.213c.456 0 .834-.186 1.133-.56.298-.373.447-.862.447-1.468 0-.412-.07-.766-.21-1.062a1.584 1.584 0 0 0-.577-.678 1.47 1.47 0 0 0-.807-.234c-.28 0-.548.074-.804.224-.255.149-.461.365-.617.647a2.024 2.024 0 0 0-.234.994c0 .61.158 1.12.475 1.527.316.407.714.61 1.194.61z"/></svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -1,45 +0,0 @@
|
||||
<template>
|
||||
<ol>
|
||||
<li :key="n" v-for="n in amount" :style="style"> </li>
|
||||
</ol>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class DeckCardBoxes extends Vue {
|
||||
@Prop() public readonly params!: string[]
|
||||
|
||||
private get amount () {
|
||||
return parseInt(this.params[0], 10)
|
||||
}
|
||||
|
||||
private get style () {
|
||||
const size = parseFloat(this.params[1])
|
||||
return {
|
||||
width: `calc(${size}em - 0.25em)`,
|
||||
height: `calc(${size}em - 0.25em)`
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
ol {
|
||||
display: flex;
|
||||
flex-flow: row wrap;
|
||||
justify-content: flex-start;
|
||||
list-style: none;
|
||||
margin: 0 0 1em 0;
|
||||
padding: 0;
|
||||
}
|
||||
ol > li {
|
||||
display: inline-block;
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
font-size: 1rem;
|
||||
margin: 0 .3em .3em 0;
|
||||
border: 0.25em solid var(--highlight-color);
|
||||
}
|
||||
</style>
|
@ -1,51 +0,0 @@
|
||||
<template>
|
||||
<ul>
|
||||
<li v-for="(param, i) in params"
|
||||
:key="`param${i}`"
|
||||
v-editable:[i]="editable"
|
||||
@keydown="handleKey(i, $event)">
|
||||
{{ param }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class DeckCardBulletList extends Vue {
|
||||
@Prop() public readonly params!: string[]
|
||||
@Prop() public readonly editable!: boolean
|
||||
|
||||
private addEntry (index: number) {
|
||||
const newParams = [...this.params]
|
||||
newParams.splice(index + 1, 0, '')
|
||||
this.$emit('replace', newParams)
|
||||
}
|
||||
|
||||
private removeEntry (index: number) {
|
||||
const newParams = [...this.params]
|
||||
newParams.splice(index, 1)
|
||||
this.$emit('replace', newParams)
|
||||
}
|
||||
|
||||
private handleKey (index: number, event: KeyboardEvent) {
|
||||
const { key, shiftKey } = event
|
||||
if (key === 'Enter' && shiftKey) {
|
||||
event.preventDefault()
|
||||
this.addEntry(index)
|
||||
} else if (key === 'Backspace') {
|
||||
const text = (event.target as HTMLElement).innerText
|
||||
if (text.trim() === '') this.removeEntry(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
ul {
|
||||
list-style-position: inside;
|
||||
margin: 0;
|
||||
padding-left: .5em;
|
||||
}
|
||||
</style>
|
@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<p>
|
||||
<span v-editable:0="editable" class="title">{{ params[0] }}</span>
|
||||
<span v-editable:1="editable">{{ params[1] }}</span>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class DeckCardDescription extends Vue {
|
||||
@Prop() public readonly params!: string[]
|
||||
@Prop() public readonly editable!: boolean
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
p { margin: 0; line-height: 1.2; }
|
||||
p > .title { font-weight: bold; font-style: italic; }
|
||||
p > .title::after { content: ' '; }
|
||||
</style>
|
@ -1,73 +0,0 @@
|
||||
<template>
|
||||
<ol>
|
||||
<li :key="titles[i]" v-for="(v, i) in params">
|
||||
<span class="title">{{ titles[i] }}</span>
|
||||
<span class="description">{{ v }} ({{ mod(v) }})</span>
|
||||
</li>
|
||||
</ol>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class DeckCardDndstats extends Vue {
|
||||
@Prop() public readonly params!: string[]
|
||||
|
||||
private titles = ['STR', 'DEX', 'CON', 'INT', 'WIS', 'CHA']
|
||||
|
||||
private mod (v: number): string {
|
||||
switch (v) {
|
||||
case 1: return '-5'
|
||||
case 2:
|
||||
case 3: return '-4'
|
||||
case 4:
|
||||
case 5: return '-3'
|
||||
case 6:
|
||||
case 7: return '-2'
|
||||
case 8:
|
||||
case 9: return '-1'
|
||||
case 10:
|
||||
case 11: return '0'
|
||||
case 12:
|
||||
case 13: return '+1'
|
||||
case 14:
|
||||
case 15: return '+2'
|
||||
case 16:
|
||||
case 17: return '+3'
|
||||
case 18:
|
||||
case 19: return '+4'
|
||||
case 20:
|
||||
case 21: return '+5'
|
||||
case 22:
|
||||
case 23: return '+6'
|
||||
case 24:
|
||||
case 25: return '+7'
|
||||
case 26:
|
||||
case 27: return '+8'
|
||||
case 28:
|
||||
case 29: return '+9'
|
||||
default: return '+10'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
ol {
|
||||
display: flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
list-style: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
ol > li {
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
justify-content: center;
|
||||
font-size: 1rem;
|
||||
text-align: center;
|
||||
}
|
||||
.title { font-weight: bold; }
|
||||
</style>
|
@ -1,136 +0,0 @@
|
||||
<template>
|
||||
<menu class="menu-bar" :class="{ active }">
|
||||
<button class="editor-button-bold" :class="{ active: value.bold }" @click="menuAction('bold')" />
|
||||
<button class="editor-button-italic" :class="{ active: value.italic }" @click="menuAction('italic')" />
|
||||
|
||||
<button class="editor-button-paragraph" :class="{ active: value.paragraph }" @click="menuAction('paragraph')" />
|
||||
<button class="editor-button-heading2" :class="{ active: value.heading2 }" @click="menuAction('heading2')" />
|
||||
<button class="editor-button-heading3" :class="{ active: value.heading3 }" @click="menuAction('heading3')" />
|
||||
|
||||
<button class="editor-button-bullet-list" :class="{ active: value.bulletList }" @click="menuAction('bulletList')" />
|
||||
<button class="editor-button-horizontal-rule" :class="{ active: value.separator}" @click="menuAction('separator')" />
|
||||
|
||||
<button class="editor-button-dropdown" :class="{ active: dropdownOpen }" @click="toggleDropdown" />
|
||||
|
||||
<div class="extended-menu" v-show="dropdownOpen">
|
||||
<button class="extended-menu-button" @click="extMenuAction('statBlock')">Stat Block (DnD5e)</button>
|
||||
<button class="extended-menu-button" @click="extMenuAction('boxes')">Empty Boxes</button>
|
||||
</div>
|
||||
</menu>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
import { blocks, marks, State } from '@/editor'
|
||||
|
||||
@Component
|
||||
export default class DeckCardEditorMenu extends Vue {
|
||||
@Prop() public readonly active!: boolean
|
||||
@Prop() public readonly value!: State
|
||||
|
||||
private dropdownOpen = false
|
||||
|
||||
private menuAction (name: string) {
|
||||
const newState = { ...this.value }
|
||||
|
||||
if (blocks.indexOf(name) >= 0) { // blocks behave like radio buttons
|
||||
blocks.forEach(block => {
|
||||
newState[block] = false
|
||||
})
|
||||
newState[name] = true
|
||||
} else if (marks.indexOf(name)) { // marks behave like checkboxes
|
||||
newState[name] = !newState[name]
|
||||
}
|
||||
|
||||
this.$emit('input', newState)
|
||||
this.$emit('action', name)
|
||||
}
|
||||
|
||||
private toggleDropdown () {
|
||||
this.dropdownOpen = !this.dropdownOpen
|
||||
this.$emit('action', 'refocus')
|
||||
}
|
||||
|
||||
private extMenuAction (name: string) {
|
||||
this.menuAction(name)
|
||||
this.dropdownOpen = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.card-front > main > .menu-bar {
|
||||
position: absolute;
|
||||
width: 70%;
|
||||
margin: -3rem 0 0 -1rem;
|
||||
padding: .2rem 1rem;
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
transition: opacity .3s .2s, visibility .3s .2s;
|
||||
background-color: var(--highlight-color);
|
||||
z-index: 2;
|
||||
}
|
||||
.card-front > main > .menu-bar.active {
|
||||
opacity: 1.0;
|
||||
visibility: visible;
|
||||
}
|
||||
.menu-bar > button {
|
||||
position: relative;
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
margin: 0 .1rem;
|
||||
background-color: #EEE;
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 75%;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
}
|
||||
.menu-bar > button:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 1.6rem;
|
||||
width: 1.6rem;
|
||||
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); }
|
||||
|
||||
.editor-button-heading2:after { content: 'H2'; }
|
||||
.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'; }
|
||||
|
||||
.menu-bar > button.editor-button-dropdown {
|
||||
width: 3.6rem;
|
||||
}
|
||||
.menu-bar > button.editor-button-dropdown:after {
|
||||
content: ' more ';
|
||||
width: 90%;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.extended-menu {
|
||||
width: 100%;
|
||||
height: 4rem;
|
||||
padding-top: .5rem;
|
||||
background: var(--highlight-color);
|
||||
}
|
||||
.extended-menu-button {
|
||||
width: 97%;
|
||||
height: 1.6rem;
|
||||
margin: 0 .1rem;
|
||||
background-color: #EEE;
|
||||
color: black;
|
||||
font-size: 1rem;
|
||||
border: none;
|
||||
}
|
||||
</style>
|
@ -1,18 +0,0 @@
|
||||
<template>
|
||||
<div :style="{flex: params[0]}"> </div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class DeckCardFill extends Vue {
|
||||
@Prop() public readonly params!: string[]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
div {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
@ -1,17 +0,0 @@
|
||||
<template>
|
||||
<p v-editable:0="editable">{{ params[0] }}</p>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class DeckCardNote extends Vue {
|
||||
@Prop() public readonly params!: string[]
|
||||
@Prop() public readonly editable!: boolean
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
p { margin: 0 0 .5em 0; line-height: 1.2; font-style: italic; }
|
||||
</style>
|
@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<p>
|
||||
<span class="title" v-editable:0="editable">{{ params[0] }}</span>
|
||||
<span class="description" v-editable:1="editable">{{ params[1] }}</span>
|
||||
</p>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class DeckCardProperty extends Vue {
|
||||
@Prop() public readonly params!: string[]
|
||||
@Prop() public readonly editable!: boolean
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
p { margin: 0 0 0 1em; line-height: 1.2; text-indent: -1em; }
|
||||
p > .title { font-weight: bold; }
|
||||
p > .title::after { content: ' '; }
|
||||
</style>
|
@ -1,34 +0,0 @@
|
||||
<template>
|
||||
<hr :class="params[0]" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class DeckCardRule extends Vue {
|
||||
@Prop() public readonly params!: string[]
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
hr {
|
||||
height: 0;
|
||||
margin: .2em 0;
|
||||
border: 2px solid var(--highlight-color);
|
||||
}
|
||||
hr.pointing-right {
|
||||
height: 0;
|
||||
margin: .2em 0;
|
||||
border-style: solid;
|
||||
border-width: 2px 0 2px 220px;
|
||||
border-color: transparent transparent transparent var(--highlight-color);
|
||||
}
|
||||
hr.pointing-left {
|
||||
height: 0;
|
||||
margin: .2em 0;
|
||||
border-style: solid;
|
||||
border-width: 2px 220px 2px 0;
|
||||
border-color: transparent var(--highlight-color) transparent transparent;
|
||||
}
|
||||
</style>
|
@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<h4 v-editable:0="editable">{{ params[0] }}</h4>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class DeckCardSection extends Vue {
|
||||
@Prop() public readonly params!: string[]
|
||||
@Prop() public readonly editable!: boolean
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h4 {
|
||||
font-size: 1.4rem;
|
||||
color: var(--highlight-color);
|
||||
margin: 0 0 .2em 0;
|
||||
font-weight: normal;
|
||||
font-variant: small-caps;
|
||||
line-height: .9em;
|
||||
border-bottom: 1px solid var(--highlight-color);
|
||||
}
|
||||
</style>
|
@ -1,22 +0,0 @@
|
||||
<template>
|
||||
<h3 v-editable:0="editable">{{ params[0] }}</h3>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class DeckCardSubtitle extends Vue {
|
||||
@Prop() public readonly params!: string[]
|
||||
@Prop() public readonly editable!: boolean
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
h3 {
|
||||
font-size: 1.4rem;
|
||||
color: var(--highlight-color);
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
}
|
||||
</style>
|
@ -1,17 +0,0 @@
|
||||
<template>
|
||||
<p v-editable:0="editable">{{ params[0] }}</p>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||
|
||||
@Component
|
||||
export default class DeckCardText extends Vue {
|
||||
@Prop() public readonly params!: string[]
|
||||
@Prop() public readonly editable!: boolean
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
p { margin: 0 0 .5em 0; line-height: 1.2; }
|
||||
</style>
|
@ -0,0 +1,45 @@
|
||||
import { BlockTool, BlockToolData, ToolConfig, ToolboxConfig, API } from '@editorjs/editorjs'
|
||||
|
||||
export interface BlockToolArgs {
|
||||
api: API;
|
||||
config: ToolConfig;
|
||||
data?: BlockToolData;
|
||||
}
|
||||
|
||||
export class BlockToolExt implements BlockTool {
|
||||
protected api: API
|
||||
protected _element: HTMLElement
|
||||
protected _data: object
|
||||
protected _config: ToolConfig
|
||||
|
||||
constructor ({ data, config, api }: BlockToolArgs) {
|
||||
this.api = api
|
||||
this._config = config
|
||||
this._data = data || {}
|
||||
this._element = this._render()
|
||||
}
|
||||
|
||||
protected get _CSS (): { [key: string]: string } {
|
||||
return { block: this.api.styles.block }
|
||||
}
|
||||
|
||||
protected _render (): HTMLElement {
|
||||
const el = document.createElement('DIV')
|
||||
el.classList.add(this._CSS.block)
|
||||
return el
|
||||
}
|
||||
|
||||
render (): HTMLElement {
|
||||
return this._element
|
||||
}
|
||||
|
||||
save (_toolsContent: HTMLElement): object {
|
||||
return {}
|
||||
}
|
||||
|
||||
static get toolbox (): ToolboxConfig {
|
||||
return { icon: '<svg></svg>', title: 'UnnamedPlugin' }
|
||||
}
|
||||
}
|
||||
|
||||
export default BlockToolExt
|
@ -1,33 +0,0 @@
|
||||
import { getFocussedNode } from './node'
|
||||
|
||||
function applyRange (callback: (range: Range) => void) {
|
||||
const range = document.createRange()
|
||||
callback(range)
|
||||
|
||||
const sel = window.getSelection()
|
||||
if (sel) {
|
||||
sel.removeAllRanges()
|
||||
sel.addRange(range)
|
||||
}
|
||||
}
|
||||
function collapseRange (node: Node, toStart = false) {
|
||||
applyRange(range => {
|
||||
range.selectNode(node)
|
||||
range.collapse(toStart)
|
||||
})
|
||||
}
|
||||
|
||||
export function moveCaretToBOL () {
|
||||
const node = getFocussedNode()
|
||||
if (node) collapseRange(node, true)
|
||||
}
|
||||
export function moveCaretToEOL () {
|
||||
const node = getFocussedNode()
|
||||
if (node) collapseRange(node, false)
|
||||
}
|
||||
export function selectLine () {
|
||||
const node = getFocussedNode()
|
||||
if (node) {
|
||||
applyRange(range => range.selectNodeContents(node))
|
||||
}
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
export const movementKeys = [
|
||||
'ArrowLeft',
|
||||
'ArrowRight',
|
||||
'ArrowUp',
|
||||
'ArrowDown',
|
||||
'Delete',
|
||||
'Backspace',
|
||||
'Enter',
|
||||
'Home',
|
||||
'End',
|
||||
'PageUp',
|
||||
'PageDown'
|
||||
]
|
||||
|
||||
export const controlSequenceKeys = ['p', 'x', 'y', 'z', 'Z']
|
||||
|
||||
export const elementNameToMenuState: KV<string> = {
|
||||
B: 'bold',
|
||||
STRONG: 'bold',
|
||||
I: 'italic',
|
||||
EM: 'italic',
|
||||
P: 'paragraph',
|
||||
H1: 'heading1',
|
||||
H2: 'heading2',
|
||||
H3: 'heading3',
|
||||
UL: 'bulletList',
|
||||
OL: 'numberedList',
|
||||
HR: 'separator'
|
||||
}
|
||||
|
||||
export const marks = ['bold', 'italic']
|
||||
export const blocks = [
|
||||
'paragraph',
|
||||
'heading1',
|
||||
'heading2',
|
||||
'heading3',
|
||||
'bulletList',
|
||||
'spacer',
|
||||
'separator',
|
||||
'statBlock'
|
||||
]
|
@ -0,0 +1,29 @@
|
||||
import { ToolConstructable } from '@editorjs/editorjs'
|
||||
import BlockTool from './block-tool'
|
||||
import icon from '../assets/editor/delimiter.svg.txt'
|
||||
const title = 'Delimiter'
|
||||
|
||||
export class Delimiter extends BlockTool {
|
||||
static get contentless () {
|
||||
return true
|
||||
}
|
||||
|
||||
protected get _CSS () {
|
||||
return {
|
||||
block: this.api.styles.block,
|
||||
wrapper: 'card-delimiter'
|
||||
}
|
||||
}
|
||||
|
||||
protected _render (): HTMLElement {
|
||||
const el = document.createElement('HR')
|
||||
el.classList.add(this._CSS.wrapper, this._CSS.block)
|
||||
return el
|
||||
}
|
||||
|
||||
static get toolbox () {
|
||||
return { icon, title }
|
||||
}
|
||||
}
|
||||
|
||||
export default Delimiter as ToolConstructable
|
@ -0,0 +1,202 @@
|
||||
import { ToolConstructable, ToolConfig, HTMLPasteEvent } from '@editorjs/editorjs'
|
||||
import { BlockToolExt, BlockToolArgs } from './block-tool'
|
||||
import icon from '../assets/editor/header.svg.txt'
|
||||
import icon1 from '../assets/editor/header1.svg.txt'
|
||||
import icon2 from '../assets/editor/header2.svg.txt'
|
||||
import icon3 from '../assets/editor/header3.svg.txt'
|
||||
import icon4 from '../assets/editor/header4.svg.txt'
|
||||
import icon5 from '../assets/editor/header5.svg.txt'
|
||||
import icon6 from '../assets/editor/header6.svg.txt'
|
||||
const title = 'Heading'
|
||||
|
||||
enum HeadingLevel {
|
||||
One = 1,
|
||||
Two = 2,
|
||||
Three = 3,
|
||||
Four = 4,
|
||||
Five = 5,
|
||||
Six = 6
|
||||
}
|
||||
|
||||
interface HeaderConfig extends ToolConfig {
|
||||
placeholder?: string;
|
||||
levels?: HeadingLevel[];
|
||||
defaultLevel?: HeadingLevel;
|
||||
}
|
||||
|
||||
interface HeaderData {
|
||||
text?: string;
|
||||
level?: HeadingLevel;
|
||||
}
|
||||
|
||||
class Heading extends BlockToolExt {
|
||||
protected _config: HeaderConfig
|
||||
protected settingsButtons: HTMLElement[] = []
|
||||
private levels: HeadingLevel[]
|
||||
private defaultLevel: HeadingLevel
|
||||
private currentLevel: HeadingLevel
|
||||
private icons: Map<HeadingLevel, string> = new Map([
|
||||
[HeadingLevel.One, icon1],
|
||||
[HeadingLevel.Two, icon2],
|
||||
[HeadingLevel.Three, icon3],
|
||||
[HeadingLevel.Four, icon4],
|
||||
[HeadingLevel.Five, icon5],
|
||||
[HeadingLevel.Six, icon6]
|
||||
])
|
||||
|
||||
constructor (args: BlockToolArgs) {
|
||||
super(args)
|
||||
this._config = args.config as HeaderConfig
|
||||
|
||||
if (this._config.levels === undefined) {
|
||||
this._config.levels = [HeadingLevel.Two, HeadingLevel.Three]
|
||||
}
|
||||
if (this._config.defaultLevel === undefined) {
|
||||
this._config.defaultLevel = HeadingLevel.Two
|
||||
}
|
||||
if (this._config.levels.indexOf(this._config.defaultLevel) === -1) {
|
||||
console.warn('(ง\'̀-\'́)ง Heading Tool: the default level specified was not found in available levels')
|
||||
}
|
||||
this.levels = this._config.levels
|
||||
this.defaultLevel = this._config.defaultLevel
|
||||
this.currentLevel = this.defaultLevel
|
||||
|
||||
// setting data will rerender the element with the right settings
|
||||
this.data = {
|
||||
level: this.currentLevel,
|
||||
text: (args.data as HeaderData).text || ''
|
||||
}
|
||||
}
|
||||
|
||||
public renderSettings (): HTMLElement {
|
||||
const wrapper = document.createElement('DIV')
|
||||
|
||||
this.levels.forEach(level => {
|
||||
const { settingsButton, settingsButtonActive } = this._CSS
|
||||
const btn = document.createElement('SPAN')
|
||||
btn.classList.add(settingsButton)
|
||||
btn.dataset.level = `${level}`
|
||||
btn.innerHTML = this.icons.get(level) || icon
|
||||
|
||||
if (this.currentLevel === level) btn.classList.add(settingsButtonActive)
|
||||
|
||||
btn.addEventListener('click', () => this.setLevel(level))
|
||||
wrapper.appendChild(btn)
|
||||
this.settingsButtons.push(btn)
|
||||
})
|
||||
|
||||
return wrapper
|
||||
}
|
||||
|
||||
public get data (): HeaderData {
|
||||
return this._data as HeaderData
|
||||
}
|
||||
|
||||
public set data (data: HeaderData) {
|
||||
const currentData = this._data as HeaderData
|
||||
|
||||
if (data.level === undefined) data.level = currentData.level || this.defaultLevel
|
||||
if (data.text === undefined) data.text = currentData.text || ''
|
||||
|
||||
this._data = data
|
||||
this.currentLevel = data.level
|
||||
|
||||
const newHeader = this._render()
|
||||
if (this._element.parentNode) {
|
||||
this._element.parentNode.replaceChild(newHeader, this._element)
|
||||
}
|
||||
this._element = newHeader
|
||||
}
|
||||
|
||||
private setLevel (level: HeadingLevel) {
|
||||
this.data = { level, text: this._element.innerHTML }
|
||||
}
|
||||
|
||||
protected get _CSS () {
|
||||
return {
|
||||
block: this.api.styles.block,
|
||||
settingsButton: this.api.styles.settingsButton,
|
||||
settingsButtonActive: this.api.styles.settingsButtonActive,
|
||||
wrapper: 'card-heading'
|
||||
}
|
||||
}
|
||||
|
||||
protected _render (): HTMLElement {
|
||||
console.log('render', `H${this.currentLevel}`, this.data)
|
||||
|
||||
const el = document.createElement(`H${this.currentLevel}`)
|
||||
el.innerHTML = this.data.text || ''
|
||||
el.classList.add(this._CSS.wrapper, this._CSS.block)
|
||||
el.contentEditable = 'true'
|
||||
el.dataset.placeholder = this._config.placeholder || ''
|
||||
return el
|
||||
}
|
||||
|
||||
// Handle pasted H1-H6 tags to substitute with header tool
|
||||
onPaste (event: HTMLPasteEvent) {
|
||||
const content = event.detail.data
|
||||
const text = content.innerHTML
|
||||
let level = this.defaultLevel
|
||||
|
||||
const tagMatch = content.tagName.match(/H(\d)/)
|
||||
if (tagMatch) level = parseInt(tagMatch[1], 10)
|
||||
|
||||
// Fallback to nearest level when specified not available
|
||||
if (this._config.levels) {
|
||||
level = this._config.levels.reduce((prevLevel, currLevel) => {
|
||||
return Math.abs(currLevel - level) < Math.abs(prevLevel - level) ? currLevel : prevLevel
|
||||
})
|
||||
}
|
||||
|
||||
this.data = { level, text }
|
||||
}
|
||||
|
||||
// Used by Editor.js paste handling API.
|
||||
// Provides configuration to handle H1-H6 tags.
|
||||
static get pasteConfig () {
|
||||
return {
|
||||
tags: ['H1', 'H2', 'H3', 'H4', 'H5', 'H6']
|
||||
}
|
||||
}
|
||||
|
||||
// Method that specified how to merge two Text blocks.
|
||||
// Called by Editor.js by backspace at the beginning of the Block
|
||||
public merge (data: HeaderData) {
|
||||
this.data = {
|
||||
text: this.data.text + (data.text || ''),
|
||||
level: this.data.level
|
||||
}
|
||||
}
|
||||
|
||||
// validate text block data
|
||||
validate (blockData: HeaderData): boolean {
|
||||
if (!blockData.text) return false
|
||||
return blockData.text.trim() !== ''
|
||||
}
|
||||
|
||||
// extract tools data from view
|
||||
save (toolsContent: HTMLElement): HeaderData {
|
||||
return {
|
||||
text: toolsContent.innerHTML,
|
||||
level: this.currentLevel
|
||||
}
|
||||
}
|
||||
|
||||
// Allow Heading to be converted to/from other blocks
|
||||
static get conversionConfig () {
|
||||
return {
|
||||
export: 'text', // use 'text' property for other blocks
|
||||
import: 'text' // fill 'text' property from other block's export string
|
||||
}
|
||||
}
|
||||
|
||||
static get sanitize (): object {
|
||||
return { level: {} }
|
||||
}
|
||||
|
||||
static get toolbox () {
|
||||
return { icon, title }
|
||||
}
|
||||
}
|
||||
|
||||
export default Heading as ToolConstructable
|
@ -1,81 +1,2 @@
|
||||
import { elementNameToMenuState, marks, blocks } from './constants'
|
||||
|
||||
export {
|
||||
isRootNode,
|
||||
isRootChild,
|
||||
isElementNode,
|
||||
isTextNode,
|
||||
isEmptyTextNode,
|
||||
getFocussedNode
|
||||
} from './node'
|
||||
|
||||
export {
|
||||
moveCaretToBOL,
|
||||
moveCaretToEOL,
|
||||
selectLine
|
||||
} from './caret'
|
||||
|
||||
export type State = KV<boolean>
|
||||
export {
|
||||
movementKeys,
|
||||
controlSequenceKeys,
|
||||
marks,
|
||||
blocks
|
||||
} from './constants'
|
||||
|
||||
function simpleAction (cmd: string, arg?: string): () => boolean {
|
||||
return () => {
|
||||
return document.execCommand(cmd, false, arg)
|
||||
}
|
||||
}
|
||||
|
||||
export const menuActionToCommand: KV<() => boolean> = {
|
||||
paragraph: simpleAction('formatblock', 'P'),
|
||||
heading1: simpleAction('formatblock', 'H1'),
|
||||
heading2: simpleAction('formatblock', 'H2'),
|
||||
heading3: simpleAction('formatblock', 'H3'),
|
||||
bulletList: simpleAction('insertUnorderedList'),
|
||||
numberedList: simpleAction('insertOrderedList'),
|
||||
separator: simpleAction('insertHorizontalRule'),
|
||||
bold: simpleAction('bold'),
|
||||
italic: simpleAction('italic')
|
||||
}
|
||||
|
||||
export function getActiveMarksAndBlocks (el: HTMLElement): {
|
||||
marks: string[];
|
||||
block: string;
|
||||
} {
|
||||
let activeBlock = 'paragraph'
|
||||
const activeMarks: string[] = []
|
||||
|
||||
const focussedEl = el.nodeName === '#text' ? el.parentElement : el
|
||||
if (!focussedEl) return { marks: activeMarks, block: activeBlock }
|
||||
|
||||
const focussedState = elementNameToMenuState[focussedEl.nodeName]
|
||||
if (!focussedState) return { marks: activeMarks, block: activeBlock }
|
||||
|
||||
if (blocks.indexOf(focussedState) >= 0) {
|
||||
activeBlock = focussedState
|
||||
return { marks: activeMarks, block: activeBlock }
|
||||
}
|
||||
|
||||
let wrappingEl = focussedEl.parentElement
|
||||
let wrappingState: string
|
||||
|
||||
if (marks.indexOf(focussedState) >= 0) {
|
||||
activeMarks.push(focussedState)
|
||||
|
||||
while (wrappingEl) {
|
||||
wrappingState = elementNameToMenuState[wrappingEl.nodeName]
|
||||
if (marks.indexOf(wrappingState) < 0) {
|
||||
if (blocks.indexOf(wrappingState) >= 0) activeBlock = wrappingState
|
||||
break
|
||||
}
|
||||
|
||||
activeMarks.push(wrappingState)
|
||||
wrappingEl = wrappingEl.parentElement
|
||||
}
|
||||
}
|
||||
|
||||
return { marks: activeMarks, block: activeBlock }
|
||||
}
|
||||
export { default as Delimiter } from './delimiter'
|
||||
export { default as Heading } from './heading'
|
||||
|
@ -1,20 +0,0 @@
|
||||
const { TEXT_NODE, ELEMENT_NODE } = Node
|
||||
export function getFocussedNode (): Node | null {
|
||||
return window.getSelection()?.focusNode || null
|
||||
}
|
||||
export function isTextNode ({ nodeType }: Node): boolean {
|
||||
return nodeType === TEXT_NODE
|
||||
}
|
||||
export function isElementNode ({ nodeType }: Node): boolean {
|
||||
return nodeType === ELEMENT_NODE
|
||||
}
|
||||
export function isEmptyTextNode (node: Node): boolean {
|
||||
return isTextNode(node) && (node as CharacterData).data === ''
|
||||
}
|
||||
export function isRootNode (node: Node): boolean {
|
||||
return (node as HTMLElement).contentEditable === 'true'
|
||||
}
|
||||
export function isRootChild (node: Node): boolean {
|
||||
// TODO: maybe use a data attribute or something for saver identification
|
||||
return node.parentElement?.contentEditable === 'true'
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
declare module '*.vue' {
|
||||
import Vue from 'vue'
|
||||
export default Vue
|
||||
}
|
||||
|
||||
declare module '*.txt' {
|
||||
const content: string
|
||||
export default content
|
||||
}
|
||||
|
||||
declare module '@editorjs/paragraph'
|
||||
declare module '@editorjs/list'
|
@ -1,4 +0,0 @@
|
||||
declare module '*.vue' {
|
||||
import Vue from 'vue'
|
||||
export default Vue
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
module.exports = {
|
||||
chainWebpack: config => {
|
||||
config.module
|
||||
.rule('raw')
|
||||
.test(/\.txt$/)
|
||||
.use('raw-loader')
|
||||
.loader('raw-loader')
|
||||
.end()
|
||||
}
|
||||
}
|