popup component, decks and cards get string ids, remove decks

This commit is contained in:
koehr 2020-07-05 20:42:22 +02:00
parent 2062beaefb
commit 08025ba9c6
9 changed files with 65 additions and 36 deletions

View file

@ -15,7 +15,7 @@
</select> </select>
<button type="submit">Save deck</button> <button type="submit">Save deck</button>
<button class="cancel" @click.prevent="$emit('close')">cancel</button> <button class="cancel" @click.prevent="$emit('cancel')">cancel</button>
</div> </div>
<DeckCard :deck="{ icon, name, description, color, cardSize, cards: [] }" /> <DeckCard :deck="{ icon, name, description, color, cardSize, cards: [] }" />
@ -47,7 +47,8 @@ export default defineComponent({
} }
}, },
watch: { watch: {
deck (deck, oldDeck) { deck (deck) {
if (deck === undefined) return
this.icon = deck.icon this.icon = deck.icon
this.name = deck.name this.name = deck.name
this.description = deck.description this.description = deck.description

5
src/components/Popup.vue Normal file
View file

@ -0,0 +1,5 @@
<template>
<Teleport to="#popup > .popup-content">
<slot></slot>
</Teleport>
</template>

View file

@ -1,9 +1,10 @@
import { CardSize } from '../consts' import { CardSize } from '../consts'
import { ICard } from '../types' import { ICard } from '../types'
import randomId from './randomId'
export function defaultCard (): ICard { export function defaultCard (): ICard {
return { return {
id: 0, id: randomId(),
name: 'no title yet', name: 'no title yet',
tags: [], tags: [],
icon: 'robe', icon: 'robe',

View file

@ -1,8 +1,9 @@
import { CardSize, PageSize, Arrangement } from '../consts' import { CardSize, PageSize, Arrangement } from '../consts'
import { IDeck } from '../types' import { IDeck } from '../types'
import randomId from './randomId'
export const defaultDeckValues: IDeck = { export const defaultDeckValues: IDeck = {
id: 0, id: '',
icon: 'robe', icon: 'robe',
name: 'the nameless', name: 'the nameless',
description: '', description: '',
@ -15,7 +16,9 @@ export const defaultDeckValues: IDeck = {
} }
export function defaultDeck (): IDeck { export function defaultDeck (): IDeck {
return { ...defaultDeckValues } const newDeck = { ...defaultDeckValues }
newDeck.id = randomId()
return newDeck
} }
export function isValidDeck (deck: any): boolean { export function isValidDeck (deck: any): boolean {

View file

@ -1,6 +1,8 @@
import { Ref } from 'vue' import { Ref } from 'vue'
import { Notification, IDeck, KV } from '../types' import { Notification, State, IDeck, KV } from '../types'
import { defaultDeck, defaultDeckValues } from '../lib/deck' import { defaultDeck } from '../lib/deck'
type Decks = State['decks']
/// actions are called like action['sub/foo'](state.sub, payload) /// actions are called like action['sub/foo'](state.sub, payload)
export default { export default {
@ -23,15 +25,14 @@ export default {
// DECK ACTIONS // DECK ACTIONS
// returns index of newly created deck // returns index of newly created deck
'decks/new' (decks: Ref<IDeck[]>): number { 'decks/new' (decks: Ref<Decks>): string {
const newDeck = defaultDeck() const newDeck = defaultDeck()
const id = decks.value.push(newDeck) - 1 const id = newDeck.id
newDeck.id = id decks.value[id] = newDeck
return newDeck.id
return id
}, },
// updates decks[updatedDeck.id] // updates decks[updatedDeck.id]
'decks/update' (decks: Ref<IDeck[]>, updatedDeck: IDeck): boolean { 'decks/update' (decks: Ref<Decks>, updatedDeck: IDeck): boolean {
const id = updatedDeck.id const id = updatedDeck.id
if (!id || !decks.value[id]) return false // can't update non-existing deck if (!id || !decks.value[id]) return false // can't update non-existing deck
@ -41,6 +42,9 @@ export default {
} }
return true return true
}, },
'decks/remove' (decks: Ref<Decks>, deckId: string) {
delete decks.value[deckId]
},
// POPUP ACTIONS // POPUP ACTIONS
'popup/show' (popup: Ref<boolean>): boolean { 'popup/show' (popup: Ref<boolean>): boolean {

View file

@ -7,7 +7,7 @@ import stateActions from './actions'
const state: State = { const state: State = {
settings: ref({}), settings: ref({}),
decks: ref([]), decks: ref({}),
notifications: ref([]), notifications: ref([]),
icons: ref(['mouth-watering', 'robe', 'thorny-triskelion']), icons: ref(['mouth-watering', 'robe', 'thorny-triskelion']),
popup: ref(false) popup: ref(false)

View file

@ -3,12 +3,12 @@ import { CardSize, Arrangement, PageSize } from './consts'
import { IDeck, ICard } from './types' import { IDeck, ICard } from './types'
interface IDeckTable { interface IDeckTable {
id: number; id: string;
name: string; name: string;
description: string; description: string;
color: string; color: string;
icon: string; icon: string;
cards: number[]; // array of card IDs cards: string[]; // array of card IDs
cardSize: CardSize; cardSize: CardSize;
arrangement: Arrangement; arrangement: Arrangement;
pageSize: PageSize; pageSize: PageSize;
@ -16,8 +16,8 @@ interface IDeckTable {
} }
export class DeckDB extends Dexie { export class DeckDB extends Dexie {
public decks: Dexie.Table<IDeckTable, number> public decks: Dexie.Table<IDeckTable, string>
public cards: Dexie.Table<ICard, number> public cards: Dexie.Table<ICard, string>
public tags: Dexie.Table<string> public tags: Dexie.Table<string>
public constructor () { public constructor () {
@ -25,8 +25,8 @@ export class DeckDB extends Dexie {
console.log('initializing deck db') console.log('initializing deck db')
this.version(1).stores({ this.version(1).stores({
decks: '++id,name', decks: '&id,name',
cards: '++id,name,*tags', cards: '&id,name,*tags',
tags: '&tag' tags: '&tag'
}) })
@ -53,7 +53,7 @@ export class DeckDB extends Dexie {
} }
// add or update card // add or update card
public async putCard (card: ICard, deckId: number) { public async putCard (card: ICard, deckId: string) {
const cardId = await this.cards.put(card) const cardId = await this.cards.put(card)
const deck = await this.decks.get(deckId) const deck = await this.decks.get(deckId)
@ -64,9 +64,9 @@ export class DeckDB extends Dexie {
} }
public async getDecks () { public async getDecks () {
const decks = await this.decks.toArray() const deckEntries = await this.decks.toArray()
return Promise.all(decks.map(async deckTable => { const decks = await Promise.all(deckEntries.map(async deckTable => {
const cardIds = deckTable.cards const cardIds = deckTable.cards
const deck: IDeck = { const deck: IDeck = {
...deckTable, ...deckTable,
@ -74,5 +74,13 @@ export class DeckDB extends Dexie {
} }
return deck return deck
})) }))
// returns object with deck ids as keys
const decksById = decks.reduce((acc, deck) => {
acc[deck.id] = deck
return acc
}, {})
return decksById
} }
} }

View file

@ -17,7 +17,7 @@ export interface ICardContent {
} }
export interface ICard { export interface ICard {
id: number; id: string;
name: string; name: string;
tags: string[]; tags: string[];
icon: string; icon: string;
@ -27,7 +27,7 @@ export interface ICard {
} }
export interface IDeck { export interface IDeck {
id: number; id: string;
name: string; name: string;
description: string; description: string;
color: string; color: string;
@ -57,7 +57,7 @@ export interface Notification {
export interface State { export interface State {
settings: Ref<Settings>; settings: Ref<Settings>;
decks: Ref<IDeck[]>; decks: Ref<{ [key: string]: IDeck }>;
notifications: Ref<Notification[]>; notifications: Ref<Notification[]>;
icons: Ref<string[]>; icons: Ref<string[]>;
popup: Ref<boolean>; popup: Ref<boolean>;

View file

@ -8,17 +8,17 @@
<Card id="_add_deck" @click="addDeck" /> <Card id="_add_deck" @click="addDeck" />
</section> </section>
<teleport to="#popup > .popup-content"> <Popup>
<div class="deck new-deck-form-wrapper"> <div class="deck new-deck-form-wrapper">
<header>Create a new deck of cards</header> <header>Create a new deck of cards</header>
<DeckForm :deck="newDeck" @save="saveDeck" @close="hidePopup" /> <DeckForm :deck="newDeck" @save="saveDeck" @cancel="cancelDeck" />
<footer class="centered"> <footer class="centered">
You can also You can also
<button @click="importDeck">import</button> <button @click="importDeck">import</button>
an existing deck. an existing deck.
</footer> </footer>
</div> </div>
</teleport> </Popup>
</template> </template>
@ -26,38 +26,45 @@
import { defineComponent, ref, computed } from 'vue' import { defineComponent, ref, computed } from 'vue'
import { useState } from '@/state' import { useState } from '@/state'
import Popup from '@/components/Popup.vue'
import Card from '@/components/Card.vue' import Card from '@/components/Card.vue'
import DeckCard from '@/components/DeckCard.vue' import DeckCard from '@/components/DeckCard.vue'
import DeckForm from '@/components/DeckForm.vue' import DeckForm from '@/components/DeckForm.vue'
export default defineComponent({ export default defineComponent({
name: 'Home', name: 'Home',
components: { Card, DeckCard, DeckForm }, components: { Popup, Card, DeckCard, DeckForm },
setup () { setup () {
const { actions: popupActions } = useState('popup') const { actions: popupActions } = useState('popup')
const { collection: decks, actions: deckActions } = useState('decks') const { collection: decks, actions: deckActions } = useState('decks')
const newDeckIndex = ref(0) const newDeckId = ref('')
const newDeck = computed(() => decks.value[newDeckIndex.value]) const newDeck = computed(() => decks.value[newDeckId.value])
const addDeck = () => { const addDeck = () => {
const idx = deckActions.new() const id = deckActions.new()
newDeckIndex.value = idx newDeckId.value = id
popupActions.show() popupActions.show()
} }
const saveDeck = (updatedDeck) => { const saveDeck = (updatedDeck) => {
console.log('saving deck', updatedDeck) updatedDeck.id = newDeckId.value
updatedDeck.id = newDeckIndex.value
deckActions.update(updatedDeck) deckActions.update(updatedDeck)
popupActions.hide() popupActions.hide()
} }
const cancelDeck = () => {
popupActions.hide()
deckActions.remove(newDeckId.value)
newDeckId.value = ''
}
return { return {
decks, decks,
addDeck, addDeck,
newDeck, newDeck,
saveDeck, saveDeck,
cancelDeck,
hidePopup: popupActions.hide hidePopup: popupActions.hide
// importDeck: deckActions.import, // importDeck: deckActions.import,
} }