Dashboard final version, TODO: update main sidebar menu
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
<script setup>
|
||||
// Advanced tooltip component (original implementation) inspired by external patterns.
|
||||
import { computed } from 'vue';
|
||||
const props = defineProps({
|
||||
hideLabel: { type: Boolean, default: false },
|
||||
hideIndicator: { type: Boolean, default: false },
|
||||
indicator: { type: String, default: 'dot' }, // 'dot' | 'line' | 'dashed'
|
||||
nameKey: { type: String, required: false },
|
||||
labelKey: { type: String, required: false },
|
||||
labelFormatter: { type: Function, required: false },
|
||||
payload: { type: Object, required: false, default: () => ({}) },
|
||||
config: { type: Object, required: false, default: () => ({}) },
|
||||
class: { type: [String, Array, Object], required: false },
|
||||
color: { type: String, required: false },
|
||||
x: { type: [Number, Date, String], required: false },
|
||||
});
|
||||
|
||||
// Build array of entries referencing config for label & color
|
||||
const entries = computed(() => {
|
||||
return Object.entries(props.payload)
|
||||
.map(([key, value]) => {
|
||||
const seriesKey = props.nameKey || key;
|
||||
const itemConfig = props.config[seriesKey] || props.config[key] || {};
|
||||
const indicatorColor = itemConfig.color || props.color;
|
||||
return { key, value, itemConfig, indicatorColor };
|
||||
})
|
||||
.filter(e => e.itemConfig && (e.itemConfig.label || e.value !== undefined));
|
||||
});
|
||||
|
||||
const singleSeries = computed(() => entries.value.length === 1 && props.indicator !== 'dot');
|
||||
|
||||
const formattedLabel = computed(() => {
|
||||
if (props.hideLabel) return null;
|
||||
if (props.labelFormatter && props.x !== undefined) {
|
||||
return props.labelFormatter(props.x);
|
||||
}
|
||||
if (props.labelKey) {
|
||||
const cfg = props.config[props.labelKey];
|
||||
return cfg?.label || props.payload[props.labelKey];
|
||||
}
|
||||
return props.x instanceof Date ? props.x.toLocaleDateString() : props.x;
|
||||
});
|
||||
|
||||
function formatValue(v) {
|
||||
if (v == null) return '';
|
||||
if (typeof v === 'number') return v.toLocaleString();
|
||||
return v;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div :class="['border border-border/50 bg-background min-w-32 rounded-lg px-2.5 py-1.5 text-xs shadow-xl', props.class]">
|
||||
<div v-if="!singleSeries && formattedLabel" class="font-medium mb-1">{{ formattedLabel }}</div>
|
||||
<div class="grid gap-1.5">
|
||||
<div
|
||||
v-for="{ key, value, itemConfig, indicatorColor } in entries"
|
||||
:key="key"
|
||||
class="flex w-full flex-wrap items-stretch gap-2"
|
||||
:class="indicator === 'dot' ? 'items-center' : 'items-start'"
|
||||
>
|
||||
<!-- Indicator -->
|
||||
<template v-if="!hideIndicator">
|
||||
<div
|
||||
:class="[
|
||||
'shrink-0 rounded-[2px] border-border',
|
||||
indicator === 'dot' && 'h-2.5 w-2.5',
|
||||
indicator === 'line' && 'w-1 h-4',
|
||||
indicator === 'dashed' && 'w-0 h-4 border-[1.5px] border-dashed bg-transparent',
|
||||
singleSeries && indicator === 'dashed' && 'my-0.5'
|
||||
]"
|
||||
:style="{ '--color-bg': indicatorColor, '--color-border': indicatorColor }"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<div :class="['flex flex-1 justify-between leading-none', singleSeries ? 'items-end' : 'items-center']">
|
||||
<div class="grid gap-1.5">
|
||||
<div v-if="singleSeries && formattedLabel" class="font-medium">{{ formattedLabel }}</div>
|
||||
<span class="text-muted-foreground">{{ itemConfig.label || formatValue(value) }}</span>
|
||||
</div>
|
||||
<span v-if="value !== undefined" class="font-mono font-medium tabular-nums">{{ formatValue(value) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user