onesided printing functionality, halfway through, typescript errors
This commit is contained in:
parent
c7022133a0
commit
f4e603b98e
13 changed files with 1996 additions and 1719 deletions
26
package.json
26
package.json
|
@ -14,27 +14,27 @@
|
||||||
"vue-router": "^3.1.5"
|
"vue-router": "^3.1.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@editorjs/editorjs": "^2.17.0",
|
"@editorjs/editorjs": "^2.18.0",
|
||||||
"@editorjs/list": "^1.4.0",
|
"@editorjs/list": "^1.5.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^2.18.0",
|
"@typescript-eslint/eslint-plugin": "^3.2.0",
|
||||||
"@typescript-eslint/parser": "^2.18.0",
|
"@typescript-eslint/parser": "^3.2.0",
|
||||||
"@vue/cli-plugin-babel": "^4.2.0",
|
"@vue/cli-plugin-babel": "^4.2.0",
|
||||||
"@vue/cli-plugin-eslint": "^4.2.0",
|
"@vue/cli-plugin-eslint": "^4.2.0",
|
||||||
"@vue/cli-plugin-pwa": "^4.2.0",
|
"@vue/cli-plugin-pwa": "^4.2.0",
|
||||||
"@vue/cli-plugin-typescript": "^4.2.0",
|
"@vue/cli-plugin-typescript": "^4.2.0",
|
||||||
"@vue/cli-service": "^4.2.0",
|
"@vue/cli-service": "^4.2.0",
|
||||||
"@vue/eslint-config-standard": "^5.1.0",
|
"@vue/eslint-config-standard": "^5.1.2",
|
||||||
"@vue/eslint-config-typescript": "^5.0.1",
|
"@vue/eslint-config-typescript": "^5.0.2",
|
||||||
"eslint": "^6.7.2",
|
"eslint": "^7.2.0",
|
||||||
"eslint-plugin-import": "^2.20.1",
|
"eslint-plugin-import": "^2.21.2",
|
||||||
"eslint-plugin-node": "^11.0.0",
|
"eslint-plugin-node": "^11.1.0",
|
||||||
"eslint-plugin-promise": "^4.2.1",
|
"eslint-plugin-promise": "^4.2.1",
|
||||||
"eslint-plugin-standard": "^4.0.0",
|
"eslint-plugin-standard": "^4.0.1",
|
||||||
"eslint-plugin-vue": "^6.1.2",
|
"eslint-plugin-vue": "^6.2.2",
|
||||||
"lint-staged": "^9.5.0",
|
"lint-staged": "^9.5.0",
|
||||||
"raw-loader": "^4.0.0",
|
"raw-loader": "^4.0.0",
|
||||||
"typescript": "~3.7.5",
|
"typescript": "~3.9.5",
|
||||||
"vue-property-decorator": "^8.4.0",
|
"vue-property-decorator": "^8.5.0",
|
||||||
"vue-template-compiler": "^2.6.11"
|
"vue-template-compiler": "^2.6.11"
|
||||||
},
|
},
|
||||||
"eslintConfig": {
|
"eslintConfig": {
|
||||||
|
|
160
src/assets/card.css
Normal file
160
src/assets/card.css
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
.card-front, .card-back {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
}
|
||||||
|
.card-front {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
.card-front > header {
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row nowrap;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: 3.6rem;
|
||||||
|
color: white;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: normal;
|
||||||
|
font-variant: small-caps;
|
||||||
|
padding: 0 1em;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
.card-front > header > h1 {
|
||||||
|
margin: 0;
|
||||||
|
line-height: .9em;
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
.card-front > header > img {
|
||||||
|
height: 3rem;
|
||||||
|
}
|
||||||
|
.card-front > main {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
margin: .1rem .4rem .5rem;
|
||||||
|
padding: .2rem 1rem;
|
||||||
|
background: white;
|
||||||
|
border-radius: 1rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
color: black;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-back {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.card-back > .icon-wrapper {
|
||||||
|
margin: 3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content .cdx-block {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content .ce-paragraph, .card-content p {
|
||||||
|
margin: 0;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content ul {
|
||||||
|
list-style-position: inside;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: .5em;
|
||||||
|
}
|
||||||
|
.card-content li > p {
|
||||||
|
display: inline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content h2 {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
color: var(--highlight-color);
|
||||||
|
margin: 0;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content h3 {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content .card-delimiter {
|
||||||
|
height: 0;
|
||||||
|
margin: .2em 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 2px solid var(--highlight-color);
|
||||||
|
}
|
||||||
|
.card-content .card-delimiter.pointing-right {
|
||||||
|
height: 0;
|
||||||
|
margin: .2em 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 2px 0 2px 220px;
|
||||||
|
border-color: transparent transparent transparent var(--highlight-color);
|
||||||
|
}
|
||||||
|
.card-content .card-delimiter.pointing-left {
|
||||||
|
height: 0;
|
||||||
|
margin: .2em 0;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 2px 220px 2px 0;
|
||||||
|
border-color: transparent var(--highlight-color) transparent transparent;
|
||||||
|
}
|
||||||
|
.card-content .cdx-list__item {
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
.card-content .card-charges-wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 1em;
|
||||||
|
}
|
||||||
|
.card-content .card-charges-wrapper.card-charges-stretch { justify-content: space-around; }
|
||||||
|
.card-content .card-charges-wrapper > .card-charge {
|
||||||
|
width: 1.0em;
|
||||||
|
height: 1.0em;
|
||||||
|
border: 2px solid var(--highlight-color);
|
||||||
|
margin: .5em .2em;
|
||||||
|
}
|
||||||
|
.card-content .card-charges-wrapper > .card-charge-circle { border-radius: 100%; }
|
||||||
|
.card-content .card-charges-wrapper > .card-charge-size-1 { width: 1.0em; height: 1.0em; }
|
||||||
|
.card-content .card-charges-wrapper > .card-charge-size-2 { width: 1.2em; height: 1.2em; }
|
||||||
|
.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; }
|
|
@ -27,7 +27,6 @@ export default class DeckCardEditor extends Vue {
|
||||||
holder: this.$refs.cardEl as HTMLElement,
|
holder: this.$refs.cardEl as HTMLElement,
|
||||||
autofocus: false,
|
autofocus: false,
|
||||||
tools: {
|
tools: {
|
||||||
// header: Heading,
|
|
||||||
list: { class: List, inlineToolbar: true },
|
list: { class: List, inlineToolbar: true },
|
||||||
heading: { class: Heading, inlineToolbar: true },
|
heading: { class: Heading, inlineToolbar: true },
|
||||||
delimiter: { class: Delimiter, inlineToolbar: false },
|
delimiter: { class: Delimiter, inlineToolbar: false },
|
||||||
|
@ -48,117 +47,3 @@ export default class DeckCardEditor extends Vue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style>
|
|
||||||
.card-content .cdx-block {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content .ce-paragraph, .card-content p {
|
|
||||||
margin: 0;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content ul {
|
|
||||||
list-style-position: inside;
|
|
||||||
margin: 0;
|
|
||||||
padding-left: .5em;
|
|
||||||
}
|
|
||||||
.card-content li > p {
|
|
||||||
display: inline;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content h2 {
|
|
||||||
font-size: 1.4rem;
|
|
||||||
color: var(--highlight-color);
|
|
||||||
margin: 0;
|
|
||||||
font-weight: normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content h3 {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-content .card-delimiter {
|
|
||||||
height: 0;
|
|
||||||
margin: .2em 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 2px solid var(--highlight-color);
|
|
||||||
}
|
|
||||||
.card-content .card-delimiter.pointing-right {
|
|
||||||
height: 0;
|
|
||||||
margin: .2em 0;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 2px 0 2px 220px;
|
|
||||||
border-color: transparent transparent transparent var(--highlight-color);
|
|
||||||
}
|
|
||||||
.card-content .card-delimiter.pointing-left {
|
|
||||||
height: 0;
|
|
||||||
margin: .2em 0;
|
|
||||||
border-style: solid;
|
|
||||||
border-width: 2px 220px 2px 0;
|
|
||||||
border-color: transparent var(--highlight-color) transparent transparent;
|
|
||||||
}
|
|
||||||
.card-content .cdx-list__item {
|
|
||||||
padding: 0;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
.card-content .card-charges-wrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-start;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 1em;
|
|
||||||
}
|
|
||||||
.card-content .card-charges-wrapper.card-charges-stretch { justify-content: space-around; }
|
|
||||||
.card-content .card-charges-wrapper > .card-charge {
|
|
||||||
width: 1.0em;
|
|
||||||
height: 1.0em;
|
|
||||||
border: 2px solid var(--highlight-color);
|
|
||||||
margin: .5em .2em;
|
|
||||||
}
|
|
||||||
.card-content .card-charges-wrapper > .card-charge-circle { border-radius: 100%; }
|
|
||||||
.card-content .card-charges-wrapper > .card-charge-size-1 { width: 1.0em; height: 1.0em; }
|
|
||||||
.card-content .card-charges-wrapper > .card-charge-size-2 { width: 1.2em; height: 1.2em; }
|
|
||||||
.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>
|
|
||||||
|
|
|
@ -47,9 +47,6 @@ export default class DeckCard extends Vue {
|
||||||
@Prop() public readonly deck!: Deck
|
@Prop() public readonly deck!: Deck
|
||||||
@Prop() public readonly isSelection!: boolean
|
@Prop() public readonly isSelection!: boolean
|
||||||
|
|
||||||
/// TODO: onEdit
|
|
||||||
// this.$emit('edit', { field: 'content', value: doc.content })
|
|
||||||
|
|
||||||
private editHeadline = false;
|
private editHeadline = false;
|
||||||
private editFieldIndex: number | null = null;
|
private editFieldIndex: number | null = null;
|
||||||
|
|
||||||
|
@ -111,6 +108,8 @@ export default class DeckCard extends Vue {
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style src="@/assets/card.css" />
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.flip-card {
|
.flip-card {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -149,69 +148,21 @@ export default class DeckCard extends Vue {
|
||||||
.flip-card:not(.active):hover > .card-front {
|
.flip-card:not(.active):hover > .card-front {
|
||||||
transform: rotateX(0) rotateY(179deg);
|
transform: rotateX(0) rotateY(179deg);
|
||||||
}
|
}
|
||||||
.card-back {
|
|
||||||
z-index: 2;
|
|
||||||
transform: rotateX(0) rotateY(-179deg);
|
|
||||||
}
|
|
||||||
.flip-card:not(.active):hover > .card-back {
|
.flip-card:not(.active):hover > .card-back {
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
transform: rotateX(0) rotateY(0);
|
transform: rotateX(0) rotateY(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-front {
|
.card-front {
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
justify-content: flex-start;
|
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
.card-front > header {
|
|
||||||
display: flex;
|
|
||||||
flex-flow: row nowrap;
|
|
||||||
justify-content: space-between;
|
|
||||||
height: 3.6rem;
|
|
||||||
color: white;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
font-weight: normal;
|
|
||||||
font-variant: small-caps;
|
|
||||||
padding: 0 1em;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
.card-front > header > h1 {
|
|
||||||
margin: .5em 0 0 0;
|
|
||||||
align-self: center;
|
|
||||||
line-height: .9em;
|
|
||||||
font-size: 2rem;
|
|
||||||
}
|
|
||||||
.card-front > header > img {
|
|
||||||
height: 3rem;
|
|
||||||
align-self: end;
|
|
||||||
}
|
|
||||||
.card-front > header > h1[contenteditable="true"] { text-decoration: underline dotted; }
|
.card-front > header > h1[contenteditable="true"] { text-decoration: underline dotted; }
|
||||||
.card-front > header > h1[contenteditable="true"]:focus { text-decoration: none; }
|
.card-front > header > h1[contenteditable="true"]:focus { text-decoration: none; }
|
||||||
|
|
||||||
.card-front > main {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
flex: 1;
|
|
||||||
height: 100%;
|
|
||||||
margin: .7rem .4rem .5rem;
|
|
||||||
padding: .2rem 1rem;
|
|
||||||
background: white;
|
|
||||||
border-radius: 1rem;
|
|
||||||
font-size: 1.2rem;
|
|
||||||
color: black;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-back {
|
.card-back {
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
z-index: 2;
|
||||||
.card-back > .icon-wrapper {
|
transform: rotateX(0) rotateY(-179deg);
|
||||||
margin: 3em;
|
|
||||||
}
|
}
|
||||||
.card-back > button {
|
.card-back > button {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
|
|
|
@ -25,6 +25,7 @@
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Emit, Vue } from 'vue-property-decorator'
|
import { Component, Prop, Emit, Vue } from 'vue-property-decorator'
|
||||||
import { Deck, CardSize } from '@/types'
|
import { Deck, CardSize } from '@/types'
|
||||||
|
import { cardSizeOptions } from '@/consts'
|
||||||
import DeckCover from '@/components/deck-cover.vue'
|
import DeckCover from '@/components/deck-cover.vue'
|
||||||
import { iconPath } from '../lib'
|
import { iconPath } from '../lib'
|
||||||
|
|
||||||
|
@ -35,10 +36,7 @@ export default class DeckForm extends Vue {
|
||||||
@Prop() public readonly deck!: Deck
|
@Prop() public readonly deck!: Deck
|
||||||
|
|
||||||
private icons = ['mouth-watering', 'robe', 'thorny-triskelion']
|
private icons = ['mouth-watering', 'robe', 'thorny-triskelion']
|
||||||
private sizes = [
|
private sizes = cardSizeOptions
|
||||||
{ title: '88x62 (Poker)', value: CardSize.Poker },
|
|
||||||
{ title: '88x56 (Bridge)', value: CardSize.Bridge }
|
|
||||||
]
|
|
||||||
|
|
||||||
private icon: string
|
private icon: string
|
||||||
private name: string
|
private name: string
|
||||||
|
|
|
@ -37,7 +37,15 @@
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Prop, Vue } from 'vue-property-decorator'
|
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||||
import { Deck, Arrangement, PageSize, CardSize } from '@/types'
|
import { Deck } from '@/types'
|
||||||
|
import {
|
||||||
|
cardSizeOptions,
|
||||||
|
pageSizeOptions,
|
||||||
|
arrangementOptions,
|
||||||
|
defaultCardSize,
|
||||||
|
defaultPageSize,
|
||||||
|
defaultArrangement
|
||||||
|
} from '@/consts'
|
||||||
import FlipSwitch from '@/components/flip-switch.vue'
|
import FlipSwitch from '@/components/flip-switch.vue'
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -46,39 +54,32 @@ import FlipSwitch from '@/components/flip-switch.vue'
|
||||||
export default class EditDeckForm extends Vue {
|
export default class EditDeckForm extends Vue {
|
||||||
@Prop() public readonly deck!: Deck
|
@Prop() public readonly deck!: Deck
|
||||||
|
|
||||||
private pageSizes = [
|
private pageSizes = pageSizeOptions
|
||||||
{ title: 'A4', value: PageSize.A4 }, // 210mm × 297mm
|
private cardSizes = cardSizeOptions
|
||||||
{ title: 'US Letter', value: PageSize.USLetter }, // 8.5in × 11in
|
private arrangements = arrangementOptions
|
||||||
{ title: 'JIS-B4', value: PageSize.JISB4 }, // 182mm × 257mm
|
|
||||||
{ title: 'A3', value: PageSize.A3 }, // 297mm × 420mm
|
|
||||||
{ title: 'A5', value: PageSize.A5 }, // 148mm × 210mm
|
|
||||||
{ title: 'US Legal', value: PageSize.USLegal }, // 8.5in × 14in
|
|
||||||
{ title: 'US Ledger', value: PageSize.USLedger }, // 11in × 17in
|
|
||||||
{ title: 'JIS-B5', value: PageSize.JISB5 } // 257mm × 364mm
|
|
||||||
]
|
|
||||||
|
|
||||||
private cardSizes = [
|
private pageSize = defaultPageSize
|
||||||
{ title: '88x62 (Poker)', value: CardSize.Poker },
|
private cardSize = defaultCardSize
|
||||||
{ title: '88x56 (Bridge)', value: CardSize.Bridge }
|
private arrangement = defaultArrangement
|
||||||
]
|
|
||||||
|
|
||||||
private arrangements = [
|
|
||||||
{ title: 'Double Sided', value: Arrangement.DoubleSided },
|
|
||||||
{ title: 'Only Front Sides', value: Arrangement.FrontOnly },
|
|
||||||
{ title: 'Side by Side', value: Arrangement.SideBySide }
|
|
||||||
]
|
|
||||||
|
|
||||||
private pageSize = PageSize.A4
|
|
||||||
private cardSize = CardSize.Poker
|
|
||||||
private arrangement = Arrangement.DoubleSided
|
|
||||||
private roundedCorners = true
|
private roundedCorners = true
|
||||||
|
|
||||||
private mounted () {
|
private mounted () {
|
||||||
this.cardSize = this.deck.cardSize
|
this.cardSize = this.deck.cardSize
|
||||||
|
this.pageSize = this.deck.pageSize
|
||||||
|
this.arrangement = this.deck.arrangement
|
||||||
|
this.roundedCorners = this.deck.roundedCorners
|
||||||
}
|
}
|
||||||
|
|
||||||
private printDeck () {
|
private printDeck () {
|
||||||
|
this.$storage.saveDeck({
|
||||||
|
...this.deck,
|
||||||
|
arrangement: this.arrangement,
|
||||||
|
pageSize: this.pageSize,
|
||||||
|
cardSize: this.cardSize,
|
||||||
|
roundedCorners: this.roundedCorners
|
||||||
|
})
|
||||||
console.log('would print on', this.pageSize, `(${this.arrangement})`, this.deck.cards.length, 'cards of size', this.cardSize, this.roundedCorners ? 'with rounded corners' : '')
|
console.log('would print on', this.pageSize, `(${this.arrangement})`, this.deck.cards.length, 'cards of size', this.cardSize, this.roundedCorners ? 'with rounded corners' : '')
|
||||||
|
window.open(`/print/${this.deck.id}`, '_blank')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
89
src/components/static-card.vue
Normal file
89
src/components/static-card.vue
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<template>
|
||||||
|
<div class="card" :style="containerStyle">
|
||||||
|
<div class="card-front" v-if="showFront">
|
||||||
|
<header>
|
||||||
|
<h1>{{ card.name }}</h1>
|
||||||
|
<img :src="icon" />
|
||||||
|
</header>
|
||||||
|
<main ref="cardEl" class="card-content">
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
<div class="card-back" v-if="showBack">BACK</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Prop, Vue } from 'vue-property-decorator'
|
||||||
|
import { Deck, Card } from '@/types'
|
||||||
|
import { iconPath } from '@/lib'
|
||||||
|
|
||||||
|
import Editor from '@editorjs/editorjs'
|
||||||
|
import List from '@editorjs/list'
|
||||||
|
import { Heading, Delimiter, Charges, DnDStats } from '@/editor'
|
||||||
|
|
||||||
|
@Component
|
||||||
|
export default class StaticCard extends Vue {
|
||||||
|
@Prop() public readonly card!: Card
|
||||||
|
@Prop() public readonly deck!: Deck
|
||||||
|
@Prop({ default: false }) public readonly showFront!: boolean
|
||||||
|
@Prop({ default: false }) public readonly showBack!: boolean
|
||||||
|
|
||||||
|
private editor!: Editor
|
||||||
|
|
||||||
|
private mounted () {
|
||||||
|
this.editor = new Editor({
|
||||||
|
holder: this.$refs.cardEl as HTMLElement,
|
||||||
|
autofocus: false,
|
||||||
|
hideToolbar: true,
|
||||||
|
tools: {
|
||||||
|
list: { class: List, inlineToolbar: false },
|
||||||
|
heading: { class: Heading, inlineToolbar: false },
|
||||||
|
delimiter: { class: Delimiter, inlineToolbar: false },
|
||||||
|
charges: { class: Charges, inlineToolbar: false },
|
||||||
|
dndstats: { class: DnDStats, inlineToolbar: false }
|
||||||
|
},
|
||||||
|
data: this.card.content,
|
||||||
|
onReady: () => {
|
||||||
|
console.log('editor is ready, what to do?')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private get icon () {
|
||||||
|
const icon = this.card.icon || this.deck.icon
|
||||||
|
return iconPath(icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get backIcon () {
|
||||||
|
const icon = this.card.backIcon || this.deck.icon
|
||||||
|
return iconPath(icon)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get containerStyle () {
|
||||||
|
const color = (this.deck && this.deck.color) || this.card.color
|
||||||
|
|
||||||
|
return {
|
||||||
|
'--highlight-color': color
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style src="@/assets/card.css" />
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.card {
|
||||||
|
height: auto;
|
||||||
|
width: auto;
|
||||||
|
background-color: var(--highlight-color);
|
||||||
|
border: none;
|
||||||
|
box-shadow: none;
|
||||||
|
margin: 0;
|
||||||
|
cursor: default;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
.card-front, .card-back {
|
||||||
|
width: var(--card-width);
|
||||||
|
height: var(--card-height);
|
||||||
|
}
|
||||||
|
</style>
|
27
src/consts.ts
Normal file
27
src/consts.ts
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import { CardSize, PageSize, Arrangement } from './types'
|
||||||
|
|
||||||
|
export const cardSizeOptions = [
|
||||||
|
{ title: '88x62 (Poker)', value: CardSize.Poker },
|
||||||
|
{ title: '88x56 (Bridge)', value: CardSize.Bridge }
|
||||||
|
]
|
||||||
|
|
||||||
|
export const pageSizeOptions = [
|
||||||
|
{ title: 'A4', value: PageSize.A4 }, // 210mm × 297mm
|
||||||
|
{ title: 'US Letter', value: PageSize.USLetter }, // 8.5in × 11in
|
||||||
|
{ title: 'JIS-B4', value: PageSize.JISB4 }, // 182mm × 257mm
|
||||||
|
{ title: 'A3', value: PageSize.A3 }, // 297mm × 420mm
|
||||||
|
{ title: 'A5', value: PageSize.A5 }, // 148mm × 210mm
|
||||||
|
{ title: 'US Legal', value: PageSize.USLegal }, // 8.5in × 14in
|
||||||
|
{ title: 'US Ledger', value: PageSize.USLedger }, // 11in × 17in
|
||||||
|
{ title: 'JIS-B5', value: PageSize.JISB5 } // 257mm × 364mm
|
||||||
|
]
|
||||||
|
|
||||||
|
export const arrangementOptions = [
|
||||||
|
{ title: 'Double Sided', value: Arrangement.DoubleSided },
|
||||||
|
{ title: 'Only Front Sides', value: Arrangement.FrontOnly },
|
||||||
|
{ title: 'Side by Side', value: Arrangement.SideBySide }
|
||||||
|
]
|
||||||
|
|
||||||
|
export const defaultPageSize = pageSizeOptions[0].value
|
||||||
|
export const defaultCardSize = cardSizeOptions[0].value
|
||||||
|
export const defaultArrangement = arrangementOptions[0].value
|
|
@ -21,7 +21,6 @@ class Charges extends ContentlessBlock {
|
||||||
|
|
||||||
constructor (args: BlockToolArgs) {
|
constructor (args: BlockToolArgs) {
|
||||||
super(args)
|
super(args)
|
||||||
console.log('new charges', args)
|
|
||||||
this._settingButtons = [
|
this._settingButtons = [
|
||||||
{ name: 'box', icon, action: (name: string) => this.setVariant(name) },
|
{ name: 'box', icon, action: (name: string) => this.setVariant(name) },
|
||||||
{ name: 'more', icon: icon, action: () => this.increaseAmount() },
|
{ name: 'more', icon: icon, action: () => this.increaseAmount() },
|
||||||
|
@ -115,8 +114,6 @@ class Charges extends ContentlessBlock {
|
||||||
el.appendChild(this.createCharge())
|
el.appendChild(this.createCharge())
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('rendered', this._amount, 'charges', el)
|
|
||||||
|
|
||||||
return el
|
return el
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
20
src/lib.ts
20
src/lib.ts
|
@ -1,4 +1,4 @@
|
||||||
import { CardSize, Deck, Card } from './types'
|
import { CardSize, PageSize, Arrangement, Deck, Card } from './types'
|
||||||
|
|
||||||
export function randomId (): string {
|
export function randomId (): string {
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
|
@ -8,7 +8,7 @@ export function randomId (): string {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function cardWHFromSize (size: CardSize): number[] {
|
export function cardWHFromSize (size: CardSize): number[] {
|
||||||
return size.split('x').map(v => parseFloat(v)).reverse()
|
return size.split('x').map(v => parseFloat(v))
|
||||||
}
|
}
|
||||||
|
|
||||||
export function iconPath (icon: string): string {
|
export function iconPath (icon: string): string {
|
||||||
|
@ -31,8 +31,11 @@ export function defaultDeck (): Deck {
|
||||||
name: 'the nameless',
|
name: 'the nameless',
|
||||||
description: '',
|
description: '',
|
||||||
color: '#3C1C00',
|
color: '#3C1C00',
|
||||||
|
cards: [],
|
||||||
cardSize: CardSize.Poker,
|
cardSize: CardSize.Poker,
|
||||||
cards: []
|
pageSize: PageSize.A4,
|
||||||
|
arrangement: Arrangement.DoubleSided,
|
||||||
|
roundedCorners: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,3 +53,14 @@ export function defaultCard (): Card {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isValidDeck (deck: any): boolean {
|
||||||
|
const example = defaultDeck() as { [key: string]: any }
|
||||||
|
|
||||||
|
for (const key in example) {
|
||||||
|
const type = typeof example[key]
|
||||||
|
return typeof deck[key] === type
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
// page width x page height
|
||||||
export const enum PageSize {
|
export const enum PageSize {
|
||||||
A4 = '210mm 297mm',
|
A4 = '210mm 297mm',
|
||||||
USLetter = '8.5in 11in',
|
USLetter = '8.5in 11in',
|
||||||
|
@ -9,9 +10,10 @@ export const enum PageSize {
|
||||||
JISB5 = '257mm 364mm'
|
JISB5 = '257mm 364mm'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// card width x card height
|
||||||
export const enum CardSize {
|
export const enum CardSize {
|
||||||
Poker = '89x64',
|
Poker = '64x89',
|
||||||
Bridge = '89x57'
|
Bridge = '57x89'
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enum Arrangement {
|
export const enum Arrangement {
|
||||||
|
@ -54,6 +56,9 @@ export interface Deck {
|
||||||
icon: string;
|
icon: string;
|
||||||
cards: Card[];
|
cards: Card[];
|
||||||
cardSize: CardSize;
|
cardSize: CardSize;
|
||||||
|
arrangement: Arrangement;
|
||||||
|
pageSize: PageSize;
|
||||||
|
roundedCorners: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Settings {
|
export interface Settings {
|
||||||
|
|
|
@ -1,31 +1,43 @@
|
||||||
<template>
|
<template>
|
||||||
<main id="print-view" name="print-view" :class="{ loading, 'not-found': notFound }" :style="pageSizeCSS">
|
<main id="print-view" name="print-view" :class="{ loading, 'not-found': notFound }" :style="pageSizeCSS">
|
||||||
<div class="page">
|
<div class="loading" v-if="loading">— loading —</div>
|
||||||
<header>Page 1</header>
|
<div class="not-found" v-else-if="notFound">Deck not found :(</div>
|
||||||
<p>foo bar baz</p>
|
<template v-else>
|
||||||
<ol>
|
<div class="page">
|
||||||
<li :key="`c${i}`" v-for="(card, i) in deck.cards">{{ card.title }}</li>
|
<Card :key="card.id" v-for="card in deck.cards"
|
||||||
</ol>
|
:card="card"
|
||||||
</div>
|
:deck="deck"
|
||||||
<div class="page">
|
:show-front="true"
|
||||||
<header>Page 2</header>
|
/>
|
||||||
<p>foo bar baz</p>
|
</div>
|
||||||
</div>
|
<div class="page">
|
||||||
|
<header>Page 2</header>
|
||||||
|
<p>foo bar baz</p>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
</main>
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from 'vue-property-decorator'
|
import { Component, Vue } from 'vue-property-decorator'
|
||||||
import { Deck } from '../types'
|
import { Deck } from '../types'
|
||||||
|
import { defaultCardSize, defaultPageSize } from '../consts'
|
||||||
|
import Card from '../components/static-card.vue'
|
||||||
import { iconPath } from '../lib'
|
import { iconPath } from '../lib'
|
||||||
|
|
||||||
@Component
|
interface Dimensions {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: { Card }
|
||||||
|
})
|
||||||
export default class PrintDeck extends Vue {
|
export default class PrintDeck extends Vue {
|
||||||
private loading = true
|
private loading = true
|
||||||
private notFound = false
|
private notFound = false
|
||||||
private deck: Deck | null = null
|
private deck: Deck | null = null
|
||||||
|
|
||||||
private size = '210mm 297mm'
|
|
||||||
private landscape = false // TODO: not yet implemented
|
private landscape = false // TODO: not yet implemented
|
||||||
|
|
||||||
private mounted () {
|
private mounted () {
|
||||||
|
@ -35,18 +47,41 @@ export default class PrintDeck extends Vue {
|
||||||
this.loading = false
|
this.loading = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private get pageSize (): Dimensions {
|
||||||
|
const pageSize = this.deck === null ? defaultPageSize : this.deck.pageSize
|
||||||
|
const [width, height] = pageSize.split(' ').map(x => parseFloat(x))
|
||||||
|
return { width, height }
|
||||||
|
}
|
||||||
|
|
||||||
|
private get cardSize (): Dimensions {
|
||||||
|
const cardSize = this.deck === null ? defaultCardSize : this.deck.cardSize
|
||||||
|
const [height, width] = cardSize.split('x').map(x => parseFloat(x))
|
||||||
|
return { width, height }
|
||||||
|
}
|
||||||
|
|
||||||
|
private get cardsPerPage (): number {
|
||||||
|
if (this.deck === null || this.deck.cards.length === 0) return 0
|
||||||
|
}
|
||||||
|
|
||||||
private get deckIcon () {
|
private get deckIcon () {
|
||||||
if (this.deck === null) return ''
|
if (this.deck === null) return ''
|
||||||
return iconPath(this.deck.icon)
|
return iconPath(this.deck.icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
private get pageSizeCSS () {
|
private get pageSizeCSS () {
|
||||||
const [w, h] = this.size.split(' ')
|
const cardHeight = `${this.cardSize.height}mm`
|
||||||
|
const cardWidth = `${this.cardSize.width}mm`
|
||||||
|
|
||||||
|
const pageHeight = `${this.pageSize.height}mm`
|
||||||
|
const pageWidth = `${this.pageSize.width}mm`
|
||||||
|
|
||||||
|
console.log(cardHeight, cardWidth, pageHeight, pageWidth)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'--width': w,
|
'--width': pageWidth,
|
||||||
'--height': h,
|
'--height': pageHeight,
|
||||||
'--size': this.size
|
'--card-width': cardWidth,
|
||||||
|
'--card-height': cardHeight
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -58,13 +93,20 @@ export default class PrintDeck extends Vue {
|
||||||
size: var(--size);
|
size: var(--size);
|
||||||
}
|
}
|
||||||
#print-view > .page {
|
#print-view > .page {
|
||||||
display: block;
|
box-sizing: border-box;
|
||||||
|
display: flex;
|
||||||
|
flex-flow: row wrap;
|
||||||
|
justify-content: flex-start;
|
||||||
|
align-content: flex-start;
|
||||||
|
page-break-after: always;
|
||||||
width: var(--width);
|
width: var(--width);
|
||||||
height: var(--height);
|
height: var(--height);
|
||||||
margin: 5mm auto;
|
margin: 5mm auto;
|
||||||
|
padding: 1cm 9mm;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
color: black;
|
color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media print {
|
@media print {
|
||||||
html,body {
|
html,body {
|
||||||
background-color: gray;
|
background-color: gray;
|
||||||
|
|
Loading…
Add table
Reference in a new issue