Skip to content

Commit

Permalink
ui: add button to show deck/folder statistics
Browse files Browse the repository at this point in the history
  • Loading branch information
shagu committed Mar 19, 2023
1 parent 8334d9f commit e6ee3ff
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 13 deletions.
221 changes: 221 additions & 0 deletions ui/elements/m-folder-details.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import { html, css } from './m-template.js'

export default class MFolderDetails extends HTMLElement {
static shadow = null

static template = html`
<m-grid horizontal=1 id=header>
<div id=title></div>
<div id=icon></div>
</m-grid>
<!-- Mana -->
<m-grid vertical=1>
<div class=header>Mana</div>
<m-grid horizontal=1 id=mana-diagram></m-grid>
<m-grid horizontal=1 id=mana-average></m-grid>
</m-grid>
<!-- Price -->
<m-grid vertical=1 id=card-prices>
<div class=header>Price</div>
<m-grid horizontal=1 id=price-total></m-grid>
<m-grid horizontal=1 id=price-average></m-grid>
</m-grid>
<!-- Card Types -->
<m-grid vertical=1 id=card-types>
</m-grid>
`

static style = css`
m-grid {
margin: 4px 2px;
padding: 4px;
background-color: var(--widget-normal);
border: 1px var(--border-normal) solid;
text-align: left;
}
m-grid[horizontal] {
margin: 2px;
border: none;
padding: 0px 4px;
}
m-grid .header {
margin: 4px 2px;
color: var(--font-dark);
width: 100%;
}
m-grid .label {
font-weight: bold;
}
m-grid .content {
text-align: right;
}
#header {
margin: 4px 2px;
padding: 4px;
border: 1px var(--border-normal) solid;
}
#header #title {
font-weight: bold;
}
#header #icon img {
height: 18px;
aspect-ratio: 1/1;
vertical-align: middle;
}
#mana-diagram .table {
display: table;
box-sizing: border-box;
width: 100%;
}
#mana-diagram .table .row {
display: table-row;
text-align: center;
}
#mana-diagram .table .row .label {
color: var(--font-dark);
}
#mana-diagram .table .cell {
display: table-cell;
vertical-align: bottom;
font-size: smaller;
}
#mana-diagram .table .cell .bar {
display: block;
background: var(--color-notify);
width: 14px;
margin: 1px 4px;
}
`

setDetails = (details) => {
// folder title and icon
if (details.icon && details.title) {
const imgSrc = `img/icons/${details.icon}.png`
const icon = this.shadow.getElementById('icon')
const title = this.shadow.getElementById('title')

icon.innerHTML = `<img src="${imgSrc}" />`
title.innerHTML = details.title || 'N/A'
}

// mana curve diagram
if (details.mana.values) {
let maxCount = 1
const values = {}

// read all mana values, detect maximum and set limit to 8
for (const [cmc, count] of Object.entries(details.mana.values)) {
maxCount = Math.max(maxCount, count)
values[Math.min(cmc, 8)] = values[Math.min(cmc, 8)] || 0
values[Math.min(cmc, 8)] += count
}

// build the ui of the mana diagram
const frame = this.shadow.getElementById('mana-diagram')
frame.innerHTML = ''

const table = document.createElement('div')
table.classList = 'table'
frame.appendChild(table)

const rowTop = document.createElement('div')
rowTop.classList = 'row'
table.appendChild(rowTop)

const rowBottom = document.createElement('div')
rowBottom.classList = 'row'
table.appendChild(rowBottom)

for (let cmc = 1; cmc <= 8; cmc++) {
const count = values[cmc] || 0
const height = Math.max(1, count / maxCount * 16)
const bar = `<div class=bar style="height: ${height}px"></div>`

const cellDisplay = document.createElement('div')
cellDisplay.classList = 'cell'
cellDisplay.innerHTML = `${count > 0 ? count : ''}${bar}`
rowTop.appendChild(cellDisplay)

const cellLabel = document.createElement('div')
cellLabel.classList = 'cell label'
cellLabel.innerHTML = cmc
rowBottom.appendChild(cellLabel)
}
}

// mana values
if (details.mana.avg) {
const frame = this.shadow.getElementById('mana-average')
frame.innerHTML = `
<div class=label>Average Mana</div>
<div class=content id=avg_mana>${details.mana.avg ? details.mana.avg.toFixed(1) : 'N/A'}</div>
`
}

// card types and count
if (details.types && details.cards) {
const frame = this.shadow.getElementById('card-types')
frame.innerHTML = `
<div class=header>Cards</div>
<m-grid horizontal=1>
<div class=label>Total</div>
<div class=content>${details.cards}</div>
</m-grid>
`

// sort all card types by card count
const sortable = Object.fromEntries(
Object.entries(details.types).sort(([, a], [, b]) => b - a)
)

for (const [type, count] of Object.entries(sortable)) {
frame.innerHTML += `
<m-grid horizontal=1>
<div class=label>${type}</div>
<div class=content id=>${count}</div>
</m-grid>
`
}
}

// card prices
if (details.price) {
const total = this.shadow.getElementById('price-total')
total.innerHTML = `
<div class=label>Total</div>
<div class=content>${details.price.sum ? details.price.sum.toFixed(2) : 'N/A'}€</div>
`
const average = this.shadow.getElementById('price-average')
average.innerHTML = `
<div class=label>Average</div>
<div class=content>${details.price.avg ? details.price.avg.toFixed(2) : 'N/A'}€</div>
`
}
}

constructor () {
super()

this.shadow = this.attachShadow({ mode: 'open' })
this.shadow.adoptedStyleSheets = [MFolderDetails.style]
this.shadow.append(document.importNode(MFolderDetails.template, true))
}
}

