Merged in feature/VBV-259-create-new-circle-component (pull request #21)
This commit is contained in:
parent
b667140d3e
commit
bac7acb021
|
|
@ -0,0 +1,135 @@
|
|||
<script setup lang="ts">
|
||||
import * as d3 from "d3";
|
||||
import * as log from "loglevel";
|
||||
import { computed, onMounted } from "vue";
|
||||
|
||||
// @ts-ignore
|
||||
import colors from "@/colors.json";
|
||||
|
||||
export type CircleSectorProgress = "none" | "in_progress" | "finished";
|
||||
|
||||
export interface CircleSectorData {
|
||||
progress: CircleSectorProgress;
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
sectors: CircleSectorData[];
|
||||
}>();
|
||||
|
||||
onMounted(async () => {
|
||||
log.debug("LearningPathCircle mounted");
|
||||
render();
|
||||
});
|
||||
|
||||
interface CircleSector extends d3.PieArcDatum<number> {
|
||||
arrowStartAngle: number;
|
||||
arrowEndAngle: number;
|
||||
progress: CircleSectorProgress;
|
||||
}
|
||||
|
||||
const pieData = computed(() => {
|
||||
const pieWeights = new Array(Math.max(props.sectors.length, 1)).fill(1);
|
||||
const pieGenerator = d3.pie();
|
||||
const angles = pieGenerator(pieWeights);
|
||||
return angles
|
||||
.map((angle) => {
|
||||
// Rotate the circle by PI (180 degrees) normally 0 = 12'o clock, now start at 6 o clock
|
||||
angle.startAngle += Math.PI;
|
||||
angle.endAngle += Math.PI;
|
||||
|
||||
return Object.assign(
|
||||
{
|
||||
startAngle: angle.startAngle,
|
||||
endAngle: angle.endAngle,
|
||||
arrowStartAngle: angle.endAngle + (angle.startAngle - angle.endAngle) / 2,
|
||||
arrowEndAngle: angle.startAngle + (angle.startAngle - angle.endAngle) / 2,
|
||||
progress: props.sectors[angle.index].progress,
|
||||
},
|
||||
angle
|
||||
);
|
||||
})
|
||||
.reverse() as CircleSector[];
|
||||
});
|
||||
|
||||
const width = 450;
|
||||
const height = 450;
|
||||
const radius = Math.min(width, height) / 2.4;
|
||||
|
||||
function getColor(sector: CircleSector) {
|
||||
let color = colors.gray[300];
|
||||
if ("in_progress" === sector.progress) {
|
||||
color = colors.sky[500];
|
||||
}
|
||||
if ("finished" === sector.progress) {
|
||||
color = colors.green[500];
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
function render() {
|
||||
const svg = d3.select(".circle-visualization");
|
||||
// Clean svg before adding new stuff.
|
||||
svg.selectAll("*").remove();
|
||||
|
||||
if (pieData.value) {
|
||||
const arrowStrokeWidth = 2;
|
||||
// Append marker as definition to the svg
|
||||
svg
|
||||
.attr("viewBox", `0 0 ${width} ${height}`)
|
||||
.append("svg:defs")
|
||||
.append("svg:marker")
|
||||
.attr("id", "triangle")
|
||||
.attr("refX", 11)
|
||||
.attr("refY", 11)
|
||||
.attr("markerWidth", 20)
|
||||
.attr("markerHeight", 20)
|
||||
.attr("markerUnits", "userSpaceOnUse")
|
||||
.attr("orient", "auto")
|
||||
.append("path")
|
||||
.attr("d", "M -1 0 l 10 0 M 0 -1 l 0 10")
|
||||
.attr("transform", "rotate(-90, 10, 0)")
|
||||
.attr("stroke-width", arrowStrokeWidth)
|
||||
.attr("stroke", colors.gray[500]);
|
||||
|
||||
const g = svg
|
||||
.append("g")
|
||||
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
|
||||
|
||||
// Generate the pie diagram wedge
|
||||
const wedgeGenerator = d3
|
||||
.arc()
|
||||
.innerRadius(radius / 2.5)
|
||||
.padAngle(12 / 360)
|
||||
.outerRadius(radius);
|
||||
|
||||
const learningSequences = g
|
||||
.selectAll(".learningSegmentArc")
|
||||
.data(pieData.value)
|
||||
.enter()
|
||||
.append("g")
|
||||
.attr("class", "learningSegmentArc");
|
||||
|
||||
learningSequences
|
||||
.transition()
|
||||
.duration(1)
|
||||
.attr("fill", (d) => {
|
||||
return getColor(d);
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
learningSequences.append("path").attr("d", wedgeGenerator);
|
||||
}
|
||||
return svg;
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="svg-container h-full content-center">
|
||||
<pre hidden>{{ pieData }}</pre>
|
||||
<pre hidden>{{ render() }}</pre>
|
||||
<svg class="circle-visualization h-full">
|
||||
<circle :cx="width / 2" :cy="height / 2" :r="radius" :color="colors.gray[300]" />
|
||||
<circle :cx="width / 2" :cy="height / 2" :r="radius / 2.5" color="white" />
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import IconLogout from "@/components/icons/IconLogout.vue";
|
||||
import IconSettings from "@/components/icons/IconSettings.vue";
|
||||
import LearningPathCircle from "@/components/learningPath/LearningPathCircle.vue";
|
||||
import HorizontalBarChart from "@/components/ui/HorizontalBarChart.vue";
|
||||
import ItCheckbox from "@/components/ui/ItCheckbox.vue";
|
||||
import ItCheckboxGroup from "@/components/ui/ItCheckboxGroup.vue";
|
||||
|
|
@ -496,6 +497,26 @@ function log(data: any) {
|
|||
/>
|
||||
<HorizontalBarChart title="Frage X" text="Fragentext" :items="barChartItems" />
|
||||
</div>
|
||||
|
||||
<h2 class="mt-8 mb-8">LearningPathCircle</h2>
|
||||
|
||||
<LearningPathCircle
|
||||
class="h-48 w-48"
|
||||
:sectors="[
|
||||
{
|
||||
progress: 'finished',
|
||||
},
|
||||
{
|
||||
progress: 'finished',
|
||||
},
|
||||
{
|
||||
progress: 'in_progress',
|
||||
},
|
||||
{
|
||||
progress: 'done',
|
||||
},
|
||||
]"
|
||||
></LearningPathCircle>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue