Skip to content

Commit bf03021

Browse files
committed
wip
1 parent d7360e3 commit bf03021

File tree

4 files changed

+116
-45
lines changed

4 files changed

+116
-45
lines changed

resources/js/dashboard/components/widgets/graph/Bar.vue

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
11
<script setup lang="ts">
22
import { GraphWidgetData } from "@/types";
33
import { DashboardWidgetProps } from "@/dashboard/types";
4-
import { VisAxis, VisBulletLegend, VisCrosshair, VisGroupedBar, VisTooltip, VisXYContainer } from "@unovis/vue";
4+
import { VisAxis, VisBulletLegend, VisCrosshair, VisGroupedBar, VisStackedBar, VisTooltip, VisXYContainer } from "@unovis/vue";
55
import {
66
AxisConfigInterface,
7-
BulletLegendConfigInterface, CrosshairConfigInterface,
8-
FitMode,
9-
GroupedBar,
7+
BulletLegendConfigInterface,
8+
CrosshairConfigInterface,
9+
FitMode, GroupedBar,
1010
GroupedBarConfigInterface,
1111
TextAlign,
12-
TooltipConfigInterface,
13-
TrimMode,
12+
TrimMode, XYContainerConfigInterface,
1413
} from '@unovis/ts'
1514
import { Datum, useXYChart } from "@/dashboard/components/widgets/graph/useXYChart";
1615
import { computed } from "vue";
@@ -23,53 +22,61 @@
2322
return props.value?.labels?.[tick as number] || '';
2423
}
2524
26-
const rotate = computed(() => props.value?.labels?.length >= 10);
25+
const rotate = computed(() => !props.widget.options.horizontal && props.value?.labels?.length >= 10);
2726
</script>
2827

2928
<template>
3029
<div class="flex flex-col gap-4">
31-
<VisXYContainer class="flex-1 min-h-0" :data="data">
30+
<VisXYContainer class="flex-1 min-h-0"
31+
v-bind="{ } as XYContainerConfigInterface<Datum>"
32+
:data="data"
33+
>
3234
<template v-if="!props.widget.minimal">
3335
<VisAxis
3436
v-bind="{
35-
type: widget.options?.horizontal ? 'x' : 'y',
37+
type: props.widget.options?.horizontal ? 'x' : 'y',
38+
3639
} as AxisConfigInterface<Datum>"
3740
/>
3841
<VisAxis
3942
v-bind="{
40-
type: widget.options?.horizontal ? 'y' : 'x',
43+
type: props.widget.options?.horizontal ? 'y' : 'x',
44+
gridLine: false,
4145
// tickValues: props.value?.labels.map((_, i) => i),
4246
numTicks: props.value?.labels.length - 1,
4347
tickFormat: tickFormat,
4448
tickTextTrimType: TrimMode.End,
45-
tickTextAlign: rotate ? TextAlign.Left : TextAlign.Center,
49+
tickTextAlign: rotate ? TextAlign.Left : props.widget.options.horizontal ? TextAlign.Right : TextAlign.Center,
4650
tickTextFitMode: rotate ? FitMode.Wrap : FitMode.Trim,
4751
tickTextAngle: rotate ? 45 : undefined,
4852
tickTextWidth: rotate ? 100 : undefined,
49-
} as AxisConfigInterface<Datum>"
53+
// } as AxisConfigInterface<Datum>"
5054
/>
5155
</template>
5256

53-
<VisCrosshair
54-
v-bind="{
57+
<template v-if="!props.widget.options?.horizontal">
58+
<VisCrosshair
59+
v-bind="{
5560
color: color,
5661
template: tooltipTemplate,
57-
// hideWhenFarFromPointer: false,
62+
hideWhenFarFromPointer: false,
5863
} as CrosshairConfigInterface<Datum>"
59-
/>
64+
/>
65+
</template>
66+
67+
<VisTooltip :triggers="{ [GroupedBar.selectors.barGroup]: tooltipTemplate }" />
6068

