tabbed DNC card, machine-numbered downloads, themed backup history
This commit is contained in:
@@ -34,6 +34,35 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- tabs: several label/value sections, one visible at a time. Keeps a
|
||||
panel with many fields to the height of its largest section - a
|
||||
30-row card cannot be split by the two-column multicol layout and
|
||||
drags one column far past the other. -->
|
||||
<div v-else-if="panel.render === 'tabs'" class="pap-tabs">
|
||||
<div class="pap-tabstrip" role="tablist">
|
||||
<button
|
||||
v-for="(section, si) in tabSections(panel)"
|
||||
:key="si"
|
||||
type="button"
|
||||
role="tab"
|
||||
class="pap-tab"
|
||||
:class="{ on: si === activeTab(panel) }"
|
||||
:aria-selected="si === activeTab(panel)"
|
||||
@click="setTab(panel, si)"
|
||||
>{{ section.label }}</button>
|
||||
</div>
|
||||
<div class="pap-kv">
|
||||
<div
|
||||
v-for="(field, fi) in (tabSections(panel)[activeTab(panel)] || {}).fields || []"
|
||||
:key="fi"
|
||||
class="pap-kv-row"
|
||||
>
|
||||
<span class="pap-kv-label">{{ field.label }}</span>
|
||||
<span class="pap-kv-value" :class="{ mono: field.mono }">{{ field.value }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- keyvalue: a label/value grid -->
|
||||
<div v-else-if="panel.render === 'keyvalue'" class="pap-kv">
|
||||
<div v-for="(field, fi) in keyvalueFields(panel)" :key="fi" class="pap-kv-row">
|
||||
@@ -71,7 +100,7 @@ import api from '../api'
|
||||
import { colorStyle } from '@/utils/colorStyle'
|
||||
import {
|
||||
toApiPath, rows, panelVisible, mapTitle, mapBadge, mapMeta, manageLink,
|
||||
keyvalueFields, tableColumns, cell, badges,
|
||||
keyvalueFields, tableColumns, cell, badges, tabSections,
|
||||
} from './pluginAssetPanels'
|
||||
|
||||
const props = defineProps({
|
||||
@@ -80,6 +109,12 @@ const props = defineProps({
|
||||
|
||||
const panels = ref([])
|
||||
|
||||
// Which tab each 'tabs' panel is showing, keyed by panel id. Kept here rather
|
||||
// than on the panel object because panels are replaced wholesale on refetch.
|
||||
const tabs = ref({})
|
||||
function activeTab(panel) { return tabs.value[panel.id] || 0 }
|
||||
function setTab(panel, index) { tabs.value = { ...tabs.value, [panel.id]: index } }
|
||||
|
||||
// Fetch the declared panels for this asset, then each panel's data endpoint.
|
||||
async function load() {
|
||||
panels.value = []
|
||||
@@ -118,10 +153,44 @@ watch(() => props.assetid, load, { immediate: true })
|
||||
.pap-meta { margin-top: 0.35rem; display: flex; flex-wrap: wrap; gap: 0.75rem; font-size: 0.82rem; color: var(--text-light); }
|
||||
.pap-empty { display: flex; align-items: center; gap: 0.6rem; }
|
||||
.pap-manage { font-size: 0.82rem; }
|
||||
.pap-kv { display: flex; flex-direction: column; gap: 0.4rem; }
|
||||
.pap-kv-row { display: flex; justify-content: space-between; gap: 1rem; }
|
||||
/* Two aligned columns rather than space-between: a ragged right edge is hard
|
||||
to scan once a panel carries more than a handful of rows, and a long value
|
||||
(a UNC path) otherwise pushes its label away from the others. */
|
||||
.pap-kv { display: grid; grid-template-columns: minmax(7rem, auto) 1fr; gap: 0.35rem 1rem; align-items: baseline; }
|
||||
.pap-kv-row { display: contents; }
|
||||
.pap-kv-label { color: var(--text-light); }
|
||||
.pap-kv-value { color: var(--text); font-weight: 500; }
|
||||
.pap-kv-value { color: var(--text); font-weight: 500; overflow-wrap: anywhere; }
|
||||
/* Tab strip: a quiet segmented control, not browser-default buttons. The
|
||||
active tab is marked by an underline AND weight, so it does not rely on
|
||||
colour alone. */
|
||||
.pap-tabstrip {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.15rem;
|
||||
margin-bottom: 0.85rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
.pap-tab {
|
||||
background: none;
|
||||
border: none;
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -1px;
|
||||
padding: 0.35rem 0.7rem;
|
||||
color: var(--text-light);
|
||||
font-size: 0.78rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.pap-tab:hover { color: var(--text); }
|
||||
.pap-tab.on {
|
||||
color: var(--primary);
|
||||
border-bottom-color: var(--primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.pap-table-wrap { overflow-x: auto; }
|
||||
.pap-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
|
||||
.pap-table th, .pap-table td { text-align: left; padding: 0.4rem 0.6rem; border-bottom: 1px solid var(--border); }
|
||||
|
||||
Reference in New Issue
Block a user