Printer card chips

This commit is contained in:
2026-08-11 15:46:21 -04:00
parent 34cb9a5e3e
commit 793dfab5c8
5 changed files with 119 additions and 16 deletions

View File

@@ -18,11 +18,17 @@
<!-- exceptions / list: one line per thing, each linking to itself -->
<ul v-else class="dc-rows">
<li v-for="(row, index) in visibleRows(card)" :key="index" class="dc-row">
<router-link v-if="row.link" :to="row.link" class="dc-row-title">
<router-link v-if="row.link" :to="row.link" class="dc-row-title"
:title="row.titletip || undefined">
{{ row.title }}
</router-link>
<span v-else class="dc-row-title dc-row-nolink">{{ row.title }}</span>
<span v-else class="dc-row-title dc-row-nolink"
:title="row.titletip || undefined">{{ row.title }}</span>
<span v-if="row.detail" class="dc-row-detail">{{ row.detail }}</span>
<span v-for="(chip, c) in row.chips" :key="c" class="dc-chip"
:class="'dc-chip-' + (chip.level || 'low')" :title="chip.title">
{{ chip.text }}
</span>
<span v-if="row.meta.length" class="dc-row-meta">
{{ row.meta.map((m) => m.text).join(' / ') }}
</span>
@@ -204,6 +210,21 @@ defineExpose({ load })
overflow: hidden;
text-overflow: ellipsis;
}
/* One chip per depleted supply. Bordered rather than filled: a row of solid
red pills reads as an emergency even when a cartridge is merely low. */
.dc-chip {
flex: none;
padding: 0.05rem 0.4rem;
border: 1px solid var(--border);
border-radius: 10px;
font-size: 0.75rem;
color: var(--text);
white-space: nowrap;
cursor: help;
}
.dc-chip-critical { border-color: var(--danger); color: var(--danger); }
.dc-chip-low { border-color: var(--warning); }
.dc-more {
display: block;
margin: 0.5rem 0 0;

View File

@@ -89,11 +89,34 @@ export function overflowCount(card) {
return Math.max(0, rows(card).length - MAXROWS)
}
// Chips are the row's own small facts, each carrying a tooltip: a depleted
// cartridge shows "Black 4%" and reveals the part number to order on hover.
// The percentage says something is wrong; the tooltip says what to do about
// it, which otherwise means opening the printer's page to find out.
export function mapChips(card, item) {
const key = card.map && card.map.chips
const list = key ? item[key] : null
if (!Array.isArray(list)) return []
return list
.filter((chip) => chip && chip.text)
.map((chip) => ({ text: chip.text, title: chip.title || '', level: chip.level || '' }))
}
// A tooltip on the row title, for context that would clutter the line - a
// printer's location, say. Never load-bearing: hover is not discoverable and
// does not exist on touch, so nothing essential goes here.
export function mapTitleTip(card, item) {
const key = card.map && card.map.titletooltip
return key ? (item[key] || '') : ''
}
export function cardRows(card) {
return rows(card).map((item) => ({
title: mapTitle(card, item),
titletip: mapTitleTip(card, item),
detail: mapDetail(card, item),
meta: mapMeta(card, item),
chips: mapChips(card, item),
link: mapLink(card, item),
timestamp: card.map && card.map.timestamp ? item[card.map.timestamp] : null,
}))

View File

@@ -163,3 +163,42 @@ describe('gating', () => {
expect(kept.map((c) => c.id)).toEqual(['ok'])
})
})
describe('chips and tooltips', () => {
const printerCard = {
render: 'exceptions',
map: {
title: 'printername',
titletooltip: 'location',
chips: 'supplies',
link: '/printers/{printerid}',
},
_data: [{
printerid: 7,
printername: 'WJ-HP-402',
location: 'Cell B, north wall',
supplies: [
{ text: 'Black 4%', title: 'CF226X (high)', level: 'critical' },
{ text: 'Cyan 9%', title: 'No part number on file for this model', level: 'low' },
],
}],
}
it('builds one chip per depleted supply, each with its reorder tooltip', () => {
const [row] = cardRows(printerCard)
expect(row.chips.map((c) => c.text)).toEqual(['Black 4%', 'Cyan 9%'])
expect(row.chips[0].title).toBe('CF226X (high)')
expect(row.chips[0].level).toBe('critical')
})
it('puts the location on the title as a tooltip, not in the line', () => {
const [row] = cardRows(printerCard)
expect(row.titletip).toBe('Cell B, north wall')
expect(row.detail).toBe('')
})
it('has no chips when a card declares none', () => {
expect(cardRows({ render: 'exceptions', map: { title: 'x' }, _data: [{ x: 'y' }] })[0].chips)
.toEqual([])
})
})