6169
<VisGroupedBar
6270
v-bind="{
6371
x: x,
6472
y: y,
65-
orientation: widget.options?.horizontal ? 'horizontal' : 'vertical',
73+
orientation: props.widget.options?.horizontal ? 'horizontal' : 'vertical',
6674
color: color,
6775
} as GroupedBarConfigInterface<Datum>"
6876
/>
69-
<VisTooltip v-bind="{ triggers: { [GroupedBar.selectors.root]: tooltipTemplate } } as TooltipConfigInterface" />
7077
</VisXYContainer>
7178

72-
<template v-if="widget.showLegend && !widget.minimal">
79+
<template v-if="props.widget.showLegend && !props.widget.minimal">
7380
<div class="flex justify-center">
7481
<VisBulletLegend
7582
v-bind="{

resources/js/dashboard/components/widgets/graph/Line.vue

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,46 @@
77
CrosshairConfigInterface,
88
CurveType,
99
LineConfigInterface, ScatterConfigInterface, XYContainerConfigInterface,
10+
Scale, TextAlign, FitMode, Scatter,
1011
} from "@unovis/ts";
1112
import { Datum, useXYChart } from "@/dashboard/components/widgets/graph/useXYChart";
1213
1314
const props = defineProps<DashboardWidgetProps<GraphWidgetData>>();
1415
15-
const { data, x, y, color, tooltipTemplate } = useXYChart(props);
16+
const { data, x, y, color, tooltipTemplate, timeScale, xScale, xTickValues } = useXYChart(props);
1617
17-
const tickFormat: AxisConfigInterface<number[]>['tickFormat'] = (tick) => {
18+
const tickFormat: AxisConfigInterface<number[]>['tickFormat'] = (tick, i) => {
19+
if(props.widget.dateLabels) {
20+
return new Intl.DateTimeFormat(undefined, { day: '2-digit', month: 'short' })
21+
.format(timeScale ? tick : new Date(props.value.labels[tick]));
22+
// return Scale.scaleTime().tickFormat()(tick);// new Intl.DateTimeFormat().format(new Date(props.value.labels[tick]));
23+
}
1824
return props.value?.labels?.[tick as number];
1925
}
26+
// const rotate = computed(() => props.value?.labels?.length >= 10);
2027
</script>
2128

2229
<template>
2330
<div class="mt-2">
24-
<VisXYContainer v-bind="{} as XYContainerConfigInterface<Datum>" :data="data">
31+
<VisXYContainer v-bind="{
32+
xScale,
33+
components: props.value?.datasets.map(dataset => new Scatter({
34+
size: 5, x: x, y: y, color: color
35+
})),
36+
} as XYContainerConfigInterface<Datum>" :data="data">
2537
<template v-if="!props.widget.minimal">
2638
<VisAxis
2739
v-bind="{
2840
type: 'x',
2941
tickFormat: tickFormat,
30-
// numTicks: props.value?.labels.length / 2,
42+
tickValues: xTickValues,
43+
// minMaxTicksOnly: true
44+
// tickValues: props.value?.labels.length < 5 ? props.value?.labels.map((_, i) => x(null, i)) as number[] : undefined,
45+
// numTicks: props.value?.labels.length < 10 ? props.value?.labels.length - 1 : undefined,
46+
// tickTextAlign: rotate ? TextAlign.Left : TextAlign.Center,
47+
// tickTextFitMode: rotate ? FitMode.Wrap : FitMode.Trim,
48+
// tickTextAngle: rotate ? 45 : undefined,
49+
// tickTextWidth: rotate ? 100 : undefined,
3150
} as AxisConfigInterface<Datum>"
3251
/>
3352
<VisAxis v-bind="{ type: 'y' } as AxisConfigInterface<Datum>" />
@@ -38,6 +57,7 @@
3857
x: x,
3958
y: y,
4059
color: color,
60+
lineWidth: 1,
4161
curveType: props.widget.options.curved ? CurveType.MonotoneX : CurveType.Linear,
4262
} as LineConfigInterface<Datum>"
4363
/>
@@ -49,12 +69,16 @@
4969
hideWhenFarFromPointer: false,
5070
} as CrosshairConfigInterface<Datum>"
5171
/>
72+
<VisTooltip />
5273

53-
<template v-for="dataset in props.value?.datasets">
54-
<VisScatter
55-
v-bind="{ size: 5, x: x, y: y, color: color } as ScatterConfigInterface<Datum>"
56-
/>
74+
<template v-if="props.value?.labels.length < 40">
75+
<template v-for="dataset in props.value?.datasets">
76+
<VisScatter
77+
v-bind="{ size: 4, x: x, y: y, strokeColor: color, color: 'var(--background)' } as ScatterConfigInterface<Datum>"
78+
/>
79+
</template>
5780
</template>
81+
5882
</VisXYContainer>
5983
<template v-if="props.widget.showLegend && !props.widget.minimal">
6084
<div class="mt-4 flex justify-center">

resources/js/dashboard/components/widgets/graph/Pie.vue

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import { GraphWidgetData } from "@/types";
44
import { DashboardWidgetProps } from "@/dashboard/types";
55
import { VisSingleContainer, VisDonut, VisTooltip, VisBulletLegend } from "@unovis/vue";
6-
import { BulletLegendConfigInterface, DonutConfigInterface } from "@unovis/ts";
6+
import { BulletLegendConfigInterface, CrosshairConfigInterface, DonutConfigInterface, Donut, Tooltip } from "@unovis/ts";
77
88
const props = defineProps<DashboardWidgetProps<GraphWidgetData>>();
99
@@ -12,6 +12,14 @@
1212
color: dataset.color,
1313
value: dataset.data[0] ?? 0,
1414
})));
15+
type Datum = typeof data.value[number];
16+
type CallbackData = { data: Datum };
17+
const tooltipTemplate = (_, i: number) => {
18+
const item = props.value?.datasets?.[i];
19+
const total = props.value?.datasets?.reduce((total, d) => total + (d.data[0] ?? 0), 0);
20+
return `<div class="text-sm">${item.label}: ${item.data}
21+
<span class="opacity-50">(${new Intl.NumberFormat(undefined, { style: 'percent', maximumFractionDigits: 2}).format((item.data[0] ?? 0) / total)})</span></div>`;
22+
}
1523
</script>
1624

1725
<template>
@@ -22,9 +30,22 @@
2230
data: data,
2331
value: d => d.value,
2432
color: d => d.color,
25-
} as DonutConfigInterface<typeof data[number]>"
33+
arcWidth: 50,
34+
35+
attributes: {
36+
[Donut.selectors.segment]: {
37+
'class'() {
38+
return `${Donut.selectors.segment} relative fill-(--color) hover:stroke-10! stroke-(--color)! transition-all [transform-box:fill-box] origin-center`;
39+
},
40+
'style'(data: CallbackData) {
41+
return `--color:${data.data.color}`;
42+
// return { '--color': data.color }
43+
}
44+
}
45+
}
46+
} as DonutConfigInterface<Datum>"
2647
/>
27-
<VisTooltip />
48+
<VisTooltip :triggers="{ [Donut.selectors.segment]: tooltipTemplate }" />
2849
</VisSingleContainer>
2950

3051
<template v-if="props.widget.showLegend && !props.widget.minimal">
Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,55 @@
11
import { DashboardWidgetProps } from "@/dashboard/types";
22
import { GraphWidgetData } from "@/types";
3-
import { computed } from "vue";
3+
import { computed, reactive, toRefs } from "vue";
44
import { XYComponentConfigInterface } from "@unovis/ts/core/xy-component/config";
5-
import { ColorAccessor, CrosshairConfigInterface } from "@unovis/ts";
6-
5+
import { AxisConfigInterface, ColorAccessor, CrosshairConfigInterface, Scale } from "@unovis/ts";
6+
import { timeTickInterval } from 'd3-time';
77
export type Datum = number[];
88

99
export function useXYChart(props: DashboardWidgetProps<GraphWidgetData>) {
10-
const data = computed<Datum[]>(() => props.value?.datasets?.reduce((res, dataset, i) => {
10+
const data = computed((): Datum[] => props.value?.datasets?.reduce((res, dataset, i) => {
1111
dataset.data.forEach((v, j) => {
1212
res[j] ??= [];
1313
res[j][i] = v;
1414
});
1515
return res;
1616
}, []));
17-
const x: XYComponentConfigInterface<Datum>['x'] = (d, i) => i;
18-
const y = computed<XYComponentConfigInterface<Datum>['y']>(() => props.value?.datasets.map((dataset, i) => (d) => d[i]));
19-
// const color: ColorAccessor<Datum | Datum[]> = (_, i) => props.value?.datasets[i]?.color;
20-
const color: ColorAccessor<Datum | Datum[]> = props.value?.datasets.map((dataset, i) => dataset.color);
21-
const tooltipTemplate: CrosshairConfigInterface<any>['template'] = function (d, x: number) {
22-
console.log(arguments);
23-
return `<div>${props.value.labels[Math.round(x)]}</div>
17+
// const timeScale = false;
18+
const timeScale = true;
19+
const x: XYComponentConfigInterface<Datum>['x'] = (d, i) => {
20+
return props.widget.dateLabels && timeScale ? new Date(props.value.labels[i]).getTime(): i;
21+
};
22+
const y = computed((): XYComponentConfigInterface<Datum>['y'] => props.value?.datasets.map((dataset, i) => (d) => d[i]));
23+
const xScale = computed((): XYComponentConfigInterface<Datum>['xScale'] => {
24+
return props.widget.dateLabels && timeScale
25+
? Scale.scaleUtc().domain([new Date(props.value.labels[0]), new Date(props.value.labels.at(-1))]) as any
26+
: undefined
27+
});
28+
const xTickValues = computed((): AxisConfigInterface<Datum>['tickValues'] => {
29+
// return undefined;
30+
if(props.widget.dateLabels && timeScale) {
31+
return props.value.labels.length < 10
32+
? props.value.labels.map((l) => new Date(l))
33+
: xScale.value.ticks(10) as any;
34+
}
35+
});
36+
const color = computed((): ColorAccessor<Datum | Datum[]> => props.value?.datasets.map((dataset, i) => dataset.color));
37+
38+
const tooltipTemplate = (d: Datum, x: number) => {
39+
const formattedLabel = props.widget.dateLabels
40+
? new Intl.DateTimeFormat(undefined, { day: '2-digit', month: 'short' }).format(timeScale ? x : new Date(props.value.labels[Math.round(x as number)]))
41+
: props.value.labels[Math.round(x as number)];
42+
return `<div class="mb-1 text-sm">${formattedLabel}</div>
2443
${
2544
props.value?.datasets.map((dataset, i) =>
26-
`<div class="flex items-center gap-2">
27-
<span class="size-3 rounded-full bg-(--color)" style="--color: ${dataset.color}"></span>
28-
${dataset.label ? `<span>${dataset.label}:</span>` : ''}
45+
`<div class="flex items-center gap-2 text-sm">
46+
<span class="size-2 rounded-full bg-(--color)" style="--color: ${dataset.color}"></span>
47+
${dataset.label ? `<span class="text-sm">${dataset.label}:</span>` : ''}
2948
${d[i]}
3049
</div>`
3150
).join('')
3251
}`;
3352
}
3453

35-
return { data, x, y, color, tooltipTemplate };
54+
return { data, x, y, color, tooltipTemplate, timeScale, xScale, xTickValues, };
3655
}

0 commit comments

Comments
 (0)