Add basic deck builder (#12)
All checks were successful
continuous-integration/drone/push Build is passing
Includes: - Basic layout - Card list - Filter cards by set/color - Filter cards by type - Filter cards by rule text - Add/remove cards to decklist - Export deck to ponyhead URL
|
@ -125,7 +125,7 @@ steps:
|
||||||
commands:
|
commands:
|
||||||
- yarn test:unit --coverage
|
- yarn test:unit --coverage
|
||||||
depends_on:
|
depends_on:
|
||||||
- dependencies
|
- test # Must run after test otherwise SQLite will get mad
|
||||||
|
|
||||||
- name: upload_coverage
|
- name: upload_coverage
|
||||||
image: plugins/s3
|
image: plugins/s3
|
||||||
|
|
1
.gitignore
vendored
|
@ -2,6 +2,7 @@
|
||||||
node_modules
|
node_modules
|
||||||
/dist
|
/dist
|
||||||
coverage
|
coverage
|
||||||
|
*.sqlite
|
||||||
|
|
||||||
# local env files
|
# local env files
|
||||||
.env.local
|
.env.local
|
||||||
|
|
16
package.json
|
@ -9,12 +9,20 @@
|
||||||
"test:unit": "vue-cli-service test:unit"
|
"test:unit": "vue-cli-service test:unit"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"axios": "^0.18.0",
|
||||||
|
"babel-core": "7.0.0-bridge.0",
|
||||||
|
"babel-eslint": "^10.0.1",
|
||||||
"buefy": "^0.8.2",
|
"buefy": "^0.8.2",
|
||||||
|
"bulma-prefers-dark": "^0.1.0-beta.0",
|
||||||
"core-js": "^2.6.5",
|
"core-js": "^2.6.5",
|
||||||
"dexie": "^2.0.4",
|
"dexie": "^2.0.4",
|
||||||
"eventemitter3": "^4.0.0",
|
"eventemitter3": "^4.0.0",
|
||||||
|
"node-sass": "^4.9.0",
|
||||||
"peerjs": "^1.0.4",
|
"peerjs": "^1.0.4",
|
||||||
"register-service-worker": "^1.6.2",
|
"register-service-worker": "^1.6.2",
|
||||||
|
"sass": "^1.18.0",
|
||||||
|
"sass-loader": "^7.1.0",
|
||||||
|
"typescript": "^3.4.3",
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
"vue-class-component": "^7.0.2",
|
"vue-class-component": "^7.0.2",
|
||||||
"vue-property-decorator": "^8.1.0",
|
"vue-property-decorator": "^8.1.0",
|
||||||
|
@ -33,17 +41,13 @@
|
||||||
"@vue/eslint-config-prettier": "^5.0.0",
|
"@vue/eslint-config-prettier": "^5.0.0",
|
||||||
"@vue/eslint-config-typescript": "^4.0.0",
|
"@vue/eslint-config-typescript": "^4.0.0",
|
||||||
"@vue/test-utils": "^1.0.0-beta.29",
|
"@vue/test-utils": "^1.0.0-beta.29",
|
||||||
"babel-core": "7.0.0-bridge.0",
|
|
||||||
"babel-eslint": "^10.0.1",
|
|
||||||
"eslint": "^5.16.0",
|
"eslint": "^5.16.0",
|
||||||
"eslint-plugin-prettier": "^3.1.0",
|
"eslint-plugin-prettier": "^3.1.0",
|
||||||
"eslint-plugin-vue": "^5.0.0",
|
"eslint-plugin-vue": "^5.0.0",
|
||||||
"node-sass": "^4.9.0",
|
"indexeddbshim": "^4.1.0",
|
||||||
"prettier": "^1.18.2",
|
"prettier": "^1.18.2",
|
||||||
"sass": "^1.18.0",
|
|
||||||
"sass-loader": "^7.1.0",
|
|
||||||
"ts-jest": "^23.0.0",
|
"ts-jest": "^23.0.0",
|
||||||
"typescript": "^3.4.3",
|
"vue-cli-plugin-axios": "^0.0.4",
|
||||||
"vue-cli-plugin-buefy": "^0.3.7",
|
"vue-cli-plugin-buefy": "^0.3.7",
|
||||||
"vue-template-compiler": "^2.6.10"
|
"vue-template-compiler": "^2.6.10"
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
{
|
{
|
||||||
"name": "mcgvue",
|
"name": "mcgvue",
|
||||||
"short_name": "mcgvue",
|
"short_name": "mcgvue",
|
||||||
"icons": [
|
"icons": [{
|
||||||
{
|
"src": "./images/icons/android-chrome-192x192.png",
|
||||||
"src": "./img/icons/android-chrome-192x192.png",
|
|
||||||
"sizes": "192x192",
|
"sizes": "192x192",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"src": "./img/icons/android-chrome-512x512.png",
|
"src": "./images/icons/android-chrome-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
}
|
}
|
||||||
|
@ -17,4 +16,4 @@
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#000000",
|
"background_color": "#000000",
|
||||||
"theme_color": "#4DBA87"
|
"theme_color": "#4DBA87"
|
||||||
}
|
}
|
|
@ -9,6 +9,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
main {
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
main.loading-box {
|
main.loading-box {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
BIN
src/assets/images/cardback.webp
Normal file
After Width: | Height: | Size: 46 KiB |
13
src/assets/images/deckbuilder/navarrow.svg
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg width="100%" height="100%" viewBox="0 0 50 82" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||||
|
<g id="Artboard1" transform="matrix(1,0,0,1,0,15.7771)">
|
||||||
|
<rect x="0" y="-15.777" width="50" height="81.873" style="fill:none;"/>
|
||||||
|
<g transform="matrix(0.457009,0,0,1.2614,2.95268,-32.3672)">
|
||||||
|
<path d="M48.243,45.605L90.773,73.875L48.243,73.875L5.712,45.605L48.243,17.336L90.773,17.336L48.243,45.605L48.243,45.605Z" style="fill:url(#_Linear1);"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
<defs>
|
||||||
|
<linearGradient id="_Linear1" x1="0" y1="0" x2="1" y2="0" gradientUnits="userSpaceOnUse" gradientTransform="matrix(137.101,65.9713,-182.089,49.6719,-34.1545,12.0871)"><stop offset="0" style="stop-color:rgb(235,235,235);stop-opacity:1"/><stop offset="1" style="stop-color:rgb(163,163,163);stop-opacity:1"/></linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 11 KiB |
BIN
src/assets/images/elements/generosity.webp
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
src/assets/images/elements/honesty.webp
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
src/assets/images/elements/kindness.webp
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/images/elements/laughter.webp
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
src/assets/images/elements/loyalty.webp
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/assets/images/elements/magic.webp
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
src/assets/images/elements/none.webp
Normal file
After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 9.2 KiB |
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 29 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 4 KiB After Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 4.6 KiB After Width: | Height: | Size: 4.6 KiB |
Before Width: | Height: | Size: 799 B After Width: | Height: | Size: 799 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 4.2 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 10 KiB |
Before Width: | Height: | Size: 26 KiB |
BIN
src/assets/images/races/alicorn.png
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
src/assets/images/races/ally.png
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
src/assets/images/races/critter.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
src/assets/images/races/dragon.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
src/assets/images/races/earthpony.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
src/assets/images/races/pegasus.png
Normal file
After Width: | Height: | Size: 28 KiB |
BIN
src/assets/images/races/unicorn.png
Normal file
After Width: | Height: | Size: 27 KiB |
|
@ -3,32 +3,44 @@
|
||||||
|
|
||||||
// Colors
|
// Colors
|
||||||
|
|
||||||
$black: hsl(0, 0%, 4%) !default;
|
$black: hsl(0, 0%, 4%) !default;
|
||||||
$black-bis: hsl(0, 0%, 7%) !default;
|
$black-bis: hsl(0, 0%, 7%) !default;
|
||||||
$black-ter: hsl(0, 0%, 14%) !default;
|
$black-ter: hsl(0, 0%, 14%) !default;
|
||||||
|
|
||||||
$grey-darker: hsl(0, 0%, 21%) !default;
|
$grey-darker: hsl(0, 0%, 21%) !default;
|
||||||
$grey-dark: hsl(0, 0%, 29%) !default;
|
$grey-dark: hsl(0, 0%, 29%) !default;
|
||||||
$grey: hsl(0, 0%, 48%) !default;
|
$grey: hsl(0, 0%, 48%) !default;
|
||||||
$grey-light: hsl(0, 0%, 71%) !default;
|
$grey-light: hsl(0, 0%, 71%) !default;
|
||||||
$grey-lighter: hsl(0, 0%, 86%) !default;
|
$grey-lighter: hsl(0, 0%, 86%) !default;
|
||||||
|
|
||||||
$white-ter: hsl(0, 0%, 96%) !default;
|
$white-ter: hsl(0, 0%, 96%) !default;
|
||||||
$white-bis: hsl(0, 0%, 98%) !default;
|
$white-bis: hsl(0, 0%, 98%) !default;
|
||||||
$white: hsl(0, 0%, 100%) !default;
|
$white: hsl(0, 0%, 100%) !default;
|
||||||
|
|
||||||
$orange: hsl(14, 100%, 53%) !default;
|
$orange: hsl(14, 100%, 53%) !default;
|
||||||
$yellow: hsl(48, 100%, 67%) !default;
|
$yellow: hsl(48, 100%, 67%) !default;
|
||||||
$green: hsl(141, 71%, 48%) !default;
|
$green: hsl(141, 71%, 48%) !default;
|
||||||
$turquoise: hsl(171, 100%, 41%) !default;
|
$turquoise: hsl(171, 100%, 41%) !default;
|
||||||
$cyan: hsl(204, 86%, 53%) !default;
|
$cyan: hsl(204, 86%, 53%) !default;
|
||||||
$blue: hsl(217, 71%, 53%) !default;
|
$blue: hsl(217, 71%, 53%) !default;
|
||||||
$purple: hsl(271, 100%, 71%) !default;
|
$purple: hsl(271, 100%, 71%) !default;
|
||||||
$red: hsl(348, 100%, 61%) !default;
|
$red: hsl(348, 100%, 61%) !default;
|
||||||
|
|
||||||
// Typography
|
// Typography
|
||||||
|
|
||||||
$family-sans-serif: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif !default;
|
$family-sans-serif: BlinkMacSystemFont,
|
||||||
|
-apple-system,
|
||||||
|
"Segoe UI",
|
||||||
|
"Roboto",
|
||||||
|
"Oxygen",
|
||||||
|
"Ubuntu",
|
||||||
|
"Cantarell",
|
||||||
|
"Fira Sans",
|
||||||
|
"Droid Sans",
|
||||||
|
"Helvetica Neue",
|
||||||
|
"Helvetica",
|
||||||
|
"Arial",
|
||||||
|
sans-serif !default;
|
||||||
$family-monospace: monospace !default;
|
$family-monospace: monospace !default;
|
||||||
$render-mode: optimizeLegibility !default;
|
$render-mode: optimizeLegibility !default;
|
||||||
|
|
||||||
|
@ -53,11 +65,11 @@ $gap: 32px !default;
|
||||||
// 960, 1152, and 1344 have been chosen because they are divisible by both 12 and 16
|
// 960, 1152, and 1344 have been chosen because they are divisible by both 12 and 16
|
||||||
$tablet: 769px !default;
|
$tablet: 769px !default;
|
||||||
// 960px container + 4rem
|
// 960px container + 4rem
|
||||||
$desktop: 960px + (2 * $gap) !default;
|
$desktop: 960px+(2 * $gap) !default;
|
||||||
// 1152px container + 4rem
|
// 1152px container + 4rem
|
||||||
$widescreen: 1152px + (2 * $gap) !default;
|
$widescreen: 1152px+(2 * $gap) !default;
|
||||||
// 1344px container + 4rem;
|
// 1344px container + 4rem;
|
||||||
$fullhd: 1344px + (2 * $gap) !default;
|
$fullhd: 1344px+(2 * $gap) !default;
|
||||||
|
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
|
|
||||||
|
@ -72,7 +84,6 @@ $speed: 86ms !default;
|
||||||
|
|
||||||
$variable-columns: true !default;
|
$variable-columns: true !default;
|
||||||
|
|
||||||
|
|
||||||
// The default Bulma derived variables are declared below
|
// The default Bulma derived variables are declared below
|
||||||
|
|
||||||
$primary: $turquoise !default;
|
$primary: $turquoise !default;
|
||||||
|
@ -150,3 +161,10 @@ $size-small: $size-7 !default;
|
||||||
$size-normal: $size-6 !default;
|
$size-normal: $size-6 !default;
|
||||||
$size-medium: $size-5 !default;
|
$size-medium: $size-5 !default;
|
||||||
$size-large: $size-4 !default;
|
$size-large: $size-4 !default;
|
||||||
|
|
||||||
|
// Input box styling
|
||||||
|
$input-focus-border-color: $turquoise;
|
||||||
|
$input-hover-border-color: scale-color($turquoise, $lightness: -30%);
|
||||||
|
|
||||||
|
$fantasy: 'Merriweather';
|
||||||
|
$primary-text: scale-color($primary, $saturation: -50%, $lightness: 20%);
|
|
@ -4,6 +4,8 @@
|
||||||
@import "~bulma/sass/utilities/derived-variables";
|
@import "~bulma/sass/utilities/derived-variables";
|
||||||
@import "~bulma";
|
@import "~bulma";
|
||||||
@import "~buefy/src/scss/buefy";
|
@import "~buefy/src/scss/buefy";
|
||||||
|
@import "dark";
|
||||||
|
@import url('https://fonts.googleapis.com/css?family=Merriweather:300,400,400i,700&display=swap');
|
||||||
|
|
||||||
html {
|
html {
|
||||||
scrollbar-color: #404245 #2f3132;
|
scrollbar-color: #404245 #2f3132;
|
||||||
|
@ -12,5 +14,5 @@ html {
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
color: white;
|
color: $white;
|
||||||
}
|
}
|
6
src/assets/scss/dark.sass
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
@charset "utf-8"
|
||||||
|
@import "~bulma-prefers-dark/sass/utilities/_all"
|
||||||
|
@import "~bulma-prefers-dark/sass/base/_all"
|
||||||
|
@import "~bulma-prefers-dark/sass/elements/_all"
|
||||||
|
@import "~bulma-prefers-dark/sass/components/_all"
|
||||||
|
@import "~bulma-prefers-dark/sass/layout/_all"
|
89
src/components/DeckBuilder/CardPicker.vue
Normal file
|
@ -0,0 +1,89 @@
|
||||||
|
<template>
|
||||||
|
<section class="cardpicker" :style="grid">
|
||||||
|
<article
|
||||||
|
@click="() => _picked(card)"
|
||||||
|
:class="cardClass(card)"
|
||||||
|
v-for="(card, i) in cards"
|
||||||
|
:key="i + card.data.ID"
|
||||||
|
>
|
||||||
|
<img :src="imageURL(card.data.ID)" />
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
$padding: 10px;
|
||||||
|
|
||||||
|
.cardpicker {
|
||||||
|
height: 100%;
|
||||||
|
display: grid;
|
||||||
|
gap: $padding;
|
||||||
|
padding: ($padding * 4) $padding;
|
||||||
|
row-gap: $padding * 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ccgcard {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
transition: 100ms all;
|
||||||
|
&.available:hover img {
|
||||||
|
box-shadow: 0 0 15px 5px rgba(200, 210, 255, 0.5);
|
||||||
|
}
|
||||||
|
&.disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue, Prop } from "vue-property-decorator";
|
||||||
|
import { Card, CardSlot, cardImageURL } from "@/mlpccg";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {}
|
||||||
|
})
|
||||||
|
export default class CardPicker extends Vue {
|
||||||
|
@Prop()
|
||||||
|
public cards!: CardSlot[];
|
||||||
|
|
||||||
|
@Prop({ default: 2 })
|
||||||
|
public rows!: number;
|
||||||
|
|
||||||
|
@Prop({ default: 5 })
|
||||||
|
public columns!: number;
|
||||||
|
|
||||||
|
@Prop({ default: false })
|
||||||
|
public ignoreLimit!: boolean;
|
||||||
|
|
||||||
|
private get grid() {
|
||||||
|
return {
|
||||||
|
gridTemplateRows: "1fr ".repeat(this.rows).trim(),
|
||||||
|
gridTemplateColumns: "1fr ".repeat(this.columns).trim()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private imageURL(id: string) {
|
||||||
|
return cardImageURL(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _picked(card: CardSlot) {
|
||||||
|
if (this.isAvailable(card)) {
|
||||||
|
this.$emit("picked", card.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private cardClass(card: CardSlot) {
|
||||||
|
const available = this.isAvailable(card);
|
||||||
|
return {
|
||||||
|
ccgcard: true,
|
||||||
|
available,
|
||||||
|
disabled: !available
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private isAvailable(card: CardSlot) {
|
||||||
|
return card.limit == 0 || card.howmany < card.limit || this.ignoreLimit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
229
src/components/DeckBuilder/DeckList.vue
Normal file
|
@ -0,0 +1,229 @@
|
||||||
|
<template>
|
||||||
|
<section class="decklist">
|
||||||
|
<section class="card-section" v-for="section in sections" :key="section">
|
||||||
|
<header>
|
||||||
|
<h1>{{ section }}</h1>
|
||||||
|
</header>
|
||||||
|
<article
|
||||||
|
class="ccgcard"
|
||||||
|
@click="() => _drop(card)"
|
||||||
|
v-for="(card, i) in getCards(section, true)"
|
||||||
|
:key="i"
|
||||||
|
>
|
||||||
|
<img :src="imageURL(card.data.ID)" class="cardbg" />
|
||||||
|
<div class="amt">{{ card.howmany }}</div>
|
||||||
|
<div class="fullname">
|
||||||
|
<div class="name">{{ card.data.Name }}</div>
|
||||||
|
<div class="subname">
|
||||||
|
{{ card.data.Subname ? card.data.Subname : "" }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</article>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import "@/assets/scss/_variables.scss";
|
||||||
|
|
||||||
|
.decklist {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-section {
|
||||||
|
header {
|
||||||
|
h1 {
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-family: $fantasy;
|
||||||
|
font-size: 10pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ccgcard {
|
||||||
|
display: flex;
|
||||||
|
align-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border: 1px solid $grey;
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 5px 3px;
|
||||||
|
margin: 2px 0;
|
||||||
|
cursor: pointer;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
div {
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardbg {
|
||||||
|
position: absolute;
|
||||||
|
margin-top: 30%;
|
||||||
|
right: -20px;
|
||||||
|
left: -20px;
|
||||||
|
max-width: none;
|
||||||
|
width: 120%;
|
||||||
|
filter: brightness(20%);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fullname {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.amt {
|
||||||
|
margin: 0 6pt 0 10pt;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 13pt;
|
||||||
|
&:after {
|
||||||
|
content: " ×";
|
||||||
|
font-weight: 300;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.name {
|
||||||
|
font-family: $fantasy;
|
||||||
|
font-size: 10.5pt;
|
||||||
|
line-height: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subname {
|
||||||
|
color: $grey-light;
|
||||||
|
font-size: 10pt;
|
||||||
|
line-height: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Component, Vue, Prop } from "vue-property-decorator";
|
||||||
|
import {
|
||||||
|
cardFullName,
|
||||||
|
CardSlot,
|
||||||
|
Card,
|
||||||
|
cardImageURL,
|
||||||
|
multiElemStr,
|
||||||
|
typeNames
|
||||||
|
} from "@/mlpccg";
|
||||||
|
|
||||||
|
function sortCards(a: CardSlot, b: CardSlot): number {
|
||||||
|
// Sort by element
|
||||||
|
// (Cards are guaranteed to be the same type)
|
||||||
|
switch (a.data.Type) {
|
||||||
|
case "Friend":
|
||||||
|
{
|
||||||
|
// Sort by requirement
|
||||||
|
if (a.data.Requirement && b.data.Requirement) {
|
||||||
|
const reqA = multiElemStr(Object.keys(a.data.Requirement));
|
||||||
|
const reqB = multiElemStr(Object.keys(b.data.Requirement));
|
||||||
|
if (reqA > reqB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (reqB > reqA) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by cost
|
||||||
|
if (a.data.Cost && b.data.Cost) {
|
||||||
|
if (a.data.Cost > b.data.Cost) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a.data.Cost < b.data.Cost) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by element
|
||||||
|
const elemA = multiElemStr(a.data.Element);
|
||||||
|
const elemB = multiElemStr(b.data.Element);
|
||||||
|
if (elemA > elemB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (elemB > elemA) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Problem":
|
||||||
|
if (a.data.ProblemRequirement && b.data.ProblemRequirement) {
|
||||||
|
const preqA = multiElemStr(Object.keys(a.data.ProblemRequirement));
|
||||||
|
const preqB = multiElemStr(Object.keys(b.data.ProblemRequirement));
|
||||||
|
if (preqA > preqB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (preqB > preqA) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Event":
|
||||||
|
case "Resource":
|
||||||
|
if (a.data.Requirement && b.data.Requirement) {
|
||||||
|
const reqA = multiElemStr(Object.keys(a.data.Requirement));
|
||||||
|
const reqB = multiElemStr(Object.keys(b.data.Requirement));
|
||||||
|
if (reqA > reqB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (reqB > reqA) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort by power
|
||||||
|
if (a.data.Power && b.data.Power) {
|
||||||
|
if (a.data.Power > b.data.Power) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (a.data.Power < b.data.Power) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all else fail, sort by name
|
||||||
|
const nameA = cardFullName(a.data);
|
||||||
|
const nameB = cardFullName(b.data);
|
||||||
|
if (nameA > nameB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (nameA < nameB) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
components: {}
|
||||||
|
})
|
||||||
|
export default class DeckList extends Vue {
|
||||||
|
@Prop()
|
||||||
|
public cards!: CardSlot[];
|
||||||
|
|
||||||
|
private fullName(card: Card): string {
|
||||||
|
return cardFullName(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
private _drop(slot: CardSlot) {
|
||||||
|
this.$emit("removed", slot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private imageURL(id: string) {
|
||||||
|
return cardImageURL(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private getCards(section: string, sort: boolean): CardSlot[] {
|
||||||
|
let cards = this.cards.filter(c => c.data.Type == section);
|
||||||
|
if (!sort) {
|
||||||
|
return cards;
|
||||||
|
}
|
||||||
|
return cards.sort(sortCards);
|
||||||
|
}
|
||||||
|
|
||||||
|
private get sections(): string[] {
|
||||||
|
return typeNames.filter(s => this.getCards(s, false).length > 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -1,15 +1,19 @@
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
|
import "./plugins/axios";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
import store from "./store";
|
import store from "./store";
|
||||||
import "./registerServiceWorker";
|
import "./registerServiceWorker";
|
||||||
import Buefy from "buefy";
|
import Buefy from "buefy";
|
||||||
import "./assets/scss/app.scss";
|
import "./assets/scss/app.scss";
|
||||||
|
import { initDB } from "./mlpccg";
|
||||||
|
|
||||||
Vue.use(Buefy);
|
Vue.use(Buefy);
|
||||||
|
|
||||||
Vue.config.productionTip = false;
|
Vue.config.productionTip = false;
|
||||||
|
|
||||||
|
initDB();
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
router,
|
router,
|
||||||
store,
|
store,
|
||||||
|
|
|
@ -1,5 +1,71 @@
|
||||||
const imgBaseURL = "https://mcg.zyg.ovh/images/cards/";
|
import { Card } from "./types";
|
||||||
|
|
||||||
export function cardImageURL(cardid: string): string {
|
export function cardFullName(card: Card): string {
|
||||||
return `${imgBaseURL}${cardid}.webp`;
|
if (card.Subname != "") {
|
||||||
|
return `${card.Name}, ${card.Subname}`;
|
||||||
|
}
|
||||||
|
return card.Name;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createPonyheadURL(cards: Card[]): string {
|
||||||
|
const cardlist = cards.map(c => `${c.ID}x1`);
|
||||||
|
return "https://ponyhead.com/deckbuilder?v1code=" + cardlist.join("-");
|
||||||
|
}
|
||||||
|
|
||||||
|
export const colorNames = [
|
||||||
|
"Loyalty",
|
||||||
|
"Honesty",
|
||||||
|
"Laughter",
|
||||||
|
"Magic",
|
||||||
|
"Generosity",
|
||||||
|
"Kindness",
|
||||||
|
"None"
|
||||||
|
];
|
||||||
|
|
||||||
|
export const typeNames = [
|
||||||
|
"Mane Character",
|
||||||
|
"Friend",
|
||||||
|
"Event",
|
||||||
|
"Resource",
|
||||||
|
"Troublemaker",
|
||||||
|
"Problem"
|
||||||
|
];
|
||||||
|
|
||||||
|
export const rarityNames = ["C", "U", "R", "SR", "UR", "RR", "F"];
|
||||||
|
|
||||||
|
// Trasform string from list to a number that can be used for comparison/sorting
|
||||||
|
function arrIndex(arr: string[]) {
|
||||||
|
return function(comp: string) {
|
||||||
|
const idx = arr.indexOf(comp);
|
||||||
|
if (idx < 0) {
|
||||||
|
return arr.length;
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const elemIndex = arrIndex(colorNames);
|
||||||
|
export const typeIndex = arrIndex(typeNames);
|
||||||
|
export const rarityIndex = arrIndex(rarityNames);
|
||||||
|
|
||||||
|
// Convert Element[] to number by scaling elements for fair comparisons
|
||||||
|
// Example: ["Loyalty", "Kindness"] -> [0, 5] -> [1, 6] -> 16
|
||||||
|
export function multiElemStr(elems: string[]): number {
|
||||||
|
return elems
|
||||||
|
.map(elemIndex)
|
||||||
|
.reduce(
|
||||||
|
(acc, elem, idx, arr) => acc + (elem + 1) * 10 ** (arr.length - idx - 1),
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cardLimit(type: string) {
|
||||||
|
switch (type) {
|
||||||
|
case "Mane Character":
|
||||||
|
return 1;
|
||||||
|
case "Problem":
|
||||||
|
return 2;
|
||||||
|
default:
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,21 +1,32 @@
|
||||||
import Dexie from "dexie";
|
import Dexie from "dexie";
|
||||||
import { Card, CardFilter } from "./types";
|
import { Card, CardFilter, StoredImages } from "./types";
|
||||||
|
import { cardFullName } from "./card";
|
||||||
|
|
||||||
class CardDatabase extends Dexie {
|
class CardDatabase extends Dexie {
|
||||||
public cards: Dexie.Table<Card, string>;
|
public cards: Dexie.Table<Card, string>;
|
||||||
|
public images: Dexie.Table<StoredImages, string>;
|
||||||
|
|
||||||
public constructor() {
|
public constructor() {
|
||||||
super("CardDatabase");
|
super("CardDatabase");
|
||||||
this.version(1).stores({
|
this.version(1).stores({
|
||||||
cards: "ID,Set,Type,Cost,Power"
|
cards: "ID,Set,Type,Cost,Power",
|
||||||
|
images: "id"
|
||||||
});
|
});
|
||||||
this.cards = this.table("cards");
|
this.cards = this.table("cards");
|
||||||
|
this.images = this.table("images");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export let Database = new CardDatabase();
|
export let Database: CardDatabase | null = null;
|
||||||
|
|
||||||
|
export function initDB() {
|
||||||
|
Database = new CardDatabase();
|
||||||
|
}
|
||||||
|
|
||||||
export async function getCards(filter: CardFilter) {
|
export async function getCards(filter: CardFilter) {
|
||||||
|
if (Database == null) {
|
||||||
|
throw new Error("Database was not initialized, init with 'initDB()'");
|
||||||
|
}
|
||||||
let table = Database.cards;
|
let table = Database.cards;
|
||||||
// Get best IDB index
|
// Get best IDB index
|
||||||
let query: Dexie.Collection<Card, string>;
|
let query: Dexie.Collection<Card, string>;
|
||||||
|
@ -43,95 +54,100 @@ export async function getCards(filter: CardFilter) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return await query
|
const results = query.filter(x => {
|
||||||
.filter(x => {
|
if (filter.Name) {
|
||||||
if (filter.Name) {
|
if (
|
||||||
if (
|
!cardFullName(x)
|
||||||
!`${x.Name}, ${x.Subname}`
|
.toLowerCase()
|
||||||
.toLowerCase()
|
.includes(filter.Name.toLowerCase())
|
||||||
.includes(filter.Name.toLowerCase())
|
) {
|
||||||
) {
|
return false;
|
||||||
return false;
|
}
|
||||||
|
}
|
||||||
|
if (filter.Rules) {
|
||||||
|
if (
|
||||||
|
!`${x.Keywords.join(" ~ ")} ~ ${x.Text}`
|
||||||
|
.toLowerCase()
|
||||||
|
.includes(filter.Rules.toLowerCase())
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filter.Traits && filter.Traits.length > 0) {
|
||||||
|
let found = false;
|
||||||
|
for (const trait of x.Traits) {
|
||||||
|
if (filter.Traits.includes(trait)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (filter.Rules) {
|
if (!found) {
|
||||||
if (
|
return false;
|
||||||
!`${x.Keywords.join(" ~ ")} ~ ${x.Text}`
|
}
|
||||||
.toLowerCase()
|
}
|
||||||
.includes(filter.Rules.toLowerCase())
|
if (filter.Sets && filter.Sets.length > 0) {
|
||||||
) {
|
if (!filter.Sets.includes(x.Set)) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filter.Types && filter.Types.length > 0) {
|
||||||
|
if (!filter.Types.includes(x.Type)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filter.Elements && filter.Elements.length > 0) {
|
||||||
|
let found = false;
|
||||||
|
for (const element of x.Element) {
|
||||||
|
if (filter.Elements.includes(element)) {
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (filter.Traits && filter.Traits.length > 0) {
|
if (x.Requirement) {
|
||||||
let found = false;
|
for (const element in x.Requirement) {
|
||||||
for (const trait of x.Traits) {
|
|
||||||
if (filter.Traits.includes(trait)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (filter.Sets && filter.Sets.length > 0) {
|
|
||||||
if (!filter.Sets.includes(x.Set)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (filter.Types && filter.Types.length > 0) {
|
|
||||||
if (!filter.Types.includes(x.Type)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (filter.Elements && filter.Elements.length > 0) {
|
|
||||||
let found = false;
|
|
||||||
for (const element of x.Element) {
|
|
||||||
if (filter.Elements.includes(element)) {
|
if (filter.Elements.includes(element)) {
|
||||||
found = true;
|
found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (x.Requirement) {
|
}
|
||||||
for (const element in x.Requirement) {
|
if (x.ProblemRequirement) {
|
||||||
if (filter.Elements.includes(element)) {
|
for (const element in x.ProblemRequirement) {
|
||||||
found = true;
|
if (filter.Elements.includes(element)) {
|
||||||
break;
|
found = true;
|
||||||
}
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (x.ProblemRequirement) {
|
|
||||||
for (const element in x.ProblemRequirement) {
|
|
||||||
if (filter.Elements.includes(element)) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!found) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (filter.Powers && filter.Powers.length > 0) {
|
// For "None" element searches, "nothing" is actually ok
|
||||||
if (
|
if (
|
||||||
typeof x.Power === "undefined" ||
|
filter.Elements.includes("None") &&
|
||||||
!filter.Powers.includes(x.Power)
|
x.Element.length == 0 &&
|
||||||
) {
|
(!x.Requirement || x.Requirement.length == 0) &&
|
||||||
return false;
|
(!x.ProblemRequirement || x.ProblemRequirement.length == 0)
|
||||||
}
|
) {
|
||||||
|
found = true;
|
||||||
}
|
}
|
||||||
if (filter.Costs && filter.Costs.length > 0) {
|
if (!found) {
|
||||||
if (typeof x.Cost === "undefined" || !filter.Costs.includes(x.Cost)) {
|
return false;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (filter.Rarities && filter.Rarities.length > 0) {
|
}
|
||||||
if (!filter.Rarities.includes(x.Rarity)) {
|
if (filter.Powers && filter.Powers.length > 0) {
|
||||||
return false;
|
if (typeof x.Power === "undefined" || !filter.Powers.includes(x.Power)) {
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
}
|
||||||
})
|
if (filter.Costs && filter.Costs.length > 0) {
|
||||||
.toArray();
|
if (typeof x.Cost === "undefined" || !filter.Costs.includes(x.Cost)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (filter.Rarities && filter.Rarities.length > 0) {
|
||||||
|
if (!filter.Rarities.includes(x.Rarity)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
return await results.toArray();
|
||||||
}
|
}
|
||||||
|
|
35
src/mlpccg/images.ts
Normal file
|
@ -0,0 +1,35 @@
|
||||||
|
import axios from "axios";
|
||||||
|
import { Database } from "./database";
|
||||||
|
|
||||||
|
const imgBaseURL = "https://mcg.zyg.ovh/images/cards/";
|
||||||
|
|
||||||
|
export function cardImageURL(cardid: string): string {
|
||||||
|
return `${imgBaseURL}${cardid}.webp`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCardImageList(): Promise<string[]> {
|
||||||
|
const req = await axios(`${imgBaseURL}list.txt`);
|
||||||
|
return req.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getImages() {
|
||||||
|
if (Database == null) {
|
||||||
|
throw new Error("Database was not initialized, init with 'initDB()'");
|
||||||
|
}
|
||||||
|
const itemcount = await Database.images.count();
|
||||||
|
if (itemcount > 100) {
|
||||||
|
// DB already filled, exit early
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const imglist = await getCardImageList();
|
||||||
|
|
||||||
|
let table = Database.images;
|
||||||
|
const promises = imglist.map(async img => {
|
||||||
|
const req = await axios({
|
||||||
|
url: `${imgBaseURL}${img}`,
|
||||||
|
responseType: "blob"
|
||||||
|
});
|
||||||
|
return table.put({ id: img, image: req.data });
|
||||||
|
});
|
||||||
|
return await Promise.all(promises);
|
||||||
|
}
|
5
src/mlpccg/index.ts
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
export * from "./card";
|
||||||
|
export * from "./database";
|
||||||
|
export * from "./set";
|
||||||
|
export * from "./types";
|
||||||
|
export * from "./images";
|
|
@ -1,8 +1,9 @@
|
||||||
import { SetFile } from "./types";
|
import { SetFile } from "./types";
|
||||||
import { Database } from "./database";
|
import { Database } from "./database";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
const baseURL = "https://mcg.zyg.ovh/setdata/";
|
const baseURL = "https://mcg.zyg.ovh/setdata/";
|
||||||
const allSets = [
|
export const allSets = [
|
||||||
"PR",
|
"PR",
|
||||||
"CN",
|
"CN",
|
||||||
"RR",
|
"RR",
|
||||||
|
@ -19,6 +20,9 @@ const allSets = [
|
||||||
];
|
];
|
||||||
|
|
||||||
export async function loadSets() {
|
export async function loadSets() {
|
||||||
|
if (Database == null) {
|
||||||
|
throw new Error("Database was not initialized, init with 'initDB()'");
|
||||||
|
}
|
||||||
const itemcount = await Database.cards.count();
|
const itemcount = await Database.cards.count();
|
||||||
if (itemcount > 100) {
|
if (itemcount > 100) {
|
||||||
// DB already filled, exit early
|
// DB already filled, exit early
|
||||||
|
@ -27,6 +31,9 @@ export async function loadSets() {
|
||||||
const sets = await Promise.all(allSets.map(set => downloadSet(set)));
|
const sets = await Promise.all(allSets.map(set => downloadSet(set)));
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
sets.map(async set => {
|
sets.map(async set => {
|
||||||
|
if (Database == null) {
|
||||||
|
throw new Error("Database was not initialized, init with 'initDB()'");
|
||||||
|
}
|
||||||
console.log(`Processing cards from ${set.Name}`);
|
console.log(`Processing cards from ${set.Name}`);
|
||||||
return await Database.cards.bulkPut(set.Cards);
|
return await Database.cards.bulkPut(set.Cards);
|
||||||
})
|
})
|
||||||
|
@ -34,11 +41,10 @@ export async function loadSets() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function downloadSet(setid: string): Promise<SetFile> {
|
async function downloadSet(setid: string): Promise<SetFile> {
|
||||||
const setfile = await fetch(`${baseURL}${setid.toLowerCase()}.json`);
|
const setdata = await axios(`${baseURL}${setid.toLowerCase()}.json`);
|
||||||
const setdata: SetFile = await setfile.json();
|
setdata.data.Cards = (setdata.data as SetFile).Cards.map(c => {
|
||||||
setdata.Cards = setdata.Cards.map(c => {
|
|
||||||
c.Set = setid;
|
c.Set = setid;
|
||||||
return c;
|
return c;
|
||||||
});
|
});
|
||||||
return setdata;
|
return setdata.data;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,11 @@ export type Rarity = "C" | "U" | "R" | "SR" | "UR" | "RR";
|
||||||
|
|
||||||
export type PowerRequirement = { [key: string]: number };
|
export type PowerRequirement = { [key: string]: number };
|
||||||
|
|
||||||
|
export interface StoredImages {
|
||||||
|
id: string;
|
||||||
|
image: Blob;
|
||||||
|
}
|
||||||
|
|
||||||
export interface SetFile {
|
export interface SetFile {
|
||||||
Name: string;
|
Name: string;
|
||||||
Cards: Card[];
|
Cards: Card[];
|
||||||
|
@ -38,3 +43,9 @@ export interface CardFilter {
|
||||||
Powers?: number[];
|
Powers?: number[];
|
||||||
Rarities?: string[];
|
Rarities?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface CardSlot {
|
||||||
|
data: Card;
|
||||||
|
limit: number;
|
||||||
|
howmany: number;
|
||||||
|
}
|
||||||
|
|
61
src/plugins/axios.js
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
import Vue from "vue";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
// Full config: https://github.com/axios/axios#request-config
|
||||||
|
// axios.defaults.baseURL = process.env.baseURL || process.env.apiUrl || '';
|
||||||
|
// axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
|
||||||
|
// axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
// baseURL: process.env.baseURL || process.env.apiUrl || ""
|
||||||
|
// timeout: 60 * 1000, // Timeout
|
||||||
|
// withCredentials: true, // Check cross-site Access-Control
|
||||||
|
};
|
||||||
|
|
||||||
|
const _axios = axios.create(config);
|
||||||
|
|
||||||
|
_axios.interceptors.request.use(
|
||||||
|
function(config) {
|
||||||
|
// Do something before request is sent
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
// Do something with request error
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add a response interceptor
|
||||||
|
_axios.interceptors.response.use(
|
||||||
|
function(response) {
|
||||||
|
// Do something with response data
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
function(error) {
|
||||||
|
// Do something with response error
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
Plugin.install = function(Vue, options) {
|
||||||
|
Vue.axios = _axios;
|
||||||
|
window.axios = _axios;
|
||||||
|
Object.defineProperties(Vue.prototype, {
|
||||||
|
axios: {
|
||||||
|
get() {
|
||||||
|
return _axios;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
$axios: {
|
||||||
|
get() {
|
||||||
|
return _axios;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
Vue.use(Plugin);
|
||||||
|
|
||||||
|
export default Plugin;
|
|
@ -1,3 +1,4 @@
|
||||||
export * from "./MockDataConnection";
|
export * from "./MockDataConnection";
|
||||||
export * from "./MockPeer";
|
export * from "./MockPeer";
|
||||||
export * from "./MockHelper";
|
export * from "./MockHelper";
|
||||||
|
export * from "./EventHook";
|
||||||
|
|
16
src/tests/unit/cards.spec.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
import { Card, createPonyheadURL, cardFullName } from "@/mlpccg";
|
||||||
|
|
||||||
|
describe("mlpccg/cards", () => {
|
||||||
|
test("Card full names are correctly generated in all cases", () => {
|
||||||
|
const card1 = { Name: "Name", Subname: "" };
|
||||||
|
const card2 = { Name: "Name1", Subname: "the Name2" };
|
||||||
|
expect(cardFullName(card1 as Card)).toEqual("Name");
|
||||||
|
expect(cardFullName(card2 as Card)).toEqual("Name1, the Name2");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Ponyhead URL is generated correctly", () => {
|
||||||
|
const cards: any[] = [{ ID: "pr10" }, { ID: "pr12" }, { ID: "pr13" }];
|
||||||
|
const url = "https://ponyhead.com/deckbuilder?v1code=pr10x1-pr12x1-pr13x1";
|
||||||
|
expect(createPonyheadURL(cards!)).toEqual(url);
|
||||||
|
});
|
||||||
|
});
|
37
src/tests/unit/components/CardPicker.spec.ts
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
import CardPicker from "@/components/DeckBuilder/CardPicker.vue";
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
|
||||||
|
// Generate 10 test cards
|
||||||
|
const testCards = new Array(10)
|
||||||
|
.fill("test")
|
||||||
|
.map((t, i) => ({ ID: `${t}${i}` }));
|
||||||
|
const testSlots = testCards.map(c => ({ data: c, limit: 3, howmany: 1 }));
|
||||||
|
|
||||||
|
describe("components/DeckBuilder/CardPicker", () => {
|
||||||
|
test("CardPicker correctly creates images for each card", () => {
|
||||||
|
const wrapper = shallowMount(CardPicker, {
|
||||||
|
propsData: {
|
||||||
|
rows: 2,
|
||||||
|
columns: 5,
|
||||||
|
cards: testSlots
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const cards = wrapper.findAll(".ccgcard");
|
||||||
|
expect(cards.contains("img")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("CardPicker correctly aligns items in a grid", () => {
|
||||||
|
const wrapper = shallowMount(CardPicker, {
|
||||||
|
propsData: {
|
||||||
|
rows: 3,
|
||||||
|
columns: 5,
|
||||||
|
cards: testSlots
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const section = wrapper.find(".cardpicker");
|
||||||
|
const style = section.attributes("style");
|
||||||
|
expect(style).toMatch(
|
||||||
|
/grid-template-rows: \S+ \S+ \S+; grid-template-columns: \S+ \S+ \S+ \S+ \S+;/i
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
36
src/tests/unit/components/DeckList.spec.ts
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
import DeckList from "@/components/DeckBuilder/DeckList.vue";
|
||||||
|
import { shallowMount } from "@vue/test-utils";
|
||||||
|
import { colorNames } from "@/mlpccg";
|
||||||
|
|
||||||
|
// Generate 10 test cards
|
||||||
|
const testCards = new Array(3).fill("test").map((t, i) => ({
|
||||||
|
ID: `test${i}`,
|
||||||
|
Name: `Test name ${i}`,
|
||||||
|
Subname: `Subname ${i}`,
|
||||||
|
Type: "Friend",
|
||||||
|
Element: [colorNames[i]],
|
||||||
|
Power: i,
|
||||||
|
Cost: i,
|
||||||
|
Requirement: { Generosity: i }
|
||||||
|
}));
|
||||||
|
const testSlots = testCards.map((c, i) => ({ data: c, limit: 3, howmany: i }));
|
||||||
|
|
||||||
|
describe("components/DeckBuilder/DeckList", () => {
|
||||||
|
test("DeckList correctly detects card info", () => {
|
||||||
|
const wrapper = shallowMount(DeckList, {
|
||||||
|
propsData: {
|
||||||
|
cards: testSlots
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const cards = wrapper.findAll(".ccgcard");
|
||||||
|
expect(cards.contains(".fullname")).toBe(true);
|
||||||
|
for (let index = 0; index < testSlots.length; index++) {
|
||||||
|
const item = cards.at(index);
|
||||||
|
const card = testSlots[index];
|
||||||
|
expect(item.find(".amt").text()).toEqual(`${card.howmany}`);
|
||||||
|
expect(item.find(".fullname .name").text()).toEqual(card.data.Name);
|
||||||
|
expect(item.find(".fullname .subname").text()).toEqual(card.data.Subname);
|
||||||
|
//TODO Add more fields check as they are added
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
39
src/tests/unit/database.spec.ts
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { loadSets, getCards, Database, initDB, cardFullName } from "@/mlpccg";
|
||||||
|
import Dexie from "dexie";
|
||||||
|
const setGlobalVars = require("indexeddbshim");
|
||||||
|
setGlobalVars(Dexie.dependencies);
|
||||||
|
|
||||||
|
describe("mlpccg/Database", () => {
|
||||||
|
beforeAll(async () => {
|
||||||
|
jest.setTimeout(15000);
|
||||||
|
initDB();
|
||||||
|
await loadSets();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getCards without a filter returns all the cards", async () => {
|
||||||
|
expect(Database).toBeTruthy();
|
||||||
|
const allCards = await Database!.cards.count();
|
||||||
|
const filtered = await getCards({});
|
||||||
|
expect(filtered).toHaveLength(allCards);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getCards with a primary filter filters card correctly", async () => {
|
||||||
|
expect(Database).toBeTruthy();
|
||||||
|
const filtered = await getCards({
|
||||||
|
Types: ["Troublemaker"]
|
||||||
|
});
|
||||||
|
for (const card of filtered) {
|
||||||
|
expect(card.Type).toBe("Troublemaker");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test("getCards with a secondary filter filters card correctly", async () => {
|
||||||
|
expect(Database).toBeTruthy();
|
||||||
|
const filtered = await getCards({
|
||||||
|
Name: "Rainbow Dash"
|
||||||
|
});
|
||||||
|
for (const card of filtered) {
|
||||||
|
expect(cardFullName(card).indexOf("Rainbow Dash") >= 0).toBeTruthy();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,6 +1,5 @@
|
||||||
import { MockHelper } from "@/testing";
|
import { MockHelper, EventHook } from "@/testing";
|
||||||
import { NetworkMessage, LocalClient, ChatMessage } from "@/network";
|
import { NetworkMessage, LocalClient, ChatMessage } from "@/network";
|
||||||
import { EventHook } from "@/testing/EventHook";
|
|
||||||
|
|
||||||
const sampleRoom = () => ({
|
const sampleRoom = () => ({
|
||||||
max_players: 3,
|
max_players: 3,
|
||||||
|
|
|
@ -1,14 +1,470 @@
|
||||||
<template>
|
<template>
|
||||||
<section class="deckbuilder"></section>
|
<section class="deckbuilder">
|
||||||
|
<section class="cardlist">
|
||||||
|
<section class="filters">
|
||||||
|
<div class="row">
|
||||||
|
<b-input
|
||||||
|
@input="textChanged"
|
||||||
|
v-model="nameFilter"
|
||||||
|
placeholder="Search name"
|
||||||
|
></b-input>
|
||||||
|
<div class="colorfilter" v-for="color in colors" :key="color">
|
||||||
|
<img
|
||||||
|
@click="toggleFilter(elementFilters, color)"
|
||||||
|
:class="filterIconClass(elementFilters, color)"
|
||||||
|
:src="elementIconURL(color)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="divider" />
|
||||||
|
<div class="typefilter" v-for="type in types" :key="type">
|
||||||
|
<img
|
||||||
|
@click="toggleFilter(typeFilters, type)"
|
||||||
|
:class="filterIconClass(typeFilters, type)"
|
||||||
|
:src="typeIconURL(type)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<b-input
|
||||||
|
@input="textChanged"
|
||||||
|
v-model="ruleFilter"
|
||||||
|
placeholder="Search rule text"
|
||||||
|
></b-input>
|
||||||
|
<div class="setfilter" v-for="set in sets" :key="set">
|
||||||
|
<img
|
||||||
|
@click="toggleFilter(setFilters, set)"
|
||||||
|
:class="filterIconClass(setFilters, set)"
|
||||||
|
:src="setIconURL(set)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="cards">
|
||||||
|
<div @click="prevPage" :class="canGoPrev ? 'prev' : 'prev unavailable'">
|
||||||
|
<img src="../assets/images/deckbuilder/navarrow.svg" />
|
||||||
|
</div>
|
||||||
|
<CardPicker
|
||||||
|
@picked="cardPicked"
|
||||||
|
:columns="columns"
|
||||||
|
:rows="rows"
|
||||||
|
:cards="currentPage"
|
||||||
|
/>
|
||||||
|
<div @click="nextPage" :class="canGoNext ? 'next' : 'next unavailable'">
|
||||||
|
<img src="../assets/images/deckbuilder/navarrow.svg" />
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
|
<section class="decklist">
|
||||||
|
<header>
|
||||||
|
<h1>{{ deckname }}</h1>
|
||||||
|
<nav class="buttons">
|
||||||
|
<b-button @click="exportToPonyhead" size="is-small deck-btn"
|
||||||
|
>Ponyhead</b-button
|
||||||
|
>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<DeckList :cards="decklist" @removed="cardRemoved" />
|
||||||
|
</section>
|
||||||
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped>
|
||||||
|
@import "@/assets/scss/_variables.scss";
|
||||||
|
|
||||||
|
.deckbuilder {
|
||||||
|
background: url("../assets/images/backgrounds/deckbuilderbg.webp") center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
height: 100vh;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 3fr minmax(250px, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cardlist {
|
||||||
|
display: grid;
|
||||||
|
grid-column: 1;
|
||||||
|
grid-template-rows: 110px 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
padding: 5px;
|
||||||
|
.row {
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
* {
|
||||||
|
margin: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 5% 1fr 5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.decklist {
|
||||||
|
padding: 10px;
|
||||||
|
padding-top: 15px;
|
||||||
|
grid-column: 2;
|
||||||
|
header {
|
||||||
|
padding-left: 1rem;
|
||||||
|
color: $primary-text;
|
||||||
|
font-family: $fantasy;
|
||||||
|
h1 {
|
||||||
|
font-size: 20pt;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorfilter,
|
||||||
|
.setfilter,
|
||||||
|
.typefilter {
|
||||||
|
cursor: pointer;
|
||||||
|
img {
|
||||||
|
opacity: 0.4;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
&.selected {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.colorfilter {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setfilter {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.typefilter {
|
||||||
|
width: 42px;
|
||||||
|
height: 42px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.prev,
|
||||||
|
.next {
|
||||||
|
opacity: 0.5;
|
||||||
|
display: flex;
|
||||||
|
cursor: pointer;
|
||||||
|
&:hover {
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
&.unavailable {
|
||||||
|
opacity: 0.1;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.next img {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.deck-btn {
|
||||||
|
padding: 0 10px;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { Component, Vue } from "vue-property-decorator";
|
import { Component, Vue } from "vue-property-decorator";
|
||||||
|
import DeckList from "@/components/DeckBuilder/DeckList.vue";
|
||||||
|
import CardPicker from "@/components/DeckBuilder/CardPicker.vue";
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardFilter,
|
||||||
|
CardSlot,
|
||||||
|
getCards,
|
||||||
|
allSets,
|
||||||
|
cardFullName,
|
||||||
|
createPonyheadURL,
|
||||||
|
multiElemStr,
|
||||||
|
typeIndex,
|
||||||
|
rarityIndex,
|
||||||
|
colorNames,
|
||||||
|
typeNames,
|
||||||
|
cardLimit
|
||||||
|
} from "@/mlpccg";
|
||||||
|
|
||||||
|
// Sort function for sorting cards
|
||||||
|
function sortByColor(a: Card, b: Card) {
|
||||||
|
const typeA = typeIndex(a.Type);
|
||||||
|
const typeB = typeIndex(b.Type);
|
||||||
|
if (typeA != typeB) {
|
||||||
|
return typeA - typeB;
|
||||||
|
}
|
||||||
|
// Same types, filter by primary element
|
||||||
|
switch (a.Type) {
|
||||||
|
case "Friend":
|
||||||
|
case "Mane Character":
|
||||||
|
{
|
||||||
|
const elemA = multiElemStr(a.Element);
|
||||||
|
const elemB = multiElemStr(b.Element);
|
||||||
|
if (elemA > elemB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (elemB > elemA) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Problem":
|
||||||
|
if (a.ProblemRequirement && b.ProblemRequirement) {
|
||||||
|
const preqA = multiElemStr(Object.keys(a.ProblemRequirement));
|
||||||
|
const preqB = multiElemStr(Object.keys(b.ProblemRequirement));
|
||||||
|
if (preqA > preqB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (preqB > preqA) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case "Event":
|
||||||
|
case "Resource":
|
||||||
|
if (a.Requirement && b.Requirement) {
|
||||||
|
const reqA = multiElemStr(Object.keys(a.Requirement));
|
||||||
|
const reqB = multiElemStr(Object.keys(b.Requirement));
|
||||||
|
if (reqA > reqB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (reqB > reqA) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Filter by power
|
||||||
|
if (a.Power && b.Power) {
|
||||||
|
if (a.Power != b.Power) {
|
||||||
|
return a.Power - b.Power;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by Rarity (not the pony)
|
||||||
|
const rarityA = rarityIndex(a.Rarity);
|
||||||
|
const rarityB = rarityIndex(b.Rarity);
|
||||||
|
if (rarityA != rarityB) {
|
||||||
|
return rarityA - rarityB;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nameA = cardFullName(a);
|
||||||
|
const nameB = cardFullName(b);
|
||||||
|
if (nameA > nameB) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
if (nameB > nameA) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
components: {}
|
components: {
|
||||||
|
DeckList,
|
||||||
|
CardPicker
|
||||||
|
}
|
||||||
})
|
})
|
||||||
export default class DeckBuilder extends Vue {}
|
export default class DeckBuilder extends Vue {
|
||||||
|
// Picked/filtered cards
|
||||||
|
private decklist!: CardSlot[];
|
||||||
|
private filtered!: Card[];
|
||||||
|
|
||||||
|
// Names
|
||||||
|
private colors!: string[];
|
||||||
|
private sets!: string[];
|
||||||
|
private types!: string[];
|
||||||
|
|
||||||
|
// Card picker size
|
||||||
|
private rows!: number;
|
||||||
|
private columns!: number;
|
||||||
|
|
||||||
|
// User Filters
|
||||||
|
private nameFilter!: string;
|
||||||
|
private ruleFilter!: string;
|
||||||
|
private setFilters!: string[];
|
||||||
|
private elementFilters!: string[];
|
||||||
|
private typeFilters!: string[];
|
||||||
|
|
||||||
|
// Decklist options
|
||||||
|
private deckname!: string;
|
||||||
|
|
||||||
|
// Navigation
|
||||||
|
private offset!: number;
|
||||||
|
|
||||||
|
private data() {
|
||||||
|
return {
|
||||||
|
decklist: [],
|
||||||
|
filtered: [],
|
||||||
|
offset: 0,
|
||||||
|
rows: 2,
|
||||||
|
columns: 5,
|
||||||
|
nameFilter: "",
|
||||||
|
ruleFilter: "",
|
||||||
|
setFilters: [],
|
||||||
|
elementFilters: [],
|
||||||
|
typeFilters: [],
|
||||||
|
colors: colorNames,
|
||||||
|
sets: allSets.slice(0, -1),
|
||||||
|
types: typeNames,
|
||||||
|
deckname: "Unnamed deck"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private mounted() {
|
||||||
|
this.applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async applyFilters() {
|
||||||
|
let filters: CardFilter = {};
|
||||||
|
if (this.setFilters.length > 0) {
|
||||||
|
filters.Sets = this.setFilters;
|
||||||
|
}
|
||||||
|
if (this.elementFilters.length > 0) {
|
||||||
|
filters.Elements = this.elementFilters;
|
||||||
|
}
|
||||||
|
if (this.typeFilters.length > 0) {
|
||||||
|
filters.Types = this.typeFilters;
|
||||||
|
}
|
||||||
|
if (this.nameFilter.length > 0) {
|
||||||
|
filters.Name = this.nameFilter;
|
||||||
|
}
|
||||||
|
if (this.ruleFilter.length > 0) {
|
||||||
|
filters.Rules = this.ruleFilter;
|
||||||
|
}
|
||||||
|
const filtered = await getCards(filters);
|
||||||
|
this.filtered = filtered.sort(sortByColor);
|
||||||
|
this.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get itemsPerPage() {
|
||||||
|
return this.columns * this.rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get currentPage(): CardSlot[] {
|
||||||
|
return this.filtered
|
||||||
|
.slice(this.offset, this.offset + this.itemsPerPage)
|
||||||
|
.map(card => {
|
||||||
|
const res = this.decklist.find(c => c.data.ID == card.ID);
|
||||||
|
if (res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
data: card,
|
||||||
|
limit: cardLimit(card.Type),
|
||||||
|
howmany: 0
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private elementIconURL(element: string): string {
|
||||||
|
return require(`../assets/images/elements/${element.toLowerCase()}.webp`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private setIconURL(set: string): string {
|
||||||
|
return require(`../assets/images/sets/${set.toUpperCase()}.webp`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private typeIconURL(type: string): string {
|
||||||
|
let urltype = type.toLowerCase();
|
||||||
|
if (urltype == "mane character") {
|
||||||
|
urltype = "mane-char";
|
||||||
|
}
|
||||||
|
return require(`../assets/images/cardtypes/${urltype}.webp`);
|
||||||
|
}
|
||||||
|
|
||||||
|
private filterIconClass(filter: string[], key: string) {
|
||||||
|
return {
|
||||||
|
selected: filter.includes(key)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private toggleFilter(filter: string[], key: string) {
|
||||||
|
const idx = filter.indexOf(key);
|
||||||
|
if (idx >= 0) {
|
||||||
|
filter.splice(idx, 1);
|
||||||
|
} else {
|
||||||
|
filter.push(key);
|
||||||
|
}
|
||||||
|
this.applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
private textChanged() {
|
||||||
|
this.applyFilters();
|
||||||
|
}
|
||||||
|
|
||||||
|
private get canGoPrev(): boolean {
|
||||||
|
return this.offset > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private get canGoNext(): boolean {
|
||||||
|
return this.offset + this.itemsPerPage < this.filtered.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
private prevPage() {
|
||||||
|
if (!this.canGoPrev) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.offset = Math.max(0, this.offset - this.itemsPerPage);
|
||||||
|
}
|
||||||
|
|
||||||
|
private nextPage() {
|
||||||
|
if (!this.canGoNext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.offset = Math.min(
|
||||||
|
this.filtered.length - this.itemsPerPage,
|
||||||
|
this.offset + this.itemsPerPage
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private cardPicked(card: Card) {
|
||||||
|
// Check if card is already in
|
||||||
|
const idx = this.decklist.findIndex(c => c.data.ID == card.ID);
|
||||||
|
if (idx >= 0) {
|
||||||
|
const deckitem = this.decklist[idx];
|
||||||
|
deckitem.howmany += 1;
|
||||||
|
Vue.set(this.decklist, idx, deckitem);
|
||||||
|
} else {
|
||||||
|
this.decklist.push({
|
||||||
|
data: card,
|
||||||
|
limit: cardLimit(card.Type),
|
||||||
|
howmany: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private exportToPonyhead() {
|
||||||
|
const url = createPonyheadURL(this.decklist.map(c => c.data));
|
||||||
|
window.open(url, "_blank");
|
||||||
|
}
|
||||||
|
|
||||||
|
private cardRemoved(card: CardSlot) {
|
||||||
|
const idx = this.decklist.findIndex(c => c.data.ID == card.data.ID);
|
||||||
|
if (idx < 0) {
|
||||||
|
throw new Error("Removing card that isn't in the deck?");
|
||||||
|
}
|
||||||
|
const deckitem = this.decklist[idx];
|
||||||
|
deckitem.howmany -= 1;
|
||||||
|
if (deckitem.howmany <= 0) {
|
||||||
|
this.decklist.splice(idx, 1);
|
||||||
|
} else {
|
||||||
|
Vue.set(this.decklist, idx, deckitem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -3,8 +3,12 @@
|
||||||
<section class="playerlist">
|
<section class="playerlist">
|
||||||
<b>Players</b>
|
<b>Players</b>
|
||||||
</section>
|
</section>
|
||||||
<section class="pool"><b>Card pool</b></section>
|
<section class="pool">
|
||||||
<section class="cardlist"><b>Cards</b></section>
|
<b>Card pool</b>
|
||||||
|
</section>
|
||||||
|
<section class="cardlist">
|
||||||
|
<b>Cards</b>
|
||||||
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -12,8 +16,8 @@
|
||||||
.draftview {
|
.draftview {
|
||||||
display: grid;
|
display: grid;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
grid-gap: 10px;
|
gap: 10px;
|
||||||
grid-template-columns: 200px 1fr 250px;
|
grid-template-columns: minmax(200px, 1fr) 3fr minmax(250px, 1fr);
|
||||||
section {
|
section {
|
||||||
grid-row: 1;
|
grid-row: 1;
|
||||||
border: 1px solid #555;
|
border: 1px solid #555;
|
||||||
|
|
164
yarn.lock
|
@ -573,6 +573,14 @@
|
||||||
"@babel/helper-regex" "^7.4.4"
|
"@babel/helper-regex" "^7.4.4"
|
||||||
regexpu-core "^4.5.4"
|
regexpu-core "^4.5.4"
|
||||||
|
|
||||||
|
"@babel/polyfill@^7.0.0":
|
||||||
|
version "7.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.6.0.tgz#6d89203f8b6cd323e8d946e47774ea35dc0619cc"
|
||||||
|
integrity sha512-q5BZJI0n/B10VaQQvln1IlDK3BTBJFbADx7tv+oXDPIDZuTo37H5Adb9jhlXm/fEN4Y7/64qD9mnrJJG7rmaTw==
|
||||||
|
dependencies:
|
||||||
|
core-js "^2.6.5"
|
||||||
|
regenerator-runtime "^0.13.2"
|
||||||
|
|
||||||
"@babel/preset-env@^7.0.0 < 7.4.0":
|
"@babel/preset-env@^7.0.0 < 7.4.0":
|
||||||
version "7.3.4"
|
version "7.3.4"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.4.tgz#887cf38b6d23c82f19b5135298bdb160062e33e1"
|
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.3.4.tgz#887cf38b6d23c82f19b5135298bdb160062e33e1"
|
||||||
|
@ -1488,6 +1496,11 @@ argparse@^1.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
sprintf-js "~1.0.2"
|
sprintf-js "~1.0.2"
|
||||||
|
|
||||||
|
argsarray@^0.0.1:
|
||||||
|
version "0.0.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/argsarray/-/argsarray-0.0.1.tgz#6e7207b4ecdb39b0af88303fa5ae22bda8df61cb"
|
||||||
|
integrity sha1-bnIHtOzbObCviDA/pa4ivajfYcs=
|
||||||
|
|
||||||
arr-diff@^2.0.0:
|
arr-diff@^2.0.0:
|
||||||
version "2.0.0"
|
version "2.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
|
resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf"
|
||||||
|
@ -1656,6 +1669,14 @@ aws4@^1.8.0:
|
||||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
|
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
|
||||||
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
|
integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
|
||||||
|
|
||||||
|
axios@^0.18.0:
|
||||||
|
version "0.18.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3"
|
||||||
|
integrity sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==
|
||||||
|
dependencies:
|
||||||
|
follow-redirects "1.5.10"
|
||||||
|
is-buffer "^2.0.2"
|
||||||
|
|
||||||
babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
|
babel-code-frame@^6.22.0, babel-code-frame@^6.26.0:
|
||||||
version "6.26.0"
|
version "6.26.0"
|
||||||
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b"
|
||||||
|
@ -1909,6 +1930,11 @@ balanced-match@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||||
|
|
||||||
|
base64-arraybuffer-es6@0.4.2:
|
||||||
|
version "0.4.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.4.2.tgz#b567d364065843113589b6c1436bd9492701c7fe"
|
||||||
|
integrity sha512-HaJx92u12By863ZXVHZs4Bp1nkKaLpbs3Ec9SI1OKzq60Hz+Ks6z7UvdD8pIx61Ck3e8F9MH/IPEu5T0xKSbkQ==
|
||||||
|
|
||||||
base64-js@^1.0.2:
|
base64-js@^1.0.2:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||||
|
@ -2199,6 +2225,11 @@ builtin-status-codes@^3.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
||||||
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
|
integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=
|
||||||
|
|
||||||
|
bulma-prefers-dark@^0.1.0-beta.0:
|
||||||
|
version "0.1.0-beta.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/bulma-prefers-dark/-/bulma-prefers-dark-0.1.0-beta.0.tgz#646350738ed00ac66d0f84ec6821a677aa1a66c5"
|
||||||
|
integrity sha512-EeDW8pQrkYEOXo2l3WykfghbUzi8jlQWGI+Cu2HwmXwQHMcoGF6yiKYCNShttN+8z3atq8fLWh3B7pqXUV4fBA==
|
||||||
|
|
||||||
bulma@0.7.5:
|
bulma@0.7.5:
|
||||||
version "0.7.5"
|
version "0.7.5"
|
||||||
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.7.5.tgz#35066c37f82c088b68f94450be758fc00a967208"
|
resolved "https://registry.yarnpkg.com/bulma/-/bulma-0.7.5.tgz#35066c37f82c088b68f94450be758fc00a967208"
|
||||||
|
@ -3169,6 +3200,13 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8, debug@^2.6.9:
|
||||||
dependencies:
|
dependencies:
|
||||||
ms "2.0.0"
|
ms "2.0.0"
|
||||||
|
|
||||||
|
debug@=3.1.0:
|
||||||
|
version "3.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261"
|
||||||
|
integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==
|
||||||
|
dependencies:
|
||||||
|
ms "2.0.0"
|
||||||
|
|
||||||
debug@^3.0.0, debug@^3.1.0, debug@^3.2.5, debug@^3.2.6:
|
debug@^3.0.0, debug@^3.1.0, debug@^3.2.5, debug@^3.2.6:
|
||||||
version "3.2.6"
|
version "3.2.6"
|
||||||
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.6.tgz#e83d17de16d8a7efb7717edbe5fb10135eee629b"
|
||||||
|
@ -3906,6 +3944,13 @@ eventsource@^1.0.7:
|
||||||
dependencies:
|
dependencies:
|
||||||
original "^1.0.0"
|
original "^1.0.0"
|
||||||
|
|
||||||
|
eventtargeter@0.5.0:
|
||||||
|
version "0.5.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/eventtargeter/-/eventtargeter-0.5.0.tgz#ab5e05cc7d96bef6a05a0a4a7053bf8fb7621ba7"
|
||||||
|
integrity sha512-FQbP+ToTYLKEF3VpyaciNbaexbvIOrXW1V1Hg7kKCT+AiX6sq8rUn1NIQiYEpA04eWzHpopH/QRHqm3K2KnLtQ==
|
||||||
|
dependencies:
|
||||||
|
"@babel/polyfill" "^7.0.0"
|
||||||
|
|
||||||
evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
|
evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3:
|
||||||
version "1.0.3"
|
version "1.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
|
resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02"
|
||||||
|
@ -4377,6 +4422,13 @@ flush-write-stream@^1.0.0:
|
||||||
inherits "^2.0.3"
|
inherits "^2.0.3"
|
||||||
readable-stream "^2.3.6"
|
readable-stream "^2.3.6"
|
||||||
|
|
||||||
|
follow-redirects@1.5.10:
|
||||||
|
version "1.5.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.5.10.tgz#7b7a9f9aea2fdff36786a94ff643ed07f4ff5e2a"
|
||||||
|
integrity sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==
|
||||||
|
dependencies:
|
||||||
|
debug "=3.1.0"
|
||||||
|
|
||||||
follow-redirects@^1.0.0:
|
follow-redirects@^1.0.0:
|
||||||
version "1.8.1"
|
version "1.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.8.1.tgz#24804f9eaab67160b0e840c085885d606371a35b"
|
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.8.1.tgz#24804f9eaab67160b0e840c085885d606371a35b"
|
||||||
|
@ -5081,6 +5133,11 @@ ignore@^4.0.3, ignore@^4.0.6:
|
||||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
resolved "https://registry.yarnpkg.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc"
|
||||||
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
integrity sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==
|
||||||
|
|
||||||
|
immediate@^3.2.2:
|
||||||
|
version "3.2.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c"
|
||||||
|
integrity sha1-0UD6j2FGWb1lQSMwl92qwlzdmRw=
|
||||||
|
|
||||||
import-cwd@^2.0.0:
|
import-cwd@^2.0.0:
|
||||||
version "2.1.0"
|
version "2.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
|
resolved "https://registry.yarnpkg.com/import-cwd/-/import-cwd-2.1.0.tgz#aa6cf36e722761285cb371ec6519f53e2435b0a9"
|
||||||
|
@ -5144,6 +5201,18 @@ indent-string@^2.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
repeating "^2.0.0"
|
repeating "^2.0.0"
|
||||||
|
|
||||||
|
indexeddbshim@^4.1.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/indexeddbshim/-/indexeddbshim-4.1.0.tgz#6fffc99a2e302445c9df7b3366ef072c9925225a"
|
||||||
|
integrity sha512-gnhy0Fz1fWU9pnIo16uKC9dGimsv/vKlXzZ9zasN2EUkx/KxtHkCIfR8I1XRqujsuTelQmn1/34RpDFycNVxtw==
|
||||||
|
dependencies:
|
||||||
|
"@babel/polyfill" "^7.0.0"
|
||||||
|
eventtargeter "0.5.0"
|
||||||
|
sync-promise "git+https://github.com/brettz9/sync-promise.git#full-sync-missing-promise-features"
|
||||||
|
typeson "5.11.0"
|
||||||
|
typeson-registry "1.0.0-alpha.26"
|
||||||
|
websql "git+https://github.com/brettz9/node-websql.git#configurable-secure2"
|
||||||
|
|
||||||
indexes-of@^1.0.1:
|
indexes-of@^1.0.1:
|
||||||
version "1.0.1"
|
version "1.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
|
resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607"
|
||||||
|
@ -5343,6 +5412,11 @@ is-buffer@^1.1.5:
|
||||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||||
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
||||||
|
|
||||||
|
is-buffer@^2.0.2:
|
||||||
|
version "2.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.3.tgz#4ecf3fcf749cbd1e472689e109ac66261a25e725"
|
||||||
|
integrity sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw==
|
||||||
|
|
||||||
is-callable@^1.1.4:
|
is-callable@^1.1.4:
|
||||||
version "1.1.4"
|
version "1.1.4"
|
||||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
|
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.4.tgz#1e1adf219e1eeb684d691f9d6a05ff0d30a24d75"
|
||||||
|
@ -7092,6 +7166,22 @@ node-notifier@^5.2.1:
|
||||||
shellwords "^0.1.1"
|
shellwords "^0.1.1"
|
||||||
which "^1.3.0"
|
which "^1.3.0"
|
||||||
|
|
||||||
|
node-pre-gyp@^0.11.0:
|
||||||
|
version "0.11.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.11.0.tgz#db1f33215272f692cd38f03238e3e9b47c5dd054"
|
||||||
|
integrity sha512-TwWAOZb0j7e9eGaf9esRx3ZcLaE5tQ2lvYy1pb5IAaG1a2e2Kv5Lms1Y4hpj+ciXJRofIxxlt5haeQ/2ANeE0Q==
|
||||||
|
dependencies:
|
||||||
|
detect-libc "^1.0.2"
|
||||||
|
mkdirp "^0.5.1"
|
||||||
|
needle "^2.2.1"
|
||||||
|
nopt "^4.0.1"
|
||||||
|
npm-packlist "^1.1.6"
|
||||||
|
npmlog "^4.0.2"
|
||||||
|
rc "^1.2.7"
|
||||||
|
rimraf "^2.6.1"
|
||||||
|
semver "^5.3.0"
|
||||||
|
tar "^4"
|
||||||
|
|
||||||
node-pre-gyp@^0.12.0:
|
node-pre-gyp@^0.12.0:
|
||||||
version "0.12.0"
|
version "0.12.0"
|
||||||
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149"
|
resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.12.0.tgz#39ba4bb1439da030295f899e3b520b7785766149"
|
||||||
|
@ -7138,6 +7228,11 @@ node-sass@^4.9.0:
|
||||||
stdout-stream "^1.4.0"
|
stdout-stream "^1.4.0"
|
||||||
"true-case-path" "^1.0.2"
|
"true-case-path" "^1.0.2"
|
||||||
|
|
||||||
|
noop-fn@^1.0.0:
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/noop-fn/-/noop-fn-1.0.0.tgz#5f33d47f13d2150df93e0cb036699e982f78ffbf"
|
||||||
|
integrity sha1-XzPUfxPSFQ35PgywNmmemC94/78=
|
||||||
|
|
||||||
"nopt@2 || 3":
|
"nopt@2 || 3":
|
||||||
version "3.0.6"
|
version "3.0.6"
|
||||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
|
resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9"
|
||||||
|
@ -9381,6 +9476,15 @@ sprintf-js@~1.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
|
||||||
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=
|
||||||
|
|
||||||
|
sqlite3@^4.0.0:
|
||||||
|
version "4.1.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/sqlite3/-/sqlite3-4.1.0.tgz#e051fb9c133be15726322a69e2e37ec560368380"
|
||||||
|
integrity sha512-RvqoKxq+8pDHsJo7aXxsFR18i+dU2Wp5o12qAJOV5LNcDt+fgJsc2QKKg3sIRfXrN9ZjzY1T7SNe/DFVqAXjaw==
|
||||||
|
dependencies:
|
||||||
|
nan "^2.12.1"
|
||||||
|
node-pre-gyp "^0.11.0"
|
||||||
|
request "^2.87.0"
|
||||||
|
|
||||||
sshpk@^1.7.0:
|
sshpk@^1.7.0:
|
||||||
version "1.16.1"
|
version "1.16.1"
|
||||||
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
|
resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877"
|
||||||
|
@ -9694,6 +9798,10 @@ symbol-tree@^3.2.2:
|
||||||
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
|
||||||
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
|
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
|
||||||
|
|
||||||
|
"sync-promise@git+https://github.com/brettz9/sync-promise.git#full-sync-missing-promise-features":
|
||||||
|
version "1.0.1"
|
||||||
|
resolved "git+https://github.com/brettz9/sync-promise.git#25845a49a00aa2d2c985a5149b97c86a1fcdc75a"
|
||||||
|
|
||||||
table@4.0.2:
|
table@4.0.2:
|
||||||
version "4.0.2"
|
version "4.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
|
resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36"
|
||||||
|
@ -9841,6 +9949,11 @@ timsort@^0.3.0:
|
||||||
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
|
resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4"
|
||||||
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q=
|
||||||
|
|
||||||
|
tiny-queue@^0.2.1:
|
||||||
|
version "0.2.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/tiny-queue/-/tiny-queue-0.2.1.tgz#25a67f2c6e253b2ca941977b5ef7442ef97a6046"
|
||||||
|
integrity sha1-JaZ/LG4lOyypQZd7XvdELvl6YEY=
|
||||||
|
|
||||||
tmp@^0.0.33:
|
tmp@^0.0.33:
|
||||||
version "0.0.33"
|
version "0.0.33"
|
||||||
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
|
||||||
|
@ -10083,6 +10196,21 @@ typescript@^3.4.3:
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.2.tgz#105b0f1934119dde543ac8eb71af3a91009efe54"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.2.tgz#105b0f1934119dde543ac8eb71af3a91009efe54"
|
||||||
integrity sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==
|
integrity sha512-lmQ4L+J6mnu3xweP8+rOrUwzmN+MRAj7TgtJtDaXE5PMyX2kCrklhg3rvOsOIfNeAWMQWO2F1GPc1kMD2vLAfw==
|
||||||
|
|
||||||
|
typeson-registry@1.0.0-alpha.26:
|
||||||
|
version "1.0.0-alpha.26"
|
||||||
|
resolved "https://registry.yarnpkg.com/typeson-registry/-/typeson-registry-1.0.0-alpha.26.tgz#d1f337584196c5d5d112ad981e0dbbd2ced30c30"
|
||||||
|
integrity sha512-R0wwXIYSiJMh+1XfvyUsCnEGVERoJcNrMl9e/ka30dJ+gQyh4/0NU9WHaqUm8oHtZzZYCz+A5fDRCiXYIq7H1Q==
|
||||||
|
dependencies:
|
||||||
|
base64-arraybuffer-es6 "0.4.2"
|
||||||
|
typeson "5.11.0"
|
||||||
|
uuid "3.3.2"
|
||||||
|
whatwg-url "7.0.0"
|
||||||
|
|
||||||
|
typeson@5.11.0:
|
||||||
|
version "5.11.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/typeson/-/typeson-5.11.0.tgz#a8273f00050be9eeef974aaa04a0c95a394f821a"
|
||||||
|
integrity sha512-S5KtLzcU4dr4BXh8VuJDYugsRGsDQYlumCbrmwuAX1a1GNpbVYK4p9wluCIfTVPFvVyV6wRfExXX6Q1+YDItEQ==
|
||||||
|
|
||||||
uglify-js@3.4.x:
|
uglify-js@3.4.x:
|
||||||
version "3.4.10"
|
version "3.4.10"
|
||||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
|
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.10.tgz#9ad9563d8eb3acdfb8d38597d2af1d815f6a755f"
|
||||||
|
@ -10268,6 +10396,11 @@ utils-merge@1.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||||
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
|
||||||
|
|
||||||
|
uuid@3.3.2:
|
||||||
|
version "3.3.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
|
||||||
|
integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
|
||||||
|
|
||||||
uuid@^3.0.1, uuid@^3.3.2:
|
uuid@^3.0.1, uuid@^3.3.2:
|
||||||
version "3.3.3"
|
version "3.3.3"
|
||||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
|
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
|
||||||
|
@ -10310,6 +10443,11 @@ vue-class-component@^7.0.1, vue-class-component@^7.0.2:
|
||||||
resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-7.1.0.tgz#b33efcb10e17236d684f70b1e96f1946ec793e87"
|
resolved "https://registry.yarnpkg.com/vue-class-component/-/vue-class-component-7.1.0.tgz#b33efcb10e17236d684f70b1e96f1946ec793e87"
|
||||||
integrity sha512-G9152NzUkz0i0xTfhk0Afc8vzdXxDR1pfN4dTwE72cskkgJtdXfrKBkMfGvDuxUh35U500g5Ve4xL8PEGdWeHg==
|
integrity sha512-G9152NzUkz0i0xTfhk0Afc8vzdXxDR1pfN4dTwE72cskkgJtdXfrKBkMfGvDuxUh35U500g5Ve4xL8PEGdWeHg==
|
||||||
|
|
||||||
|
vue-cli-plugin-axios@^0.0.4:
|
||||||
|
version "0.0.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/vue-cli-plugin-axios/-/vue-cli-plugin-axios-0.0.4.tgz#29d4eb48275c7fe15b92e1fd5d95fbe2a966436f"
|
||||||
|
integrity sha512-p2b/fvPJuPBnvU8027PAAuU5DiOzUn2lku8XLG/f6c8FU0N+/MXWZAlOuHhqd9e7+KIZitwe/c8qlmv7TglbTg==
|
||||||
|
|
||||||
vue-cli-plugin-buefy@^0.3.7:
|
vue-cli-plugin-buefy@^0.3.7:
|
||||||
version "0.3.7"
|
version "0.3.7"
|
||||||
resolved "https://registry.yarnpkg.com/vue-cli-plugin-buefy/-/vue-cli-plugin-buefy-0.3.7.tgz#31e5637529482a5a4564676f539db16278b0895c"
|
resolved "https://registry.yarnpkg.com/vue-cli-plugin-buefy/-/vue-cli-plugin-buefy-0.3.7.tgz#31e5637529482a5a4564676f539db16278b0895c"
|
||||||
|
@ -10619,6 +10757,16 @@ websocket-extensions@>=0.1.1:
|
||||||
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
|
resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.3.tgz#5d2ff22977003ec687a4b87073dfbbac146ccf29"
|
||||||
integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==
|
integrity sha512-nqHUnMXmBzT0w570r2JpJxfiSD1IzoI+HGVdd3aZ0yNi3ngvQ4jv1dtHt5VGxfI2yj5yqImPhOK4vmIh2xMbGg==
|
||||||
|
|
||||||
|
"websql@git+https://github.com/brettz9/node-websql.git#configurable-secure2":
|
||||||
|
version "1.0.0"
|
||||||
|
resolved "git+https://github.com/brettz9/node-websql.git#5149bc0763376ca757fc32dc74345ada0467bfbb"
|
||||||
|
dependencies:
|
||||||
|
argsarray "^0.0.1"
|
||||||
|
immediate "^3.2.2"
|
||||||
|
noop-fn "^1.0.0"
|
||||||
|
sqlite3 "^4.0.0"
|
||||||
|
tiny-queue "^0.2.1"
|
||||||
|
|
||||||
whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
|
whatwg-encoding@^1.0.1, whatwg-encoding@^1.0.3:
|
||||||
version "1.0.5"
|
version "1.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
|
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz#5abacf777c32166a51d085d6b4f3e7d27113ddb0"
|
||||||
|
@ -10631,19 +10779,19 @@ whatwg-mimetype@^2.1.0, whatwg-mimetype@^2.2.0:
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
|
||||||
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
|
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
|
||||||
|
|
||||||
whatwg-url@^6.4.1:
|
whatwg-url@7.0.0, whatwg-url@^7.0.0:
|
||||||
version "6.5.0"
|
version "7.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd"
|
||||||
integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==
|
integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
lodash.sortby "^4.7.0"
|
lodash.sortby "^4.7.0"
|
||||||
tr46 "^1.0.1"
|
tr46 "^1.0.1"
|
||||||
webidl-conversions "^4.0.2"
|
webidl-conversions "^4.0.2"
|
||||||
|
|
||||||
whatwg-url@^7.0.0:
|
whatwg-url@^6.4.1:
|
||||||
version "7.0.0"
|
version "6.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd"
|
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.5.0.tgz#f2df02bff176fd65070df74ad5ccbb5a199965a8"
|
||||||
integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==
|
integrity sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
lodash.sortby "^4.7.0"
|
lodash.sortby "^4.7.0"
|
||||||
tr46 "^1.0.1"
|
tr46 "^1.0.1"
|
||||||
|
|