customElements.define('m-folder-details', MFolderDetails)
1 change: 1 addition & 0 deletions ui/elements/m-widgets.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ import './m-popover.js'

// load site elements
import './m-card.js'
import './m-folder-details.js'

export default {}
10 changes: 10 additions & 0 deletions ui/frontend.html
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@
</m-grid>
</m-popover>

<m-popover name="folder-details">
<m-folder-details id="folder-details"></m-folder-details>
</m-popover>

<div id="window">
<div id="header">
<!-- toolbar main buttons -->
Expand Down Expand Up @@ -187,6 +191,12 @@
</svg>
</m-button>

<m-button m-popover="folder-details" class="menu-button" id="menu-folder-details" disabled>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<path d="M 15 3 L 15 19 L 19 19 L 19 3 L 15 3 z M 9 10 L 9 19 L 13 19 L 13 10 L 9 10 z M 3 14 L 3 19 L 7 19 L 7 14 L 3 14 z "/>
</svg>
</m-button>

<m-button m-popover="main-menu" class="menu-button" id="menu-main">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 22 22">
<path d="m3 5v2h16v-2h-16m0 5v2h16v-2h-16m0 5v2h16v-2h-16" />
Expand Down
42 changes: 29 additions & 13 deletions ui/frontend.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,30 +49,33 @@ frontend.objcompare = (a, b, o) => {
frontend.getFolderDetails = (content, stats) => {
stats.types = stats.types || { /* creature: 0, enchantment: 0, .. */ }
stats.price = stats.price || { num: 0, sum: 0, min: 0, max: 0, avg: 0 }
stats.mana = stats.mana || { num: 0, sum: 0, min: 0, max: 0, avg: 0 }
stats.mana = stats.mana || { num: 0, sum: 0, min: 0, max: 0, avg: 0, values: {} }

for (const card of content) {
if (card.types) {
for (const type of card.types) {
stats.types[type] = stats.types[type] || 0
stats.types[type] = stats.types[type]++
stats.types[type]++
}
}

if (card.price) {
stats.price.num++
stats.price.sum += card.price
stats.price.min = Math.min(stats.price.min, card.price)
stats.price.min = stats.price.min === 0 ? card.price : Math.min(stats.price.min, card.price)
stats.price.max = Math.max(stats.price.max, card.price)
stats.price.avg = (stats.price.sum / stats.price.num).toFixed(2)
stats.price.avg = stats.price.sum / stats.price.num
}

if (card.cmc && card.cmc > 0) {
stats.mana.num++
stats.mana.sum += card.cmc
stats.mana.min = Math.min(stats.mana.min, card.cmc)
stats.mana.min = stats.price.min === 0 ? card.cmc : Math.min(stats.mana.min, card.cmc)
stats.mana.max = Math.max(stats.mana.max, card.cmc)
stats.mana.avg = (stats.mana.sum / stats.mana.num).toFixed(2)
stats.mana.avg = stats.mana.sum / stats.mana.num

stats.mana.values[card.cmc] = stats.mana.values[card.cmc] || 0
stats.mana.values[card.cmc]++
}

stats.cards = stats.cards || 0
Expand All @@ -81,20 +84,27 @@ frontend.getFolderDetails = (content, stats) => {
}

frontend.getCollectionDetails = () => {
const details = { collection: {}, current: {}, view: {} }
const details = { collection: { }, current: { }, view: { } }

// scan library
for (const [, content] of Object.entries(frontend.db)) {
for (const [folder, content] of Object.entries(frontend.db)) {
frontend.getFolderDetails(content, details.collection)
details.collection.title = (folder === '.' ? 'Library' : folder)
details.collection.icon = frontend.getColorIcon(folder)

details.collection.lists = details.collection.lists || 0
if (content.length > 0) details.collection.lists++
}

// scan current folder
frontend.getFolderDetails(frontend.db[frontend.path], details.current)
details.current.title = (frontend.path === '.' ? 'Library' : frontend.path)
details.current.icon = frontend.getColorIcon(frontend.db[frontend.path])

// scan current view
frontend.getFolderDetails(frontend.view, details.view)
details.view.title = 'Current View'
details.view.icon = frontend.getColorIcon(frontend.view)

return details
}
Expand Down Expand Up @@ -294,11 +304,16 @@ frontend.reloadSidebar = () => {
combineSameContainer.appendChild(combineSameInput)
}

frontend.reloadStatusbar = () => {
const details = frontend.getCollectionDetails()
frontend.reloadDetails = () => {
const divFooter = document.getElementById('footer')
const divDetails = document.getElementById('folder-details')

const details = frontend.details
const collection = details.collection
const current = details.current

divFooter.innerHTML = `Collection with <b>${details.collection.cards}</b> cards in <b>${details.collection.lists}</b> folders worth <b>${details.collection.price.sum.toFixed(2)}€</b>.`
divDetails.setDetails(current)
divFooter.innerHTML = `Collection with <b>${collection.cards}</b> cards in <b>${collection.lists}</b> folders worth <b>${collection.price.sum.toFixed(2)}€</b>.`
}

frontend.isSelection = (card, compare) => {
Expand Down Expand Up @@ -392,7 +407,8 @@ frontend.reloadPreview = () => {
frontend.uiLock = (state) => {
const uiLock = [
'import-button', 'menu-filter', 'card-search', 'button-color-w', 'button-color-u',
'button-color-b', 'button-color-r', 'button-color-g', 'button-color-c', 'button-color-m'
'button-color-b', 'button-color-r', 'button-color-g', 'button-color-c', 'button-color-m',
'menu-folder-details'
]

for (const element of uiLock) {
Expand All @@ -418,7 +434,7 @@ frontend.reload = () => {

// reload ui panels
frontend.reloadSidebar()
frontend.reloadStatusbar()
frontend.reloadDetails()
frontend.reloadView()
frontend.reloadPreview()
}
Expand Down

0 comments on commit e6ee3ff

Please sign in to comment.